[monorepo] refactor retries in Jenkins to step level (#29008)

* [monorepo] refactor retries in Jenkins to step level

Change the junit reports to use step specific file names. The [hash]
template option was neat in getting unique file names, but results in
duplicate test reports on retry.

* [patches] add support for .cjs config files for mocha-multi-reporters

GitOrigin-RevId: 3a749441470b5ba633e71319589606cfbe860952
This commit is contained in:
Jakob Ackermann
2025-10-10 14:06:39 +02:00
committed by Copybot
parent f0b2466f93
commit d7623b576f
14 changed files with 163 additions and 69 deletions

View File

@@ -0,0 +1,13 @@
diff --git a/node_modules/cypress-multi-reporters/lib/MultiReporters.js b/node_modules/cypress-multi-reporters/lib/MultiReporters.js
index 98dc4ef..b2a97bf 100644
--- a/node_modules/cypress-multi-reporters/lib/MultiReporters.js
+++ b/node_modules/cypress-multi-reporters/lib/MultiReporters.js
@@ -160,7 +160,7 @@ MultiReporters.prototype.getCustomOptions = function (options) {
debug('options file (custom)', customOptionsFile);
try {
- if ('.js' === path.extname(customOptionsFile)) {
+ if (['.js', '.cjs'].includes(path.extname(customOptionsFile))) {
customOptions = require(customOptionsFile);
}
else {

View File

@@ -0,0 +1,13 @@
diff --git a/node_modules/mocha-multi-reporters/lib/MultiReporters.js b/node_modules/mocha-multi-reporters/lib/MultiReporters.js
index d61e019..e7a9515 100644
--- a/node_modules/mocha-multi-reporters/lib/MultiReporters.js
+++ b/node_modules/mocha-multi-reporters/lib/MultiReporters.js
@@ -153,7 +153,7 @@ MultiReporters.prototype.getCustomOptions = function (options) {
debug('options file (custom)', customOptionsFile);
try {
- if ('.js' === path.extname(customOptionsFile)) {
+ if (['.js', '.cjs'].includes(path.extname(customOptionsFile))) {
customOptions = require(customOptionsFile);
}
else {

View File

@@ -23,7 +23,6 @@ pipeline {
timestamps() timestamps()
// Abort build after hitting first failure. // Abort build after hitting first failure.
parallelsAlwaysFailFast() parallelsAlwaysFailFast()
retry(3)
timeout(time: 15, unit: 'MINUTES') timeout(time: 15, unit: 'MINUTES')
} }
environment { environment {
@@ -64,7 +63,9 @@ pipeline {
parallel { parallel {
stage('Install deps') { stage('Install deps') {
steps { steps {
sh 'make monorepo_setup' retry(count: 3) {
sh 'make monorepo_setup'
}
script { script {
job_npm_install_done = true job_npm_install_done = true
} }
@@ -103,8 +104,12 @@ pipeline {
} }
} }
dir('copybara/public/repo/server-ce') { dir('copybara/public/repo/server-ce') {
sh 'make build-base' retry(count: 3) {
sh 'make build-community' sh 'make build-base'
}
retry(count: 3) {
sh 'make build-community'
}
} }
script { script {
job_server_ce_build_done = true job_server_ce_build_done = true
@@ -135,7 +140,9 @@ pipeline {
} }
} }
dir('server-pro') { dir('server-pro') {
sh 'make build-ci' retry(count: 3) {
sh 'make build-ci'
}
} }
script { script {
job_server_pro_build_done = true job_server_pro_build_done = true
@@ -186,7 +193,9 @@ pipeline {
} }
} }
dir('server-ce/test') { dir('server-ce/test') {
sh 'make test-e2e' retry(count: 3) {
sh 'make test-e2e'
}
} }
} }
} }
@@ -202,7 +211,9 @@ pipeline {
} }
} }
dir('server-ce/test') { dir('server-ce/test') {
sh 'make test-e2e' retry(count: 3) {
sh 'make test-e2e'
}
} }
} }
} }
@@ -218,7 +229,9 @@ pipeline {
} }
} }
dir('server-ce/test') { dir('server-ce/test') {
sh 'make test-e2e' retry(count: 3) {
sh 'make test-e2e'
}
} }
} }
} }
@@ -234,7 +247,9 @@ pipeline {
} }
} }
dir('server-ce/test') { dir('server-ce/test') {
sh 'make test-e2e' retry(count: 3) {
sh 'make test-e2e'
}
} }
} }
} }
@@ -250,7 +265,9 @@ pipeline {
} }
} }
dir('server-ce/test') { dir('server-ce/test') {
sh 'make test-e2e' retry(count: 3) {
sh 'make test-e2e'
}
} }
} }
} }
@@ -266,7 +283,9 @@ pipeline {
} }
} }
dir('server-ce/test') { dir('server-ce/test') {
sh 'make test-e2e' retry(count: 3) {
sh 'make test-e2e'
}
} }
} }
} }
@@ -282,7 +301,9 @@ pipeline {
} }
} }
dir('server-ce/test') { dir('server-ce/test') {
sh 'make test-e2e' retry(count: 3) {
sh 'make test-e2e'
}
} }
} }
} }
@@ -298,7 +319,9 @@ pipeline {
} }
} }
dir('server-ce/test') { dir('server-ce/test') {
sh 'make test-e2e' retry(count: 3) {
sh 'make test-e2e'
}
} }
} }
} }
@@ -314,7 +337,9 @@ pipeline {
} }
} }
dir('server-ce/test') { dir('server-ce/test') {
sh 'make test-e2e' retry(count: 3) {
sh 'make test-e2e'
}
} }
} }
} }

View File

@@ -38,7 +38,7 @@ if (process.env.CI) {
reporterOptions = { reporterOptions = {
reporter: `${process.env.MONOREPO}/node_modules/cypress-multi-reporters`, reporter: `${process.env.MONOREPO}/node_modules/cypress-multi-reporters`,
reporterOptions: { reporterOptions: {
configFile: 'cypress/cypress-multi-reporters.json', configFile: 'cypress/cypress-multi-reporters.cjs',
}, },
} }
} }

View File

@@ -0,0 +1,10 @@
module.exports = {
reporterEnabled: 'spec, mocha-junit-reporter',
mochaJunitReporterReporterOptions: {
mochaFile: `cypress-reports/junit-${process.env.CYPRESS_SHARD}-[suiteFilename].xml`,
includePending: true,
jenkinsMode: true,
jenkinsClassnamePrefix: 'Server Pro E2E tests',
rootSuiteTitle: 'Server Pro E2E tests',
},
}

View File

@@ -1,10 +0,0 @@
{
"reporterEnabled": "spec, mocha-junit-reporter",
"mochaJunitReporterReporterOptions": {
"mochaFile": "cypress-reports/junit-[hash]-[suiteFilename].xml",
"includePending": true,
"useFullSuiteTitle": true,
"jenkinsMode": true,
"rootSuiteTitle": "Server Pro E2E tests"
}
}

View File

@@ -7,7 +7,6 @@ pipeline {
} }
options { options {
timestamps() timestamps()
parallelsAlwaysFailFast()
timeout(time: 15, unit: 'MINUTES') timeout(time: 15, unit: 'MINUTES')
} }
environment { environment {
@@ -23,15 +22,12 @@ pipeline {
stage('Build') { stage('Build') {
steps { steps {
dir('services/git-bridge') { dir('services/git-bridge') {
sh 'make docker_build_base' retry(count: 3) {
sh 'make docker_build_base'
}
} }
} }
} }
stage('Install monorepo') {
steps {
sh 'make monorepo_setup'
}
}
} }
} }
stage('Stage 2') { stage('Stage 2') {
@@ -39,7 +35,9 @@ pipeline {
stage('Build production and push') { stage('Build production and push') {
steps { steps {
dir('services/git-bridge') { dir('services/git-bridge') {
sh 'make docker_build' retry(count: 3) {
sh 'make docker_build'
}
sh 'make push_branch' sh 'make push_branch'
} }
} }
@@ -51,15 +49,12 @@ pipeline {
} }
} }
} }
stage('Format Jenkinsfile') {
steps {
sh 'bin/run monorepo npm run format:jenkins'
}
}
stage('Test') { stage('Test') {
steps { steps {
dir('services/git-bridge') { dir('services/git-bridge') {
sh 'make docker_test' retry(count: 3) {
sh 'make docker_test'
}
} }
} }
} }

View File

@@ -10,7 +10,6 @@ pipeline {
options { options {
// Print timestamp next to each log line. // Print timestamp next to each log line.
timestamps() timestamps()
retry(3)
timeout(time: 15, unit: 'MINUTES') timeout(time: 15, unit: 'MINUTES')
} }
environment { environment {
@@ -20,7 +19,9 @@ pipeline {
stages { stages {
stage('Install') { stage('Install') {
steps { steps {
sh 'make install' retry(count: 3) {
sh 'make monorepo_setup'
}
} }
} }
stage('Build') { stage('Build') {

View File

@@ -10,7 +10,6 @@ pipeline {
options { options {
// Print timestamp next to each log line. // Print timestamp next to each log line.
timestamps() timestamps()
retry(3)
timeout(time: 15, unit: 'MINUTES') timeout(time: 15, unit: 'MINUTES')
} }
environment { environment {
@@ -42,8 +41,12 @@ pipeline {
steps { steps {
dir('services/web') { dir('services/web') {
sh 'bin/copy_external_pages' sh 'bin/copy_external_pages'
sh 'make build_deps' retry(count: 3) {
sh 'make build_dev' sh 'make build_deps'
}
retry(count: 3) {
sh 'make build_dev'
}
sh 'make build_test_frontend_ct' sh 'make build_test_frontend_ct'
} }
} }
@@ -89,77 +92,99 @@ pipeline {
stage('Acceptance SaaS') { stage('Acceptance SaaS') {
steps { steps {
dir('services/web') { dir('services/web') {
sh 'make test_acceptance_app_saas' retry(count: 3) {
sh 'make test_acceptance_app_saas'
}
} }
} }
} }
stage('Acceptance Server CE') { stage('Acceptance Server CE') {
steps { steps {
dir('services/web') { dir('services/web') {
sh "make test_acceptance_app_server_ce" retry(count: 3) {
sh "make test_acceptance_app_server_ce"
}
} }
} }
} }
stage('Acceptance Server Pro') { stage('Acceptance Server Pro') {
steps { steps {
dir('services/web') { dir('services/web') {
sh "make test_acceptance_app_server_pro" retry(count: 3) {
sh "make test_acceptance_app_server_pro"
}
} }
} }
} }
stage('test_acceptance_modules_merged_saas_1') { stage('test_acceptance_modules_merged_saas_1') {
steps { steps {
dir('services/web') { dir('services/web') {
sh "make test_acceptance_modules_merged_saas_1" retry(count: 3) {
sh "make test_acceptance_modules_merged_saas_1"
}
} }
} }
} }
stage('test_acceptance_modules_merged_saas_2') { stage('test_acceptance_modules_merged_saas_2') {
steps { steps {
dir('services/web') { dir('services/web') {
sh "make test_acceptance_modules_merged_saas_2" retry(count: 3) {
sh "make test_acceptance_modules_merged_saas_2"
}
} }
} }
} }
stage('test_acceptance_modules_merged_saas_3') { stage('test_acceptance_modules_merged_saas_3') {
steps { steps {
dir('services/web') { dir('services/web') {
sh "make test_acceptance_modules_merged_saas_3" retry(count: 3) {
sh "make test_acceptance_modules_merged_saas_3"
}
} }
} }
} }
stage('test_acceptance_modules_merged_saas_4') { stage('test_acceptance_modules_merged_saas_4') {
steps { steps {
dir('services/web') { dir('services/web') {
sh "make test_acceptance_modules_merged_saas_4" retry(count: 3) {
sh "make test_acceptance_modules_merged_saas_4"
}
} }
} }
} }
stage('test_acceptance_modules_merged_server_ce') { stage('test_acceptance_modules_merged_server_ce') {
steps { steps {
dir('services/web') { dir('services/web') {
sh "make test_acceptance_modules_merged_server_ce" retry(count: 3) {
sh "make test_acceptance_modules_merged_server_ce"
}
} }
} }
} }
stage('test_acceptance_modules_merged_server_pro') { stage('test_acceptance_modules_merged_server_pro') {
steps { steps {
dir('services/web') { dir('services/web') {
sh "make test_acceptance_modules_merged_server_pro" retry(count: 3) {
sh "make test_acceptance_modules_merged_server_pro"
}
} }
} }
} }
stage('test_frontend') { stage('test_frontend') {
steps { steps {
dir('services/web') { dir('services/web') {
sh "make test_frontend" retry(count: 3) {
sh "make test_frontend"
}
} }
} }
} }
stage('test_writefull') { stage('test_writefull') {
steps { steps {
dir('services/web') { dir('services/web') {
sh "make test_writefull" retry(count: 3) {
sh "make test_writefull"
}
} }
} }
} }
@@ -169,7 +194,9 @@ pipeline {
} }
steps { steps {
dir('services/web') { dir('services/web') {
sh "make test_frontend_ct_core_other" retry(count: 3) {
sh "make test_frontend_ct_core_other"
}
} }
} }
} }
@@ -179,7 +206,9 @@ pipeline {
} }
steps { steps {
dir('services/web') { dir('services/web') {
sh "make test_frontend_ct_core_features" retry(count: 3) {
sh "make test_frontend_ct_core_features"
}
} }
} }
} }
@@ -189,35 +218,45 @@ pipeline {
} }
steps { steps {
dir('services/web') { dir('services/web') {
sh "make test_frontend_ct_modules" retry(count: 3) {
sh "make test_frontend_ct_modules"
}
} }
} }
} }
stage('test_frontend_ct_editor_visual') { stage('test_frontend_ct_editor_visual') {
steps { steps {
dir('services/web') { dir('services/web') {
sh "make test_frontend_ct_editor_visual" retry(count: 3) {
sh "make test_frontend_ct_editor_visual"
}
} }
} }
} }
stage('test_frontend_ct_editor_other') { stage('test_frontend_ct_editor_other') {
steps { steps {
dir('services/web') { dir('services/web') {
sh "make test_frontend_ct_editor_other" retry(count: 3) {
sh "make test_frontend_ct_editor_other"
}
} }
} }
} }
stage('Test Unit ESM') { stage('Test Unit ESM') {
steps { steps {
dir('services/web') { dir('services/web') {
sh "make test_unit_esm" retry(count: 3) {
sh "make test_unit_esm"
}
} }
} }
} }
stage('Test Unit Mocha') { stage('Test Unit Mocha') {
steps { steps {
dir('services/web') { dir('services/web') {
sh "make test_unit_mocha" retry(count: 3) {
sh "make test_unit_mocha"
}
} }
} }
} }
@@ -225,7 +264,7 @@ pipeline {
stages { stages {
stage('Wait a bit to give tests all the CPU capacity') { stage('Wait a bit to give tests all the CPU capacity') {
steps { steps {
sh 'sleep 120' sh 'sleep 90'
} }
} }
stage('Build Webpack') { stage('Build Webpack') {
@@ -246,7 +285,9 @@ pipeline {
steps { steps {
dir('services/web') { dir('services/web') {
sh 'make tar' sh 'make tar'
sh 'bin/cdn_upload' retry(count: 3) {
sh 'bin/cdn_upload'
}
} }
} }
} }
@@ -261,7 +302,9 @@ pipeline {
steps { steps {
dir('services/web') { dir('services/web') {
sh 'gcloud secrets versions access latest --secret=web-sentryclirc > .sentryclirc' sh 'gcloud secrets versions access latest --secret=web-sentryclirc > .sentryclirc'
sh 'make sentry_upload' retry(count: 3) {
sh 'make sentry_upload'
}
} }
} }
post { post {
@@ -286,7 +329,7 @@ pipeline {
} }
post { post {
always { always {
junit checksName: 'Web test results', testResults: 'services/web/data/reports/junit-*.xml' junit checksName: 'Web test results', testResults: 'services/web/data/reports/junit-*.xml,services/web/data/reports/junit-*/**/*.xml'
} }
// Ensure tear down of test containers, then run general Jenkins VM cleanup. // Ensure tear down of test containers, then run general Jenkins VM cleanup.
cleanup { cleanup {

View File

@@ -1,5 +1,5 @@
BUILD_DIR_NAME ?= web BUILD_DIR_NAME ?= web
MODULE_NAME := $(notdir $(shell pwd)) export MODULE_NAME := $(notdir $(shell pwd))
MODULE_DIR := modules/$(MODULE_NAME) MODULE_DIR := modules/$(MODULE_NAME)
PROJECT_NAME = web PROJECT_NAME = web

View File

@@ -1,7 +1,7 @@
{ {
"reporterEnabled": "spec, mocha-junit-reporter", "reporterEnabled": "spec, mocha-junit-reporter",
"mochaJunitReporterReporterOptions": { "mochaJunitReporterReporterOptions": {
"mochaFile": "data/reports/junit-cypress-[hash].xml", "mochaFile": "data/reports/junit-cypress-[suiteFilename].xml",
"includePending": true, "includePending": true,
"jenkinsMode": true, "jenkinsMode": true,
"jenkinsClassnamePrefix": "Web Cypress tests", "jenkinsClassnamePrefix": "Web Cypress tests",

View File

@@ -20,6 +20,7 @@ services:
env_file: docker-compose.common.env env_file: docker-compose.common.env
environment: environment:
CI: CI:
MODULE_NAME:
MOCHA_ROOT_SUITE_NAME: MOCHA_ROOT_SUITE_NAME:
BASE_CONFIG: BASE_CONFIG:
OVERLEAF_CONFIG: OVERLEAF_CONFIG:
@@ -42,6 +43,7 @@ services:
env_file: docker-compose.common.env env_file: docker-compose.common.env
environment: environment:
CI: CI:
MODULE_NAME:
MOCHA_ROOT_SUITE_NAME: MOCHA_ROOT_SUITE_NAME:
BASE_CONFIG: BASE_CONFIG:
OVERLEAF_CONFIG: OVERLEAF_CONFIG:

View File

@@ -16,6 +16,7 @@ services:
BASE_CONFIG: BASE_CONFIG:
OVERLEAF_CONFIG: OVERLEAF_CONFIG:
CI: CI:
MODULE_NAME:
MOCHA_ROOT_SUITE_NAME: MOCHA_ROOT_SUITE_NAME:
MOCHA_GREP: ${MOCHA_GREP} MOCHA_GREP: ${MOCHA_GREP}
LOG_LEVEL: ${LOG_LEVEL:-} LOG_LEVEL: ${LOG_LEVEL:-}
@@ -43,6 +44,7 @@ services:
BASE_CONFIG: BASE_CONFIG:
OVERLEAF_CONFIG: OVERLEAF_CONFIG:
CI: CI:
MODULE_NAME:
MOCHA_ROOT_SUITE_NAME: MOCHA_ROOT_SUITE_NAME:
MOCHA_GREP: ${MOCHA_GREP} MOCHA_GREP: ${MOCHA_GREP}
LOG_LEVEL: ${LOG_LEVEL:-} LOG_LEVEL: ${LOG_LEVEL:-}

View File

@@ -1,7 +1,7 @@
module.exports = { module.exports = {
reporterEnabled: 'spec, mocha-junit-reporter', reporterEnabled: 'spec, mocha-junit-reporter',
mochaJunitReporterReporterOptions: { mochaJunitReporterReporterOptions: {
mochaFile: 'data/reports/junit-mocha-[hash]-[suiteFilename].xml', mochaFile: `data/reports/junit-mocha-${process.env.MOCHA_ROOT_SUITE_NAME}-${process.env.MODULE_NAME}.xml`,
includePending: true, includePending: true,
jenkinsMode: true, jenkinsMode: true,
jenkinsClassnamePrefix: process.env.MOCHA_ROOT_SUITE_NAME, jenkinsClassnamePrefix: process.env.MOCHA_ROOT_SUITE_NAME,