Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c8b86569ff | ||
|
|
42c1309b20 |
@@ -45,7 +45,7 @@ test:web:
|
|||||||
script:
|
script:
|
||||||
- uv sync --all-packages
|
- uv sync --all-packages
|
||||||
- cd web && uv run pytest tests/ -x -q
|
- cd web && uv run pytest tests/ -x -q
|
||||||
- cd web && uv run ruff check src/ tests/
|
- uv run ruff check .
|
||||||
|
|
||||||
# ── Tag (pull-based deploy) ───────────────────────────────────────────────────
|
# ── Tag (pull-based deploy) ───────────────────────────────────────────────────
|
||||||
# Creates v<N> tag after all tests pass. The on-server supervisor polls for new
|
# Creates v<N> tag after all tests pass. The on-server supervisor polls for new
|
||||||
|
|||||||
8
Makefile
8
Makefile
@@ -2,7 +2,7 @@ TAILWIND_VERSION := v4.1.18
|
|||||||
TAILWIND := web/bin/tailwindcss
|
TAILWIND := web/bin/tailwindcss
|
||||||
SOPS_DOTENV := sops --input-type dotenv --output-type dotenv
|
SOPS_DOTENV := sops --input-type dotenv --output-type dotenv
|
||||||
|
|
||||||
.PHONY: help dev css-build css-watch \
|
.PHONY: help dev css-build css-watch install-hooks \
|
||||||
secrets-decrypt-dev secrets-decrypt-prod \
|
secrets-decrypt-dev secrets-decrypt-prod \
|
||||||
secrets-edit-dev secrets-edit-prod \
|
secrets-edit-dev secrets-edit-prod \
|
||||||
secrets-encrypt-dev secrets-encrypt-prod \
|
secrets-encrypt-dev secrets-encrypt-prod \
|
||||||
@@ -13,6 +13,7 @@ help:
|
|||||||
@echo " dev Start full dev environment (migrate, seed, app + worker + CSS watcher)"
|
@echo " dev Start full dev environment (migrate, seed, app + worker + CSS watcher)"
|
||||||
@echo " css-build Build + minify Tailwind CSS"
|
@echo " css-build Build + minify Tailwind CSS"
|
||||||
@echo " css-watch Watch + rebuild Tailwind CSS"
|
@echo " css-watch Watch + rebuild Tailwind CSS"
|
||||||
|
@echo " install-hooks Install git pre-commit hook (run once after cloning)"
|
||||||
@echo " secrets-decrypt-dev Decrypt .env.dev.sops → .env"
|
@echo " secrets-decrypt-dev Decrypt .env.dev.sops → .env"
|
||||||
@echo " secrets-decrypt-prod Decrypt .env.prod.sops → .env"
|
@echo " secrets-decrypt-prod Decrypt .env.prod.sops → .env"
|
||||||
@echo " secrets-edit-dev Edit .env.dev.sops in \$$EDITOR"
|
@echo " secrets-edit-dev Edit .env.dev.sops in \$$EDITOR"
|
||||||
@@ -23,6 +24,11 @@ help:
|
|||||||
|
|
||||||
# ── Dev environment ───────────────────────────────────────────────────────────
|
# ── Dev environment ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
install-hooks:
|
||||||
|
cp scripts/hooks/pre-commit .git/hooks/pre-commit
|
||||||
|
chmod +x .git/hooks/pre-commit
|
||||||
|
@echo "✓ pre-commit hook installed"
|
||||||
|
|
||||||
dev:
|
dev:
|
||||||
@./web/scripts/dev_run.sh
|
@./web/scripts/dev_run.sh
|
||||||
|
|
||||||
|
|||||||
@@ -15,8 +15,14 @@ import sys
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import yfinance as yf
|
import yfinance as yf
|
||||||
from extract_core import content_hash, end_run, landing_path, open_state_db, start_run
|
from extract_core import (
|
||||||
from extract_core import write_bytes_atomic
|
content_hash,
|
||||||
|
end_run,
|
||||||
|
landing_path,
|
||||||
|
open_state_db,
|
||||||
|
start_run,
|
||||||
|
write_bytes_atomic,
|
||||||
|
)
|
||||||
|
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
level=logging.INFO,
|
level=logging.INFO,
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
from .normalize import normalize_zipped_csv
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
@@ -7,8 +6,16 @@ from io import BytesIO
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import niquests
|
import niquests
|
||||||
from extract_core import end_run, landing_path, normalize_etag, open_state_db, start_run
|
from extract_core import (
|
||||||
from extract_core import write_bytes_atomic
|
end_run,
|
||||||
|
landing_path,
|
||||||
|
normalize_etag,
|
||||||
|
open_state_db,
|
||||||
|
start_run,
|
||||||
|
write_bytes_atomic,
|
||||||
|
)
|
||||||
|
|
||||||
|
from .normalize import normalize_zipped_csv
|
||||||
|
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
level=logging.INFO,
|
level=logging.INFO,
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import zipfile
|
|
||||||
import gzip
|
import gzip
|
||||||
from io import BytesIO
|
|
||||||
import pathlib
|
import pathlib
|
||||||
|
import zipfile
|
||||||
|
from io import BytesIO
|
||||||
|
|
||||||
|
|
||||||
def normalize_zipped_csv(buffer: BytesIO)->BytesIO:
|
def normalize_zipped_csv(buffer: BytesIO)->BytesIO:
|
||||||
|
|||||||
@@ -90,11 +90,13 @@ exclude = [
|
|||||||
"notebooks",
|
"notebooks",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
line-length = 100
|
||||||
indent-width = 4
|
indent-width = 4
|
||||||
|
|
||||||
target-version = "py313"
|
target-version = "py313"
|
||||||
|
|
||||||
[tool.ruff.lint]
|
[tool.ruff.lint]
|
||||||
|
select = ["E", "F", "I", "UP"]
|
||||||
|
|
||||||
ignore = [
|
ignore = [
|
||||||
"E501", # line too long (handled by formatter)
|
"E501", # line too long (handled by formatter)
|
||||||
|
|||||||
18
scripts/hooks/pre-commit
Normal file
18
scripts/hooks/pre-commit
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Pre-commit hook: ruff lint + auto-fix.
|
||||||
|
# Install: make install-hooks
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
REPO_ROOT="$(git rev-parse --show-toplevel)"
|
||||||
|
RUFF="$REPO_ROOT/.venv/bin/ruff"
|
||||||
|
|
||||||
|
if [[ ! -x "$RUFF" ]]; then
|
||||||
|
echo "pre-commit: ruff not found at $RUFF — run 'uv sync' first" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "→ ruff check"
|
||||||
|
"$RUFF" check --fix "$REPO_ROOT"
|
||||||
|
|
||||||
|
# Re-stage any files ruff fixed so they land in the commit.
|
||||||
|
git diff --name-only | xargs -r git add
|
||||||
@@ -7,7 +7,6 @@ from unittest.mock import MagicMock
|
|||||||
|
|
||||||
from cftc_cot.normalize import find_csv_inner_filename, normalize_zipped_csv
|
from cftc_cot.normalize import find_csv_inner_filename, normalize_zipped_csv
|
||||||
|
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# normalize.py
|
# normalize.py
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ from unittest.mock import MagicMock
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
import xlwt # noqa: F401 — needed to create XLS fixtures; skip tests if missing
|
import xlwt # noqa: F401 — needed to create XLS fixtures; skip tests if missing
|
||||||
|
|
||||||
from ice_stocks.ice_api import fetch_report_listings, find_latest_report
|
from ice_stocks.ice_api import fetch_report_listings, find_latest_report
|
||||||
from ice_stocks.xls_parse import OLE2_MAGIC, detect_file_format, xls_to_rows
|
from ice_stocks.xls_parse import OLE2_MAGIC, detect_file_format, xls_to_rows
|
||||||
|
|
||||||
|
|||||||
@@ -36,14 +36,6 @@ dev-dependencies = [
|
|||||||
"ruff>=0.3.0",
|
"ruff>=0.3.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[tool.ruff]
|
|
||||||
line-length = 100
|
|
||||||
target-version = "py311"
|
|
||||||
|
|
||||||
[tool.ruff.lint]
|
|
||||||
select = ["E", "F", "I", "UP"]
|
|
||||||
ignore = ["E501"]
|
|
||||||
|
|
||||||
[tool.pytest.ini_options]
|
[tool.pytest.ini_options]
|
||||||
asyncio_mode = "auto"
|
asyncio_mode = "auto"
|
||||||
testpaths = ["tests"]
|
testpaths = ["tests"]
|
||||||
|
|||||||
@@ -23,8 +23,7 @@ def admin_required(f):
|
|||||||
async def decorated(*args, **kwargs):
|
async def decorated(*args, **kwargs):
|
||||||
if "admin" not in (g.get("user") or {}).get("roles", []):
|
if "admin" not in (g.get("user") or {}).get("roles", []):
|
||||||
await flash("Admin access required.", "error")
|
await flash("Admin access required.", "error")
|
||||||
from quart import redirect as _redirect
|
from quart import redirect as _redirect, url_for as _url_for
|
||||||
from quart import url_for as _url_for
|
|
||||||
return _redirect(_url_for("auth.login"))
|
return _redirect(_url_for("auth.login"))
|
||||||
return await f(*args, **kwargs)
|
return await f(*args, **kwargs)
|
||||||
return decorated
|
return decorated
|
||||||
|
|||||||
@@ -105,8 +105,7 @@ def create_app() -> Quart:
|
|||||||
# Health check
|
# Health check
|
||||||
@app.route("/health")
|
@app.route("/health")
|
||||||
async def health():
|
async def health():
|
||||||
from .analytics import _db_path as serving_db_path
|
from .analytics import _db_path as serving_db_path, fetch_analytics
|
||||||
from .analytics import fetch_analytics
|
|
||||||
from .core import fetch_one
|
from .core import fetch_one
|
||||||
result = {"status": "healthy", "sqlite": "ok", "duckdb": "ok"}
|
result = {"status": "healthy", "sqlite": "ok", "duckdb": "ok"}
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -13,8 +13,7 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from paddle_billing import Client as PaddleClient
|
from paddle_billing import Client as PaddleClient, Environment, Options
|
||||||
from paddle_billing import Environment, Options
|
|
||||||
from paddle_billing.Entities.Shared import CurrencyCode, Money, TaxCategory
|
from paddle_billing.Entities.Shared import CurrencyCode, Money, TaxCategory
|
||||||
from paddle_billing.Resources.Prices.Operations import CreatePrice
|
from paddle_billing.Resources.Prices.Operations import CreatePrice
|
||||||
from paddle_billing.Resources.Products.Operations import CreateProduct
|
from paddle_billing.Resources.Products.Operations import CreateProduct
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ from unittest.mock import AsyncMock, patch
|
|||||||
|
|
||||||
import aiosqlite
|
import aiosqlite
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from beanflows import core
|
from beanflows import core
|
||||||
from beanflows.app import create_app
|
from beanflows.app import create_app
|
||||||
|
|
||||||
|
|||||||
@@ -2,10 +2,6 @@
|
|||||||
Unit tests for billing SQL helpers, feature/limit access, and plan determination.
|
Unit tests for billing SQL helpers, feature/limit access, and plan determination.
|
||||||
"""
|
"""
|
||||||
import pytest
|
import pytest
|
||||||
from hypothesis import HealthCheck, given
|
|
||||||
from hypothesis import settings as h_settings
|
|
||||||
from hypothesis import strategies as st
|
|
||||||
|
|
||||||
from beanflows.billing.routes import (
|
from beanflows.billing.routes import (
|
||||||
can_access_feature,
|
can_access_feature,
|
||||||
get_billing_customer,
|
get_billing_customer,
|
||||||
@@ -18,6 +14,7 @@ from beanflows.billing.routes import (
|
|||||||
upsert_subscription,
|
upsert_subscription,
|
||||||
)
|
)
|
||||||
from beanflows.core import config
|
from beanflows.core import config
|
||||||
|
from hypothesis import HealthCheck, given, settings as h_settings, strategies as st
|
||||||
|
|
||||||
# ════════════════════════════════════════════════════════════
|
# ════════════════════════════════════════════════════════════
|
||||||
# get_subscription
|
# get_subscription
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
Tests for the billing event hook system.
|
Tests for the billing event hook system.
|
||||||
"""
|
"""
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from beanflows.billing.routes import _billing_hooks, _fire_hooks, on_billing_event
|
from beanflows.billing.routes import _billing_hooks, _fire_hooks, on_billing_event
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -144,9 +144,8 @@ class TestCancelRoute:
|
|||||||
# subscription_required decorator
|
# subscription_required decorator
|
||||||
# ════════════════════════════════════════════════════════════
|
# ════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
from quart import Blueprint # noqa: E402
|
|
||||||
|
|
||||||
from beanflows.auth.routes import subscription_required # noqa: E402
|
from beanflows.auth.routes import subscription_required # noqa: E402
|
||||||
|
from quart import Blueprint # noqa: E402
|
||||||
|
|
||||||
test_bp = Blueprint("test", __name__)
|
test_bp = Blueprint("test", __name__)
|
||||||
|
|
||||||
|
|||||||
@@ -5,12 +5,9 @@ Covers signature verification, event parsing, subscription lifecycle transitions
|
|||||||
import json
|
import json
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from conftest import make_webhook_payload, sign_payload
|
|
||||||
from hypothesis import HealthCheck, given
|
|
||||||
from hypothesis import settings as h_settings
|
|
||||||
from hypothesis import strategies as st
|
|
||||||
|
|
||||||
from beanflows.billing.routes import get_billing_customer, get_subscription
|
from beanflows.billing.routes import get_billing_customer, get_subscription
|
||||||
|
from conftest import make_webhook_payload, sign_payload
|
||||||
|
from hypothesis import HealthCheck, given, settings as h_settings, strategies as st
|
||||||
|
|
||||||
WEBHOOK_PATH = "/billing/webhook/paddle"
|
WEBHOOK_PATH = "/billing/webhook/paddle"
|
||||||
SIG_HEADER = "Paddle-Signature"
|
SIG_HEADER = "Paddle-Signature"
|
||||||
|
|||||||
@@ -3,8 +3,6 @@ Tests for role-based access control: role_required decorator, grant/revoke/ensur
|
|||||||
and admin route protection.
|
and admin route protection.
|
||||||
"""
|
"""
|
||||||
import pytest
|
import pytest
|
||||||
from quart import Blueprint
|
|
||||||
|
|
||||||
from beanflows import core
|
from beanflows import core
|
||||||
from beanflows.auth.routes import (
|
from beanflows.auth.routes import (
|
||||||
ensure_admin_role,
|
ensure_admin_role,
|
||||||
@@ -12,6 +10,7 @@ from beanflows.auth.routes import (
|
|||||||
revoke_role,
|
revoke_role,
|
||||||
role_required,
|
role_required,
|
||||||
)
|
)
|
||||||
|
from quart import Blueprint
|
||||||
|
|
||||||
# ════════════════════════════════════════════════════════════
|
# ════════════════════════════════════════════════════════════
|
||||||
# grant_role / revoke_role
|
# grant_role / revoke_role
|
||||||
|
|||||||
Reference in New Issue
Block a user