@metasession.co/devaudit-cli 0.1.62 → 0.1.63
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@metasession.co/devaudit-cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.63",
|
|
4
4
|
"description": "DevAudit CLI — installs, syncs, and operates the Metasession SDLC across consumer projects.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@clack/prompts": "^0.8.2",
|
|
36
|
-
"@metasession.co/devaudit-plugin-sdk": "^0.1.
|
|
36
|
+
"@metasession.co/devaudit-plugin-sdk": "^0.1.63",
|
|
37
37
|
"ajv": "^8.20.0",
|
|
38
38
|
"commander": "^12.1.0",
|
|
39
39
|
"consola": "^3.2.3",
|
|
@@ -244,6 +244,13 @@ MAX_ATTEMPTS=${UPLOAD_MAX_ATTEMPTS:-5}
|
|
|
244
244
|
INITIAL_BACKOFF_SECONDS=${UPLOAD_INITIAL_BACKOFF_SECONDS:-1}
|
|
245
245
|
UPLOAD_CONNECT_TIMEOUT_SECONDS=${UPLOAD_CONNECT_TIMEOUT_SECONDS:-10}
|
|
246
246
|
UPLOAD_MAX_TIME_SECONDS=${UPLOAD_MAX_TIME_SECONDS:-120}
|
|
247
|
+
# DevAudit-Installer#189 — files above this size use the presigned R2 URL
|
|
248
|
+
# upload flow (3-step: request URL → PUT to R2 → notify portal) instead of
|
|
249
|
+
# the multipart POST. Avoids the portal's FormData body parser limit.
|
|
250
|
+
# 25MB = 26214400 bytes.
|
|
251
|
+
PRESIGNED_THRESHOLD_BYTES=${PRESIGNED_THRESHOLD:-26214400}
|
|
252
|
+
PRESIGNED_MAX_ATTEMPTS=${PRESIGNED_MAX_ATTEMPTS:-3}
|
|
253
|
+
PRESIGNED_UPLOAD_MAX_TIME_SECONDS=${PRESIGNED_UPLOAD_MAX_TIME_SECONDS:-300}
|
|
247
254
|
|
|
248
255
|
is_unedited_starter_stub() {
|
|
249
256
|
# Match BOTH banner phrasings the SDLC has shipped (v0.1.36 changed
|
|
@@ -253,6 +260,153 @@ is_unedited_starter_stub() {
|
|
|
253
260
|
grep -aqE 'STARTER TEMPLATE.+REPLACE BEFORE' "$1"
|
|
254
261
|
}
|
|
255
262
|
|
|
263
|
+
# DevAudit-Installer#189 — Presigned R2 URL upload for large files (>25MB).
|
|
264
|
+
# 3-step flow: (1) request presigned URL from portal, (2) PUT file to R2,
|
|
265
|
+
# (3) notify portal that upload is complete. Each step retries on 429/5xx
|
|
266
|
+
# and connection errors. Returns 0 on success, 1 on failure.
|
|
267
|
+
upload_presigned() {
|
|
268
|
+
local file="$1"
|
|
269
|
+
local filename
|
|
270
|
+
filename=$(basename "$file")
|
|
271
|
+
local file_size
|
|
272
|
+
file_size=$(stat -c%s "$file" 2>/dev/null || stat -f%z "$file")
|
|
273
|
+
|
|
274
|
+
# Derive a MIME type from the extension (best-effort).
|
|
275
|
+
local mime_type="application/octet-stream"
|
|
276
|
+
case "$filename" in
|
|
277
|
+
*.zip) mime_type="application/zip" ;;
|
|
278
|
+
*.json) mime_type="application/json" ;;
|
|
279
|
+
*.png) mime_type="image/png" ;;
|
|
280
|
+
*.pdf) mime_type="application/pdf" ;;
|
|
281
|
+
*.md) mime_type="text/markdown" ;;
|
|
282
|
+
*.html) mime_type="text/html" ;;
|
|
283
|
+
esac
|
|
284
|
+
|
|
285
|
+
local presign_url="${DEVAUDIT_BASE_URL}/api/evidence/upload-url"
|
|
286
|
+
local complete_url="${DEVAUDIT_BASE_URL}/api/evidence/upload-complete"
|
|
287
|
+
|
|
288
|
+
# --- Step 1: Request presigned upload URL ---
|
|
289
|
+
local attempt backoff http_code curl_exit resp_body upload_url evidence_id
|
|
290
|
+
attempt=1
|
|
291
|
+
backoff=$INITIAL_BACKOFF_SECONDS
|
|
292
|
+
upload_url=""
|
|
293
|
+
evidence_id=""
|
|
294
|
+
while [ "$attempt" -le "$PRESIGNED_MAX_ATTEMPTS" ]; do
|
|
295
|
+
resp_body=$(mktemp)
|
|
296
|
+
http_code=$(curl -s -o "$resp_body" -w "%{http_code}" \
|
|
297
|
+
-X POST -L --max-redirs 3 \
|
|
298
|
+
--connect-timeout "$UPLOAD_CONNECT_TIMEOUT_SECONDS" \
|
|
299
|
+
--max-time "$UPLOAD_MAX_TIME_SECONDS" \
|
|
300
|
+
"$presign_url" \
|
|
301
|
+
-H "Authorization: Bearer ${DEVAUDIT_API_KEY}" \
|
|
302
|
+
-H "Content-Type: application/json" \
|
|
303
|
+
-d "{
|
|
304
|
+
\"projectSlug\": \"${PROJECT_SLUG}\",
|
|
305
|
+
\"requirementId\": \"${REQUIREMENT_ID}\",
|
|
306
|
+
\"evidenceType\": \"${EVIDENCE_TYPE}\",
|
|
307
|
+
\"fileName\": \"${filename}\",
|
|
308
|
+
\"fileSizeBytes\": ${file_size},
|
|
309
|
+
\"mimeType\": \"${mime_type}\",
|
|
310
|
+
\"metadata\": ${METADATA},
|
|
311
|
+
\"releaseVersion\": \"${RELEASE_VERSION}\",
|
|
312
|
+
\"createReleaseIfMissing\": \"${CREATE_RELEASE_IF_MISSING}\",
|
|
313
|
+
\"releaseBranch\": \"${BRANCH}\",
|
|
314
|
+
\"environment\": \"${ENVIRONMENT}\",
|
|
315
|
+
\"evidenceCategory\": \"${EVIDENCE_CATEGORY}\",
|
|
316
|
+
\"sdlcStage\": \"${SDLC_STAGE}\"
|
|
317
|
+
}") || curl_exit=$?
|
|
318
|
+
curl_exit=${curl_exit:-0}
|
|
319
|
+
if [ "$curl_exit" -eq 0 ] && [ "$http_code" -ge 200 ] && [ "$http_code" -lt 300 ]; then
|
|
320
|
+
upload_url=$(jq -r '.uploadUrl // empty' "$resp_body" 2>/dev/null || true)
|
|
321
|
+
evidence_id=$(jq -r '.evidenceId // empty' "$resp_body" 2>/dev/null || true)
|
|
322
|
+
rm -f "$resp_body"
|
|
323
|
+
if [ -n "$upload_url" ] && [ -n "$evidence_id" ] && [ "$upload_url" != "null" ] && [ "$evidence_id" != "null" ]; then
|
|
324
|
+
break
|
|
325
|
+
fi
|
|
326
|
+
# Portal responded 2xx but didn't return presigned URL fields —
|
|
327
|
+
# presigned URL flow not configured. Fall back to multipart.
|
|
328
|
+
echo -n "(portal did not return presigned URL, falling back to multipart) "
|
|
329
|
+
return 255
|
|
330
|
+
fi
|
|
331
|
+
rm -f "$resp_body"
|
|
332
|
+
if [ "$curl_exit" -ne 0 ] || [ "$http_code" = "429" ] || { [ "$http_code" -ge 500 ] && [ "$http_code" -lt 600 ]; }; then
|
|
333
|
+
if [ "$attempt" -lt "$PRESIGNED_MAX_ATTEMPTS" ]; then
|
|
334
|
+
echo -n "(step 1: HTTP ${http_code}, retry in ${backoff}s) "
|
|
335
|
+
sleep "$backoff"
|
|
336
|
+
attempt=$((attempt + 1))
|
|
337
|
+
backoff=$((backoff * 2))
|
|
338
|
+
continue
|
|
339
|
+
fi
|
|
340
|
+
fi
|
|
341
|
+
# Non-retriable error (4xx other than 429).
|
|
342
|
+
echo -n "(step 1 failed: HTTP ${http_code}) "
|
|
343
|
+
return 1
|
|
344
|
+
done
|
|
345
|
+
|
|
346
|
+
if [ -z "$upload_url" ] || [ -z "$evidence_id" ]; then
|
|
347
|
+
echo -n "(step 1: no presigned URL after ${attempt} attempts) "
|
|
348
|
+
return 1
|
|
349
|
+
fi
|
|
350
|
+
|
|
351
|
+
# --- Step 2: Upload directly to R2 via presigned URL ---
|
|
352
|
+
attempt=1
|
|
353
|
+
backoff=$INITIAL_BACKOFF_SECONDS
|
|
354
|
+
while [ "$attempt" -le "$PRESIGNED_MAX_ATTEMPTS" ]; do
|
|
355
|
+
http_code=$(curl -s -o /dev/null -w "%{http_code}" \
|
|
356
|
+
-X PUT \
|
|
357
|
+
-H "Content-Type: ${mime_type}" \
|
|
358
|
+
--connect-timeout "$UPLOAD_CONNECT_TIMEOUT_SECONDS" \
|
|
359
|
+
--max-time "$PRESIGNED_UPLOAD_MAX_TIME_SECONDS" \
|
|
360
|
+
--data-binary @"$file" \
|
|
361
|
+
"$upload_url") || curl_exit=$?
|
|
362
|
+
curl_exit=${curl_exit:-0}
|
|
363
|
+
if [ "$curl_exit" -eq 0 ] && [ "$http_code" -ge 200 ] && [ "$http_code" -lt 300 ]; then
|
|
364
|
+
break
|
|
365
|
+
fi
|
|
366
|
+
if [ "$curl_exit" -ne 0 ] || [ "$http_code" = "429" ] || { [ "$http_code" -ge 500 ] && [ "$http_code" -lt 600 ]; }; then
|
|
367
|
+
if [ "$attempt" -lt "$PRESIGNED_MAX_ATTEMPTS" ]; then
|
|
368
|
+
echo -n "(step 2: HTTP ${http_code}, retry in ${backoff}s) "
|
|
369
|
+
sleep "$backoff"
|
|
370
|
+
attempt=$((attempt + 1))
|
|
371
|
+
backoff=$((backoff * 2))
|
|
372
|
+
continue
|
|
373
|
+
fi
|
|
374
|
+
fi
|
|
375
|
+
echo -n "(step 2 failed: HTTP ${http_code}) "
|
|
376
|
+
return 1
|
|
377
|
+
done
|
|
378
|
+
|
|
379
|
+
# --- Step 3: Notify portal that upload is complete ---
|
|
380
|
+
attempt=1
|
|
381
|
+
backoff=$INITIAL_BACKOFF_SECONDS
|
|
382
|
+
while [ "$attempt" -le "$PRESIGNED_MAX_ATTEMPTS" ]; do
|
|
383
|
+
http_code=$(curl -s -o /dev/null -w "%{http_code}" \
|
|
384
|
+
-X POST -L --max-redirs 3 \
|
|
385
|
+
--connect-timeout "$UPLOAD_CONNECT_TIMEOUT_SECONDS" \
|
|
386
|
+
--max-time "$UPLOAD_MAX_TIME_SECONDS" \
|
|
387
|
+
"$complete_url" \
|
|
388
|
+
-H "Authorization: Bearer ${DEVAUDIT_API_KEY}" \
|
|
389
|
+
-H "Content-Type: application/json" \
|
|
390
|
+
-d "{\"evidenceId\": \"${evidence_id}\"}") || curl_exit=$?
|
|
391
|
+
curl_exit=${curl_exit:-0}
|
|
392
|
+
if [ "$curl_exit" -eq 0 ] && [ "$http_code" -ge 200 ] && [ "$http_code" -lt 300 ]; then
|
|
393
|
+
return 0
|
|
394
|
+
fi
|
|
395
|
+
if [ "$curl_exit" -ne 0 ] || [ "$http_code" = "429" ] || { [ "$http_code" -ge 500 ] && [ "$http_code" -lt 600 ]; }; then
|
|
396
|
+
if [ "$attempt" -lt "$PRESIGNED_MAX_ATTEMPTS" ]; then
|
|
397
|
+
echo -n "(step 3: HTTP ${http_code}, retry in ${backoff}s) "
|
|
398
|
+
sleep "$backoff"
|
|
399
|
+
attempt=$((attempt + 1))
|
|
400
|
+
backoff=$((backoff * 2))
|
|
401
|
+
continue
|
|
402
|
+
fi
|
|
403
|
+
fi
|
|
404
|
+
echo -n "(step 3 failed: HTTP ${http_code}) "
|
|
405
|
+
return 1
|
|
406
|
+
done
|
|
407
|
+
return 1
|
|
408
|
+
}
|
|
409
|
+
|
|
256
410
|
for FILE in "${FILES[@]}"; do
|
|
257
411
|
FILENAME=$(basename "$FILE")
|
|
258
412
|
if is_unedited_starter_stub "$FILE"; then
|
|
@@ -262,6 +416,27 @@ for FILE in "${FILES[@]}"; do
|
|
|
262
416
|
fi
|
|
263
417
|
FILE_SIZE=$(stat -c%s "$FILE" 2>/dev/null || stat -f%z "$FILE")
|
|
264
418
|
echo -n "Uploading ${FILENAME}... "
|
|
419
|
+
|
|
420
|
+
# DevAudit-Installer#189 — large files use the presigned R2 URL flow
|
|
421
|
+
# to bypass the portal's multipart body parser limit. If the portal
|
|
422
|
+
# doesn't support presigned URLs (returns 255), fall back to multipart.
|
|
423
|
+
if [ "$FILE_SIZE" -ge "$PRESIGNED_THRESHOLD_BYTES" ]; then
|
|
424
|
+
PRESIGNED_RESULT=0
|
|
425
|
+
upload_presigned "$FILE" || PRESIGNED_RESULT=$?
|
|
426
|
+
if [ "$PRESIGNED_RESULT" -eq 0 ]; then
|
|
427
|
+
echo "OK (${FILE_SIZE} bytes, presigned R2 upload)"
|
|
428
|
+
SUCCEEDED=$((SUCCEEDED + 1))
|
|
429
|
+
TOTAL_SIZE=$((TOTAL_SIZE + FILE_SIZE))
|
|
430
|
+
continue
|
|
431
|
+
elif [ "$PRESIGNED_RESULT" -ne 255 ]; then
|
|
432
|
+
echo "FAILED (presigned upload after ${PRESIGNED_MAX_ATTEMPTS} attempts)"
|
|
433
|
+
FAILED=$((FAILED + 1))
|
|
434
|
+
continue
|
|
435
|
+
fi
|
|
436
|
+
# Result 255 — portal doesn't support presigned URLs, fall through
|
|
437
|
+
# to the existing multipart flow.
|
|
438
|
+
fi
|
|
439
|
+
|
|
265
440
|
# `-L` follows 3xx redirects (devaudit-installer#143). The portal host
|
|
266
441
|
# has moved before (devaudit.metasession.co → devaudit.ai); without -L
|
|
267
442
|
# every consumer's CI silently fails on a stale base URL. `--max-redirs 3`
|
|
@@ -639,6 +639,13 @@ jobs:
|
|
|
639
639
|
(cd e2e-artifacts && zip -qr playwright-report.zip playwright-report/) 2>/dev/null || true
|
|
640
640
|
fi
|
|
641
641
|
if [ -f e2e-artifacts/playwright-report.zip ]; then
|
|
642
|
+
# DevAudit-Installer#189 — the Playwright HTML report zip
|
|
643
|
+
# (~50–100MB) exceeds the portal's multipart body parser
|
|
644
|
+
# limit. The JSON results above are the canonical evidence;
|
|
645
|
+
# the HTML report is a human-facing convenience. Soft-fail
|
|
646
|
+
# so the job doesn't block on a portal-side size limit.
|
|
647
|
+
# Once the portal's presigned R2 URL flow is live (#189
|
|
648
|
+
# Option C), this can be reverted to a hard fail.
|
|
642
649
|
if bash scripts/upload-evidence.sh \
|
|
643
650
|
{{PROJECT_SLUG}} "$REQ" test_report \
|
|
644
651
|
e2e-artifacts/playwright-report.zip \
|
|
@@ -647,8 +654,7 @@ jobs:
|
|
|
647
654
|
then
|
|
648
655
|
:
|
|
649
656
|
else
|
|
650
|
-
echo "::warning::playwright HTML report upload failed for ${REQ}"
|
|
651
|
-
UPLOAD_FAILURES=$((UPLOAD_FAILURES + 1))
|
|
657
|
+
echo "::warning::playwright HTML report upload failed for ${REQ} (non-blocking — JSON results are canonical evidence)"
|
|
652
658
|
fi
|
|
653
659
|
fi
|
|
654
660
|
done
|