#!/usr/bin/env bash # Production readiness scanner — run from repo root or pass path as $1 # SPDX-License-Identifier: Apache-2.0 set -euo pipefail REPO="${1:-.}" REPO="$(cd "$REPO" && pwd)" STRICT="${STRICT:-0}" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" ERR=0 WARN=0 INFO=0 err() { echo "ERROR $*"; ERR=$((ERR + 1)); } warn() { echo "WARN $*"; WARN=$((WARN + 1)); } info() { echo "INFO $*"; } pass() { echo "OK $*"; } echo "=== Production readiness: $REPO ===" echo "strict=$STRICT" echo # --- Secrets in tracked files --- if command -v git >/dev/null 2>&1 && git -C "$REPO" rev-parse --is-inside-work-tree >/dev/null 2>&1; then if git -C "$REPO" ls-files | grep -qE '^\.env$|credentials\.json|\.pem$'; then err "[SEC-01] Tracked sensitive files (.env, credentials, pem)" else pass "[SEC-01] No obvious secret filenames tracked" fi while IFS= read -r f; do [ -f "$REPO/$f" ] || continue if grep -qE 'BEGIN (RSA |EC )?PRIVATE KEY|api[_-]?key\s*=\s*["\x27][a-zA-Z0-9]{20,}' "$REPO/$f" 2>/dev/null; then err "[SEC-01] Possible secret in tracked file: $f" fi done < <(git -C "$REPO" ls-files '*.yml' '*.yaml' '*.json' '*.md' '*.sh' '*.js' '*.go' '*.env*' 2>/dev/null | head -500) else warn "[SEC-01] Not a git repo — skip secret scan" fi # --- node_modules in git --- if command -v git >/dev/null 2>&1 && git -C "$REPO" rev-parse --is-inside-work-tree >/dev/null 2>&1; then NM_COUNT=$(git -C "$REPO" ls-files '**/node_modules/**' 2>/dev/null | wc -l | tr -d ' ') if [ "${NM_COUNT:-0}" -gt 0 ]; then err "[DC-06] node_modules tracked ($NM_COUNT files) — git rm -r --cached and .gitignore" else pass "[DC-06] node_modules not tracked" fi fi # --- Node Dockerfiles: lockfile + npm ci --- while IFS= read -r df; do [ -f "$df" ] || continue dir="$(dirname "$df")" if grep -qE 'npm install' "$df" && ! grep -qE 'npm ci' "$df"; then warn "[DC-07] $df uses npm install without npm ci" fi if grep -qE 'npm ci|npm install' "$df"; then if [ ! -f "$dir/package-lock.json" ] && [ ! -f "$dir/yarn.lock" ] && [ ! -f "$dir/pnpm-lock.yaml" ]; then warn "[DC-07] $df has no lockfile in $dir" else pass "[DC-07] $df has lockfile" fi fi done < <(find "$REPO" -name Dockerfile \ -not -path '*/node_modules/*' \ -not -path '*/fabric-samples-upstream/*' \ -not -path '*/.git/*' \ 2>/dev/null) # --- Gateway patterns (if present) --- GW="$REPO/gateway/server.js" if [ -f "$GW" ]; then if grep -q 'withChainLock' "$GW" && grep -q 'evaluateChaincodeRaw' "$GW"; then if grep -A2 'evaluateChaincodeRaw' "$GW" | grep -q 'withChainLock'; then warn "[GW-02] gateway evaluates may be serialized behind withChainLock" else pass "[GW-02] evaluates not behind global chain lock" fi fi if grep -qE "app\.get\(['\"]/health" "$GW"; then pass "[GW-01] /health route present" else warn "[GW-01] no /health in gateway/server.js" fi fi # --- Deploy docs --- for doc in README.md docs/COOLIFY_DEPLOY.md docs/GATEWAY.md AGENTS.md; do if [ -f "$REPO/$doc" ]; then pass "[HY-01] found $doc" break fi done # --- Compose (delegate) --- if [ -f "$REPO/docker-compose.yml" ] || [ -f "$REPO/docker-compose.yaml" ]; then echo echo "--- docker-compose validation ---" COMPOSE_ERR=0 bash "$SCRIPT_DIR/validate-docker-compose.sh" "$REPO" || COMPOSE_ERR=$? if [ "$COMPOSE_ERR" -ne 0 ]; then ERR=$((ERR + COMPOSE_ERR)) fi fi # --- Go tests hint --- if [ -f "$REPO/Makefile" ] && grep -q 'test' "$REPO/Makefile"; then info "[HY-03] Makefile has test target — run in CI" fi if [ -d "$REPO/chaincode" ] || find "$REPO" -name go.mod -print -quit 2>/dev/null | grep -q .; then info "Consider: cd chaincode && go test ./..." fi echo echo "=== Summary ===" echo "errors=$ERR warnings=$WARN" if [ "$ERR" -gt 0 ]; then echo "VERDICT: FAIL" exit 1 fi if [ "$STRICT" = "1" ] && [ "$WARN" -gt 0 ]; then echo "VERDICT: FAIL (strict mode, warnings treated as errors)" exit 1 fi echo "VERDICT: PASS" exit 0