Lock Python dependencies and ignore frontend env files
This commit is contained in:
@@ -21,7 +21,7 @@ jobs:
|
|||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
pip install -r requirements.txt -r requirements-dev.txt
|
pip install -r requirements.lock -r requirements-dev.lock
|
||||||
|
|
||||||
- name: Ruff
|
- name: Ruff
|
||||||
run: python -m ruff check .
|
run: python -m ruff check .
|
||||||
|
|||||||
@@ -44,6 +44,11 @@ Thumbs.db
|
|||||||
frontend/node_modules/
|
frontend/node_modules/
|
||||||
frontend/.next/
|
frontend/.next/
|
||||||
frontend/.vercel/
|
frontend/.vercel/
|
||||||
|
frontend/.env
|
||||||
|
frontend/.env.local
|
||||||
|
frontend/.env.production
|
||||||
|
frontend/.env.*
|
||||||
|
!frontend/.env.example
|
||||||
frontend/*.tsbuildinfo
|
frontend/*.tsbuildinfo
|
||||||
frontend/.codex-next-dev*.log
|
frontend/.codex-next-dev*.log
|
||||||
frontend/.codex-next-start*.log
|
frontend/.codex-next-start*.log
|
||||||
|
|||||||
+2
-2
@@ -15,10 +15,10 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
|
|||||||
gcc libhdf5-dev libnetcdf-dev && \
|
gcc libhdf5-dev libnetcdf-dev && \
|
||||||
rm -rf /var/lib/apt/lists/*
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
COPY requirements.txt .
|
COPY requirements.lock .
|
||||||
|
|
||||||
RUN --mount=type=cache,target=/root/.cache/pip \
|
RUN --mount=type=cache,target=/root/.cache/pip \
|
||||||
pip install --prefer-binary -r requirements.txt
|
pip install --prefer-binary -r requirements.lock
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
.git
|
||||||
|
.next
|
||||||
|
node_modules
|
||||||
|
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
.env.production
|
||||||
|
.env.*
|
||||||
|
!.env.example
|
||||||
|
|
||||||
|
*.log
|
||||||
|
*.tsbuildinfo
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
# PolyWeather 前端最小配置(本地 / Vercel)
|
|
||||||
# 只部署天气看板时,先填下面 4 项即可。
|
|
||||||
|
|
||||||
# 必填:后端 FastAPI 基础地址
|
|
||||||
# 默认供 Next.js API Route 在服务端代理后端使用。
|
|
||||||
POLYWEATHER_API_BASE_URL=http://127.0.0.1:8000
|
|
||||||
|
|
||||||
# 可选:浏览器直连后端 FastAPI 基础地址。
|
|
||||||
# 在 Vercel 免费额度下建议配置为 VPS HTTPS 域名,让 AI / METAR / scan 等
|
|
||||||
# 长耗时请求绕过 Vercel Functions / Fluid Compute。
|
|
||||||
# 例如:https://api.example.com
|
|
||||||
# 本地开发时注释掉,让 Next.js API Route 代理请求,避免 CORS 问题
|
|
||||||
# NEXT_PUBLIC_POLYWEATHER_API_BASE_URL=http://38.54.27.70:8080
|
|
||||||
|
|
||||||
# 必填:Supabase 前端公钥(鉴权开启时必须)
|
|
||||||
NEXT_PUBLIC_SUPABASE_URL=https://bttgfgupldyowkdhriqb.supabase.co
|
|
||||||
NEXT_PUBLIC_SUPABASE_ANON_KEY=sb_publishable_1z0DR7nZ1Juf_HGTASA8WA_uxcHJnby
|
|
||||||
|
|
||||||
# 必填:生产环境站点 URL(OAuth 回调强制使用此域名)
|
|
||||||
# 设置后,所有登录回调将始终跳转到此域名,而非当前浏览器地址。
|
|
||||||
# 生产环境必须设为 https://polyweather.top
|
|
||||||
NEXT_PUBLIC_SITE_URL=https://polyweather.top
|
|
||||||
|
|
||||||
# 常用:前端鉴权开关
|
|
||||||
# true: 启用 Supabase 登录
|
|
||||||
# false: 关闭登录能力,访客模式
|
|
||||||
POLYWEATHER_AUTH_ENABLED=true
|
|
||||||
|
|
||||||
# 常用:是否强制登录
|
|
||||||
# true: middleware 强制登录后才能访问主页面
|
|
||||||
# false: 登录可选,访客可浏览
|
|
||||||
POLYWEATHER_AUTH_REQUIRED=true
|
|
||||||
|
|
||||||
# 关闭本地开发鉴权绕过
|
|
||||||
NEXT_PUBLIC_POLYWEATHER_LOCAL_FULL_ACCESS=false
|
|
||||||
|
|
||||||
# 可选:分享式看板访问令牌
|
|
||||||
# 设置后,可通过 /?access_token=<token> 打开受保护看板
|
|
||||||
POLYWEATHER_DASHBOARD_ACCESS_TOKEN=
|
|
||||||
|
|
||||||
# 可选:前端 API Route 转发到后端时附带的共享令牌
|
|
||||||
# 仅当后端启用了 entitlement / 订阅校验时需要
|
|
||||||
POLYWEATHER_BACKEND_ENTITLEMENT_TOKEN=
|
|
||||||
|
|
||||||
# 可选:钱包支付 / Telegram 入口
|
|
||||||
NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID=8ce6332b57a42e606cf28224e82d1e02
|
|
||||||
NEXT_PUBLIC_WALLETCONNECT_POLYGON_RPC_URL=https://polygon-bor-rpc.publicnode.com
|
|
||||||
NEXT_PUBLIC_PAYMENT_ALLOWED_HOSTS=polyweather.top,www.polyweather.top
|
|
||||||
POLYWEATHER_OPS_ADMIN_EMAILS=yhrsc30@gmail.com
|
|
||||||
NEXT_PUBLIC_TELEGRAM_GROUP_URL=https://t.me/your_group
|
|
||||||
NEXT_PUBLIC_TELEGRAM_BOT_URL=https://t.me/polyyuanbot
|
|
||||||
NEXT_PUBLIC_TELEGRAM_LOGIN_BOT_USERNAME=polyyuanbot
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
NEXT_PUBLIC_SUPABASE_URL=https://bttgfgupldyowkdhriqb.supabase.co
|
|
||||||
NEXT_PUBLIC_SUPABASE_ANON_KEY=sb_publishable_1z0DR7nZ1Juf_HGTASA8WA_uxcHJnby
|
|
||||||
NEXT_PUBLIC_SITE_URL=https://polyweather.top
|
|
||||||
NEXT_PUBLIC_POLYWEATHER_API_BASE_URL=https://api.polyweather.top
|
|
||||||
NEXT_PUBLIC_POLYWEATHER_LOCAL_FULL_ACCESS=false
|
|
||||||
NEXT_PUBLIC_PAYMENT_ALLOWED_HOSTS=polyweather.top,www.polyweather.top
|
|
||||||
NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID=8ce6332b57a42e606cf28224e82d1e02
|
|
||||||
NEXT_PUBLIC_WALLETCONNECT_POLYGON_RPC_URL=https://polygon-bor-rpc.publicnode.com
|
|
||||||
@@ -1 +1,6 @@
|
|||||||
.vercel
|
.vercel
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
.env.production
|
||||||
|
.env.*
|
||||||
|
!.env.example
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
# This file was autogenerated by uv via the following command:
|
||||||
|
# uv pip compile --python-version 3.11 --universal --constraint requirements.lock --output-file requirements-dev.lock requirements-dev.txt
|
||||||
|
anyio==4.13.0
|
||||||
|
# via
|
||||||
|
# -c requirements.lock
|
||||||
|
# httpx
|
||||||
|
certifi==2026.5.20
|
||||||
|
# via
|
||||||
|
# -c requirements.lock
|
||||||
|
# httpcore
|
||||||
|
# httpx
|
||||||
|
colorama==0.4.6 ; sys_platform == 'win32'
|
||||||
|
# via
|
||||||
|
# -c requirements.lock
|
||||||
|
# pytest
|
||||||
|
h11==0.16.0
|
||||||
|
# via
|
||||||
|
# -c requirements.lock
|
||||||
|
# httpcore
|
||||||
|
httpcore==1.0.9
|
||||||
|
# via
|
||||||
|
# -c requirements.lock
|
||||||
|
# httpx
|
||||||
|
httpx==0.28.1
|
||||||
|
# via
|
||||||
|
# -c requirements.lock
|
||||||
|
# -r requirements-dev.txt
|
||||||
|
idna==3.18
|
||||||
|
# via
|
||||||
|
# -c requirements.lock
|
||||||
|
# anyio
|
||||||
|
# httpx
|
||||||
|
iniconfig==2.3.0
|
||||||
|
# via pytest
|
||||||
|
packaging==26.2
|
||||||
|
# via pytest
|
||||||
|
pluggy==1.6.0
|
||||||
|
# via pytest
|
||||||
|
pygments==2.20.0
|
||||||
|
# via pytest
|
||||||
|
pytest==8.4.2
|
||||||
|
# via -r requirements-dev.txt
|
||||||
|
ruff==0.15.17
|
||||||
|
# via -r requirements-dev.txt
|
||||||
|
typing-extensions==4.15.0 ; python_full_version < '3.13'
|
||||||
|
# via
|
||||||
|
# -c requirements.lock
|
||||||
|
# anyio
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
pytest
|
pytest>=8.4,<9
|
||||||
ruff
|
ruff>=0.12,<1
|
||||||
httpx
|
httpx>=0.28,<1
|
||||||
|
|||||||
@@ -0,0 +1,204 @@
|
|||||||
|
# This file was autogenerated by uv via the following command:
|
||||||
|
# uv pip compile --python-version 3.11 --universal --output-file requirements.lock requirements.txt
|
||||||
|
aiohappyeyeballs==2.6.2
|
||||||
|
# via aiohttp
|
||||||
|
aiohttp==3.14.1
|
||||||
|
# via
|
||||||
|
# pytelegrambotapi
|
||||||
|
# web3
|
||||||
|
aiosignal==1.4.0
|
||||||
|
# via aiohttp
|
||||||
|
annotated-doc==0.0.4
|
||||||
|
# via fastapi
|
||||||
|
annotated-types==0.7.0
|
||||||
|
# via pydantic
|
||||||
|
anyio==4.13.0
|
||||||
|
# via
|
||||||
|
# httpx
|
||||||
|
# starlette
|
||||||
|
async-timeout==5.0.1 ; python_full_version < '3.11.3'
|
||||||
|
# via redis
|
||||||
|
attrs==26.1.0
|
||||||
|
# via aiohttp
|
||||||
|
bitarray==3.8.1
|
||||||
|
# via eth-account
|
||||||
|
certifi==2026.5.20
|
||||||
|
# via
|
||||||
|
# httpcore
|
||||||
|
# httpx
|
||||||
|
# netcdf4
|
||||||
|
# requests
|
||||||
|
cftime==1.6.5
|
||||||
|
# via netcdf4
|
||||||
|
charset-normalizer==3.4.7
|
||||||
|
# via requests
|
||||||
|
ckzg==2.1.7
|
||||||
|
# via eth-account
|
||||||
|
click==8.4.1
|
||||||
|
# via uvicorn
|
||||||
|
colorama==0.4.6 ; sys_platform == 'win32'
|
||||||
|
# via
|
||||||
|
# click
|
||||||
|
# loguru
|
||||||
|
cytoolz==1.1.0 ; implementation_name == 'cpython'
|
||||||
|
# via eth-utils
|
||||||
|
eth-abi==5.2.0
|
||||||
|
# via
|
||||||
|
# eth-account
|
||||||
|
# web3
|
||||||
|
eth-account==0.13.7
|
||||||
|
# via web3
|
||||||
|
eth-hash==0.8.0
|
||||||
|
# via
|
||||||
|
# eth-utils
|
||||||
|
# web3
|
||||||
|
eth-keyfile==0.8.1
|
||||||
|
# via eth-account
|
||||||
|
eth-keys==0.7.0
|
||||||
|
# via
|
||||||
|
# eth-account
|
||||||
|
# eth-keyfile
|
||||||
|
eth-rlp==2.2.0
|
||||||
|
# via eth-account
|
||||||
|
eth-typing==6.0.0
|
||||||
|
# via
|
||||||
|
# eth-abi
|
||||||
|
# eth-keys
|
||||||
|
# eth-utils
|
||||||
|
# web3
|
||||||
|
eth-utils==6.0.0
|
||||||
|
# via
|
||||||
|
# eth-abi
|
||||||
|
# eth-account
|
||||||
|
# eth-keyfile
|
||||||
|
# eth-keys
|
||||||
|
# eth-rlp
|
||||||
|
# rlp
|
||||||
|
# web3
|
||||||
|
fastapi==0.137.0
|
||||||
|
# via -r requirements.txt
|
||||||
|
frozenlist==1.8.0
|
||||||
|
# via
|
||||||
|
# aiohttp
|
||||||
|
# aiosignal
|
||||||
|
h11==0.16.0
|
||||||
|
# via
|
||||||
|
# httpcore
|
||||||
|
# uvicorn
|
||||||
|
hexbytes==1.3.1
|
||||||
|
# via
|
||||||
|
# eth-account
|
||||||
|
# eth-rlp
|
||||||
|
# web3
|
||||||
|
httpcore==1.0.9
|
||||||
|
# via httpx
|
||||||
|
httpx==0.28.1
|
||||||
|
# via -r requirements.txt
|
||||||
|
idna==3.18
|
||||||
|
# via
|
||||||
|
# anyio
|
||||||
|
# httpx
|
||||||
|
# requests
|
||||||
|
# yarl
|
||||||
|
loguru==0.7.3
|
||||||
|
# via -r requirements.txt
|
||||||
|
multidict==6.7.1
|
||||||
|
# via
|
||||||
|
# aiohttp
|
||||||
|
# yarl
|
||||||
|
netcdf4==1.7.4
|
||||||
|
# via -r requirements.txt
|
||||||
|
numpy==2.4.6
|
||||||
|
# via
|
||||||
|
# -r requirements.txt
|
||||||
|
# cftime
|
||||||
|
# netcdf4
|
||||||
|
parsimonious==0.10.0
|
||||||
|
# via eth-abi
|
||||||
|
propcache==0.5.2
|
||||||
|
# via
|
||||||
|
# aiohttp
|
||||||
|
# yarl
|
||||||
|
pyaes==1.6.1
|
||||||
|
# via telethon
|
||||||
|
pyasn1==0.6.3
|
||||||
|
# via rsa
|
||||||
|
pycryptodome==3.23.0
|
||||||
|
# via
|
||||||
|
# eth-hash
|
||||||
|
# eth-keyfile
|
||||||
|
pydantic==2.13.4
|
||||||
|
# via
|
||||||
|
# eth-account
|
||||||
|
# eth-utils
|
||||||
|
# fastapi
|
||||||
|
# web3
|
||||||
|
pydantic-core==2.46.4
|
||||||
|
# via pydantic
|
||||||
|
pytelegrambotapi==4.34.0
|
||||||
|
# via -r requirements.txt
|
||||||
|
python-dotenv==1.2.2
|
||||||
|
# via -r requirements.txt
|
||||||
|
pyunormalize==17.0.0
|
||||||
|
# via web3
|
||||||
|
pywin32==312 ; sys_platform == 'win32'
|
||||||
|
# via web3
|
||||||
|
pyyaml==6.0.3
|
||||||
|
# via -r requirements.txt
|
||||||
|
redis==6.4.0
|
||||||
|
# via -r requirements.txt
|
||||||
|
regex==2026.5.9
|
||||||
|
# via parsimonious
|
||||||
|
requests==2.34.2
|
||||||
|
# via
|
||||||
|
# -r requirements.txt
|
||||||
|
# pytelegrambotapi
|
||||||
|
# web3
|
||||||
|
rlp==4.1.0
|
||||||
|
# via
|
||||||
|
# eth-account
|
||||||
|
# eth-rlp
|
||||||
|
rsa==4.9.1
|
||||||
|
# via telethon
|
||||||
|
starlette==1.3.1
|
||||||
|
# via fastapi
|
||||||
|
telethon==1.43.2
|
||||||
|
# via -r requirements.txt
|
||||||
|
toolz==1.1.0 ; implementation_name == 'cpython' or implementation_name == 'pypy'
|
||||||
|
# via
|
||||||
|
# cytoolz
|
||||||
|
# eth-utils
|
||||||
|
types-requests==2.33.0.20260518
|
||||||
|
# via web3
|
||||||
|
typing-extensions==4.15.0
|
||||||
|
# via
|
||||||
|
# aiohttp
|
||||||
|
# aiosignal
|
||||||
|
# anyio
|
||||||
|
# eth-typing
|
||||||
|
# fastapi
|
||||||
|
# pydantic
|
||||||
|
# pydantic-core
|
||||||
|
# starlette
|
||||||
|
# typing-inspection
|
||||||
|
# web3
|
||||||
|
typing-inspection==0.4.2
|
||||||
|
# via
|
||||||
|
# fastapi
|
||||||
|
# pydantic
|
||||||
|
urllib3==2.7.0
|
||||||
|
# via
|
||||||
|
# requests
|
||||||
|
# types-requests
|
||||||
|
uvicorn==0.49.0
|
||||||
|
# via -r requirements.txt
|
||||||
|
web3==7.16.0
|
||||||
|
# via -r requirements.txt
|
||||||
|
websockets==15.0.1
|
||||||
|
# via
|
||||||
|
# -r requirements.txt
|
||||||
|
# web3
|
||||||
|
win32-setctime==1.2.0 ; sys_platform == 'win32'
|
||||||
|
# via loguru
|
||||||
|
yarl==1.24.2
|
||||||
|
# via aiohttp
|
||||||
+14
-14
@@ -1,14 +1,14 @@
|
|||||||
requests
|
requests>=2.32,<3
|
||||||
httpx
|
httpx>=0.28,<1
|
||||||
loguru
|
loguru>=0.7,<1
|
||||||
pyTelegramBotAPI
|
pyTelegramBotAPI>=4.23,<5
|
||||||
telethon
|
telethon>=1.40,<2
|
||||||
python-dotenv
|
python-dotenv>=1.1,<2
|
||||||
PyYAML
|
PyYAML>=6.0,<7
|
||||||
netCDF4
|
netCDF4>=1.7,<2
|
||||||
numpy
|
numpy>=2.2,<3
|
||||||
web3
|
web3>=7.13,<8
|
||||||
fastapi
|
fastapi>=0.115,<1
|
||||||
uvicorn
|
uvicorn>=0.34,<1
|
||||||
websockets
|
websockets>=15,<16
|
||||||
redis>=5.0.0
|
redis>=5.0,<7
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ REPLACE_WITH_EXAMPLE: dict[str, str] = {
|
|||||||
".env": ".env.example",
|
".env": ".env.example",
|
||||||
".env.local": ".env.example",
|
".env.local": ".env.example",
|
||||||
"frontend/.env.local": "frontend/.env.example",
|
"frontend/.env.local": "frontend/.env.example",
|
||||||
|
"frontend/.env.production": "frontend/.env.example",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
|
||||||
|
|
||||||
|
def _meaningful_requirement_lines(path: Path) -> list[str]:
|
||||||
|
return [
|
||||||
|
line.strip()
|
||||||
|
for line in path.read_text(encoding="utf-8").splitlines()
|
||||||
|
if line.strip() and not line.lstrip().startswith("#")
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def test_python_dependency_builds_use_lock_files() -> None:
|
||||||
|
assert (ROOT / "requirements.lock").is_file()
|
||||||
|
assert (ROOT / "requirements-dev.lock").is_file()
|
||||||
|
|
||||||
|
dockerfile = (ROOT / "Dockerfile").read_text(encoding="utf-8")
|
||||||
|
assert "COPY requirements.lock ." in dockerfile
|
||||||
|
assert "pip install --prefer-binary -r requirements.lock" in dockerfile
|
||||||
|
assert "pip install --prefer-binary -r requirements.txt" not in dockerfile
|
||||||
|
|
||||||
|
ci = (ROOT / ".github" / "workflows" / "ci.yml").read_text(encoding="utf-8")
|
||||||
|
assert "pip install -r requirements.lock -r requirements-dev.lock" in ci
|
||||||
|
assert "pip install -r requirements.txt -r requirements-dev.txt" not in ci
|
||||||
|
|
||||||
|
|
||||||
|
def test_requirement_input_files_keep_upper_bounds() -> None:
|
||||||
|
for path in (ROOT / "requirements.txt", ROOT / "requirements-dev.txt"):
|
||||||
|
for line in _meaningful_requirement_lines(path):
|
||||||
|
assert "<" in line, f"{path.name} entry lacks an upper bound: {line}"
|
||||||
|
|
||||||
|
|
||||||
|
def test_frontend_runtime_env_files_are_not_tracked_or_in_docker_context() -> None:
|
||||||
|
if shutil.which("git") is None or not (ROOT / ".git").exists():
|
||||||
|
pytest.skip("git metadata is required for tracking policy checks")
|
||||||
|
|
||||||
|
tracked = subprocess.run(
|
||||||
|
[
|
||||||
|
"git",
|
||||||
|
"ls-files",
|
||||||
|
"--",
|
||||||
|
"frontend/.env.local",
|
||||||
|
"frontend/.env.production",
|
||||||
|
],
|
||||||
|
cwd=ROOT,
|
||||||
|
check=True,
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
)
|
||||||
|
assert tracked.stdout.strip() == ""
|
||||||
|
|
||||||
|
frontend_gitignore = (ROOT / "frontend" / ".gitignore").read_text(
|
||||||
|
encoding="utf-8"
|
||||||
|
)
|
||||||
|
assert ".env.local" in frontend_gitignore
|
||||||
|
assert ".env.production" in frontend_gitignore
|
||||||
|
|
||||||
|
frontend_dockerignore = (ROOT / "frontend" / ".dockerignore").read_text(
|
||||||
|
encoding="utf-8"
|
||||||
|
)
|
||||||
|
assert ".env" in frontend_dockerignore
|
||||||
|
assert ".env.*" in frontend_dockerignore
|
||||||
|
assert "!.env.example" in frontend_dockerignore
|
||||||
Reference in New Issue
Block a user