fix: bootstrap SSL/CA for research CLI on corporate MacBooks
Extract app/ssl_bootstrap.py (shared with FastAPI main), wire it into research scripts, and teach run_tier1_macbook.sh to locate combined-ca-bundle.pem, certifi, optional USE_CORP_PROXY, plus --ssl-check diagnostics.
This commit is contained in:
@@ -29,6 +29,10 @@ ROOT = Path(__file__).resolve().parents[1]
|
||||
if str(ROOT) not in sys.path:
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from app.ssl_bootstrap import bootstrap_ssl # noqa: E402
|
||||
|
||||
bootstrap_ssl()
|
||||
|
||||
FMP_STABLE = "https://financialmodelingprep.com/stable"
|
||||
DDL = """
|
||||
CREATE TABLE IF NOT EXISTS earnings_events (
|
||||
|
||||
@@ -34,6 +34,10 @@ ROOT = Path(__file__).resolve().parents[1]
|
||||
if str(ROOT) not in sys.path:
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from app.ssl_bootstrap import bootstrap_ssl # noqa: E402
|
||||
|
||||
bootstrap_ssl()
|
||||
|
||||
from app.services.sector_map import ( # noqa: E402
|
||||
DEFAULT_SECTOR_MAP_PATH,
|
||||
coverage_stats,
|
||||
|
||||
@@ -45,6 +45,10 @@ ROOT = Path(__file__).resolve().parents[1]
|
||||
if str(ROOT) not in sys.path:
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from app.ssl_bootstrap import bootstrap_ssl # noqa: E402
|
||||
|
||||
bootstrap_ssl()
|
||||
|
||||
|
||||
def _parse_args() -> argparse.Namespace:
|
||||
p = argparse.ArgumentParser(description=__doc__)
|
||||
|
||||
@@ -27,6 +27,10 @@ ROOT = Path(__file__).resolve().parents[1]
|
||||
if str(ROOT) not in sys.path:
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from app.ssl_bootstrap import bootstrap_ssl # noqa: E402
|
||||
|
||||
bootstrap_ssl()
|
||||
|
||||
from app.services.sector_map import SECTOR_ETFS # noqa: E402
|
||||
|
||||
|
||||
|
||||
@@ -28,6 +28,10 @@ ROOT = Path(__file__).resolve().parents[1]
|
||||
if str(ROOT) not in sys.path:
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from app.ssl_bootstrap import bootstrap_ssl # noqa: E402
|
||||
|
||||
bootstrap_ssl()
|
||||
|
||||
IRON_IC_BAR = 0.03
|
||||
MIN_RELIABLE = 12
|
||||
SUE_CARRY_DAYS = 63
|
||||
|
||||
@@ -35,6 +35,10 @@ ROOT = Path(__file__).resolve().parents[1]
|
||||
if str(ROOT) not in sys.path:
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from app.ssl_bootstrap import bootstrap_ssl # noqa: E402
|
||||
|
||||
bootstrap_ssl()
|
||||
|
||||
ERA_SPLIT = date(2021, 1, 1)
|
||||
SURVIVORSHIP_BANNER = (
|
||||
"SURVIVORSHIP BIAS: today's constituents backfilled historically. "
|
||||
|
||||
@@ -40,6 +40,10 @@ ROOT = Path(__file__).resolve().parents[1]
|
||||
if str(ROOT) not in sys.path:
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from app.ssl_bootstrap import bootstrap_ssl # noqa: E402
|
||||
|
||||
bootstrap_ssl()
|
||||
|
||||
from app.services.sector_map import ( # noqa: E402
|
||||
DEFAULT_SECTOR_MAP_PATH,
|
||||
SECTOR_ETFS,
|
||||
|
||||
@@ -33,11 +33,21 @@ ALPACA_SLEEP="${ALPACA_SLEEP:-0.15}"
|
||||
FMP_LIMIT="${FMP_LIMIT:-250}"
|
||||
FMP_SLEEP="${FMP_SLEEP:-0.35}"
|
||||
PYTHON="${PYTHON:-python3}"
|
||||
USE_CORP_PROXY="${USE_CORP_PROXY:-0}"
|
||||
|
||||
PHASE="depth" # depth | all | earnings | harness | coverage
|
||||
PHASE="depth" # depth | all | earnings | harness | coverage | ssl-check
|
||||
|
||||
usage() {
|
||||
sed -n '2,22p' "$0" | sed 's/^# \?//'
|
||||
sed -n '2,25p' "$0" | sed 's/^# \?//'
|
||||
cat <<'EOF'
|
||||
|
||||
SSL / network (corporate MacBook)
|
||||
SSL errors usually mean the corp root CA is missing from Python.
|
||||
1) Put combined-ca-bundle.pem in the repo root OR $HOME
|
||||
2) Or: export SSL_CERT_FILE=/path/to/combined-ca-bundle.pem
|
||||
3) Behind corp proxy: USE_CORP_PROXY=1 ./scripts/run_tier1_macbook.sh
|
||||
4) Diagnose: ./scripts/run_tier1_macbook.sh --ssl-check
|
||||
EOF
|
||||
exit "${1:-0}"
|
||||
}
|
||||
|
||||
@@ -48,6 +58,8 @@ while [[ $# -gt 0 ]]; do
|
||||
--harness-only) PHASE=harness; shift ;;
|
||||
--coverage-only) PHASE=coverage; shift ;;
|
||||
--depth) PHASE=depth; shift ;;
|
||||
--ssl-check) PHASE=ssl; shift ;;
|
||||
--corp-proxy) USE_CORP_PROXY=1; shift ;;
|
||||
--prod-snap) PROD_SNAP="$2"; shift 2 ;;
|
||||
--research-snap) RESEARCH_SNAP="$2"; shift 2 ;;
|
||||
--history-days) HISTORY_DAYS="$2"; shift 2 ;;
|
||||
@@ -71,6 +83,80 @@ fi
|
||||
log() { printf '\n==> %s\n' "$*"; }
|
||||
die() { echo "ERROR: $*" >&2; exit 1; }
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# TLS bootstrap — same corp CA path the FastAPI app uses
|
||||
# ---------------------------------------------------------------------------
|
||||
setup_ssl() {
|
||||
export USE_CORP_PROXY
|
||||
|
||||
# Prefer explicit env, then repo / home corporate bundle, then certifi.
|
||||
if [[ -z "${SSL_CERT_FILE:-}" ]]; then
|
||||
if [[ -f "$ROOT/combined-ca-bundle.pem" ]]; then
|
||||
export SSL_CERT_FILE="$ROOT/combined-ca-bundle.pem"
|
||||
elif [[ -f "$HOME/combined-ca-bundle.pem" ]]; then
|
||||
export SSL_CERT_FILE="$HOME/combined-ca-bundle.pem"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -n "${SSL_CERT_FILE:-}" && -f "$SSL_CERT_FILE" ]]; then
|
||||
export REQUESTS_CA_BUNDLE="$SSL_CERT_FILE"
|
||||
export CURL_CA_BUNDLE="$SSL_CERT_FILE"
|
||||
log "SSL CA bundle: $SSL_CERT_FILE"
|
||||
else
|
||||
# Fall back to certifi if installed
|
||||
local certifi_path
|
||||
certifi_path="$("$PYTHON" -c 'import certifi; print(certifi.where())' 2>/dev/null || true)"
|
||||
if [[ -n "$certifi_path" && -f "$certifi_path" ]]; then
|
||||
export SSL_CERT_FILE="$certifi_path"
|
||||
export REQUESTS_CA_BUNDLE="$certifi_path"
|
||||
export CURL_CA_BUNDLE="$certifi_path"
|
||||
log "SSL CA bundle (certifi): $SSL_CERT_FILE"
|
||||
else
|
||||
log "WARNING: no CA bundle found — SSL may fail on corp networks"
|
||||
log " Copy combined-ca-bundle.pem to $ROOT/ or \$HOME/"
|
||||
log " Or: export SSL_CERT_FILE=/path/to/combined-ca-bundle.pem"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "$USE_CORP_PROXY" == "1" ]]; then
|
||||
export HTTP_PROXY="${HTTP_PROXY:-http://aproxy.corproot.net:8080}"
|
||||
export HTTPS_PROXY="${HTTPS_PROXY:-http://aproxy.corproot.net:8080}"
|
||||
export NO_PROXY="${NO_PROXY:-corproot.net,sharedtcs.net,127.0.0.1,localhost,bix.swisscom.com,swisscom.com}"
|
||||
export http_proxy="$HTTP_PROXY" https_proxy="$HTTPS_PROXY" no_proxy="$NO_PROXY"
|
||||
log "Corp proxy enabled: $HTTPS_PROXY"
|
||||
fi
|
||||
|
||||
# Ensure Python process sees the same bootstrap (patches ssl for alpaca-py).
|
||||
export PYTHONPATH="${ROOT}${PYTHONPATH:+:$PYTHONPATH}"
|
||||
}
|
||||
|
||||
ssl_check() {
|
||||
setup_ssl
|
||||
log "SSL diagnostic"
|
||||
"$PYTHON" - <<'PY'
|
||||
from app.ssl_bootstrap import bootstrap_ssl, ssl_status
|
||||
import json
|
||||
import urllib.request
|
||||
|
||||
ca = bootstrap_ssl()
|
||||
print(json.dumps(ssl_status(), indent=2))
|
||||
print("bootstrap_ssl ->", ca)
|
||||
urls = [
|
||||
"https://data.alpaca.markets/v2/stocks/SPY/bars?timeframe=1Day&limit=1",
|
||||
"https://financialmodelingprep.com/stable/profile?symbol=AAPL",
|
||||
"https://www.alphavantage.co/query?function=TIME_SERIES_DAILY&symbol=IBM",
|
||||
]
|
||||
for url in urls:
|
||||
try:
|
||||
req = urllib.request.Request(url, headers={"User-Agent": "signal-platform-ssl-check"})
|
||||
with urllib.request.urlopen(req, timeout=20) as resp:
|
||||
print(f"OK {resp.status} {url[:60]}...")
|
||||
except Exception as exc:
|
||||
print(f"FAIL {type(exc).__name__}: {exc}")
|
||||
print(f" {url[:80]}")
|
||||
PY
|
||||
}
|
||||
|
||||
need_file() {
|
||||
[[ -f "$1" ]] || die "missing $1"
|
||||
}
|
||||
@@ -138,8 +224,12 @@ run_harness() {
|
||||
}
|
||||
|
||||
log "cwd=$ROOT python=$PYTHON phase=$PHASE workers=$WORKERS"
|
||||
setup_ssl
|
||||
|
||||
case "$PHASE" in
|
||||
ssl)
|
||||
ssl_check
|
||||
;;
|
||||
coverage)
|
||||
run_coverage
|
||||
;;
|
||||
|
||||
Reference in New Issue
Block a user