clouddreamai-cicd-setup 1.0.1 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -52,16 +52,16 @@ class TemplateGenerator {
52
52
  /**
53
53
  * 生成 .gitlab-ci.yml 文件内容
54
54
  */
55
- generateGitLabCI(projectType, config, gitlabHost) {
56
- // 根据项目类型选择模板
57
- const templateName = this.getTemplateNameByType(projectType);
55
+ generateGitLabCI(projectType, config, gitlabHost, registryType) {
56
+ // 根据项目类型和 Registry 类型选择模板
57
+ const templateName = this.getTemplateNameByType(projectType, registryType);
58
58
  const templatePath = path.join(this.templatesDir, 'gitlab-ci', `${templateName}.yml`);
59
59
  // 读取模板
60
60
  const template = fs.readFileSync(templatePath, 'utf-8');
61
61
  // 准备替换变量
62
62
  const variables = {
63
63
  APP_NAME: config.name,
64
- DOCKER_IMAGE: config.dockerImage,
64
+ DOCKER_IMAGE: config.dockerImage || '',
65
65
  DEPLOY_DIR: config.deployDir,
66
66
  DEV_PORT: config.devPort.toString(),
67
67
  PROD_PORT: config.prodPort.toString(),
@@ -69,6 +69,7 @@ class TemplateGenerator {
69
69
  PROD_URL: config.prodUrl || `http://localhost:${config.prodPort}`,
70
70
  GITLAB_HOST: gitlabHost.replace(/^https?:\/\//, ''),
71
71
  LINT_COMMAND: this.getLintCommand(projectType),
72
+ REGISTRY_TYPE: registryType,
72
73
  };
73
74
  // 替换占位符
74
75
  return this.replacePlaceholders(template, variables);
@@ -84,6 +85,8 @@ class TemplateGenerator {
84
85
  DEPLOY_DIR: config.deployDir,
85
86
  DEV_PORT: config.devPort.toString(),
86
87
  PROD_PORT: config.prodPort.toString(),
88
+ DEV_DOMAIN: config.devDomain || '',
89
+ PROD_DOMAIN: config.prodDomain || '',
87
90
  };
88
91
  return this.replacePlaceholders(template, variables);
89
92
  }
@@ -98,10 +101,26 @@ class TemplateGenerator {
98
101
  };
99
102
  return this.replacePlaceholders(template, variables);
100
103
  }
104
+ /**
105
+ * 生成 docker-compose.yml 文件内容
106
+ */
107
+ generateDockerCompose(projectType, config) {
108
+ // 根据项目类型选择模板
109
+ const templateName = this.getDockerComposeTemplate(projectType);
110
+ const templatePath = path.join(this.templatesDir, 'docker-compose', `${templateName}.yml`);
111
+ // 读取模板
112
+ const template = fs.readFileSync(templatePath, 'utf-8');
113
+ // 准备替换变量
114
+ const variables = {
115
+ APP_NAME: config.name,
116
+ DOCKER_IMAGE: config.dockerImage || config.name,
117
+ };
118
+ return this.replacePlaceholders(template, variables);
119
+ }
101
120
  /**
102
121
  * 生成所有文件到指定目录
103
122
  */
104
- generateAll(outputDir, projectType, config, gitlabHost) {
123
+ async generateAll(outputDir, projectType, config, gitlabHost, registryType) {
105
124
  try {
106
125
  const files = [];
107
126
  // 确保输出目录存在
@@ -114,21 +133,48 @@ class TemplateGenerator {
114
133
  fs.mkdirSync(ciDir, { recursive: true });
115
134
  }
116
135
  // 生成 .gitlab-ci.yml
117
- const gitlabCIContent = this.generateGitLabCI(projectType, config, gitlabHost);
136
+ const gitlabCIContent = this.generateGitLabCI(projectType, config, gitlabHost, registryType);
118
137
  const gitlabCIPath = path.join(outputDir, '.gitlab-ci.yml');
119
138
  fs.writeFileSync(gitlabCIPath, gitlabCIContent, 'utf-8');
120
139
  files.push(gitlabCIPath);
140
+ // 生成 docker-compose.yml
141
+ const dockerComposePath = path.join(outputDir, 'docker-compose.yml');
142
+ if (fs.existsSync(dockerComposePath)) {
143
+ // 文件已存在,询问用户是否覆盖
144
+ const inquirer = (await Promise.resolve().then(() => __importStar(require('inquirer')))).default;
145
+ const { overwrite } = await inquirer.prompt([
146
+ {
147
+ type: 'confirm',
148
+ name: 'overwrite',
149
+ message: '检测到现有 docker-compose.yml,是否覆盖?(覆盖后请检查配置一致性)',
150
+ default: false,
151
+ },
152
+ ]);
153
+ if (overwrite) {
154
+ const dockerComposeContent = this.generateDockerCompose(projectType, config);
155
+ fs.writeFileSync(dockerComposePath, dockerComposeContent, 'utf-8');
156
+ files.push(dockerComposePath);
157
+ }
158
+ else {
159
+ console.log('⚠️ 跳过生成 docker-compose.yml,保留现有文件');
160
+ }
161
+ }
162
+ else {
163
+ const dockerComposeContent = this.generateDockerCompose(projectType, config);
164
+ fs.writeFileSync(dockerComposePath, dockerComposeContent, 'utf-8');
165
+ files.push(dockerComposePath);
166
+ }
121
167
  // 生成 ci/deploy.sh
122
168
  const deployScriptContent = this.generateDeployScript(config);
123
169
  const deployScriptPath = path.join(ciDir, 'deploy.sh');
124
- fs.writeFileSync(deployScriptContent, deployScriptPath, 'utf-8');
170
+ fs.writeFileSync(deployScriptPath, deployScriptContent, 'utf-8');
125
171
  // 添加执行权限
126
172
  fs.chmodSync(deployScriptPath, '755');
127
173
  files.push(deployScriptPath);
128
174
  // 生成 ci/generate-env.sh
129
175
  const envScriptContent = this.generateEnvScript(config);
130
176
  const envScriptPath = path.join(ciDir, 'generate-env.sh');
131
- fs.writeFileSync(envScriptContent, envScriptPath, 'utf-8');
177
+ fs.writeFileSync(envScriptPath, envScriptContent, 'utf-8');
132
178
  // 添加执行权限
133
179
  fs.chmodSync(envScriptPath, '755');
134
180
  files.push(envScriptPath);
@@ -155,16 +201,35 @@ class TemplateGenerator {
155
201
  return result;
156
202
  }
157
203
  /**
158
- * 根据项目类型获取模板名称
204
+ * 根据项目类型和 Registry 类型获取模板名称
205
+ */
206
+ getTemplateNameByType(projectType, registryType) {
207
+ // 基础模板映射
208
+ const baseTemplateMapping = {
209
+ nestjs: 'nestjs',
210
+ vue: 'vue',
211
+ react: 'vue', // React 使用和 Vue 相同的模板
212
+ node: 'nestjs', // 通用 Node.js 项目使用 NestJS 模板
213
+ };
214
+ const baseTemplate = baseTemplateMapping[projectType] || 'nestjs';
215
+ // 如果使用 GitLab Registry,使用专用模板
216
+ if (registryType === 'gitlab') {
217
+ return `${baseTemplate}-gitlab`;
218
+ }
219
+ // 其他类型(dockerhub, custom, none)使用标准模板
220
+ return baseTemplate;
221
+ }
222
+ /**
223
+ * 根据项目类型获取 docker-compose 模板名称
159
224
  */
160
- getTemplateNameByType(projectType) {
161
- const mapping = {
225
+ getDockerComposeTemplate(projectType) {
226
+ const templateMapping = {
162
227
  nestjs: 'nestjs',
163
228
  vue: 'vue',
164
229
  react: 'vue', // React 使用和 Vue 相同的模板
165
230
  node: 'nestjs', // 通用 Node.js 项目使用 NestJS 模板
166
231
  };
167
- return mapping[projectType] || 'nestjs';
232
+ return templateMapping[projectType] || 'nestjs';
168
233
  }
169
234
  /**
170
235
  * 根据项目类型获取 lint 命令
@@ -1 +1 @@
1
- {"version":3,"file":"template.js","sourceRoot":"","sources":["../../src/utils/template.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqMH,0DAEC;AArMD,uCAAyB;AACzB,2CAA6B;AAG7B;;GAEG;AACH,MAAa,iBAAiB;IAG5B;QACE,sDAAsD;QACtD,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,iBAAiB,CAAC,CAAC;IAC9D,CAAC;IAED;;OAEG;IACH,gBAAgB,CACd,WAAwB,EACxB,MAAqB,EACrB,UAAkB;QAElB,aAAa;QACb,MAAM,YAAY,GAAG,IAAI,CAAC,qBAAqB,CAAC,WAAW,CAAC,CAAC;QAC7D,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAC5B,IAAI,CAAC,YAAY,EACjB,WAAW,EACX,GAAG,YAAY,MAAM,CACtB,CAAC;QAEF,OAAO;QACP,MAAM,QAAQ,GAAG,EAAE,CAAC,YAAY,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;QAExD,SAAS;QACT,MAAM,SAAS,GAA2B;YACxC,QAAQ,EAAE,MAAM,CAAC,IAAI;YACrB,YAAY,EAAE,MAAM,CAAC,WAAW;YAChC,UAAU,EAAE,MAAM,CAAC,SAAS;YAC5B,QAAQ,EAAE,MAAM,CAAC,OAAO,CAAC,QAAQ,EAAE;YACnC,SAAS,EAAE,MAAM,CAAC,QAAQ,CAAC,QAAQ,EAAE;YACrC,OAAO,EAAE,MAAM,CAAC,MAAM,IAAI,oBAAoB,MAAM,CAAC,OAAO,EAAE;YAC9D,QAAQ,EAAE,MAAM,CAAC,OAAO,IAAI,oBAAoB,MAAM,CAAC,QAAQ,EAAE;YACjE,WAAW,EAAE,UAAU,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC;YACnD,YAAY,EAAE,IAAI,CAAC,cAAc,CAAC,WAAW,CAAC;SAC/C,CAAC;QAEF,QAAQ;QACR,OAAO,IAAI,CAAC,mBAAmB,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IACvD,CAAC;IAED;;OAEG;IACH,oBAAoB,CAAC,MAAqB;QACxC,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,SAAS,EAAE,WAAW,CAAC,CAAC;QAC1E,MAAM,QAAQ,GAAG,EAAE,CAAC,YAAY,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;QAExD,MAAM,SAAS,GAA2B;YACxC,QAAQ,EAAE,MAAM,CAAC,IAAI;YACrB,UAAU,EAAE,MAAM,CAAC,SAAS;YAC5B,QAAQ,EAAE,MAAM,CAAC,OAAO,CAAC,QAAQ,EAAE;YACnC,SAAS,EAAE,MAAM,CAAC,QAAQ,CAAC,QAAQ,EAAE;SACtC,CAAC;QAEF,OAAO,IAAI,CAAC,mBAAmB,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IACvD,CAAC;IAED;;OAEG;IACH,iBAAiB,CAAC,MAAqB;QACrC,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAC5B,IAAI,CAAC,YAAY,EACjB,SAAS,EACT,iBAAiB,CAClB,CAAC;QACF,MAAM,QAAQ,GAAG,EAAE,CAAC,YAAY,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;QAExD,MAAM,SAAS,GAA2B;YACxC,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,QAAQ,EAAE,EAAE,WAAW;SAClD,CAAC;QAEF,OAAO,IAAI,CAAC,mBAAmB,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IACvD,CAAC;IAED;;OAEG;IACH,WAAW,CACT,SAAiB,EACjB,WAAwB,EACxB,MAAqB,EACrB,UAAkB;QAElB,IAAI,CAAC;YACH,MAAM,KAAK,GAAa,EAAE,CAAC;YAE3B,WAAW;YACX,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;gBAC9B,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAC/C,CAAC;YAED,YAAY;YACZ,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;YACzC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC1B,EAAE,CAAC,SAAS,CAAC,KAAK,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAC3C,CAAC;YAED,oBAAoB;YACpB,MAAM,eAAe,GAAG,IAAI,CAAC,gBAAgB,CAC3C,WAAW,EACX,MAAM,EACN,UAAU,CACX,CAAC;YACF,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,gBAAgB,CAAC,CAAC;YAC5D,EAAE,CAAC,aAAa,CAAC,YAAY,EAAE,eAAe,EAAE,OAAO,CAAC,CAAC;YACzD,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YAEzB,kBAAkB;YAClB,MAAM,mBAAmB,GAAG,IAAI,CAAC,oBAAoB,CAAC,MAAM,CAAC,CAAC;YAC9D,MAAM,gBAAgB,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC;YACvD,EAAE,CAAC,aAAa,CAAC,mBAAmB,EAAE,gBAAgB,EAAE,OAAO,CAAC,CAAC;YACjE,SAAS;YACT,EAAE,CAAC,SAAS,CAAC,gBAAgB,EAAE,KAAK,CAAC,CAAC;YACtC,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;YAE7B,wBAAwB;YACxB,MAAM,gBAAgB,GAAG,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;YACxD,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,iBAAiB,CAAC,CAAC;YAC1D,EAAE,CAAC,aAAa,CAAC,gBAAgB,EAAE,aAAa,EAAE,OAAO,CAAC,CAAC;YAC3D,SAAS;YACT,EAAE,CAAC,SAAS,CAAC,aAAa,EAAE,KAAK,CAAC,CAAC;YACnC,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;YAE1B,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;QAClC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,KAAK,EAAE,EAAE;gBACT,KAAK,EAAE,WAAW,MAAM,CAAC,KAAK,CAAC,EAAE;aAClC,CAAC;QACJ,CAAC;IACH,CAAC;IAED;;;OAGG;IACK,mBAAmB,CACzB,QAAgB,EAChB,SAAiC;QAEjC,IAAI,MAAM,GAAG,QAAQ,CAAC;QAEtB,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;YACrD,MAAM,WAAW,GAAG,IAAI,MAAM,CAAC,SAAS,GAAG,QAAQ,EAAE,GAAG,CAAC,CAAC;YAC1D,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;QAC9C,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;;OAEG;IACK,qBAAqB,CAAC,WAAwB;QACpD,MAAM,OAAO,GAAgC;YAC3C,MAAM,EAAE,QAAQ;YAChB,GAAG,EAAE,KAAK;YACV,KAAK,EAAE,KAAK,EAAE,sBAAsB;YACpC,IAAI,EAAE,QAAQ,EAAE,4BAA4B;SAC7C,CAAC;QAEF,OAAO,OAAO,CAAC,WAAW,CAAC,IAAI,QAAQ,CAAC;IAC1C,CAAC;IAED;;OAEG;IACK,cAAc,CAAC,WAAwB;QAC7C,MAAM,QAAQ,GAAgC;YAC5C,MAAM,EAAE,oBAAoB;YAC5B,GAAG,EAAE,oBAAoB;YACzB,KAAK,EAAE,oBAAoB;YAC3B,IAAI,EAAE,cAAc;SACrB,CAAC;QAEF,OAAO,QAAQ,CAAC,WAAW,CAAC,IAAI,cAAc,CAAC;IACjD,CAAC;CACF;AAvLD,8CAuLC;AAED;;GAEG;AACH,SAAgB,uBAAuB;IACrC,OAAO,IAAI,iBAAiB,EAAE,CAAC;AACjC,CAAC"}
1
+ {"version":3,"file":"template.js","sourceRoot":"","sources":["../../src/utils/template.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clouddreamai-cicd-setup",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "CloudDreamAI GitLab CI/CD 自动配置工具 - 支持 NestJS/Vue/React 项目的一键 CI/CD 配置",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -0,0 +1,17 @@
1
+ version: '3.8'
2
+
3
+ services:
4
+ {{APP_NAME}}:
5
+ image: {{DOCKER_IMAGE}}:latest
6
+ container_name: {{APP_NAME}}
7
+ ports:
8
+ - "${APP_PORT}:${APP_PORT}"
9
+ env_file:
10
+ - .env
11
+ restart: unless-stopped
12
+ networks:
13
+ - app-network
14
+
15
+ networks:
16
+ app-network:
17
+ driver: bridge
@@ -0,0 +1,15 @@
1
+ version: '3.8'
2
+
3
+ services:
4
+ {{APP_NAME}}:
5
+ image: {{DOCKER_IMAGE}}:latest
6
+ container_name: {{APP_NAME}}
7
+ ports:
8
+ - "${APP_PORT}:${APP_PORT}"
9
+ restart: unless-stopped
10
+ networks:
11
+ - app-network
12
+
13
+ networks:
14
+ app-network:
15
+ driver: bridge
@@ -0,0 +1,170 @@
1
+ # GitLab CI/CD 配置文件 - NestJS 项目(GitLab Container Registry)
2
+ stages:
3
+ - lint
4
+ - build
5
+ - deploy
6
+
7
+ # 全局变量
8
+ variables:
9
+ DOCKER_DRIVER: overlay2
10
+ NODE_VERSION: "20"
11
+ APP_NAME: "{{APP_NAME}}"
12
+ GIT_STRATEGY: fetch
13
+ GIT_SSL_NO_VERIFY: "true"
14
+ DOCKER_PULL_POLICY: if-not-present
15
+ # 使用 GitLab Container Registry
16
+ DOCKER_IMAGE: $CI_REGISTRY_IMAGE
17
+
18
+ # 全局缓存配置
19
+ cache:
20
+ key: ${CI_COMMIT_REF_SLUG}-${CI_JOB_NAME}
21
+ paths:
22
+ - node_modules/
23
+ - .npm/
24
+
25
+ # 代码质量检查阶段
26
+ lint:
27
+ stage: lint
28
+ image:
29
+ name: node:20-alpine
30
+ pull_policy: if-not-present
31
+ cache:
32
+ key: ${CI_COMMIT_REF_SLUG}-node-modules
33
+ paths:
34
+ - node_modules/
35
+ - .npm/
36
+ policy: pull-push
37
+ before_script:
38
+ - apk add --no-cache git curl
39
+ - git --version
40
+ - echo "Setting up Git authentication..."
41
+ - git config --global http.sslVerify false
42
+ - git config --global url."https://oauth2:${GITLAB_ACCESS_TOKEN}@{{GITLAB_HOST}}".insteadOf "http://{{GITLAB_HOST}}"
43
+ - git config --global url."https://oauth2:${GITLAB_ACCESS_TOKEN}@{{GITLAB_HOST}}".insteadOf "https://{{GITLAB_HOST}}"
44
+ - npm ci --cache .npm --prefer-offline
45
+ script:
46
+ - {{LINT_COMMAND}}
47
+ only:
48
+ - merge_requests
49
+ - main
50
+ - develop
51
+
52
+ # 构建阶段(使用 GitLab Container Registry)
53
+ build:
54
+ stage: build
55
+ cache: {}
56
+ image:
57
+ name: docker:24.0.5
58
+ pull_policy: if-not-present
59
+ before_script:
60
+ - docker info
61
+ - echo "登录到 GitLab Container Registry..."
62
+ # 使用 GitLab CI 预定义变量登录
63
+ - echo "$CI_REGISTRY_PASSWORD" | docker login -u $CI_REGISTRY_USER --password-stdin $CI_REGISTRY
64
+ script:
65
+ - echo "尝试拉取最新镜像作为缓存..."
66
+ - docker pull $DOCKER_IMAGE:latest || true
67
+ - echo "构建Docker镜像..."
68
+ - docker build --build-arg BUILDKIT_INLINE_CACHE=1 --cache-from $DOCKER_IMAGE:latest -t $DOCKER_IMAGE:$CI_COMMIT_SHA -t $DOCKER_IMAGE:latest .
69
+ - echo "推送镜像到 GitLab Container Registry..."
70
+ - docker push $DOCKER_IMAGE:$CI_COMMIT_SHA
71
+ - docker push $DOCKER_IMAGE:latest
72
+ only:
73
+ - main
74
+ - develop
75
+
76
+ # 开发环境部署(develop 分支自动触发)
77
+ deploy_dev:
78
+ stage: deploy
79
+ image:
80
+ name: alpine:latest
81
+ pull_policy: if-not-present
82
+ variables:
83
+ ENV_TYPE: "development"
84
+ ENV_FILE_CONTENT: $DEV_ENV_FILE
85
+ DEPLOY_DIR: "{{DEPLOY_DIR}}"
86
+ APP_PORT: "{{DEV_PORT}}"
87
+ cache: {}
88
+ before_script:
89
+ - apk add --no-cache sshpass bash openssl rsync
90
+ - mkdir -p ~/.ssh
91
+ - chmod 700 ~/.ssh
92
+ # 使用密码认证,跳过 SSH 主机密钥检查
93
+ - echo "StrictHostKeyChecking no" >> ~/.ssh/config
94
+ - chmod 600 ~/.ssh/config
95
+ - echo "StrictHostKeyChecking no" >> ~/.ssh/config
96
+ - chmod 600 ~/.ssh/config
97
+ script:
98
+ - sshpass -p "$SSH_PASSWORD" ssh root@$TEST_SERVER_HOST "mkdir -p $DEPLOY_DIR"
99
+ - sshpass -p "$SSH_PASSWORD" ssh root@$TEST_SERVER_HOST "which rsync || apk add --no-cache rsync"
100
+ - sshpass -p "$SSH_PASSWORD" rsync -avz --delete --exclude='.git/' --exclude='node_modules/' . root@$TEST_SERVER_HOST:$DEPLOY_DIR/
101
+ # 传递 GitLab Registry 凭据到部署服务器
102
+ - sshpass -p "$SSH_PASSWORD" ssh root@$TEST_SERVER_HOST "export REGISTRY_URL='$CI_REGISTRY' && export REGISTRY_USERNAME='$CI_REGISTRY_USER' && export REGISTRY_PASSWORD='$CI_REGISTRY_PASSWORD' && export APP_PORT='$APP_PORT' && cd $DEPLOY_DIR && chmod +x ci/deploy.sh && bash ci/deploy.sh $ENV_TYPE gitlab"
103
+ environment:
104
+ name: development
105
+ url: {{DEV_URL}}
106
+ only:
107
+ - develop
108
+
109
+ # 生产环境部署(main 分支手动触发)
110
+ deploy_prod:
111
+ stage: deploy
112
+ image:
113
+ name: alpine:latest
114
+ pull_policy: if-not-present
115
+ variables:
116
+ ENV_TYPE: "production"
117
+ ENV_FILE_CONTENT: $PROD_ENV_FILE
118
+ DEPLOY_DIR: "{{DEPLOY_DIR}}"
119
+ APP_PORT: "{{PROD_PORT}}"
120
+ cache: {}
121
+ before_script:
122
+ - apk add --no-cache sshpass bash openssl rsync
123
+ - mkdir -p ~/.ssh
124
+ - chmod 700 ~/.ssh
125
+ # 使用密码认证,跳过 SSH 主机密钥检查
126
+ - echo "StrictHostKeyChecking no" >> ~/.ssh/config
127
+ - chmod 600 ~/.ssh/config
128
+ - echo "StrictHostKeyChecking no" >> ~/.ssh/config
129
+ - chmod 600 ~/.ssh/config
130
+ script:
131
+ - sshpass -p "$SSH_PASSWORD" ssh root@$PROD_SERVER_HOST "mkdir -p $DEPLOY_DIR"
132
+ - sshpass -p "$SSH_PASSWORD" ssh root@$PROD_SERVER_HOST "which rsync || apk add --no-cache rsync"
133
+ - sshpass -p "$SSH_PASSWORD" rsync -avz --delete --exclude='.git/' --exclude='node_modules/' . root@$PROD_SERVER_HOST:$DEPLOY_DIR/
134
+ # 传递 GitLab Registry 凭据到部署服务器
135
+ - sshpass -p "$SSH_PASSWORD" ssh root@$PROD_SERVER_HOST "export REGISTRY_URL='$CI_REGISTRY' && export REGISTRY_USERNAME='$CI_REGISTRY_USER' && export REGISTRY_PASSWORD='$CI_REGISTRY_PASSWORD' && export APP_PORT='$APP_PORT' && cd $DEPLOY_DIR && chmod +x ci/deploy.sh && bash ci/deploy.sh $ENV_TYPE gitlab"
136
+ environment:
137
+ name: production
138
+ url: {{PROD_URL}}
139
+ only:
140
+ - main
141
+ when: manual
142
+
143
+ # 回滚部署(可选)
144
+ rollback:
145
+ stage: deploy
146
+ image:
147
+ name: alpine:latest
148
+ pull_policy: if-not-present
149
+ variables:
150
+ DEPLOY_DIR: "{{DEPLOY_DIR}}"
151
+ APP_PORT: "{{PROD_PORT}}"
152
+ cache: {}
153
+ before_script:
154
+ - apk add --no-cache openssh-client openssl
155
+ - mkdir -p ~/.ssh
156
+ - chmod 700 ~/.ssh
157
+ # 使用密码认证,跳过 SSH 主机密钥检查
158
+ - echo "StrictHostKeyChecking no" >> ~/.ssh/config
159
+ - chmod 600 ~/.ssh/config
160
+ - chmod 644 ~/.ssh/known_hosts
161
+ script:
162
+ - sshpass -p "$SSH_PASSWORD" ssh root@$PROD_SERVER_HOST "cd $DEPLOY_DIR && export APP_PORT=$APP_PORT && docker-compose down"
163
+ - sshpass -p "$SSH_PASSWORD" ssh root@$PROD_SERVER_HOST "cd $DEPLOY_DIR && export APP_PORT=$APP_PORT && docker-compose pull"
164
+ - sshpass -p "$SSH_PASSWORD" ssh root@$PROD_SERVER_HOST "cd $DEPLOY_DIR && export APP_PORT=$APP_PORT && docker-compose up -d"
165
+ environment:
166
+ name: production
167
+ url: {{PROD_URL}}
168
+ when: manual
169
+ only:
170
+ - main
@@ -83,21 +83,18 @@ deploy_dev:
83
83
  ENV_FILE_CONTENT: $DEV_ENV_FILE
84
84
  DEPLOY_DIR: "{{DEPLOY_DIR}}"
85
85
  APP_PORT: "{{DEV_PORT}}"
86
+ REGISTRY_TYPE: "{{REGISTRY_TYPE}}"
86
87
  cache: {}
87
88
  before_script:
88
- - apk add --no-cache openssh-client docker-compose bash openssl rsync
89
- - eval $(ssh-agent -s)
89
+ - apk add --no-cache sshpass bash openssl rsync
90
90
  - mkdir -p ~/.ssh
91
91
  - chmod 700 ~/.ssh
92
- - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - 2>/dev/null || (echo "无法加载SSH密钥,尝试不同格式..." && echo "$SSH_PRIVATE_KEY" | base64 -d | ssh-add - 2>/dev/null || echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa && chmod 600 ~/.ssh/id_rsa && ssh-add ~/.ssh/id_rsa)
93
- - ssh-keyscan $TEST_SERVER_HOST >> ~/.ssh/known_hosts
94
92
  - chmod 644 ~/.ssh/known_hosts
95
93
  script:
96
- - ssh root@$TEST_SERVER_HOST "mkdir -p $DEPLOY_DIR"
97
- - ssh root@$TEST_SERVER_HOST "which rsync || apk add --no-cache rsync"
98
- - rsync -avz --delete --exclude='.git/' --exclude='node_modules/' . root@$TEST_SERVER_HOST:$DEPLOY_DIR/
99
- - ssh root@$TEST_SERVER_HOST "export DOCKER_HUB_USERNAME='$DOCKER_HUB_USERNAME' && export DOCKER_HUB_PASSWORD='$DOCKER_HUB_PASSWORD' && export APP_PORT='$APP_PORT' && cd $DEPLOY_DIR && chmod +x ci/deploy.sh && bash ci/deploy.sh $ENV_TYPE"
100
- - ssh root@$TEST_SERVER_HOST "sleep 30 && curl -f http://localhost:$APP_PORT/api || exit 1"
94
+ - sshpass -p "$SSH_PASSWORD" ssh root@$TEST_SERVER_HOST "mkdir -p $DEPLOY_DIR"
95
+ - sshpass -p "$SSH_PASSWORD" ssh root@$TEST_SERVER_HOST "which rsync || apk add --no-cache rsync"
96
+ - sshpass -p "$SSH_PASSWORD" rsync -avz --delete --exclude='.git/' --exclude='node_modules/' . root@$TEST_SERVER_HOST:$DEPLOY_DIR/
97
+ - sshpass -p "$SSH_PASSWORD" ssh root@$TEST_SERVER_HOST "export DOCKER_HUB_USERNAME='$DOCKER_HUB_USERNAME' && export DOCKER_HUB_PASSWORD='$DOCKER_HUB_PASSWORD' && export APP_PORT='$APP_PORT' && cd $DEPLOY_DIR && chmod +x ci/deploy.sh && bash ci/deploy.sh $ENV_TYPE $REGISTRY_TYPE"
101
98
  environment:
102
99
  name: development
103
100
  url: {{DEV_URL}}
@@ -115,21 +112,18 @@ deploy_prod:
115
112
  ENV_FILE_CONTENT: $PROD_ENV_FILE
116
113
  DEPLOY_DIR: "{{DEPLOY_DIR}}"
117
114
  APP_PORT: "{{PROD_PORT}}"
115
+ REGISTRY_TYPE: "{{REGISTRY_TYPE}}"
118
116
  cache: {}
119
117
  before_script:
120
- - apk add --no-cache openssh-client docker-compose bash openssl rsync
121
- - eval $(ssh-agent -s)
118
+ - apk add --no-cache sshpass bash openssl rsync
122
119
  - mkdir -p ~/.ssh
123
120
  - chmod 700 ~/.ssh
124
- - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - 2>/dev/null || (echo "无法加载SSH密钥,尝试不同格式..." && echo "$SSH_PRIVATE_KEY" | base64 -d | ssh-add - 2>/dev/null || echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa && chmod 600 ~/.ssh/id_rsa && ssh-add ~/.ssh/id_rsa)
125
- - ssh-keyscan $PROD_SERVER_HOST >> ~/.ssh/known_hosts
126
121
  - chmod 644 ~/.ssh/known_hosts
127
122
  script:
128
- - ssh root@$PROD_SERVER_HOST "mkdir -p $DEPLOY_DIR"
129
- - ssh root@$PROD_SERVER_HOST "which rsync || apk add --no-cache rsync"
130
- - rsync -avz --delete --exclude='.git/' --exclude='node_modules/' . root@$PROD_SERVER_HOST:$DEPLOY_DIR/
131
- - ssh root@$PROD_SERVER_HOST "export DOCKER_HUB_USERNAME='$DOCKER_HUB_USERNAME' && export DOCKER_HUB_PASSWORD='$DOCKER_HUB_PASSWORD' && export APP_PORT='$APP_PORT' && cd $DEPLOY_DIR && chmod +x ci/deploy.sh && bash ci/deploy.sh $ENV_TYPE"
132
- - ssh root@$PROD_SERVER_HOST "sleep 30 && curl -f http://localhost:$APP_PORT/api || exit 1"
123
+ - sshpass -p "$SSH_PASSWORD" ssh root@$PROD_SERVER_HOST "mkdir -p $DEPLOY_DIR"
124
+ - sshpass -p "$SSH_PASSWORD" ssh root@$PROD_SERVER_HOST "which rsync || apk add --no-cache rsync"
125
+ - sshpass -p "$SSH_PASSWORD" rsync -avz --delete --exclude='.git/' --exclude='node_modules/' . root@$PROD_SERVER_HOST:$DEPLOY_DIR/
126
+ - sshpass -p "$SSH_PASSWORD" ssh root@$PROD_SERVER_HOST "export DOCKER_HUB_USERNAME='$DOCKER_HUB_USERNAME' && export DOCKER_HUB_PASSWORD='$DOCKER_HUB_PASSWORD' && export APP_PORT='$APP_PORT' && cd $DEPLOY_DIR && chmod +x ci/deploy.sh && bash ci/deploy.sh $ENV_TYPE $REGISTRY_TYPE"
133
127
  environment:
134
128
  name: production
135
129
  url: {{PROD_URL}}
@@ -149,16 +143,13 @@ rollback:
149
143
  cache: {}
150
144
  before_script:
151
145
  - apk add --no-cache openssh-client openssl
152
- - eval $(ssh-agent -s)
153
146
  - mkdir -p ~/.ssh
154
147
  - chmod 700 ~/.ssh
155
- - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - 2>/dev/null || (echo "无法加载SSH密钥,尝试不同格式..." && echo "$SSH_PRIVATE_KEY" | base64 -d | ssh-add - 2>/dev/null || echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa && chmod 600 ~/.ssh/id_rsa && ssh-add ~/.ssh/id_rsa)
156
- - ssh-keyscan $PROD_SERVER_HOST >> ~/.ssh/known_hosts
157
148
  - chmod 644 ~/.ssh/known_hosts
158
149
  script:
159
- - ssh root@$PROD_SERVER_HOST "cd $DEPLOY_DIR && export APP_PORT=$APP_PORT && docker-compose down"
160
- - ssh root@$PROD_SERVER_HOST "cd $DEPLOY_DIR && export APP_PORT=$APP_PORT && docker-compose pull"
161
- - ssh root@$PROD_SERVER_HOST "cd $DEPLOY_DIR && export APP_PORT=$APP_PORT && docker-compose up -d"
150
+ - sshpass -p "$SSH_PASSWORD" ssh root@$PROD_SERVER_HOST "cd $DEPLOY_DIR && export APP_PORT=$APP_PORT && docker-compose down"
151
+ - sshpass -p "$SSH_PASSWORD" ssh root@$PROD_SERVER_HOST "cd $DEPLOY_DIR && export APP_PORT=$APP_PORT && docker-compose pull"
152
+ - sshpass -p "$SSH_PASSWORD" ssh root@$PROD_SERVER_HOST "cd $DEPLOY_DIR && export APP_PORT=$APP_PORT && docker-compose up -d"
162
153
  environment:
163
154
  name: production
164
155
  url: {{PROD_URL}}
@@ -0,0 +1,168 @@
1
+ # GitLab CI/CD 配置文件 - Vue/React 项目(GitLab Container Registry)
2
+ stages:
3
+ - lint
4
+ - build
5
+ - deploy
6
+
7
+ # 全局变量
8
+ variables:
9
+ DOCKER_DRIVER: overlay2
10
+ NODE_VERSION: "20"
11
+ APP_NAME: "{{APP_NAME}}"
12
+ GIT_STRATEGY: fetch
13
+ GIT_SSL_NO_VERIFY: "true"
14
+ DOCKER_PULL_POLICY: if-not-present
15
+ # 使用 GitLab Container Registry
16
+ DOCKER_IMAGE: $CI_REGISTRY_IMAGE
17
+
18
+ # 全局缓存配置
19
+ cache:
20
+ key: ${CI_COMMIT_REF_SLUG}-${CI_JOB_NAME}
21
+ paths:
22
+ - node_modules/
23
+ - .npm/
24
+
25
+ # 代码质量检查阶段
26
+ lint:
27
+ stage: lint
28
+ image:
29
+ name: node:20-alpine
30
+ pull_policy: if-not-present
31
+ cache:
32
+ key: ${CI_COMMIT_REF_SLUG}-node-modules
33
+ paths:
34
+ - node_modules/
35
+ - .npm/
36
+ policy: pull-push
37
+ before_script:
38
+ - apk add --no-cache git curl
39
+ - git --version
40
+ - echo "Setting up Git authentication..."
41
+ - git config --global http.sslVerify false
42
+ - git config --global url."https://oauth2:${GITLAB_ACCESS_TOKEN}@{{GITLAB_HOST}}".insteadOf "http://{{GITLAB_HOST}}"
43
+ - git config --global url."https://oauth2:${GITLAB_ACCESS_TOKEN}@{{GITLAB_HOST}}".insteadOf "https://{{GITLAB_HOST}}"
44
+ - npm ci --cache .npm --prefer-offline
45
+ script:
46
+ - {{LINT_COMMAND}}
47
+ only:
48
+ - merge_requests
49
+ - main
50
+ - develop
51
+
52
+ # 构建阶段(使用 GitLab Container Registry)
53
+ build:
54
+ stage: build
55
+ cache: {}
56
+ image:
57
+ name: docker:24.0.5
58
+ pull_policy: if-not-present
59
+ before_script:
60
+ - docker info
61
+ - echo "登录到 GitLab Container Registry..."
62
+ # 使用 GitLab CI 预定义变量登录
63
+ - echo "$CI_REGISTRY_PASSWORD" | docker login -u $CI_REGISTRY_USER --password-stdin $CI_REGISTRY
64
+ script:
65
+ - echo "尝试拉取最新镜像作为缓存..."
66
+ - docker pull $DOCKER_IMAGE:latest || true
67
+ - echo "构建Docker镜像..."
68
+ - docker build --build-arg BUILDKIT_INLINE_CACHE=1 --cache-from $DOCKER_IMAGE:latest -t $DOCKER_IMAGE:$CI_COMMIT_SHA -t $DOCKER_IMAGE:latest .
69
+ - echo "推送镜像到 GitLab Container Registry..."
70
+ - docker push $DOCKER_IMAGE:$CI_COMMIT_SHA
71
+ - docker push $DOCKER_IMAGE:latest
72
+ only:
73
+ - main
74
+ - develop
75
+
76
+ # 开发环境部署(develop 分支自动触发)
77
+ deploy_dev:
78
+ stage: deploy
79
+ image:
80
+ name: alpine:latest
81
+ pull_policy: if-not-present
82
+ variables:
83
+ ENV_TYPE: "development"
84
+ ENV_FILE_CONTENT: $DEV_ENV_FILE
85
+ DEPLOY_DIR: "{{DEPLOY_DIR}}"
86
+ APP_PORT: "{{DEV_PORT}}"
87
+ cache: {}
88
+ before_script:
89
+ - apk add --no-cache sshpass bash openssl rsync
90
+ - mkdir -p ~/.ssh
91
+ - chmod 700 ~/.ssh
92
+ - # 使用密码认证,跳过 SSH 主机密钥检查
93
+ - echo "StrictHostKeyChecking no" >> ~/.ssh/config
94
+ - chmod 600 ~/.ssh/config
95
+ - chmod 644 ~/.ssh/known_hosts
96
+ script:
97
+ - sshpass -p "$SSH_PASSWORD" ssh root@$TEST_SERVER_HOST "mkdir -p $DEPLOY_DIR"
98
+ - sshpass -p "$SSH_PASSWORD" ssh root@$TEST_SERVER_HOST "which rsync || apk add --no-cache rsync"
99
+ - sshpass -p "$SSH_PASSWORD" rsync -avz --delete --exclude='.git/' --exclude='node_modules/' . root@$TEST_SERVER_HOST:$DEPLOY_DIR/
100
+ # 传递 GitLab Registry 凭据到部署服务器
101
+ - sshpass -p "$SSH_PASSWORD" ssh root@$TEST_SERVER_HOST "export REGISTRY_URL='$CI_REGISTRY' && export REGISTRY_USERNAME='$CI_REGISTRY_USER' && export REGISTRY_PASSWORD='$CI_REGISTRY_PASSWORD' && export APP_PORT='$APP_PORT' && cd $DEPLOY_DIR && chmod +x ci/deploy.sh && bash ci/deploy.sh $ENV_TYPE gitlab"
102
+ environment:
103
+ name: development
104
+ url: {{DEV_URL}}
105
+ only:
106
+ - develop
107
+
108
+ # 生产环境部署(main 分支手动触发)
109
+ deploy_prod:
110
+ stage: deploy
111
+ image:
112
+ name: alpine:latest
113
+ pull_policy: if-not-present
114
+ variables:
115
+ ENV_TYPE: "production"
116
+ ENV_FILE_CONTENT: $PROD_ENV_FILE
117
+ DEPLOY_DIR: "{{DEPLOY_DIR}}"
118
+ APP_PORT: "{{PROD_PORT}}"
119
+ cache: {}
120
+ before_script:
121
+ - apk add --no-cache sshpass bash openssl rsync
122
+ - mkdir -p ~/.ssh
123
+ - chmod 700 ~/.ssh
124
+ - # 使用密码认证,跳过 SSH 主机密钥检查
125
+ - echo "StrictHostKeyChecking no" >> ~/.ssh/config
126
+ - chmod 600 ~/.ssh/config
127
+ - chmod 644 ~/.ssh/known_hosts
128
+ script:
129
+ - sshpass -p "$SSH_PASSWORD" ssh root@$PROD_SERVER_HOST "mkdir -p $DEPLOY_DIR"
130
+ - sshpass -p "$SSH_PASSWORD" ssh root@$PROD_SERVER_HOST "which rsync || apk add --no-cache rsync"
131
+ - sshpass -p "$SSH_PASSWORD" rsync -avz --delete --exclude='.git/' --exclude='node_modules/' . root@$PROD_SERVER_HOST:$DEPLOY_DIR/
132
+ # 传递 GitLab Registry 凭据到部署服务器
133
+ - sshpass -p "$SSH_PASSWORD" ssh root@$PROD_SERVER_HOST "export REGISTRY_URL='$CI_REGISTRY' && export REGISTRY_USERNAME='$CI_REGISTRY_USER' && export REGISTRY_PASSWORD='$CI_REGISTRY_PASSWORD' && export APP_PORT='$APP_PORT' && cd $DEPLOY_DIR && chmod +x ci/deploy.sh && bash ci/deploy.sh $ENV_TYPE gitlab"
134
+ environment:
135
+ name: production
136
+ url: {{PROD_URL}}
137
+ only:
138
+ - main
139
+ when: manual
140
+
141
+ # 回滚部署(可选)
142
+ rollback:
143
+ stage: deploy
144
+ image:
145
+ name: alpine:latest
146
+ pull_policy: if-not-present
147
+ variables:
148
+ DEPLOY_DIR: "{{DEPLOY_DIR}}"
149
+ APP_PORT: "{{PROD_PORT}}"
150
+ cache: {}
151
+ before_script:
152
+ - apk add --no-cache openssh-client openssl
153
+ - mkdir -p ~/.ssh
154
+ - chmod 700 ~/.ssh
155
+ - # 使用密码认证,跳过 SSH 主机密钥检查
156
+ - echo "StrictHostKeyChecking no" >> ~/.ssh/config
157
+ - chmod 600 ~/.ssh/config
158
+ - chmod 644 ~/.ssh/known_hosts
159
+ script:
160
+ - sshpass -p "$SSH_PASSWORD" ssh root@$PROD_SERVER_HOST "cd $DEPLOY_DIR && export APP_PORT=$APP_PORT && docker-compose down"
161
+ - sshpass -p "$SSH_PASSWORD" ssh root@$PROD_SERVER_HOST "cd $DEPLOY_DIR && export APP_PORT=$APP_PORT && docker-compose pull"
162
+ - sshpass -p "$SSH_PASSWORD" ssh root@$PROD_SERVER_HOST "cd $DEPLOY_DIR && export APP_PORT=$APP_PORT && docker-compose up -d"
163
+ environment:
164
+ name: production
165
+ url: {{PROD_URL}}
166
+ when: manual
167
+ only:
168
+ - main