Lock Python dependencies and ignore frontend env files
This commit is contained in:
@@ -21,7 +21,7 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
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
|
||||
run: python -m ruff check .
|
||||
|
||||
@@ -44,6 +44,11 @@ Thumbs.db
|
||||
frontend/node_modules/
|
||||
frontend/.next/
|
||||
frontend/.vercel/
|
||||
frontend/.env
|
||||
frontend/.env.local
|
||||
frontend/.env.production
|
||||
frontend/.env.*
|
||||
!frontend/.env.example
|
||||
frontend/*.tsbuildinfo
|
||||
frontend/.codex-next-dev*.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 && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
COPY requirements.txt .
|
||||
COPY requirements.lock .
|
||||
|
||||
RUN --mount=type=cache,target=/root/.cache/pip \
|
||||
pip install --prefer-binary -r requirements.txt
|
||||
pip install --prefer-binary -r requirements.lock
|
||||
|
||||
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
|
||||
.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
|
||||
ruff
|
||||
httpx
|
||||
pytest>=8.4,<9
|
||||
ruff>=0.12,<1
|
||||
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
|
||||
httpx
|
||||
loguru
|
||||
pyTelegramBotAPI
|
||||
telethon
|
||||
python-dotenv
|
||||
PyYAML
|
||||
netCDF4
|
||||
numpy
|
||||
web3
|
||||
fastapi
|
||||
uvicorn
|
||||
websockets
|
||||
redis>=5.0.0
|
||||
requests>=2.32,<3
|
||||
httpx>=0.28,<1
|
||||
loguru>=0.7,<1
|
||||
pyTelegramBotAPI>=4.23,<5
|
||||
telethon>=1.40,<2
|
||||
python-dotenv>=1.1,<2
|
||||
PyYAML>=6.0,<7
|
||||
netCDF4>=1.7,<2
|
||||
numpy>=2.2,<3
|
||||
web3>=7.13,<8
|
||||
fastapi>=0.115,<1
|
||||
uvicorn>=0.34,<1
|
||||
websockets>=15,<16
|
||||
redis>=5.0,<7
|
||||
|
||||
@@ -51,6 +51,7 @@ REPLACE_WITH_EXAMPLE: dict[str, str] = {
|
||||
".env": ".env.example",
|
||||
".env.local": ".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