-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathJenkinsfile_CNP
More file actions
623 lines (559 loc) · 22.3 KB
/
Jenkinsfile_CNP
File metadata and controls
623 lines (559 loc) · 22.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
#!groovy
/* groovylint-disable CompileStatic */
import groovy.transform.Field
import uk.gov.hmcts.contino.GithubAPI
@Library('Infrastructure')
// Pipeline identifiers
String opalName = 'opal'
String type = 'angular'
String product = opalName
String component = 'frontend'
@Field String masterProjectName = 'HMCTS/opal-frontend/master'
@Field String browserChrome = 'chrome'
@Field String browserEdge = 'edge'
@Field String browserFirefox = 'firefox'
@Field String stringTrue = String.valueOf(true)
@Field String stringFalse = String.valueOf(false)
@Field List<String> cachedPrLabels = []
// Secret key for KeyVault lookups
String secretKey = 'opal-$'.concat('{env}')
// Common literals to reduce duplication warnings
String stageBuild = 'build'
String stageTest = 'test'
String stageFunctionalDev = 'functionalTest:dev'
String stageFunctionalStg = 'functionalTest:stg'
String skipLabelComponent = 'skip_opal_component'
@Field String weightsRoot = 'cypress/parallel/weights'
@Field String pipelineWeightsDir = "${weightsRoot}/pipeline"
@Field String smokeWeights = 'smoke-parallel-weights.json'
@Field String functionalWeights = 'functional-parallel-weights.json'
@Field String componentWeights = 'component-parallel-weights.json'
@Field String functionalArtifactsGlob = 'functional-output/**'
@Field String weightsPipelineArtifactsFilter = '**/cypress/parallel/weights/pipeline/*'
@Field String mkdirPipelineCmd = "mkdir -p ${pipelineWeightsDir}"
@Field String copySmokeToPipelineCmd = "cp -r ${weightsRoot}/${smokeWeights} ${pipelineWeightsDir}/"
@Field String copyFunctionalToPipelineCmd = "cp -r ${weightsRoot}/${functionalWeights} ${pipelineWeightsDir}/"
String smokeXmlReport = 'cypress/reports/smokeXmlReport.html'
String functionalXmlReport = 'cypress/reports/functionalXmlReport.html'
// Build an Azure KeyVault secret entry
static Map<String, Object> secret(String secretName, String envVar) {
return [$class : 'AzureKeyVaultSecret',
secretType : 'Secret',
name : secretName,
version : '',
envVariable: envVar
]
}
// Return the preferred browser for generic pipeline runs.
String defaultBrowser() {
return browserEdge
}
// Normalize the browser selected for the current pipeline run.
String resolvedBrowserToRun() {
return (env.BROWSER_TO_RUN ?: defaultBrowser()).trim().toLowerCase()
}
// Resolve the functional report directory for the active browser.
String functionalOutputDir() {
return "functional-output/prod/${resolvedBrowserToRun()}/cucumber/"
}
// Resolve the functional report filename for the active browser.
String functionalReportFile() {
return "${resolvedBrowserToRun()}-report.html"
}
// Build the functional report display name for the active browser.
String functionalReportName() {
return "Cucumber Functional Test Report (${resolvedBrowserToRun()})"
}
// Resolve the smoke report directory for the active browser.
String smokeOutputDir() {
return "smoke-output/prod/${resolvedBrowserToRun()}/cucumber/"
}
// Return the smoke report filename.
String smokeReportFile() {
return 'smoke-report.html'
}
// Build the smoke report display name for the active browser.
String smokeReportName() {
return "Cucumber Smoke Test Report (${resolvedBrowserToRun()})"
}
// Return whether smoke execution/report publishing is enabled for this run.
boolean shouldPublishSmokeReport() {
return (env.SKIP_SMOKE ?: stringFalse).trim().toLowerCase() != stringTrue
}
String accountArtifactPath = 'functional-output/account_evidence/created-accounts.json'
String accountEvidenceArtifacts = 'functional-output/account_evidence/**'
String runTagPrefix = 'run_tag:'
// Vault secrets injected into the pipeline
Map<String, Object> secrets = [
(secretKey): [
secret('OpalTestUserEmail', 'OPAL_TEST_USER_EMAIL'),
secret('OpalTestUserPassword', 'OPAL_TEST_USER_PASSWORD'),
secret('opalLegacyJCDEGatewayUsername', 'OPAL_LEGACY_GATEWAY_USERNAME'),
secret('opalLegacyJCDEGatewayPassword', 'OPAL_LEGACY_GATEWAY_PASSWORD')
],
]
// Yarn helper for build/test commands
uk.gov.hmcts.contino.YarnBuilder yarnBuilder = new uk.gov.hmcts.contino.YarnBuilder(this)
// Archive pipeline artifacts without failing when none are present.
void archiveArtifact(String path) {
archiveArtifacts artifacts: path, allowEmptyArchive: true
}
// Publish an HTML report directory with Jenkins HTML Publisher
void publishHtmlReport(String dir, String files, String name) {
publishHTML target: [
allowMissing : true,
alwaysLinkToLastBuild: true,
keepAll : true,
reportDir : dir,
reportFiles : files,
reportName : name
]
}
// Check whether the requested browser binary is available on the Jenkins agent.
boolean isBrowserInstalled(String browser) {
return sh(
returnStatus: true,
script: """
case '${browser}' in
${browserEdge})
command -v microsoft-edge >/dev/null 2>&1 ||
command -v microsoft-edge-stable >/dev/null 2>&1 ||
command -v msedge >/dev/null 2>&1 ||
[ -x '/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge' ]
;;
${browserChrome})
command -v google-chrome >/dev/null 2>&1 ||
command -v google-chrome-stable >/dev/null 2>&1 ||
command -v chrome >/dev/null 2>&1 ||
[ -x '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome' ]
;;
${browserFirefox})
command -v firefox >/dev/null 2>&1 ||
command -v firefox-bin >/dev/null 2>&1 ||
[ -x '/Applications/Firefox.app/Contents/MacOS/firefox' ]
;;
*)
exit 1
;;
esac
"""
) == 0
}
// Emit a prominent banner when the pipeline falls back to a different browser.
void echoBrowserFallbackBanner(String missingBrowser, String fallbackBrowser) {
echo """\
========================================================================
${missingBrowser.toUpperCase()} IS NOT INSTALLED ON THIS JENKINS AGENT
SWITCHING TO ${fallbackBrowser.toUpperCase()} FOR THIS PIPELINE RUN
========================================================================
""".stripIndent()
}
// Emit a prominent banner when a browser is unavailable on the current agent.
void echoBrowserMissingBanner(String browser) {
echo """\
========================================================================
${browser.toUpperCase()} IS NOT INSTALLED ON THIS JENKINS AGENT
${browser.toUpperCase()}-SPECIFIC RUNS WILL NOT BE AVAILABLE ON THIS AGENT
========================================================================
""".stripIndent()
}
// Choose the browser for this run, falling back from Edge to Chrome when needed.
String resolvePipelineBrowser() {
String requestedBrowser = resolvedBrowserToRun()
if (requestedBrowser == browserEdge && !isBrowserInstalled(browserEdge)) {
if (isBrowserInstalled(browserChrome)) {
echoBrowserFallbackBanner(browserEdge, browserChrome)
return browserChrome
}
error '''\
========================================================================
EDGE IS NOT INSTALLED ON THIS JENKINS AGENT
CHROME IS ALSO NOT AVAILABLE AS A FALLBACK
INSTALL A SUPPORTED BROWSER OR SET BROWSER_TO_RUN EXPLICITLY
========================================================================
'''.stripIndent()
}
return requestedBrowser
}
// Resolve project/job name (PR vs master)
String getProjectName() {
String projectPrefix = 'HMCTS/opal-frontend/'
echo "Branch name: ${env.BRANCH_NAME}"
echo "Labels: ${cachedPrLabels}"
List<String> candidateLabels = cachedPrLabels.findAll { String label ->
label.startsWith('opal-frontend-pr-') || label.startsWith('rel:opal-frontend-pr-')
}
for (label in candidateLabels) {
java.util.regex.Matcher matcher = (label =~ /opal-frontend-pr-(\d+)/)
if (matcher.find()) {
return projectPrefix + 'PR-' + matcher.group(1)
}
}
return masterProjectName
}
// Collect test/skip labels for routing
List<String> getProjectLabels() {
List<String> testLabels = []
for (String label in cachedPrLabels.findAll { String cachedLabel -> cachedLabel.startsWith('test_') }) {
testLabels.add(label)
}
for (String label in cachedPrLabels.findAll { String cachedLabel -> cachedLabel.startsWith('skip_') }) {
testLabels.add(label)
}
return testLabels
}
// Restore weight files from last successful build
void performArtifactOperations() {
sh "${mkdirPipelineCmd}"
sh """
echo "Listing all artifacts before copy artifacts:"
ls -l1a ${weightsRoot}/
ls -l1a ${pipelineWeightsDir}/
"""
script {
String projectNameValue = projectName
String fallbackProjectName = masterProjectName
boolean copied = false
try {
copyArtifacts(
projectName: projectNameValue,
selector: lastSuccessful(),
filter: weightsPipelineArtifactsFilter
)
copied = true
} catch (hudson.AbortException e) {
echo "Error copying artifacts from ${projectNameValue}: ${e}"
}
if (!copied && projectNameValue != fallbackProjectName) {
try {
copyArtifacts(
projectName: fallbackProjectName,
selector: lastSuccessful(),
filter: weightsPipelineArtifactsFilter
)
copied = true
} catch (hudson.AbortException e) {
echo "Error copying artifacts from ${fallbackProjectName}: ${e}"
}
}
if (!copied) {
echo 'No prior weight artifacts found; continuing without cached weights.'
}
sh """
echo "Listing all artifacts after copy artifacts:"
ls -l1a ${weightsRoot}/
ls -l1a ${pipelineWeightsDir}/
"""
}
try {
sh """
if [ -f ${pipelineWeightsDir}/${smokeWeights} ]; then
mv ${pipelineWeightsDir}/${smokeWeights} ${weightsRoot}/${smokeWeights}
echo "Moved smoke-parallel-weights.json"
else
echo "smoke-parallel-weights.json not found, skipping move"
fi
if [ -f ${pipelineWeightsDir}/${functionalWeights} ]; then
mv ${pipelineWeightsDir}/${functionalWeights} ${weightsRoot}/${functionalWeights}
echo "Moved functional-parallel-weights.json"
else
echo "functional-parallel-weights.json not found, skipping move"
fi
if [ -f ${pipelineWeightsDir}/${componentWeights} ]; then
mv ${pipelineWeightsDir}/${componentWeights} ${weightsRoot}/${componentWeights}
echo "Moved component-parallel-weights.json"
else
echo "component-parallel-weights.json not found, skipping move"
fi
"""
} catch (hudson.AbortException e) {
echo "Artifacts not found or copy failed: ${e}"
}
sh """
echo "Listing all copied artifacts:"
ls -l1a ${weightsRoot}/
ls -l1a ${pipelineWeightsDir}/
"""
}
// Set TEST_SPECS based on labels or default to all
void setupTestSpecifications() {
script {
String projectNameValue = projectName
List<String> labels = projectLabels
sh "echo 'Test Labels: ${labels}'"
List<String> directories = []
// Add additional labels as needed
if (labels.contains('test_authorisation')) {
directories.add('authorisation')
}
if (labels.contains('test_enq')) {
directories.add('accountEnquiry')
}
if (labels.contains('test_remo')) {
directories.add('reciprocalMaintenance')
}
if (labels.contains('test_mac')) {
directories.add('manualAccountCreation')
}
sh "echo 'Directories: ${directories}'"
if (directories.empty || projectNameValue == masterProjectName) {
// No specific test labels, include all directories
env.TEST_SPECS = 'cypress/e2e/functional/opal/**/*.feature'
} else {
// Build the glob pattern using curly braces
String dirList = directories.join(',')
env.TEST_SPECS = "cypress/e2e/functional/opal/{required,${dirList}}/**/*.feature"
}
sh "echo 'Test Specs: ${env.TEST_SPECS}'"
}
}
// Configure PR env deployment for dev fines service
void determineDevEnvironmentDeployment() {
env.DEV_ENABLE_OPAL_FINES_SERVICE = true // Default to true for development environment as FE wont work without it
env.DEV_OPAL_FINES_SERVICE_URL = "https://opal-frontend-pr-${env.CHANGE_ID}-fines-service.dev.platform.hmcts.net"
env.DEV_OPAL_FINES_SERVICE_IMAGE_SUFFIX = 'latest'
if (cachedPrLabels.contains('enable_keep_helm')) {
for (label in cachedPrLabels.findAll { String cachedLabel -> cachedLabel.startsWith('enable_opal_') }) {
//Fines service
if (label ==~ /enable_opal_fines_service.*/) {
env.DEV_ENABLE_OPAL_FINES_SERVICE = true
env.DEV_OPAL_FINES_SERVICE_URL =
"https://opal-frontend-pr-${env.CHANGE_ID}-fines-service.dev.platform.hmcts.net"
if (label ==~ /enable_opal_fines_service:pr-.*/) {
env.DEV_OPAL_FINES_SERVICE_IMAGE_SUFFIX = label.replace('enable_opal_fines_service:', '')
}
echo "Deploying Opal fines service ${env.DEV_OPAL_FINES_SERVICE_URL}"
}
}
}
}
// Fail the build if .only or @only are present
void checkFocusedTests() {
sh '''
echo "=== Focused test checks (component + feature) ==="
set +e
COMPONENT_ONLY_FILES=$(grep -R -l --include='*.cy.ts' '\\.only' cypress/component 2>/dev/null)
FEATURE_ONLY_FILES=$(grep -R -l --include='*.feature' '@only' cypress/e2e/functional/opal/features 2>/dev/null)
set -e
HAS_FAILURE=0
if [ -n "$COMPONENT_ONLY_FILES" ]; then
echo ""
echo "Found focused component specs (.only):"
echo "$COMPONENT_ONLY_FILES" | sed 's/^/ - /'
HAS_FAILURE=1
else
echo "No focused component specs found."
fi
if [ -n "$FEATURE_ONLY_FILES" ]; then
echo ""
echo "Found @only tags in feature files:"
echo "$FEATURE_ONLY_FILES" | sed 's/^/ - /'
HAS_FAILURE=1
else
echo "No @only tags found in feature files."
fi
if [ "$HAS_FAILURE" -eq 1 ]; then
echo ""
echo "FAIL: Remove the focused tests above before running component tests."
exit 1
fi
echo "Focused test checks passed."
'''
}
properties([
// Permit dependent jobs to copy artifacts from this build
copyArtifactPermission('*')
])
// --- Dev legacy toggle (PR label driven) ---
String changeId = env.CHANGE_ID?.trim()
boolean isPR = changeId != null && !changeId.empty
GithubAPI githubApi = new GithubAPI(this)
try {
cachedPrLabels = githubApi.getLabels(env.BRANCH_NAME)
} catch (hudson.AbortException e) {
error 'Unable to read PR labels from GitHub, so label-driven pipeline behavior cannot be resolved. ' +
"GitHub label lookup failed with: ${e.message}"
}
boolean legacyEnabled = cachedPrLabels.contains('enable_legacy_mode')
// Safe defaults (current behaviour)
env.DEV_DEFAULT_APP_MODE = opalName
env.DEV_OPAL_LEGACY_GATEWAY_URL = 'https://opal-legacy-db-stub.staging.platform.hmcts.net/opal'
env.OPAL_LOG_LEVEL = env.OPAL_LOG_LEVEL ?: 'INFO'
// Opt-in: Dev → JCDE (PR builds only)
if (legacyEnabled && isPR) {
env.DEV_DEFAULT_APP_MODE = 'legacy'
env.DEV_OPAL_LEGACY_GATEWAY_URL = 'https://opal.clouddev.online/opal'
env.OPAL_LOG_LEVEL = 'DEBUG'
} else if (legacyEnabled && !isPR) {
echo 'Legacy mode label detected but this is not a PR build; ignoring.'
}
echo "DEV_DEFAULT_APP_MODE=${env.DEV_DEFAULT_APP_MODE}"
echo "DEV_OPAL_LEGACY_GATEWAY_URL=${env.DEV_OPAL_LEGACY_GATEWAY_URL}"
echo "OPAL_LOG_LEVEL=${env.OPAL_LOG_LEVEL}"
// Find the first label like: run_tag:<expression>
String tagLabel = cachedPrLabels.find { String label -> label.startsWith(runTagPrefix) }
String skipTagExpression = 'not @skip'
String baseTags = tagLabel ? tagLabel.substring(runTagPrefix.length()).trim() : ''
// Guard: legacy mode must explicitly scope tests via run_tag:<expression>
if (legacyEnabled && isPR && !baseTags) {
error 'Legacy mode requires a populated run_tag:<expression> label ' +
'(e.g. run_tag:@UAT-Technical) to avoid running the full suite.'
}
if (tagLabel) {
env.TAGS = baseTags ? "(${baseTags}) and ${skipTagExpression}" : skipTagExpression
env.CYPRESS_TAGS = env.TAGS
env.SKIP_SMOKE = stringTrue
echo "Running scenarios matching TAGS=${env.TAGS}"
echo "Skipping smoke tests for tagged run: ${tagLabel}"
} else {
env.TAGS = skipTagExpression
env.CYPRESS_TAGS = env.TAGS
env.SKIP_SMOKE = stringFalse
echo "Running all scenarios except @skip (TAGS=${env.TAGS})"
}
// Primary pipeline definition and lifecycle hooks
withPipeline(type, product, component) {
// Inject KeyVault secrets required for login/setup
loadVaultSecrets(secrets)
// Configure PR-only deployment knobs before helm installs run
before('akschartsinstall') {
onPR {
determineDevEnvironmentDeployment()
}
}
// Build frontend bundle after checkout when previous steps succeed
afterSuccess(stageBuild) {
yarnBuilder.yarn('rebuild puppeteer')
yarnBuilder.yarn(stageBuild)
}
// Restore previous parallelization weights and set test spec globs
afterSuccess('checkout') {
if (!isBrowserInstalled(browserFirefox)) {
echoBrowserMissingBanner(browserFirefox)
}
env.BROWSER_TO_RUN = resolvePipelineBrowser()
echo "Using browser for this pipeline run: ${env.BROWSER_TO_RUN}"
performArtifactOperations()
setupTestSpecifications()
script {
List<String> labels = projectLabels
if (labels.contains(skipLabelComponent)) {
echo "Skipping focused test checks due to label: ${skipLabelComponent}"
} else {
echo 'Running focused test checks (no skip label present)'
checkFocusedTests()
}
}
}
// Persist updated component weights after component test stage
afterSuccess(stageTest) {
script {
String sourceJobName = env.JOB_NAME
sh "${mkdirPipelineCmd}"
sh '''
if [ -f cypress/parallel/weights/component-parallel-weights.json ]; then
cp -r cypress/parallel/weights/component-parallel-weights.json cypress/parallel/weights/pipeline/
else
echo "component-parallel-weights.json not found, skipping copy"
fi
'''
sh"echo 'Archiving weight files in ${sourceJobName}'"
archiveArtifacts(
artifacts: "${pipelineWeightsDir}/${componentWeights}",
allowEmptyArchive: true
)
}
}
// Always run component suite unless explicitly skipped; publish artifacts
afterAlways(stageTest) {
script {
List<String> labels = projectLabels
if (labels.contains(skipLabelComponent)) {
echo "Skipping opal component tests due to label: ${skipLabelComponent}"
} else {
echo 'Running opal component tests (no skip label present)'
try {
yarnBuilder.yarn('test:component')
} catch (hudson.AbortException e) {
echo "Opal component tests failed: ${e}"
currentBuild.result = 'FAILURE'
} finally {
archiveArtifact(functionalArtifactsGlob)
publishHtmlReport(
"functional-output/component/${resolvedBrowserToRun()}/html/",
'component-report.html',
"Component Test Report (${resolvedBrowserToRun()})"
)
}
}
}
}
// Archive smoke/functional weights and reports after dev functional run
afterSuccess(stageFunctionalDev) {
script {
String sourceJobName = env.JOB_NAME
sh "${mkdirPipelineCmd}"
sh "${copySmokeToPipelineCmd}"
sh "${copyFunctionalToPipelineCmd}"
sh"echo 'Archiving weight files in ${sourceJobName}'"
archiveArtifact("${pipelineWeightsDir}/${smokeWeights}")
archiveArtifact("${pipelineWeightsDir}/${functionalWeights}")
archiveArtifact(smokeXmlReport)
archiveArtifact(functionalXmlReport)
}
}
// Archive smoke/functional weights and reports after staging functional run
afterSuccess(stageFunctionalStg) {
script {
String sourceJobName = env.JOB_NAME
sh "${mkdirPipelineCmd}"
sh "${copySmokeToPipelineCmd}"
sh "${copyFunctionalToPipelineCmd}"
sh"echo 'Archiving weight files in ${sourceJobName}'"
archiveArtifact("${pipelineWeightsDir}/${smokeWeights}")
archiveArtifact("${pipelineWeightsDir}/${functionalWeights}")
archiveArtifact(smokeXmlReport)
archiveArtifact(functionalXmlReport)
}
}
// Publish HTML and JSON artifacts for staging functional runs
afterAlways(stageFunctionalStg) {
publishHtmlReport(
functionalOutputDir(),
functionalReportFile(),
functionalReportName()
)
archiveArtifact(functionalArtifactsGlob)
archiveArtifact(accountArtifactPath)
archiveArtifact(accountEvidenceArtifacts)
if (shouldPublishSmokeReport()) {
publishHtmlReport(
smokeOutputDir(),
smokeReportFile(),
smokeReportName()
)
} else {
echo "Skipping smoke report publishing because SKIP_SMOKE=${env.SKIP_SMOKE}"
}
}
// Publish HTML and JSON artifacts for dev functional runs
afterAlways(stageFunctionalDev) {
publishHtmlReport(
functionalOutputDir(),
functionalReportFile(),
functionalReportName()
)
archiveArtifact(functionalArtifactsGlob)
archiveArtifact(accountArtifactPath)
archiveArtifact(accountEvidenceArtifacts)
if (shouldPublishSmokeReport()) {
publishHtmlReport(
smokeOutputDir(),
smokeReportFile(),
smokeReportName()
)
} else {
echo "Skipping smoke report publishing because SKIP_SMOKE=${env.SKIP_SMOKE}"
}
}
}