secure-husky-setup 1.0.8 → 1.0.10
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.
- package/lib/ci.js +104 -137
- package/lib/sonarqube.js +1 -1
- package/package.json +1 -1
package/lib/ci.js
CHANGED
|
@@ -5,19 +5,6 @@
|
|
|
5
5
|
// File: lib/ci.js
|
|
6
6
|
// Purpose: Sets up Newman API tests + Smoke Tests as a pre-push git hook
|
|
7
7
|
// and copies the GitHub Actions CI workflow into the project.
|
|
8
|
-
//
|
|
9
|
-
// FLEXIBILITY NOTE:
|
|
10
|
-
// Test logic lives in scripts/run-ci-checks.sh (standalone script).
|
|
11
|
-
// The pre-push hook simply calls that script.
|
|
12
|
-
// To move tests to pre-commit in future, just add one line to pre-commit hook:
|
|
13
|
-
// ./scripts/run-ci-checks.sh
|
|
14
|
-
// No logic needs to be rewritten.
|
|
15
|
-
//
|
|
16
|
-
// New files introduced:
|
|
17
|
-
// - lib/ci.js (this file)
|
|
18
|
-
// - templates/ci-tests.yml (GitHub Actions workflow template)
|
|
19
|
-
// Changes made to existing files:
|
|
20
|
-
// - bin/index.js (4 new lines added — see comments there)
|
|
21
8
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
22
9
|
|
|
23
10
|
const fs = require('fs-extra');
|
|
@@ -25,14 +12,8 @@ const path = require('path');
|
|
|
25
12
|
const { execSync } = require('child_process');
|
|
26
13
|
const { logInfo, logSuccess, logError } = require('./logger');
|
|
27
14
|
|
|
28
|
-
// Added by Arjun — path to the CI workflow template bundled with this package
|
|
29
15
|
const TEMPLATE_PATH = path.resolve(__dirname, '../templates/ci-tests.yml');
|
|
30
16
|
|
|
31
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
32
|
-
// Added by Arjun — writes scripts/run-ci-checks.sh into the project
|
|
33
|
-
// This is the STANDALONE script containing all test logic.
|
|
34
|
-
// It can be called from pre-push, pre-commit, or any other hook.
|
|
35
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
36
17
|
exports.setupCIScript = async () => {
|
|
37
18
|
const scriptsDir = path.join(process.cwd(), 'scripts');
|
|
38
19
|
const scriptPath = path.join(scriptsDir, 'run-ci-checks.sh');
|
|
@@ -51,10 +32,6 @@ exports.setupCIScript = async () => {
|
|
|
51
32
|
logInfo("To move tests to pre-commit in future: add './scripts/run-ci-checks.sh' to .husky/pre-commit.");
|
|
52
33
|
};
|
|
53
34
|
|
|
54
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
55
|
-
// Added by Arjun — sets up .husky/pre-push hook
|
|
56
|
-
// Simply calls run-ci-checks.sh — no logic lives here.
|
|
57
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
58
35
|
exports.setupPrePushHook = async () => {
|
|
59
36
|
const huskyDir = path.join(process.cwd(), '.husky');
|
|
60
37
|
const hookPath = path.join(huskyDir, 'pre-push');
|
|
@@ -75,9 +52,6 @@ exports.setupPrePushHook = async () => {
|
|
|
75
52
|
logSuccess("Pre-push hook created — calls scripts/run-ci-checks.sh.");
|
|
76
53
|
};
|
|
77
54
|
|
|
78
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
79
|
-
// Added by Arjun — copies ci-tests.yml into .github/workflows/
|
|
80
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
81
55
|
exports.setupCIWorkflow = async () => {
|
|
82
56
|
const targetDir = path.join(process.cwd(), '.github', 'workflows');
|
|
83
57
|
const targetFile = path.join(targetDir, 'ci-tests.yml');
|
|
@@ -99,9 +73,6 @@ exports.setupCIWorkflow = async () => {
|
|
|
99
73
|
logSuccess("GitHub Actions workflow copied to .github/workflows/ci-tests.yml");
|
|
100
74
|
};
|
|
101
75
|
|
|
102
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
103
|
-
// Added by Arjun — validates package.json has required scripts
|
|
104
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
105
76
|
exports.validateProject = async () => {
|
|
106
77
|
const pkgPath = path.join(process.cwd(), 'package.json');
|
|
107
78
|
|
|
@@ -128,9 +99,6 @@ exports.validateProject = async () => {
|
|
|
128
99
|
}
|
|
129
100
|
};
|
|
130
101
|
|
|
131
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
132
|
-
// Added by Arjun — ensures package-lock.json exists (required by npm ci)
|
|
133
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
134
102
|
exports.ensurePackageLock = async () => {
|
|
135
103
|
const lockPath = path.join(process.cwd(), 'package-lock.json');
|
|
136
104
|
const yarnPath = path.join(process.cwd(), 'yarn.lock');
|
|
@@ -149,39 +117,23 @@ exports.ensurePackageLock = async () => {
|
|
|
149
117
|
}
|
|
150
118
|
};
|
|
151
119
|
|
|
152
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
153
|
-
// Pre-push hook — thin wrapper, just calls the standalone script
|
|
154
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
155
120
|
function buildPrePushHook() {
|
|
156
121
|
return `#!/bin/sh
|
|
157
122
|
|
|
158
123
|
# ---------------------------------------------------------------
|
|
159
124
|
# Pre-push hook — Newman + Smoke Tests
|
|
160
125
|
# Delegates all logic to scripts/run-ci-checks.sh
|
|
161
|
-
#
|
|
162
|
-
# To move tests to pre-commit in future:
|
|
163
|
-
# Remove this file and add this line to .husky/pre-commit:
|
|
164
|
-
# ./scripts/run-ci-checks.sh
|
|
165
126
|
# ---------------------------------------------------------------
|
|
166
127
|
|
|
167
128
|
./scripts/run-ci-checks.sh
|
|
168
129
|
`;
|
|
169
130
|
}
|
|
170
131
|
|
|
171
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
172
|
-
// Standalone CI checks script — ALL test logic lives here
|
|
173
|
-
// Can be called from pre-push, pre-commit, CI, or manually:
|
|
174
|
-
// sh scripts/run-ci-checks.sh
|
|
175
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
176
132
|
function buildCIScript() {
|
|
177
133
|
return `#!/bin/sh
|
|
178
134
|
|
|
179
135
|
# ---------------------------------------------------------------
|
|
180
136
|
# run-ci-checks.sh — Smoke Tests + Newman API Tests
|
|
181
|
-
#
|
|
182
|
-
# Called by .husky/pre-push by default.
|
|
183
|
-
# To move to pre-commit: add './scripts/run-ci-checks.sh' to .husky/pre-commit
|
|
184
|
-
# To run manually: sh scripts/run-ci-checks.sh
|
|
185
137
|
# ---------------------------------------------------------------
|
|
186
138
|
|
|
187
139
|
# ---------------------------------------------------------------
|
|
@@ -214,110 +166,125 @@ echo ""
|
|
|
214
166
|
echo "[CI Checks] Starting checks..."
|
|
215
167
|
|
|
216
168
|
# ---------------------------------------------------------------
|
|
217
|
-
#
|
|
169
|
+
# Check if start script exists
|
|
218
170
|
# ---------------------------------------------------------------
|
|
219
|
-
|
|
220
|
-
|
|
171
|
+
START_SCRIPT=$(node -e "const p=require('./package.json'); console.log(p.scripts&&p.scripts.start?'yes':'no')" 2>/dev/null)
|
|
172
|
+
TEST_SCRIPT=$(node -e "const p=require('./package.json'); console.log(p.scripts&&p.scripts.test?'yes':'no')" 2>/dev/null)
|
|
221
173
|
|
|
222
|
-
|
|
223
|
-
|
|
174
|
+
if [ "$START_SCRIPT" = "no" ]; then
|
|
175
|
+
echo "[Smoke Tests] No start script in package.json — skipping smoke tests."
|
|
176
|
+
else
|
|
224
177
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
178
|
+
# ---------------------------------------------------------------
|
|
179
|
+
# Step 1: Smoke Tests
|
|
180
|
+
# ---------------------------------------------------------------
|
|
181
|
+
echo ""
|
|
182
|
+
echo "[Smoke Tests] Starting server..."
|
|
183
|
+
|
|
184
|
+
npm start &
|
|
185
|
+
SERVER_PID=\$!
|
|
186
|
+
|
|
187
|
+
# Auto-detect port — tries common ports
|
|
188
|
+
SERVER_UP=0
|
|
189
|
+
for i in \$(seq 1 30); do
|
|
190
|
+
for PORT_TRY in 3000 5000 8000 8080 4000 4200 3001; do
|
|
191
|
+
if curl -sf http://localhost:\$PORT_TRY > /dev/null 2>&1; then
|
|
192
|
+
PORT=\$PORT_TRY
|
|
193
|
+
SERVER_UP=1
|
|
194
|
+
echo "[Smoke Tests] Server is up on port \$PORT."
|
|
195
|
+
break 2
|
|
196
|
+
fi
|
|
197
|
+
done
|
|
198
|
+
echo "[Smoke Tests] Waiting for server... (\$i/30)"
|
|
199
|
+
sleep 1
|
|
200
|
+
done
|
|
201
|
+
|
|
202
|
+
if [ \$SERVER_UP -eq 0 ]; then
|
|
203
|
+
echo "[Smoke Tests] Server did not start in time. Aborting."
|
|
204
|
+
kill \$SERVER_PID 2>/dev/null
|
|
205
|
+
exit 1
|
|
230
206
|
fi
|
|
231
|
-
echo "[Smoke Tests] Waiting for server... (\$i/30)"
|
|
232
|
-
sleep 1
|
|
233
|
-
done
|
|
234
|
-
|
|
235
|
-
echo "[Smoke Tests] Running npm test..."
|
|
236
|
-
npm test
|
|
237
|
-
SMOKE_EXIT=\$?
|
|
238
|
-
|
|
239
|
-
kill \$SERVER_PID 2>/dev/null
|
|
240
207
|
|
|
241
|
-
if [
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
COLLECTIONS=\$(find . \\
|
|
255
|
-
-not -path '*/node_modules/*' \\
|
|
256
|
-
-not -path '*/.git/*' \\
|
|
257
|
-
-not -path '*/scripts/*' \\
|
|
258
|
-
\\( -name "*.postman_collection.json" -o -name "collection.json" \\) \\
|
|
259
|
-
2>/dev/null)
|
|
260
|
-
|
|
261
|
-
if [ -z "\$COLLECTIONS" ]; then
|
|
262
|
-
echo "[Newman] No Postman collection found. Skipping."
|
|
263
|
-
exit 0
|
|
264
|
-
fi
|
|
265
|
-
|
|
266
|
-
if ! command -v newman > /dev/null 2>&1; then
|
|
267
|
-
echo "[Newman] Installing newman globally..."
|
|
268
|
-
npm install -g newman newman-reporter-htmlextra
|
|
269
|
-
fi
|
|
270
|
-
|
|
271
|
-
npm start &
|
|
272
|
-
SERVER_PID=\$!
|
|
273
|
-
|
|
274
|
-
for i in \$(seq 1 30); do
|
|
275
|
-
if curl -sf http://localhost:\${PORT:-3000}/health > /dev/null 2>&1 || \\
|
|
276
|
-
curl -sf http://localhost:\${PORT:-3000} > /dev/null 2>&1; then
|
|
277
|
-
echo "[Newman] Server is up."
|
|
278
|
-
break
|
|
208
|
+
if [ "$TEST_SCRIPT" = "no" ]; then
|
|
209
|
+
echo "[Smoke Tests] No test script in package.json — skipping npm test."
|
|
210
|
+
else
|
|
211
|
+
echo "[Smoke Tests] Running npm test..."
|
|
212
|
+
npm test
|
|
213
|
+
SMOKE_EXIT=\$?
|
|
214
|
+
|
|
215
|
+
if [ \$SMOKE_EXIT -ne 0 ]; then
|
|
216
|
+
kill \$SERVER_PID 2>/dev/null
|
|
217
|
+
echo "[Smoke Tests] Failed. Push blocked."
|
|
218
|
+
exit 1
|
|
219
|
+
fi
|
|
220
|
+
echo "[Smoke Tests] Passed. ✔"
|
|
279
221
|
fi
|
|
280
|
-
sleep 1
|
|
281
|
-
done
|
|
282
222
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
223
|
+
# ---------------------------------------------------------------
|
|
224
|
+
# Step 2: Newman
|
|
225
|
+
# ---------------------------------------------------------------
|
|
226
|
+
echo ""
|
|
227
|
+
echo "[Newman] Looking for Postman collections..."
|
|
228
|
+
|
|
229
|
+
COLLECTIONS=\$(find . \\
|
|
230
|
+
-not -path '*/node_modules/*' \\
|
|
231
|
+
-not -path '*/.git/*' \\
|
|
232
|
+
-not -path '*/scripts/*' \\
|
|
233
|
+
\\( -name "*.postman_collection.json" -o -name "collection.json" \\) \\
|
|
234
|
+
2>/dev/null)
|
|
235
|
+
|
|
236
|
+
if [ -z "\$COLLECTIONS" ]; then
|
|
237
|
+
echo "[Newman] No Postman collection found. Skipping."
|
|
238
|
+
kill \$SERVER_PID 2>/dev/null
|
|
239
|
+
exit 0
|
|
299
240
|
fi
|
|
300
241
|
|
|
301
|
-
newman
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
--reporter-htmlextra-export "newman-reports/\${REPORT_NAME}-report.html" \\
|
|
306
|
-
--bail
|
|
242
|
+
if ! command -v newman > /dev/null 2>&1; then
|
|
243
|
+
echo "[Newman] Installing newman globally..."
|
|
244
|
+
npm install -g newman newman-reporter-htmlextra
|
|
245
|
+
fi
|
|
307
246
|
|
|
308
|
-
|
|
309
|
-
|
|
247
|
+
mkdir -p newman-reports
|
|
248
|
+
|
|
249
|
+
ENV_FILE=\$(find . \\
|
|
250
|
+
-not -path '*/node_modules/*' \\
|
|
251
|
+
-not -path '*/.git/*' \\
|
|
252
|
+
-name "*.postman_environment.json" \\
|
|
253
|
+
2>/dev/null | head -1)
|
|
254
|
+
|
|
255
|
+
NEWMAN_EXIT=0
|
|
256
|
+
for COLLECTION in \$COLLECTIONS; do
|
|
257
|
+
REPORT_NAME=\$(basename "\$COLLECTION" .json)
|
|
258
|
+
echo "[Newman] Running: \$COLLECTION"
|
|
259
|
+
|
|
260
|
+
ENV_FLAG=""
|
|
261
|
+
if [ -n "\$ENV_FILE" ]; then
|
|
262
|
+
ENV_FLAG="--environment \$ENV_FILE"
|
|
263
|
+
fi
|
|
264
|
+
|
|
265
|
+
newman run "\$COLLECTION" \\
|
|
266
|
+
\$ENV_FLAG \\
|
|
267
|
+
--env-var "baseUrl=http://localhost:\${PORT:-3000}" \\
|
|
268
|
+
--reporters cli,htmlextra \\
|
|
269
|
+
--reporter-htmlextra-export "newman-reports/\${REPORT_NAME}-report.html" \\
|
|
270
|
+
--bail
|
|
271
|
+
|
|
272
|
+
if [ \$? -ne 0 ]; then
|
|
273
|
+
NEWMAN_EXIT=1
|
|
274
|
+
fi
|
|
275
|
+
done
|
|
276
|
+
|
|
277
|
+
kill \$SERVER_PID 2>/dev/null
|
|
278
|
+
|
|
279
|
+
if [ \$NEWMAN_EXIT -ne 0 ]; then
|
|
280
|
+
echo "[Newman] One or more collections failed. Push blocked."
|
|
281
|
+
exit 1
|
|
310
282
|
fi
|
|
311
|
-
done
|
|
312
283
|
|
|
313
|
-
|
|
284
|
+
echo "[Newman] All collections passed. ✔"
|
|
314
285
|
|
|
315
|
-
if [ \$NEWMAN_EXIT -ne 0 ]; then
|
|
316
|
-
echo "[Newman] One or more collections failed. Push blocked."
|
|
317
|
-
exit 1
|
|
318
286
|
fi
|
|
319
287
|
|
|
320
|
-
echo "[Newman] All collections passed. ✔"
|
|
321
288
|
exit 0
|
|
322
289
|
`;
|
|
323
290
|
}
|
package/lib/sonarqube.js
CHANGED
|
@@ -12,7 +12,7 @@ const SONAR_PROPS_FILE = 'sonar-project.properties';
|
|
|
12
12
|
// Switch to GCP later by changing SONAR_HOST_URL and SONAR_TOKEN
|
|
13
13
|
// ---------------------------------------------------------------
|
|
14
14
|
const SONAR_HOST_URL = 'http://192.168.1.72:9000';
|
|
15
|
-
const SONAR_TOKEN = '
|
|
15
|
+
const SONAR_TOKEN = 'sqa_4579719d9a294abff391f803ddb08ca6d32d27c5';
|
|
16
16
|
// const SONAR_ORG = 'arjunlatiwala'; // only needed for SonarCloud
|
|
17
17
|
|
|
18
18
|
// Auto-create project on SonarQube via API so developer never needs to do it manually
|