@freshworks/shiftleft-tools 1.1.8

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.
Files changed (106) hide show
  1. package/README.md +351 -0
  2. package/bin/shiftleft.js +95 -0
  3. package/package.json +57 -0
  4. package/src/commands/doctor.js +208 -0
  5. package/src/commands/init-postman.js +298 -0
  6. package/src/commands/init-rules.js +78 -0
  7. package/src/commands/link.js +172 -0
  8. package/src/commands/protect.js +61 -0
  9. package/src/commands/run-tests.js +182 -0
  10. package/src/commands/setup-pipeline.js +209 -0
  11. package/src/commands/update.js +203 -0
  12. package/src/index.js +4 -0
  13. package/src/utils/copy-tree.js +98 -0
  14. package/src/utils/gitignore.js +26 -0
  15. package/src/utils/logger.js +9 -0
  16. package/src/utils/manifest.js +145 -0
  17. package/src/utils/stack.js +80 -0
  18. package/src/utils/template.js +135 -0
  19. package/templates/AGENTS.md +109 -0
  20. package/templates/CLAUDE.md +3 -0
  21. package/templates/jenkins/Jenkinsfile-java.groovy +432 -0
  22. package/templates/jenkins/Jenkinsfile-node.groovy +450 -0
  23. package/templates/postman/.husky/pre-commit +19 -0
  24. package/templates/postman/.prettierrc.json +5 -0
  25. package/templates/postman/README.md.ejs +147 -0
  26. package/templates/postman/collections/01-core.json.ejs +91 -0
  27. package/templates/postman/config/local.json.ejs +12 -0
  28. package/templates/postman/config/staging.json.ejs +26 -0
  29. package/templates/postman/environments/local.postman_environment.json.ejs +31 -0
  30. package/templates/postman/environments/staging.postman_environment.json.ejs +31 -0
  31. package/templates/postman/gitignore +16 -0
  32. package/templates/postman/npmrc +31 -0
  33. package/templates/postman/package.json.ejs +66 -0
  34. package/templates/postman/run-all-shim.sh +16 -0
  35. package/templates/postman/scripts/auth/generate-jwt.sh +113 -0
  36. package/templates/postman/scripts/auth/get-issuer-secret.sh +140 -0
  37. package/templates/postman/scripts/infra/start-mocks.sh +138 -0
  38. package/templates/postman/scripts/infra/stop-mocks.sh +43 -0
  39. package/templates/postman/scripts/lib/api_coverage.py +1122 -0
  40. package/templates/postman/scripts/lib/cleanup-reports.sh +101 -0
  41. package/templates/postman/scripts/lib/cleanup-stryker.sh +44 -0
  42. package/templates/postman/scripts/lib/report_combined.py +527 -0
  43. package/templates/postman/scripts/lib/report_consolidated.py +363 -0
  44. package/templates/postman/scripts/lib/report_generator.py +121 -0
  45. package/templates/postman/scripts/lib/report_migration.py +156 -0
  46. package/templates/postman/scripts/lib/report_mutation.py +110 -0
  47. package/templates/postman/scripts/lib/report_unit.py +353 -0
  48. package/templates/postman/scripts/lib/report_utils.py +973 -0
  49. package/templates/postman/scripts/report-generators/generate-consolidated-report.sh +445 -0
  50. package/templates/postman/scripts/report-generators/java-api-coverage-matrix.sh +257 -0
  51. package/templates/postman/scripts/report-generators/mutation-report.sh +672 -0
  52. package/templates/postman/scripts/report-generators/node-api-coverage-matrix.sh +167 -0
  53. package/templates/postman/scripts/report-generators/stage-report-artifacts.sh +27 -0
  54. package/templates/postman/scripts/run-all.sh +452 -0
  55. package/templates/postman/scripts/runners/run-mutation-tests.sh +113 -0
  56. package/templates/postman/scripts/runners/run-tests-local.sh +936 -0
  57. package/templates/postman/scripts/runners/run-tests-staging.sh +741 -0
  58. package/templates/postman-node/README.md.ejs +26 -0
  59. package/templates/postman-node/collections/crud/01-bootstrap.json.ejs +34 -0
  60. package/templates/postman-node/config/local.json.ejs +46 -0
  61. package/templates/postman-node/config/staging.json.ejs +31 -0
  62. package/templates/postman-node/local.test.env.ejs +3 -0
  63. package/templates/postman-node/mocks/external.js +14 -0
  64. package/templates/postman-node/package.json.ejs +39 -0
  65. package/templates/postman-node/requirements.txt +1 -0
  66. package/templates/postman-node/scripts/database/cleanup-mysql.sh +12 -0
  67. package/templates/postman-node/scripts/database/run-migrations.js +29 -0
  68. package/templates/postman-node/scripts/database/start-mysql.sh +34 -0
  69. package/templates/postman-node/scripts/database/wait-for-mysql.sh +36 -0
  70. package/templates/postman-node/scripts/lib/api_coverage_node.py +1137 -0
  71. package/templates/postman-node/scripts/lib/fetch-jwt.sh +86 -0
  72. package/templates/postman-node/scripts/lib/run-newman.sh +104 -0
  73. package/templates/postman-node/scripts/lib/setup-database.sh +55 -0
  74. package/templates/postman-node/scripts/lib/start-app.sh +48 -0
  75. package/templates/postman-node/scripts/lib/utils.sh +114 -0
  76. package/templates/postman-node/scripts/report-generators/stage-report-artifacts.sh +26 -0
  77. package/templates/postman-node/scripts/run-all.sh +303 -0
  78. package/templates/postman-node/scripts/runners/run-tests.sh +123 -0
  79. package/templates/postman-node/scripts/setup-mocks.js.ejs +29 -0
  80. package/templates/postman-node/stryker.config.js.ejs +51 -0
  81. package/templates/rules/local-test-setup.mdc +420 -0
  82. package/templates/rules/testing-node.mdc +66 -0
  83. package/templates/rules/testing.mdc +248 -0
  84. package/templates/skills/_shared/postman-standards.md +380 -0
  85. package/templates/skills/enhance-test-pipeline/SKILL-java.md +483 -0
  86. package/templates/skills/enhance-test-pipeline/SKILL-node.md +431 -0
  87. package/templates/skills/enhance-test-pipeline/SKILL.md +9 -0
  88. package/templates/skills/review-test-suite/SKILL-java.md +137 -0
  89. package/templates/skills/review-test-suite/SKILL-node.md +78 -0
  90. package/templates/skills/review-test-suite/SKILL.md +9 -0
  91. package/templates/skills/run-test-suite/SKILL-java.md +186 -0
  92. package/templates/skills/run-test-suite/SKILL-node.md +191 -0
  93. package/templates/skills/run-test-suite/SKILL.md +9 -0
  94. package/templates/skills/setup-api-tests/SKILL-java.md +1094 -0
  95. package/templates/skills/setup-api-tests/SKILL-node.md +141 -0
  96. package/templates/skills/setup-api-tests/SKILL.md +9 -0
  97. package/templates/skills/setup-mutation-tests/SKILL-java.md +303 -0
  98. package/templates/skills/setup-mutation-tests/SKILL-node.md +408 -0
  99. package/templates/skills/setup-mutation-tests/SKILL.md +9 -0
  100. package/templates/skills/setup-test-pipeline/SKILL-java.md +454 -0
  101. package/templates/skills/setup-test-pipeline/SKILL-node.md +318 -0
  102. package/templates/skills/setup-test-pipeline/SKILL.md +9 -0
  103. package/templates/skills/write-api-tests/SKILL-java.md +115 -0
  104. package/templates/skills/write-api-tests/SKILL-node.md +83 -0
  105. package/templates/skills/write-api-tests/SKILL.md +9 -0
  106. package/templates/stryker.config.js +50 -0
@@ -0,0 +1,450 @@
1
+ #!groovy
2
+
3
+ utilObj = new Utils()
4
+
5
+ pipeline {
6
+
7
+ agent {
8
+ kubernetes {
9
+ yaml k8sPodTemplate(pod: 'jnlp+node+dind24')
10
+ defaultContainer 'node-base'
11
+ }
12
+ }
13
+
14
+ // Artifacts will be retained 10 days or 50 builds
15
+ options {
16
+ buildDiscarder(logRotator(numToKeepStr: '10', artifactNumToKeepStr: '50'))
17
+ disableConcurrentBuilds()
18
+ }
19
+
20
+ environment {
21
+ NODE_VERSION = utilObj.getNodeVersionFromPackageJSON()
22
+ }
23
+
24
+ parameters {
25
+ choice(choices: 'dev\nstaging\nproduction', description: '', name: 'deployTo')
26
+ string(name: 'image_version', defaultValue: '', description: 'Image version for Staging/Production deployment [Recommended]')
27
+ }
28
+
29
+ stages {
30
+ stage('Checkout & Setup') {
31
+ when {
32
+ expression {
33
+ params.deployTo == 'dev'
34
+ }
35
+ }
36
+ steps {
37
+ checkoutCode(env.NODE_VERSION)
38
+ }
39
+ }
40
+
41
+ stage ('Code Sanity') {
42
+ when {
43
+ expression {
44
+ params.deployTo == 'dev' && !params.deployOnly
45
+ }
46
+ }
47
+ steps {
48
+ doCodeSanity(env.NODE_VERSION)
49
+ }
50
+ }
51
+
52
+ stage ('Unit Tests') {
53
+ when {
54
+ expression {
55
+ params.deployTo == 'dev' && !params.deployOnly
56
+ }
57
+ }
58
+ steps {
59
+ runUnitTests(env.NODE_VERSION)
60
+ }
61
+ }
62
+
63
+ stage ('Mutation Tests') {
64
+ when {
65
+ expression {
66
+ params.deployTo == 'dev' && !params.deployOnly
67
+ }
68
+ }
69
+ steps {
70
+ script {
71
+ // Full scan (all scoped files) for CI — local dev uses "since" mode (changed files only)
72
+ utilObj.runCmd('yarn install --ignore-engines && yarn mutation-tests:full', env.NODE_VERSION)
73
+ }
74
+ }
75
+ post {
76
+ always {
77
+ publishHTML([
78
+ allowMissing: true,
79
+ alwaysLinkToLastBuild: true,
80
+ keepAll: true,
81
+ reportDir: 'coverage/mutation',
82
+ reportFiles: 'index.html',
83
+ reportName: 'Stryker Mutation Report',
84
+ reportTitles: 'Mutation testing (full scan)'
85
+ ])
86
+ }
87
+ }
88
+ }
89
+
90
+ stage ('Integration Tests') {
91
+ when {
92
+ expression {
93
+ params.deployTo == 'dev' && !params.deployOnly
94
+ }
95
+ }
96
+ steps {
97
+ runIntegrationTests(env.NODE_VERSION)
98
+ }
99
+ }
100
+
101
+ stage('SonarQube analysis') {
102
+ when {
103
+ expression {
104
+ params.deployTo == 'dev' && !params.deployOnly
105
+ }
106
+ }
107
+ steps {
108
+ runSonarQubeAnalysis()
109
+ }
110
+ }
111
+
112
+ // Shift-left Postman (isoforge + Newman), API coverage matrix, and combined quality report.
113
+ stage('Staging Postman, API Coverage & Quality Report') {
114
+ when {
115
+ expression {
116
+ params.deployTo == 'dev' && !params.deployOnly
117
+ }
118
+ }
119
+ steps {
120
+ script {
121
+ runShiftLeftStagingPostman()
122
+ }
123
+ script {
124
+ sh """
125
+ set -e
126
+ # Library scripts are staged from the shiftleft-tools package (gitignored cache)
127
+ shiftleft stage-scripts
128
+ # Adjust routes dir to match your actual route/controller dir (src/controllers, src/routes, etc.)
129
+ ./postman/scripts/report-generators/node-api-coverage-matrix.sh ./src/controllers ./postman
130
+ """
131
+ }
132
+ script {
133
+ generateQualityReport()
134
+ }
135
+ }
136
+ post {
137
+ always {
138
+ publishHTML([
139
+ allowMissing: true,
140
+ alwaysLinkToLastBuild: true,
141
+ keepAll: true,
142
+ reportDir: 'postman/reports',
143
+ reportFiles: 'api-coverage-matrix-latest.html',
144
+ reportName: 'API Coverage Matrix',
145
+ reportTitles: 'Postman vs Express route coverage'
146
+ ])
147
+ }
148
+ }
149
+ }
150
+
151
+ stage ('Build') {
152
+ when {
153
+ branch 'master'
154
+ expression {
155
+ params.deployTo == 'dev' && params.image_version == ''
156
+ }
157
+ }
158
+ steps {
159
+ buildProject(env.NODE_VERSION, '', false)
160
+ }
161
+ }
162
+
163
+ stage('Security Tests') {
164
+ when {
165
+ expression {
166
+ params.deployTo == 'dev'
167
+ }
168
+ }
169
+ steps {
170
+ runSecurityTests('SERVICE_NAME')
171
+ }
172
+ }
173
+
174
+ stage ('Promote Image') {
175
+ when {
176
+ branch 'master'
177
+ expression {
178
+ params.deployTo == 'production'
179
+ }
180
+ }
181
+ steps {
182
+ promote('SERVICE_NAME', false, params.image_version)
183
+ }
184
+ }
185
+
186
+ stage ('Deploy Image') {
187
+ when {
188
+ branch 'master'
189
+ }
190
+ steps {
191
+ deployArgo(params.deployTo, 'SERVICE_NAME', params.image_version)
192
+ }
193
+ }
194
+ }
195
+
196
+ post {
197
+ always {
198
+ sendEmail()
199
+ deleteDir()
200
+ }
201
+ }
202
+ }
203
+
204
+ /**
205
+ * Shift-left: build PR image (tag from shiftleftBuildLabels), isoforge shiftleft_feature_stack for SERVICE_NAME,
206
+ * run Postman against staging with routing header to isolation pod, publish reports, teardown.
207
+ * Gate: PR comment body must start with "shiftleft"
208
+ * Replace ISO_HEADER and ISO_HEADER_VALUE with values from isoforge header_map for this service.
209
+ */
210
+ // Ensure the shiftleft CLI is installed. `shiftleft stage-scripts` / run-all.sh
211
+ // (which execs `shiftleft test`) stage the library scripts from the package, so
212
+ // CI agents need the CLI. Idempotent. Requires a Jenkins "Secret text" credential
213
+ // (id below) holding the Nexus npm basic-auth token, i.e. the value for
214
+ // //nexuscentral.runwayci.com/repository/npm-group/:_auth= (base64 of user:pass).
215
+ def ensureShiftleftCli() {
216
+ withCredentials([string(credentialsId: 'nexus-npm-auth', variable: 'NEXUS_NPM_AUTH')]) {
217
+ sh '''#!/bin/bash --login
218
+ set -e
219
+ if ! command -v shiftleft >/dev/null 2>&1; then
220
+ echo "//nexuscentral.runwayci.com/repository/npm-group/:_auth=${NEXUS_NPM_AUTH}" >> "$HOME/.npmrc"
221
+ npm install -g shiftleft-tools \
222
+ --registry=https://nexuscentral.runwayci.com/repository/npm-group/ --prefer-online
223
+ fi
224
+ '''
225
+ }
226
+ }
227
+
228
+ def runShiftLeftStagingPostman() {
229
+ ensureShiftleftCli()
230
+ def buildCause = ''
231
+ def latestArgoSha = ''
232
+ def serviceName = utilObj.getK8ServiceName(utilObj.getRepoName())
233
+ // Replace with your service's isoforge routing header and account ID
234
+ def isoRouteHeader = 'ISO_HEADER'
235
+ def isoRouteValue = 'ISO_HEADER_VALUE'
236
+
237
+ try {
238
+ def causes = currentBuild.rawBuild.getCauses()
239
+ if (causes && causes.size() > 0) {
240
+ def cause = causes[0]
241
+ if (cause?.class?.toString()?.contains('GitHubPullRequestCommentCause')) {
242
+ buildCause = cause.getCommentBody() ?: ''
243
+ }
244
+ cause = null
245
+ }
246
+ } catch (Exception e) {
247
+ echo "Could not get build cause: ${e.message}"
248
+ }
249
+
250
+ echo "Build Cause = ${buildCause}"
251
+ echo "K8s service (isoforge/Argo): ${serviceName}"
252
+ if (!buildCause.startsWith('shiftleft')) {
253
+ echo 'Branch build with no intention of shift left integration test.'
254
+ return
255
+ }
256
+
257
+ lock('SERVICE_NAME-postman-stage') {
258
+ def sl = shiftleftBuildLabels()
259
+ def branchNameSetup = "${sl.gitBranchBase}/${serviceName}-shiftleftFeatureStack"
260
+ def branchNameTeardown = "${sl.gitBranchBase.replace('iso-setup-', 'iso-teardown-')}/${serviceName}-shiftleftFeatureStack"
261
+ def imageTag = sl.imageTag
262
+
263
+ echo "gitBranchBase: ${sl.gitBranchBase} imageTag: ${imageTag}"
264
+ echo "Branch setup: ${branchNameSetup} teardown: ${branchNameTeardown}"
265
+ echo "Building image for isolation (tag: ${imageTag})"
266
+ buildProject(env.NODE_VERSION, '', false, imageTag)
267
+
268
+ // SECURITY: GH_TOKEN only exposed to git/gh commands
269
+ withCredentials([string(credentialsId: 'GITHUB_RUNWAYCI_TOKEN', variable: 'GH_TOKEN')]) {
270
+ utilObj.runCmd("""
271
+ set -euo pipefail
272
+ git clone git@github.com:freshdesk/freshapps-k8s.git
273
+ cd freshapps-k8s
274
+ isoforge version
275
+ git checkout -b ${branchNameSetup}
276
+
277
+ isoforge setup \\
278
+ --service ${serviceName} \\
279
+ --namespace shiftleft_feature_stack \\
280
+ --imageTag ${imageTag} \\
281
+ --header ${isoRouteHeader} \\
282
+ --headerValue ${isoRouteValue}
283
+
284
+ if [[ -n "\$(git status --porcelain)" ]]; then
285
+ git add .
286
+ git commit -m "Isolation setup for ${serviceName} in shiftleftFeatureStack"
287
+ git push origin ${branchNameSetup}
288
+ else
289
+ echo "No changes to commit, check previous steps"
290
+ exit 1
291
+ fi
292
+
293
+ gh pr create \\
294
+ --title "Staging Isoforge-${BUILD_NUMBER} ${serviceName} shiftleftFeatureStack" \\
295
+ --body "Isolation setup for ${serviceName} in shiftleftFeatureStack" \\
296
+ --base main \\
297
+ --head ${branchNameSetup}
298
+
299
+ gh pr merge ${branchNameSetup} \\
300
+ --squash \\
301
+ --admin
302
+
303
+ git checkout main
304
+ git pull origin main
305
+ """, NODE_VERSION)
306
+
307
+ latestArgoSha = sh(returnStdout: true, script: '''
308
+ cd freshapps-k8s && git rev-parse HEAD
309
+ ''').trim()
310
+ }
311
+ echo "Latest SHA (freshapps-k8s) after setup = ${latestArgoSha}"
312
+
313
+ withCredentials([string(credentialsId: 'ARGO_API_TOKEN', variable: 'ARGO_API_TOKEN')]) {
314
+ utilObj.runCmd("""
315
+ argoapi application --revision ${latestArgoSha} --service ${serviceName} \\
316
+ --namespace NAMESPACE
317
+ """, NODE_VERSION)
318
+ }
319
+
320
+ // IRSA on the pod supplies AWS for Secrets Manager access
321
+ updateShiftLeftStatus('pending')
322
+ try {
323
+ utilObj.runCmd("""
324
+ set -euo pipefail
325
+ cd postman
326
+ npm ci --ignore-scripts
327
+ npm audit --audit-level=critical 2>&1 || echo "WARNING: audit found issues"
328
+ npm audit signatures 2>&1 || echo "WARNING: signature verification failed"
329
+ npm audit --json > ../audit-report.json 2>&1 || true
330
+ # Library scripts are staged from the shiftleft-tools package (gitignored cache)
331
+ shiftleft stage-scripts
332
+ ./scripts/run-tests.sh staging
333
+ """, NODE_VERSION)
334
+ echo 'Shift left Postman tests completed'
335
+ updateShiftLeftStatus('success', '/PostmanTestReport-ShiftLeft/')
336
+ } catch (Exception e) {
337
+ updateShiftLeftStatus('failure', '/PostmanTestReport-ShiftLeft/')
338
+ echo "Staging Postman tests failed: ${e.getMessage()}"
339
+ currentBuild.result = 'UNSTABLE'
340
+ }
341
+
342
+ archiveArtifacts artifacts: 'audit-report.json', allowEmptyArchive: true
343
+
344
+ if (fileExists('postman/reports')) {
345
+ publishHTML([
346
+ allowMissing: true,
347
+ alwaysLinkToLastBuild: true,
348
+ keepAll: true,
349
+ reportDir: 'postman/reports',
350
+ reportFiles: 'consolidated-staging-*.html',
351
+ reportName: 'PostmanTestReport-ShiftLeft',
352
+ reportTitles: 'SERVICE_NAME Staging (Isoforge) Postman'
353
+ ])
354
+ } else {
355
+ echo 'postman/reports missing, skipping HTML report publication'
356
+ }
357
+
358
+ withCredentials([string(credentialsId: 'GITHUB_RUNWAYCI_TOKEN', variable: 'GH_TOKEN')]) {
359
+ utilObj.runCmd("""
360
+ set -euo pipefail
361
+ cd freshapps-k8s
362
+ git pull origin main
363
+ git checkout -b ${branchNameTeardown}
364
+
365
+ isoforge teardown \\
366
+ --service ${serviceName} \\
367
+ --namespace shiftleft_feature_stack
368
+
369
+ if [[ -n "\$(git status --porcelain)" ]]; then
370
+ git add .
371
+ git commit -m "Teardown ${serviceName} in shiftleftFeatureStack"
372
+ git push origin ${branchNameTeardown}
373
+ else
374
+ echo "No changes to commit for teardown, check previous steps"
375
+ exit 1
376
+ fi
377
+
378
+ gh pr create \\
379
+ --title "Staging Isoforge-Teardown-${BUILD_NUMBER} ${serviceName}" \\
380
+ --body "Isolation teardown for ${serviceName} in shiftleftFeatureStack" \\
381
+ --base main \\
382
+ --head ${branchNameTeardown}
383
+
384
+ gh pr merge ${branchNameTeardown} \\
385
+ --squash \\
386
+ --admin
387
+ """, NODE_VERSION)
388
+ }
389
+
390
+ withCredentials([string(credentialsId: 'ARGO_API_TOKEN', variable: 'ARGO_API_TOKEN')]) {
391
+ utilObj.runCmd("""
392
+ argoapi sync
393
+ """, NODE_VERSION)
394
+ }
395
+ }
396
+ }
397
+
398
+ def generateQualityReport() {
399
+ ensureShiftleftCli()
400
+ echo "Generating Combined Quality Report..."
401
+ try {
402
+ utilObj.runCmd("""
403
+ set -euo pipefail
404
+ cd postman/scripts
405
+
406
+ # Stage Stryker/nyc HTML under postman/reports/artifacts for relative links in quality report
407
+ ./stage-report-artifacts.sh "\${WORKSPACE}" "\${WORKSPACE}/postman/reports" || true
408
+
409
+ # Generate combined report — reuses existing results from prior stages:
410
+ # --skip-unit reuses unit test results from Unit Tests stage
411
+ # --skip-mutation reuses Stryker results from Mutation Tests stage
412
+ # --skip-postman reuses Newman JSON from ShiftLeft stage
413
+ # --skip-coverage reuses API coverage matrix already generated
414
+ ./run-all.sh --skip-unit --skip-mutation --skip-postman --skip-coverage --no-delay
415
+ """, NODE_VERSION)
416
+ } catch (Exception e) {
417
+ echo "Quality report generation failed: ${e.getMessage()}"
418
+ currentBuild.result = 'UNSTABLE'
419
+ }
420
+
421
+ if (fileExists('postman/reports')) {
422
+ def qualityReport = sh(
423
+ returnStdout: true,
424
+ script: "ls -t postman/reports/quality-report-*.html 2>/dev/null | head -1 | xargs -r basename"
425
+ ).trim()
426
+ if (qualityReport) {
427
+ publishHTML([
428
+ allowMissing: true,
429
+ alwaysLinkToLastBuild: true,
430
+ keepAll: true,
431
+ reportDir: 'postman/reports',
432
+ reportFiles: qualityReport,
433
+ includes: '**/*',
434
+ reportName: 'QualityReport',
435
+ reportTitles: 'Combined Quality Report'
436
+ ])
437
+ }
438
+ archiveArtifacts artifacts: 'postman/reports/**', allowEmptyArchive: true
439
+ }
440
+ }
441
+
442
+ def updateShiftLeftStatus(String state, String targetUrlPath = '') {
443
+ withCredentials([string(credentialsId: 'GITHUB_RUNWAYCI_TOKEN', variable: 'GITHUB_RUNWAYCI_TOKEN')]) {
444
+ def targetUrl = targetUrlPath ? "${env.BUILD_URL}${targetUrlPath}" : "${env.BUILD_URL}"
445
+ utilObj.runCmd("""
446
+ octocat status --context shiftleft-SERVICE_NAME-postman --description "ShiftLeft SERVICE_NAME Postman" --state ${state} --target_url "${targetUrl}"
447
+ octocat label --label shifted-left
448
+ """, NODE_VERSION)
449
+ }
450
+ }
@@ -0,0 +1,19 @@
1
+ #!/bin/sh
2
+ # Auto-format staged JSON files in postman folder before commit
3
+
4
+ # Get list of staged JSON files in postman folder
5
+ STAGED_JSON=$(git diff --cached --name-only --diff-filter=ACM -- 'postman/**/*.json')
6
+
7
+ if [ -n "$STAGED_JSON" ]; then
8
+ echo "Formatting staged Postman JSON files..."
9
+
10
+ # Format each staged file
11
+ echo "$STAGED_JSON" | while read -r file; do
12
+ if [ -f "$file" ]; then
13
+ npx --prefix postman prettier --write "$file"
14
+ git add "$file"
15
+ fi
16
+ done
17
+
18
+ echo "Done formatting."
19
+ fi
@@ -0,0 +1,5 @@
1
+ {
2
+ "printWidth": 120,
3
+ "tabWidth": 2,
4
+ "useTabs": false
5
+ }
@@ -0,0 +1,147 @@
1
+ # <%= serviceName %> Postman Tests
2
+
3
+ API integration tests using Postman/Newman.
4
+
5
+ ## Prerequisites
6
+
7
+ - Node.js 18+
8
+ - npm 8.4+
9
+ - Application running locally (for local tests)
10
+ <% if (includeStaging) { %>- AWS CLI configured with staging profile (for staging tests)
11
+ - `jq` installed (`brew install jq`)<% } %>
12
+
13
+ ## Setup
14
+
15
+ ```bash
16
+ cd postman
17
+ npm install
18
+ ```
19
+
20
+ ## Running Tests
21
+
22
+ ### Local Environment
23
+
24
+ Start the application first, then:
25
+
26
+ ```bash
27
+ cd postman/scripts
28
+ ./runners/run-tests-local.sh
29
+ ```
30
+ <% if (includeStaging) { %>
31
+ ### Staging Environment
32
+
33
+ ```bash
34
+ cd postman/scripts
35
+ AWS_PROFILE=staging ./runners/run-tests-staging.sh
36
+ ```
37
+
38
+ Staging config is in `postman/config/staging.json` — set your AWS secret name, JWT parameters, and base URL there.
39
+ <% } %>
40
+ ### All Tests + Quality Report (Unit + Mutation + Postman + Coverage)
41
+
42
+ ```bash
43
+ cd postman/scripts
44
+ ./run-all.sh # Run everything (local)
45
+ ./run-all.sh --skip-mutation # Skip slow mutation tests
46
+ ./run-all.sh --env staging # Run Postman against staging
47
+ ./run-all.sh --skip-unit --skip-mutation # Postman + coverage only
48
+ ```
49
+
50
+ ## Test Reports
51
+
52
+ Reports are generated in `postman/reports/`:
53
+ - `quality-report-*.html` — Combined quality report (unit + mutation + Postman + coverage)
54
+ - `consolidated-*.html` — Postman test summary across all collections
55
+ - `api-coverage-matrix-*.html` — API endpoint coverage matrix
56
+ - `test-local-*.html` / `test-staging-*.html` — Per-collection Newman reports
57
+
58
+ ## API Coverage
59
+
60
+ Check that all endpoints have 2xx test coverage:
61
+
62
+ ```bash
63
+ cd postman/scripts
64
+ ./report-generators/java-api-coverage-matrix.sh
65
+ ```
66
+
67
+ ## Mutation Testing
68
+
69
+ Verify unit test quality with PIT:
70
+
71
+ ```bash
72
+ cd postman/scripts
73
+ ./report-generators/mutation-report.sh
74
+ ```
75
+
76
+ ## Directory Structure
77
+
78
+ ```
79
+ postman/
80
+ collections/ # Newman collection JSON files
81
+ environments/ # Postman environment files
82
+ config/ # Environment config (staging.json etc.)
83
+ reports/ # Generated test reports (gitignored)
84
+ scripts/
85
+ run-all.sh # Unified runner (all phases)
86
+ runners/
87
+ run-tests-local.sh # Local Postman tests
88
+ run-tests-staging.sh # Staging Postman tests
89
+ report-generators/
90
+ java-api-coverage-matrix.sh # API coverage analysis
91
+ mutation-report.sh # PIT mutation report
92
+ generate-consolidated-report.sh
93
+ stage-report-artifacts.sh
94
+ auth/ # JWT generation scripts
95
+ infra/ # WireMock start/stop scripts
96
+ lib/ # Python report generators
97
+ ```
98
+
99
+ ## Adding New Tests
100
+
101
+ 1. Create or update collection in `collections/`
102
+ 2. Follow naming: `XX-description.json`
103
+ 3. Add to `runners/run-tests-local.sh`<% if (includeStaging) { %> and `runners/run-tests-staging.sh`<% } %>
104
+ 4. Run coverage report to verify 100% 2xx coverage
105
+
106
+ ## Collection Naming
107
+
108
+ - `01-core.json` - Core CRUD operations
109
+ - `02-authorization.json` - Auth tests
110
+ - `03-validation.json` - Invalid input tests
111
+ - `XX-feature.json` - Feature-specific tests
112
+
113
+ ## Test Naming Conventions
114
+
115
+ - `GET - Endpoint - 200 valid request`
116
+ - `POST - Endpoint - 201 created`
117
+ - `GET - Endpoint - 404 not found`
118
+ - `POST - Endpoint - 400 invalid input`
119
+
120
+ ## Environment Variables
121
+
122
+ Key variables in `environments/`:
123
+ - `base_url` - API base URL
124
+ - `auth_token` - JWT token
125
+ - `environment` - `local`<% if (includeStaging) { %> or `staging`<% } %>
126
+ <% if (includeStaging) { %>
127
+ ## Staging Config (`config/staging.json`)
128
+
129
+ ```json
130
+ {
131
+ "base_url": "https://your-service-staging.freshworks.com/api/v1",
132
+ "aws": {
133
+ "secretName": "your-secret-name",
134
+ "client": "your_client",
135
+ "issuer": "your_issuer",
136
+ "region": "us-west-2"
137
+ },
138
+ "jwt": {
139
+ "api_issuer": "your_client",
140
+ "api_account_id": "1",
141
+ "api_account_domain": "freshworks.com"
142
+ }
143
+ }
144
+ ```
145
+
146
+ AWS credentials are fetched via CLI (`AWS_PROFILE=staging`) and cleared before npm runs to prevent supply chain exposure.
147
+ <% } %>