Commit d76745a3 authored by liguangyu06's avatar liguangyu06
Browse files

Run Jenkins pipeline with Windows cmd instead of bash.

parent cb16cb6c
...@@ -34,6 +34,15 @@ def sendReportEmail() { ...@@ -34,6 +34,15 @@ def sendReportEmail() {
} }
} }
/** Windows 节点用 bat(cmd),不用 Git Bash。 */
def runCi() {
if (isUnix()) {
sh 'node scripts/run-ci.mjs --suite="$SUITE" --shards="$SHARDS"'
} else {
bat 'node scripts\\run-ci.mjs --suite=%SUITE% --shards=%SHARDS%'
}
}
pipeline { pipeline {
agent any agent any
...@@ -70,45 +79,70 @@ pipeline { ...@@ -70,45 +79,70 @@ pipeline {
PW_RETRIES = "${params.RETRIES}" PW_RETRIES = "${params.RETRIES}"
RUN_FULL = "${params.RUN_FULL ? '1' : '0'}" RUN_FULL = "${params.RUN_FULL ? '1' : '0'}"
PLAYWRIGHT_BROWSERS_PATH = "${WORKSPACE}/.playwright-browsers" PLAYWRIGHT_BROWSERS_PATH = "${WORKSPACE}/.playwright-browsers"
// 在 Jenkins 凭据里创建 Username/Password,ID 与此一致;或在任务里覆盖
SPD_CREDENTIALS_ID = "${env.SPD_CREDENTIALS_ID ?: 'spd-ui-test-account'}" SPD_CREDENTIALS_ID = "${env.SPD_CREDENTIALS_ID ?: 'spd-ui-test-account'}"
} }
stages { stages {
stage('Prepare') { stage('Prepare') {
steps { steps {
sh ''' script {
set -e if (isUnix()) {
node -v sh '''
npm -v set -e
test "$(node -p "process.versions.node.split('.')[0]")" -ge 20 node -v
''' npm -v
node -e "if (parseInt(process.versions.node.split('.')[0], 10) < 20) process.exit(1)"
'''
} else {
bat '''
node -v
npm -v
node -e "if (parseInt(process.versions.node.split('.')[0], 10) < 20) process.exit(1)"
'''
}
}
} }
} }
stage('Install') { stage('Install') {
steps { steps {
sh ''' script {
set -e if (isUnix()) {
npm ci sh '''
npx playwright install-deps chromium || true set -e
npm run playwright:install npm ci
''' npx playwright install-deps chromium || true
npm run playwright:install
'''
} else {
bat '''
call npm ci
if errorlevel 1 exit /b 1
call npm run playwright:install
if errorlevel 1 exit /b 1
'''
}
}
} }
} }
stage('Typecheck') { stage('Typecheck') {
steps { steps {
sh 'npm run typecheck' script {
if (isUnix()) {
sh 'npm run typecheck'
} else {
bat 'call npm run typecheck'
}
}
} }
} }
stage('Test') { stage('Test') {
steps { steps {
script { script {
// 已注入账号,或 Agent 上有 .env 时不强制绑凭据
if (env.TEST_USER?.trim() || fileExists('.env')) { if (env.TEST_USER?.trim() || fileExists('.env')) {
sh 'node scripts/run-ci.mjs --suite="$SUITE" --shards="$SHARDS"' runCi()
} else { } else {
withCredentials([ withCredentials([
usernamePassword( usernamePassword(
...@@ -117,7 +151,7 @@ pipeline { ...@@ -117,7 +151,7 @@ pipeline {
passwordVariable: 'TEST_PASS' passwordVariable: 'TEST_PASS'
) )
]) { ]) {
sh 'node scripts/run-ci.mjs --suite="$SUITE" --shards="$SHARDS"' runCi()
} }
} }
} }
...@@ -131,10 +165,10 @@ pipeline { ...@@ -131,10 +165,10 @@ pipeline {
archiveArtifacts artifacts: 'reports/latest/**, reports/ci-*/summary.json, reports/ci-*/meta.txt', allowEmptyArchive: true, fingerprint: true archiveArtifacts artifacts: 'reports/latest/**, reports/ci-*/summary.json, reports/ci-*/meta.txt', allowEmptyArchive: true, fingerprint: true
script { script {
if (fileExists('reports/latest/summary.json')) { if (fileExists('reports/latest/summary.json')) {
def desc = sh( def descScript = 'node -e "var s=require(\'./reports/latest/summary.json\'); var c=s.counts||{}; console.log(s.suite+\' shards=\'+s.shards+\' workers=\'+s.workers+\' fail=\'+(c.failures||0)+\'/\'+(c.tests||0))"'
script: 'node -p "const s=require(\'./reports/latest/summary.json\'); const c=s.counts||{}; `${s.suite} shards=${s.shards} workers=${s.workers} fail=${c.failures||0}/${c.tests||0}`"', def desc = isUnix()
returnStdout: true, ? sh(script: descScript, returnStdout: true).trim()
).trim() : bat(script: "@echo off\n${descScript}", returnStdout: true).trim()
currentBuild.description = desc currentBuild.description = desc
} }
} }
...@@ -165,7 +199,11 @@ pipeline { ...@@ -165,7 +199,11 @@ pipeline {
} }
} }
withEnv(["BUILD_RESULT=${currentBuild.currentResult}"]) { withEnv(["BUILD_RESULT=${currentBuild.currentResult}"]) {
sh 'node scripts/ci-email-summary.mjs || true' if (isUnix()) {
sh 'node scripts/ci-email-summary.mjs || true'
} else {
bat returnStatus: true, script: 'node scripts\\ci-email-summary.mjs'
}
} }
sendReportEmail() sendReportEmail()
} }
......
...@@ -36,9 +36,9 @@ Chromium 缓存在项目内 `.playwright-browsers/`(已 gitignore),与 `@p ...@@ -36,9 +36,9 @@ Chromium 缓存在项目内 `.playwright-browsers/`(已 gitignore),与 `@p
产物在 `reports/latest/`:Playwright HTML、`junit.xml`、Allure results、`summary.json`。定时跑完会发 **HTML 摘要邮件**(失败列表 + Jenkins 报告链接),不要把完整报告当附件。收件人配 `REPORT_EMAIL_TO`,SMTP 与任务拆分见 [docs/jenkins.md](docs/jenkins.md) 产物在 `reports/latest/`:Playwright HTML、`junit.xml`、Allure results、`summary.json`。定时跑完会发 **HTML 摘要邮件**(失败列表 + Jenkins 报告链接),不要把完整报告当附件。收件人配 `REPORT_EMAIL_TO`,SMTP 与任务拆分见 [docs/jenkins.md](docs/jenkins.md)
Jenkins:仓库根目录 `Jenkinsfile`,配置说明[docs/jenkins.md](docs/jenkins.md)。上 Git 后建 Pipeline from SCM,凭据 ID 默认 `spd-ui-test-account` Jenkins:仓库根目录 `Jenkinsfile`。逐步配置[docs/Jenkins配置指南.md](docs/Jenkins配置指南.md),套件与并行约定见 [docs/jenkins.md](docs/jenkins.md)凭据 ID 默认 `spd-ui-test-account`
GitLab`.gitlab-ci.yml` 对 MR/分支跑 `npm run typecheck`。冒烟需在 GitLab CI/CD Variables 配置 `TEST_USER` / `TEST_PASS`(可选 `BASE_URL`),用 Web 流水线或定时任务触发,不要把 nightly 绑在每次推送上 GitLab 只作代码仓库;定时执行走 Jenkins,不必配 GitLab CI 变量
本地按页调试仍可用 `npm run test:page -- warn-low`,或原来的 `npm run test:warn-low` 本地按页调试仍可用 `npm run test:page -- warn-low`,或原来的 `npm run test:warn-low`
......
# Jenkins 怎么配(Windows,不用 bash)
不要选「自由风格」里的 **Execute Windows batch command** 手写一长串命令。
**Pipeline**,它会自动读仓库里的 `Jenkinsfile`。流水线已改成 **Windows 的 cmd**,不用 Git Bash。
首页原来的 `spd3.0`、电解任务不要动。只新建 `spd-ui-smoke`
---
## 第一步:这台电脑要能运行 node
cmd 里执行:
```bat
node -v
```
要看到 `v20` 开头。没有就先装 Node.js 20,装完:
```bat
net stop Jenkins
net start Jenkins
```
再新开一个 cmd,再执行 `node -v`
拉代码还需要 Git(只要 `git.exe`,不要配 bash):
```bat
git --version
```
没有就装 Git for Windows,一直下一步即可。
---
## 第二步:装插件
**Manage Jenkins****Plugins****Available plugins**,搜索并勾选:
- Pipeline
- Git
- Credentials Binding
- JUnit
- HTML Publisher
- Email Extension
**Install**,勾选装完重启,再登录。
---
## 第三步:加两个凭据
**Manage Jenkins****Credentials****System****Global credentials****Add Credentials**
**第一个(拉 GitLab 代码)**
- 类型:Username with password
- 用户名 / 密码:GitLab 账号
- ID:`gitlab-spd-ui`
**第二个(登录 SPD)**
- 类型:Username with password
- 用户名 / 密码:SPD 测试账号
- ID 必须是:`spd-ui-test-account`
---
## 第四步:新建 Pipeline 任务
1. **新建 Item**
2. 名字:`spd-ui-smoke`
3.**Pipeline**(不要选自由风格软件项目)
4. **OK**
拉到最下面 **Pipeline**
- Definition:选 **Pipeline script from SCM**
- SCM:选 **Git**
- Repository URL:`http://10.17.65.20/gitlab/test/autotest-playwright-web-spd.git`
- Credentials:选 `gitlab-spd-ui`
- Branch:`*/main`
- Script Path:`Jenkinsfile`
**Save**。点 **Build Now**。点 `#1`**Console Output** 等它跑完。
跑完能打开 **Playwright Report** 就算成功。
---
## 出错对照
| 控制台出现 | 原因 |
|------------|------|
| node 不是内部或外部命令 | 没装 Node,或装完没重启 Jenkins 服务 |
| git 不是内部命令 | 没装 Git |
| Authentication failed | GitLab 凭据错了 |
| spd-ui-test-account not found | 第二个凭据 ID 写错了 |
不要在「自由风格 → Execute Windows batch command」里贴命令。那种方式和仓库 `Jenkinsfile` 不是同一条路。
# Jenkins 定时执行与报告 # Jenkins 定时执行与报告
**从零配置(Windows 服务、插件、凭据、三个定时任务、邮件、故障)见 [Jenkins配置指南.md](./Jenkins配置指南.md)。**
仓库入口是 `node scripts/run-ci.mjs`,套件清单在 `config/suites.config.mjs`。不要再把 `package.json` 里上百条页面脚本配进 Jenkins。 仓库入口是 `node scripts/run-ci.mjs`,套件清单在 `config/suites.config.mjs`。不要再把 `package.json` 里上百条页面脚本配进 Jenkins。
## 推荐任务拆分 ## 推荐任务拆分
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment