techbro 0.0.0-stage → 0.5.0

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 (122) hide show
  1. package/README.md +453 -2
  2. package/assets/baseline-rules.yml +60 -0
  3. package/assets/caller-workflow.yml +26 -0
  4. package/assets/review-action.yml +27 -0
  5. package/assets/review-config.json +4 -0
  6. package/assets/review-workflow.yml +443 -0
  7. package/assets/review.mjs +721 -0
  8. package/bin/techbro.js +9 -0
  9. package/package.json +23 -4
  10. package/src/cli.js +690 -0
  11. package/src/cluster.js +134 -0
  12. package/src/config.js +35 -0
  13. package/src/doctor.js +125 -0
  14. package/src/github.js +152 -0
  15. package/src/init.js +167 -0
  16. package/src/kube-project.js +468 -0
  17. package/src/kube.js +317 -0
  18. package/src/managed.js +63 -0
  19. package/src/project.js +202 -0
  20. package/src/review.js +165 -0
  21. package/src/secret.js +80 -0
  22. package/src/setup.js +91 -0
  23. package/src/team-workflow.js +106 -0
  24. package/src/team.js +145 -0
  25. package/src/templates.js +44 -0
  26. package/src/upgrade.js +94 -0
  27. package/templates/_default/.agents/skills/README.md +36 -0
  28. package/templates/_default/.agents/skills/database-migrations/SKILL.md +72 -0
  29. package/templates/_default/.agents/skills/frontend-feature/SKILL.md +65 -0
  30. package/templates/_default/.agents/skills/release-safe-change/SKILL.md +75 -0
  31. package/templates/_default/.agents/skills/rust-backend-feature/SKILL.md +68 -0
  32. package/templates/_default/.editorconfig +15 -0
  33. package/templates/_default/.env.example +9 -0
  34. package/templates/_default/.github/workflows/ci.yml +60 -0
  35. package/templates/_default/.nvmrc +1 -0
  36. package/templates/_default/Cargo.lock +1834 -0
  37. package/templates/_default/Cargo.toml +3 -0
  38. package/templates/_default/README.md +257 -0
  39. package/templates/_default/TEMPLATE.md +29 -0
  40. package/templates/_default/_dockerignore +8 -0
  41. package/templates/_default/_gitignore +8 -0
  42. package/templates/_default/apps/backend/Cargo.toml +22 -0
  43. package/templates/_default/apps/backend/Dockerfile +34 -0
  44. package/templates/_default/apps/backend/README.md +13 -0
  45. package/templates/_default/apps/backend/src/config.rs +111 -0
  46. package/templates/_default/apps/backend/src/controllers/health.rs +36 -0
  47. package/templates/_default/apps/backend/src/controllers/items.rs +79 -0
  48. package/templates/_default/apps/backend/src/controllers/mod.rs +2 -0
  49. package/templates/_default/apps/backend/src/error.rs +115 -0
  50. package/templates/_default/apps/backend/src/lib.rs +7 -0
  51. package/templates/_default/apps/backend/src/main.rs +71 -0
  52. package/templates/_default/apps/backend/src/models/item.rs +94 -0
  53. package/templates/_default/apps/backend/src/models/mod.rs +1 -0
  54. package/templates/_default/apps/backend/src/routes.rs +94 -0
  55. package/templates/_default/apps/backend/src/state.rs +12 -0
  56. package/templates/_default/apps/backend/src/views/items.rs +18 -0
  57. package/templates/_default/apps/backend/src/views/mod.rs +1 -0
  58. package/templates/_default/apps/backend/tests/api.rs +234 -0
  59. package/templates/_default/apps/frontend/.env.example +2 -0
  60. package/templates/_default/apps/frontend/.prettierignore +3 -0
  61. package/templates/_default/apps/frontend/.prettierrc +1 -0
  62. package/templates/_default/apps/frontend/Dockerfile +15 -0
  63. package/templates/_default/apps/frontend/README.md +39 -0
  64. package/templates/_default/apps/frontend/_dockerignore +3 -0
  65. package/templates/_default/apps/frontend/_gitignore +24 -0
  66. package/templates/_default/apps/frontend/components.json +25 -0
  67. package/templates/_default/apps/frontend/eslint.config.js +37 -0
  68. package/templates/_default/apps/frontend/index.html +13 -0
  69. package/templates/_default/apps/frontend/jsconfig.json +7 -0
  70. package/templates/_default/apps/frontend/nginx.conf +30 -0
  71. package/templates/_default/apps/frontend/package-lock.json +4338 -0
  72. package/templates/_default/apps/frontend/package.json +49 -0
  73. package/templates/_default/apps/frontend/public/favicon.svg +1 -0
  74. package/templates/_default/apps/frontend/src/App.jsx +11 -0
  75. package/templates/_default/apps/frontend/src/App.test.jsx +40 -0
  76. package/templates/_default/apps/frontend/src/api/client.js +50 -0
  77. package/templates/_default/apps/frontend/src/api/client.test.js +70 -0
  78. package/templates/_default/apps/frontend/src/api/items.js +26 -0
  79. package/templates/_default/apps/frontend/src/api/system.js +11 -0
  80. package/templates/_default/apps/frontend/src/components/ItemList.jsx +20 -0
  81. package/templates/_default/apps/frontend/src/components/ItemList.test.jsx +18 -0
  82. package/templates/_default/apps/frontend/src/components/Layout.jsx +35 -0
  83. package/templates/_default/apps/frontend/src/components/StatusBadge.jsx +22 -0
  84. package/templates/_default/apps/frontend/src/components/StepList.jsx +19 -0
  85. package/templates/_default/apps/frontend/src/components/ui/button.jsx +52 -0
  86. package/templates/_default/apps/frontend/src/components/ui/card.jsx +83 -0
  87. package/templates/_default/apps/frontend/src/components/ui/input.jsx +19 -0
  88. package/templates/_default/apps/frontend/src/hooks/useBackendStatus.js +33 -0
  89. package/templates/_default/apps/frontend/src/hooks/useItems.js +47 -0
  90. package/templates/_default/apps/frontend/src/index.css +130 -0
  91. package/templates/_default/apps/frontend/src/lib/config.js +2 -0
  92. package/templates/_default/apps/frontend/src/lib/utils.js +6 -0
  93. package/templates/_default/apps/frontend/src/main.jsx +11 -0
  94. package/templates/_default/apps/frontend/src/models/items.js +24 -0
  95. package/templates/_default/apps/frontend/src/models/items.test.js +24 -0
  96. package/templates/_default/apps/frontend/src/pages/ItemsPage.jsx +41 -0
  97. package/templates/_default/apps/frontend/src/pages/WelcomePage.jsx +156 -0
  98. package/templates/_default/apps/frontend/src/pages/WelcomePage.test.jsx +55 -0
  99. package/templates/_default/apps/frontend/src/routes.jsx +17 -0
  100. package/templates/_default/apps/frontend/src/shadcn.css +655 -0
  101. package/templates/_default/apps/frontend/src/test/setup.js +1 -0
  102. package/templates/_default/apps/frontend/vite.config.js +25 -0
  103. package/templates/_default/database/Dockerfile +9 -0
  104. package/templates/_default/database/README.md +15 -0
  105. package/templates/_default/database/migrations/postgres/20261008120000_create_items.sql +5 -0
  106. package/templates/_default/database/migrations/sqlite/20261008120000_create_items.sql +5 -0
  107. package/templates/_default/docker-compose.prod.yml +53 -0
  108. package/templates/_default/docker-compose.yml +69 -0
  109. package/templates/_default/docs/api/README.md +41 -0
  110. package/templates/_default/docs/api/openapi.yaml +152 -0
  111. package/templates/_default/docs/decisions/0001-adopt-engineering-convention.md +13 -0
  112. package/templates/_default/docs/decisions/README.md +6 -0
  113. package/templates/_default/docs/deployment.md +46 -0
  114. package/templates/_default/docs/docker.md +63 -0
  115. package/templates/_default/docs/getting-started.md +214 -0
  116. package/templates/_default/package.json +15 -0
  117. package/templates/_default/rust-toolchain.toml +3 -0
  118. package/templates/_default/scripts/check +10 -0
  119. package/templates/_default/scripts/db-migrate +25 -0
  120. package/templates/_default/scripts/install-hooks +5 -0
  121. package/templates/_default/scripts/pre-push +3 -0
  122. package/templates/_default/scripts/setup +26 -0
@@ -0,0 +1,443 @@
1
+ name: techbro review
2
+
3
+ on:
4
+ workflow_call:
5
+ inputs:
6
+ sha:
7
+ description: Commit sha to review (40 hex)
8
+ required: true
9
+ type: string
10
+ request_id:
11
+ description: Request UUID
12
+ required: true
13
+ type: string
14
+ secrets:
15
+ SONAR_TOKEN:
16
+ required: false
17
+
18
+ permissions: {}
19
+
20
+ jobs:
21
+ authorize:
22
+ # Trusted helper, rules and team data come from the team action, never from the project checkout.
23
+ if: github.ref_name == github.event.repository.default_branch
24
+ runs-on: ubuntu-latest
25
+ timeout-minutes: 5
26
+ permissions:
27
+ contents: read
28
+ outputs:
29
+ ok: ${{ steps.authz.outputs.ok }}
30
+ project: ${{ steps.authz.outputs.project }}
31
+ owner: ${{ steps.authz.outputs.owner }}
32
+ repo: ${{ steps.authz.outputs.repo }}
33
+ sonar_key: ${{ steps.authz.outputs.sonar_key }}
34
+ test_configured: ${{ steps.authz.outputs.test_configured }}
35
+ test_runtime: ${{ steps.authz.outputs.test_runtime }}
36
+ test_node_version: ${{ steps.authz.outputs.test_node_version }}
37
+ test_workdir: ${{ steps.authz.outputs.test_workdir }}
38
+ test_install: ${{ steps.authz.outputs.test_install }}
39
+ test_command: ${{ steps.authz.outputs.test_command }}
40
+ test_coverage: ${{ steps.authz.outputs.test_coverage }}
41
+ test_rust_version: ${{ steps.authz.outputs.test_rust_version }}
42
+ fail_on: ${{ steps.authz.outputs.fail_on }}
43
+ sonar_mode: ${{ steps.authz.outputs.sonar_mode }}
44
+ dup_mode: ${{ steps.authz.outputs.dup_mode }}
45
+ dup_max_percent: ${{ steps.authz.outputs.dup_max_percent }}
46
+ steps:
47
+ - name: Stage trusted helper, rules and team data
48
+ uses: __TEAM__/.github/actions/techbro@main
49
+ timeout-minutes: 3
50
+ with:
51
+ expect-version: "__VERSION__"
52
+ - name: Authorize request
53
+ id: authz
54
+ timeout-minutes: 3
55
+ env:
56
+ GH_TOKEN: ${{ github.token }}
57
+ REPOSITORY: ${{ github.repository }}
58
+ EVENT_NAME: ${{ github.event_name }}
59
+ ACTOR: ${{ github.actor }}
60
+ REF_NAME: ${{ github.ref_name }}
61
+ DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
62
+ INPUT_SHA: ${{ inputs.sha }}
63
+ INPUT_REQUEST_ID: ${{ inputs.request_id }}
64
+ TEAM_DIR: team
65
+ run: node ctl/review.mjs authorize
66
+ - name: Upload trusted control files
67
+ uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
68
+ timeout-minutes: 3
69
+ with:
70
+ name: techbro-ctl
71
+ path: ctl
72
+ retention-days: 1
73
+ if-no-files-found: error
74
+
75
+ prepare:
76
+ # Checks out the exact sha with the job token only, then archives it without .git. It never runs project code.
77
+ needs: authorize
78
+ if: needs.authorize.outputs.ok == 'true'
79
+ runs-on: ubuntu-latest
80
+ timeout-minutes: 15
81
+ permissions:
82
+ contents: read
83
+ outputs:
84
+ default_head: ${{ steps.snap.outputs.default_head }}
85
+ steps:
86
+ - name: Download control files
87
+ uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
88
+ timeout-minutes: 3
89
+ with:
90
+ name: techbro-ctl
91
+ path: ctl
92
+ - name: Checkout exact commit
93
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
94
+ timeout-minutes: 5
95
+ with:
96
+ ref: ${{ inputs.sha }}
97
+ path: checkout
98
+ persist-credentials: false
99
+ - name: Snapshot source (no .git)
100
+ id: snap
101
+ timeout-minutes: 10
102
+ env:
103
+ GH_TOKEN: ${{ github.token }}
104
+ run: node ctl/review.mjs snapshot
105
+ - name: Upload source snapshot
106
+ uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
107
+ timeout-minutes: 5
108
+ with:
109
+ name: techbro-source
110
+ path: snapshot
111
+ retention-days: 1
112
+ include-hidden-files: true
113
+ if-no-files-found: error
114
+
115
+ tests:
116
+ # Runs untrusted project code with no credentials and no job token. Its outputs are untrusted.
117
+ needs: [authorize, prepare]
118
+ if: needs.prepare.result == 'success' && needs.authorize.outputs.test_configured == 'true'
119
+ runs-on: ubuntu-latest
120
+ timeout-minutes: 40
121
+ permissions: {}
122
+ outputs:
123
+ install_outcome: ${{ steps.install.outcome }}
124
+ test_outcome: ${{ steps.test.outcome }}
125
+ cov_artifact_id: ${{ steps.upcov.outputs.artifact-id }}
126
+ env:
127
+ TB_WORKDIR: ${{ needs.authorize.outputs.test_workdir }}
128
+ TB_INSTALL: ${{ needs.authorize.outputs.test_install }}
129
+ TB_COMMAND: ${{ needs.authorize.outputs.test_command }}
130
+ TB_COVERAGE: ${{ needs.authorize.outputs.test_coverage }}
131
+ steps:
132
+ - name: Download source
133
+ uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
134
+ timeout-minutes: 5
135
+ with:
136
+ name: techbro-source
137
+ path: src
138
+ - name: Set up Node
139
+ if: needs.authorize.outputs.test_runtime == 'node' || needs.authorize.outputs.test_runtime == 'rust-node'
140
+ uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
141
+ timeout-minutes: 5
142
+ with:
143
+ node-version: ${{ needs.authorize.outputs.test_node_version }}
144
+ - name: Set up Rust toolchain (project rust-toolchain file, or pinned override)
145
+ if: needs.authorize.outputs.test_runtime == 'rust-node'
146
+ timeout-minutes: 10
147
+ working-directory: src/${{ needs.authorize.outputs.test_workdir }}
148
+ env:
149
+ TB_RUST: ${{ needs.authorize.outputs.test_rust_version }}
150
+ run: |
151
+ if [ -n "$TB_RUST" ]; then
152
+ rustup toolchain install "$TB_RUST" --profile minimal -c rustfmt,clippy
153
+ echo "RUSTUP_TOOLCHAIN=$TB_RUST" >> "$GITHUB_ENV"
154
+ elif [ -f rust-toolchain.toml ] || [ -f rust-toolchain ]; then
155
+ rustup toolchain install --profile minimal -c rustfmt,clippy
156
+ else
157
+ rustup toolchain install stable --profile minimal -c rustfmt,clippy
158
+ echo "RUSTUP_TOOLCHAIN=stable" >> "$GITHUB_ENV"
159
+ fi
160
+ cargo --version
161
+ - name: Install
162
+ id: install
163
+ if: env.TB_INSTALL != ''
164
+ continue-on-error: true
165
+ timeout-minutes: 10
166
+ working-directory: src/${{ needs.authorize.outputs.test_workdir }}
167
+ run: bash -c "$TB_INSTALL"
168
+ - name: Test
169
+ id: test
170
+ if: steps.install.outcome != 'failure'
171
+ continue-on-error: true
172
+ timeout-minutes: 20
173
+ working-directory: src/${{ needs.authorize.outputs.test_workdir }}
174
+ run: bash -c "$TB_COMMAND"
175
+ - name: Collect coverage
176
+ id: cov
177
+ if: always()
178
+ timeout-minutes: 3
179
+ run: |
180
+ mkdir -p "$RUNNER_TEMP/out"
181
+ f="src/$TB_WORKDIR/$TB_COVERAGE"
182
+ if [ -n "$TB_COVERAGE" ] && [ -f "$f" ] && [ ! -L "$f" ] && [ "$(stat -c %s "$f")" -le 5242880 ]; then
183
+ case "$(realpath "$f")" in
184
+ "$(realpath src)"/*) cp "$f" "$RUNNER_TEMP/out/lcov.info" && echo "present=true" >> "$GITHUB_OUTPUT" ;;
185
+ esac
186
+ fi
187
+ - name: Upload coverage
188
+ id: upcov
189
+ if: always() && steps.cov.outputs.present == 'true'
190
+ uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
191
+ timeout-minutes: 3
192
+ with:
193
+ name: techbro-test-result
194
+ path: ${{ runner.temp }}/out
195
+ retention-days: 1
196
+ if-no-files-found: error
197
+
198
+ opengrep:
199
+ # No secrets, no job token. Raw scanner output (messages, snippets) is never uploaded or logged.
200
+ needs: [authorize, prepare]
201
+ if: needs.prepare.result == 'success'
202
+ runs-on: ubuntu-latest
203
+ timeout-minutes: 45
204
+ permissions: {}
205
+ outputs:
206
+ result_sha: ${{ steps.sanitize.outputs.sha }}
207
+ env:
208
+ OPENGREP_VERSION: v1.30.2
209
+ OPENGREP_SHA256: a66aa3278457f02b287b985a45b6762aebcaba5000f2689245fd1ed86d1456c7
210
+ steps:
211
+ - name: Download control files
212
+ uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
213
+ timeout-minutes: 3
214
+ with:
215
+ name: techbro-ctl
216
+ path: ctl
217
+ - name: Download source
218
+ uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
219
+ timeout-minutes: 5
220
+ with:
221
+ name: techbro-source
222
+ path: src
223
+ - name: Install opengrep (pinned, sha256-verified)
224
+ timeout-minutes: 5
225
+ run: |
226
+ curl -fsSL --retry 3 -o "$RUNNER_TEMP/opengrep" "https://github.com/opengrep/opengrep/releases/download/${OPENGREP_VERSION}/opengrep_manylinux_x86"
227
+ echo "${OPENGREP_SHA256} $RUNNER_TEMP/opengrep" | sha256sum -c -
228
+ chmod +x "$RUNNER_TEMP/opengrep"
229
+ - name: Scan
230
+ id: sanitize
231
+ timeout-minutes: 30
232
+ run: |
233
+ mkdir -p "$RUNNER_TEMP/out"
234
+ cfg=(--config "$GITHUB_WORKSPACE/ctl/rules/baseline.yml")
235
+ if [ -d ctl/rules/custom ]; then cfg+=(--config "$GITHUB_WORKSPACE/ctl/rules/custom"); fi
236
+ printf "node_modules/\nvendor/\n.git/\n" > src/.semgrepignore
237
+ rc=0
238
+ (cd src && timeout 1500 "$RUNNER_TEMP/opengrep" scan "${cfg[@]}" --json --output "$RUNNER_TEMP/og-raw.json" --disable-version-check --timeout 30 --max-target-bytes 2000000 .) >/dev/null 2>&1 || rc=$?
239
+ RAW="$RUNNER_TEMP/og-raw.json" RC="$rc" OUT="$RUNNER_TEMP/out/opengrep.json" node ctl/review.mjs sanitize
240
+ - name: Upload sanitized findings
241
+ if: always()
242
+ uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
243
+ timeout-minutes: 3
244
+ with:
245
+ name: techbro-opengrep-result
246
+ path: ${{ runner.temp }}/out/opengrep.json
247
+ retention-days: 1
248
+ if-no-files-found: ignore
249
+
250
+ jscpd:
251
+ # No secrets, no job token, no project config or hooks. Raw clone fragments are never uploaded or logged.
252
+ needs: [authorize, prepare]
253
+ if: needs.prepare.result == 'success' && needs.authorize.outputs.dup_mode == 'required'
254
+ runs-on: ubuntu-latest
255
+ timeout-minutes: 30
256
+ permissions: {}
257
+ outputs:
258
+ result_sha: ${{ steps.jsan.outputs.sha }}
259
+ env:
260
+ JSCPD_VERSION: 5.4.1
261
+ steps:
262
+ - name: Download control files
263
+ uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
264
+ timeout-minutes: 3
265
+ with:
266
+ name: techbro-ctl
267
+ path: ctl
268
+ - name: Download source
269
+ uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
270
+ timeout-minutes: 5
271
+ with:
272
+ name: techbro-source
273
+ path: src
274
+ - name: Set up Node
275
+ uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
276
+ timeout-minutes: 5
277
+ with:
278
+ node-version: "20"
279
+ - name: Install jscpd (pinned, no install scripts)
280
+ timeout-minutes: 5
281
+ run: |
282
+ mkdir -p "$RUNNER_TEMP/jscpd"
283
+ (cd "$RUNNER_TEMP/jscpd" && npm init -y >/dev/null && npm install --ignore-scripts --no-audit --no-fund "jscpd@${JSCPD_VERSION}")
284
+ - name: Detect duplication
285
+ id: jsan
286
+ timeout-minutes: 20
287
+ run: |
288
+ mkdir -p "$RUNNER_TEMP/out" "$RUNNER_TEMP/jraw"
289
+ printf '{}' > "$RUNNER_TEMP/jscpd-config.json"
290
+ rc=0
291
+ (cd src && timeout 1100 "$RUNNER_TEMP/jscpd/node_modules/.bin/jscpd" --config "$RUNNER_TEMP/jscpd-config.json" --reporters json --output "$RUNNER_TEMP/jraw" --min-lines 5 --min-tokens 50 --silent --fail-on-empty --ignore '**/node_modules/**,**/vendor/**,**/dist/**,**/build/**,**/target/**,**/.git/**,**/*.lock,**/package-lock.json,**/pnpm-lock.yaml,**/yarn.lock,**/Cargo.lock,**/*.min.js' .) >/dev/null 2>&1 || rc=$?
292
+ RAW="$RUNNER_TEMP/jraw/jscpd-report.json" RC="$rc" OUT="$RUNNER_TEMP/out/jscpd.json" node ctl/review.mjs jscpd-sanitize
293
+ rm -rf "$RUNNER_TEMP/jraw"
294
+ - name: Upload sanitized duplication result
295
+ if: always()
296
+ uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
297
+ timeout-minutes: 3
298
+ with:
299
+ name: techbro-jscpd-result
300
+ path: ${{ runner.temp }}/out/jscpd.json
301
+ retention-days: 1
302
+ if-no-files-found: ignore
303
+
304
+ sonar:
305
+ # Credential job: only the scanner runs here, never install/test scripts. Project scanner settings are removed.
306
+ needs: [authorize, prepare, tests]
307
+ if: ${{ !cancelled() && needs.prepare.result == 'success' }}
308
+ runs-on: ubuntu-latest
309
+ timeout-minutes: 40
310
+ concurrency:
311
+ group: techbro-sonar-${{ needs.authorize.outputs.sonar_key || github.run_id }}
312
+ cancel-in-progress: false
313
+ permissions: {}
314
+ outputs:
315
+ result_sha: ${{ steps.sres.outputs.sha }}
316
+ steps:
317
+ - name: Download control files
318
+ uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
319
+ timeout-minutes: 3
320
+ with:
321
+ name: techbro-ctl
322
+ path: ctl
323
+ - name: Download source
324
+ uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
325
+ timeout-minutes: 5
326
+ with:
327
+ name: techbro-source
328
+ path: src
329
+ - name: Check Sonar configuration
330
+ id: gate
331
+ timeout-minutes: 3
332
+ env:
333
+ SONAR_HOST_URL: ${{ vars.SONAR_HOST_URL }}
334
+ HAS_TOKEN: ${{ secrets.SONAR_TOKEN != '' }}
335
+ SONAR_KEY: ${{ needs.authorize.outputs.sonar_key }}
336
+ DEFAULT_HEAD: ${{ needs.prepare.outputs.default_head }}
337
+ SONAR_MODE: ${{ needs.authorize.outputs.sonar_mode }}
338
+ GATE_OUT: ${{ runner.temp }}/gate.json
339
+ run: node ctl/review.mjs sonar-gate
340
+ - name: Download test coverage
341
+ if: steps.gate.outputs.status == 'ready' && needs.tests.result == 'success' && needs.tests.outputs.cov_artifact_id != ''
342
+ continue-on-error: true
343
+ uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
344
+ timeout-minutes: 3
345
+ with:
346
+ artifact-ids: ${{ needs.tests.outputs.cov_artifact_id }}
347
+ merge-multiple: true
348
+ path: cov
349
+ - name: Import bounded LCOV coverage
350
+ id: cov
351
+ if: steps.gate.outputs.status == 'ready'
352
+ timeout-minutes: 3
353
+ env:
354
+ COV_DIR: cov
355
+ COV_XML: ${{ runner.temp }}/sonar-coverage.xml
356
+ COV_OUT: ${{ runner.temp }}/cov.json
357
+ run: node ctl/review.mjs sonar-coverage
358
+ - name: Remove project scanner settings
359
+ if: steps.gate.outputs.status == 'ready'
360
+ timeout-minutes: 3
361
+ run: rm -rf src/sonar-project.properties src/.sonarcloud.properties src/.scannerwork
362
+ - name: Sonar scan
363
+ id: scan
364
+ if: steps.gate.outputs.status == 'ready'
365
+ continue-on-error: true
366
+ uses: SonarSource/sonarqube-scan-action@d209202bc7d53ff1cc128f7f907dac145c9d6ae9 # v8.3.0
367
+ timeout-minutes: 25
368
+ env:
369
+ SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
370
+ SONAR_HOST_URL: ${{ vars.SONAR_HOST_URL }}
371
+ with:
372
+ projectBaseDir: src
373
+ args: >
374
+ -Dsonar.projectKey=${{ needs.authorize.outputs.sonar_key }}
375
+ -Dsonar.sources=.
376
+ -Dsonar.scm.disabled=true
377
+ -Dsonar.qualitygate.wait=true
378
+ -Dsonar.qualitygate.timeout=600
379
+ ${{ steps.cov.outputs.arg }}
380
+ - name: Collect Sonar result
381
+ id: sres
382
+ if: always()
383
+ timeout-minutes: 5
384
+ env:
385
+ SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
386
+ SONAR_KEY: ${{ needs.authorize.outputs.sonar_key }}
387
+ SCAN_OUTCOME: ${{ steps.scan.outcome }}
388
+ GATE_OUT: ${{ runner.temp }}/gate.json
389
+ COV_OUT: ${{ runner.temp }}/cov.json
390
+ OUT: ${{ runner.temp }}/out/sonar.json
391
+ run: node ctl/review.mjs sonar-result
392
+ - name: Upload Sonar result
393
+ if: always()
394
+ uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
395
+ timeout-minutes: 3
396
+ with:
397
+ name: techbro-sonar-result
398
+ path: ${{ runner.temp }}/out/sonar.json
399
+ retention-days: 1
400
+ if-no-files-found: ignore
401
+
402
+ report:
403
+ needs: [authorize, prepare, tests, opengrep, jscpd, sonar]
404
+ if: ${{ always() }}
405
+ runs-on: ubuntu-latest
406
+ timeout-minutes: 10
407
+ permissions:
408
+ contents: read
409
+ steps:
410
+ - name: Stage trusted helper
411
+ uses: __TEAM__/.github/actions/techbro@main
412
+ timeout-minutes: 3
413
+ with:
414
+ expect-version: "__VERSION__"
415
+ - name: Download job results
416
+ continue-on-error: true
417
+ uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
418
+ timeout-minutes: 3
419
+ with:
420
+ pattern: techbro-*-result
421
+ path: ${{ runner.temp }}/results
422
+ - name: Build report
423
+ timeout-minutes: 3
424
+ env:
425
+ NEEDS: ${{ toJSON(needs) }}
426
+ OG_SHA: ${{ needs.opengrep.outputs.result_sha }}
427
+ SN_SHA: ${{ needs.sonar.outputs.result_sha }}
428
+ JD_SHA: ${{ needs.jscpd.outputs.result_sha }}
429
+ ART_DIR: ${{ runner.temp }}/results
430
+ RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
431
+ INPUT_PROJECT: ${{ needs.authorize.outputs.project }}
432
+ INPUT_SHA: ${{ inputs.sha }}
433
+ INPUT_REQUEST_ID: ${{ inputs.request_id }}
434
+ run: node ctl/review.mjs report
435
+ - name: Upload report
436
+ if: always()
437
+ uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
438
+ timeout-minutes: 3
439
+ with:
440
+ name: techbro-review-report
441
+ path: report
442
+ retention-days: 14
443
+ if-no-files-found: error