@deepstorm/cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (148) hide show
  1. package/README.md +72 -0
  2. package/dist/agents/reef-inspect-figma.md +77 -0
  3. package/dist/agents/reef-review-backend.md.tmpl +112 -0
  4. package/dist/agents/reef-review-frontend.md.tmpl +78 -0
  5. package/dist/agents/reef-review-infra.md +47 -0
  6. package/dist/agents/reef-review-security.md.tmpl +80 -0
  7. package/dist/agents/reef-scope-analysis.md +64 -0
  8. package/dist/build-registry.js +375 -0
  9. package/dist/cli.js +8581 -0
  10. package/dist/config-schema.json +133 -0
  11. package/dist/env-examples/context7.env-example +19 -0
  12. package/dist/env-examples/feishu-wiki.env-example +16 -0
  13. package/dist/env-examples/figma.env-example +16 -0
  14. package/dist/env-examples/github.env-example +20 -0
  15. package/dist/env-examples/jira.env-example +20 -0
  16. package/dist/hooks/mcp-hook.sh +77 -0
  17. package/dist/hooks/reef-auto-format.sh.tmpl +72 -0
  18. package/dist/hooks/reef-block-dangerous.sh +70 -0
  19. package/dist/hooks/reef-hooks.json +72 -0
  20. package/dist/hooks/reef-intent-detect.sh +129 -0
  21. package/dist/hooks/reef-protect-files.sh +55 -0
  22. package/dist/hooks/reef-run-tests.sh +84 -0
  23. package/dist/hooks/reef-scope-check.sh +386 -0
  24. package/dist/hooks/reef-scope-ci.sh +28 -0
  25. package/dist/hooks/reef-scope-gate.sh +115 -0
  26. package/dist/hooks/reef-scope-pre-commit.sh.tmpl +28 -0
  27. package/dist/hooks/reef-scope-setup.sh +204 -0
  28. package/dist/hooks/reef-scope-split.sh +203 -0
  29. package/dist/hooks/sweep-hooks.json +14 -0
  30. package/dist/hooks/sweep-mcp-hook.sh +77 -0
  31. package/dist/hooks/tide-hooks.json +14 -0
  32. package/dist/hooks/tide-session-preload.sh +17 -0
  33. package/dist/mcp/code-hosting/github.json +20 -0
  34. package/dist/mcp/design-tools/figma.json +19 -0
  35. package/dist/mcp/docs-reference/context7.json +28 -0
  36. package/dist/mcp/e2e-testing/playwright.json +13 -0
  37. package/dist/mcp/knowledge-base/feishu-wiki.json +19 -0
  38. package/dist/mcp/project-management/jira.json +27 -0
  39. package/dist/mcp-skills/deepflow-mcp-feishu-wiki-read/SKILL.md +65 -0
  40. package/dist/mcp-skills/deepflow-mcp-feishu-wiki-write/SKILL.md +63 -0
  41. package/dist/mcp-skills/deepflow-mcp-figma-read/SKILL.md +98 -0
  42. package/dist/mcp-skills/deepflow-mcp-github-read/SKILL.md +62 -0
  43. package/dist/mcp-skills/deepflow-mcp-github-write/SKILL.md +63 -0
  44. package/dist/mcp-skills/deepflow-mcp-jira-read/SKILL.md +80 -0
  45. package/dist/mcp-skills/deepflow-mcp-jira-write/SKILL.md +74 -0
  46. package/dist/mcp-skills/deepflow-mcp-playwright-read/SKILL.md +79 -0
  47. package/dist/mcp-skills/deepstorm-mcp-feishu-wiki-read/SKILL.md +65 -0
  48. package/dist/mcp-skills/deepstorm-mcp-feishu-wiki-write/SKILL.md +63 -0
  49. package/dist/mcp-skills/deepstorm-mcp-figma-read/SKILL.md +98 -0
  50. package/dist/mcp-skills/deepstorm-mcp-github-read/SKILL.md +62 -0
  51. package/dist/mcp-skills/deepstorm-mcp-github-write/SKILL.md +63 -0
  52. package/dist/mcp-skills/deepstorm-mcp-jira-read/SKILL.md +80 -0
  53. package/dist/mcp-skills/deepstorm-mcp-jira-write/SKILL.md +74 -0
  54. package/dist/mcp-skills/deepstorm-mcp-playwright-read/SKILL.md +79 -0
  55. package/dist/registry.json +818 -0
  56. package/dist/skills/atoll-ops/SKILL.md +46 -0
  57. package/dist/skills/reef-commit/SKILL.md +127 -0
  58. package/dist/skills/reef-gen-backend/SKILL.md.tmpl +87 -0
  59. package/dist/skills/reef-gen-backend/variants/java/steps.md +28 -0
  60. package/dist/skills/reef-gen-backend/variants/python/steps.md +70 -0
  61. package/dist/skills/reef-gen-frontend/SKILL.md.tmpl +83 -0
  62. package/dist/skills/reef-gen-frontend/variants/angular/steps.md +30 -0
  63. package/dist/skills/reef-harden/EXAMPLES.md +89 -0
  64. package/dist/skills/reef-harden/SKILL.md +136 -0
  65. package/dist/skills/reef-pr/SKILL.md +97 -0
  66. package/dist/skills/reef-review/SKILL.md.tmpl +107 -0
  67. package/dist/skills/reef-scope/SKILL.md +134 -0
  68. package/dist/skills/reef-start/SKILL.md.tmpl +562 -0
  69. package/dist/skills/reef-start/references/jira-start-subagent.md +60 -0
  70. package/dist/skills/reef-style-backend/SKILL.md.tmpl +134 -0
  71. package/dist/skills/reef-style-backend/fragments/java/ai/spring-ai/examples/chat-client.md +96 -0
  72. package/dist/skills/reef-style-backend/fragments/java/ai/spring-ai/examples/rag.md +94 -0
  73. package/dist/skills/reef-style-backend/fragments/java/ai/spring-ai/examples/structured-output.md +62 -0
  74. package/dist/skills/reef-style-backend/fragments/java/ai/spring-ai/examples/tool-calling.md +68 -0
  75. package/dist/skills/reef-style-backend/fragments/java/ai/spring-ai/quick-reference.md +220 -0
  76. package/dist/skills/reef-style-backend/fragments/java/api-spec/quick-reference.md +148 -0
  77. package/dist/skills/reef-style-backend/fragments/java/db-migration/liquibase/examples/database-migration.md +131 -0
  78. package/dist/skills/reef-style-backend/fragments/java/db-migration/liquibase/quick-reference.md +103 -0
  79. package/dist/skills/reef-style-backend/fragments/java/dependency-management/quick-reference.md +119 -0
  80. package/dist/skills/reef-style-backend/fragments/java/exception-handling/examples/error-code-enum.md +101 -0
  81. package/dist/skills/reef-style-backend/fragments/java/exception-handling/quick-reference.md +181 -0
  82. package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/examples/controller.md +95 -0
  83. package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/examples/dto-mapper.md +121 -0
  84. package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/examples/infrastructure.md +179 -0
  85. package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/examples/service-entity.md +202 -0
  86. package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/examples/testing.md +107 -0
  87. package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/quick-reference.md +83 -0
  88. package/dist/skills/reef-style-backend/fragments/java/orm/hibernate/quick-reference.md +150 -0
  89. package/dist/skills/reef-style-backend/fragments/java/security-redlines/quick-reference.md +197 -0
  90. package/dist/skills/reef-style-backend/fragments/java/test/data-jpa-test/examples/user-repository-test.md +69 -0
  91. package/dist/skills/reef-style-backend/fragments/java/test/data-jpa-test/quick-reference.md +101 -0
  92. package/dist/skills/reef-style-backend/fragments/java/test/junit5/examples/user-service-test.md +61 -0
  93. package/dist/skills/reef-style-backend/fragments/java/test/junit5/quick-reference.md +100 -0
  94. package/dist/skills/reef-style-backend/fragments/java/test/spring-mvc-test/examples/user-controller-test.md +61 -0
  95. package/dist/skills/reef-style-backend/fragments/java/test/spring-mvc-test/quick-reference.md +85 -0
  96. package/dist/skills/reef-style-backend/fragments/java/test/spring-service-test/examples/user-service-integration-test.md +56 -0
  97. package/dist/skills/reef-style-backend/fragments/java/test/spring-service-test/quick-reference.md +83 -0
  98. package/dist/skills/reef-style-backend/fragments/python/alembic-migration/quick-reference.md +77 -0
  99. package/dist/skills/reef-style-backend/fragments/python/api-spec/quick-reference.md +164 -0
  100. package/dist/skills/reef-style-backend/fragments/python/dependency-management/quick-reference.md +139 -0
  101. package/dist/skills/reef-style-backend/fragments/python/exception-handling/quick-reference.md +177 -0
  102. package/dist/skills/reef-style-backend/fragments/python/fastapi-quick-reference/quick-reference.md +101 -0
  103. package/dist/skills/reef-style-backend/fragments/python/langchain/quick-reference.md +135 -0
  104. package/dist/skills/reef-style-backend/fragments/python/pytest-testing/quick-reference.md +111 -0
  105. package/dist/skills/reef-style-backend/fragments/python/ruff-mypy-toolchain/quick-reference.md +83 -0
  106. package/dist/skills/reef-style-backend/fragments/python/security-redlines/quick-reference.md +207 -0
  107. package/dist/skills/reef-style-backend/fragments/python/sqlalchemy-orm/quick-reference.md +91 -0
  108. package/dist/skills/reef-style-backend/variants/java/examples/code-wrapping.md +227 -0
  109. package/dist/skills/reef-style-backend/variants/java/examples/contributor-pattern.md +97 -0
  110. package/dist/skills/reef-style-backend/variants/java/quick-reference.md +117 -0
  111. package/dist/skills/reef-style-backend/variants/python/examples/crud-router.md +71 -0
  112. package/dist/skills/reef-style-backend/variants/python/examples/pydantic-schema.md +45 -0
  113. package/dist/skills/reef-style-backend/variants/python/examples/pytest-fixture.md +60 -0
  114. package/dist/skills/reef-style-backend/variants/python/examples/sqlalchemy-model.md +31 -0
  115. package/dist/skills/reef-style-backend/variants/python/quick-reference.md +111 -0
  116. package/dist/skills/reef-style-frontend/SKILL.md.tmpl +70 -0
  117. package/dist/skills/reef-style-frontend/fragments/css/tailwind/quick-reference.md +79 -0
  118. package/dist/skills/reef-style-frontend/fragments/test/vitest/examples/testing.md +150 -0
  119. package/dist/skills/reef-style-frontend/fragments/test/vitest/quick-reference.md +141 -0
  120. package/dist/skills/reef-style-frontend/fragments/ts-config/strict/quick-reference.md +112 -0
  121. package/dist/skills/reef-style-frontend/fragments/ui-lib/primeng/examples/ui-components.md +134 -0
  122. package/dist/skills/reef-style-frontend/fragments/ui-lib/primeng/quick-reference.md +66 -0
  123. package/dist/skills/reef-style-frontend/variants/angular/examples/code-wrapping.md +252 -0
  124. package/dist/skills/reef-style-frontend/variants/angular/examples/component-types-pipes.md +56 -0
  125. package/dist/skills/reef-style-frontend/variants/angular/examples/entity-types.md +100 -0
  126. package/dist/skills/reef-style-frontend/variants/angular/examples/forms-layer.md +119 -0
  127. package/dist/skills/reef-style-frontend/variants/angular/examples/service-routing.md +95 -0
  128. package/dist/skills/reef-style-frontend/variants/angular/quick-reference.md +100 -0
  129. package/dist/skills/reef-testcase/SKILL.md +138 -0
  130. package/dist/skills/reef-testcase/references/coverage-dimensions.md +99 -0
  131. package/dist/skills/reef-testcase/references/test-case-template.md +63 -0
  132. package/dist/skills/sweep-init/SKILL.md +286 -0
  133. package/dist/skills/sweep-init/scripts/flow-selector.mjs +305 -0
  134. package/dist/skills/sweep-plan/SKILL.md.tmpl +312 -0
  135. package/dist/skills/sweep-plan/references/test-flow-template.md +46 -0
  136. package/dist/skills/sweep-run/SKILL.md +437 -0
  137. package/dist/skills/sweep-run/scripts/env-manager.mjs +208 -0
  138. package/dist/skills/sweep-run/scripts/flow-parser.mjs +329 -0
  139. package/dist/skills/sweep-run/scripts/flow-selector.mjs +483 -0
  140. package/dist/skills/sweep-run/scripts/mcp-manager.mjs +208 -0
  141. package/dist/skills/sweep-run/scripts/spec-compiler.mjs +303 -0
  142. package/dist/skills/tide-discuss/SKILL.md.tmpl +449 -0
  143. package/dist/skills/tide-discuss/references/checklists.md +88 -0
  144. package/dist/skills/tide-discuss/references/data-format.md +237 -0
  145. package/dist/skills/tide-discuss/references/prd-template.md +134 -0
  146. package/dist/skills/tide-discuss/references/publish-flow.md +167 -0
  147. package/dist/skills/tide-discuss/references/role-prompts.md +105 -0
  148. package/package.json +38 -0
@@ -0,0 +1,101 @@
1
+ # FastAPI 最佳实践
2
+
3
+ ## 路由组织
4
+
5
+ ```python
6
+ # ✅ 好:按领域分组路由
7
+ # app/api/v1/users.py
8
+ router = APIRouter(prefix="/api/v1/users", tags=["users"])
9
+
10
+ @router.get("/", response_model=list[UserResponse])
11
+ async def list_users(
12
+ skip: int = Query(0, ge=0),
13
+ limit: int = Query(20, ge=1, le=100),
14
+ user_service: UserService = Depends(),
15
+ ):
16
+ return await user_service.list_users(skip=skip, limit=limit)
17
+ ```
18
+
19
+ **规范:**
20
+ - 每个模块一个 `APIRouter`,在 `app/api/v1/__init__.py` 中 `include_router`
21
+ - 路径用 kebab-case:`/api/v1/user-roles`,不用 `/api/v1/userRoles`
22
+ - 路径变量用 snake_case:`/api/v1/users/{user_id}`
23
+ - 列表接口**必须**有分页参数
24
+
25
+ ## 依赖注入
26
+
27
+ ```python
28
+ # ✅ 好:用 Depends() 隐式注入
29
+ @app.get("/users/{user_id}")
30
+ async def get_user(
31
+ user_id: int,
32
+ user_service: UserService = Depends(),
33
+ ):
34
+ return await user_service.get_user(user_id)
35
+
36
+ # ❌ 坏:在 Router 中直接实例化 Service
37
+ @app.get("/users/{user_id}")
38
+ async def get_user(user_id: int): # ← 没有依赖注入
39
+ service = UserService() # ← 手动实例化
40
+ return await service.get_user(user_id)
41
+ ```
42
+
43
+ **规范:**
44
+ - Service 类使用 `__init__` 接收依赖,注册为 `Depends()`
45
+ - 不要用全局单例模式
46
+ - DAO / Repository 也通过 `Depends()` 注入到 Service
47
+
48
+ ## Pydantic Schema
49
+
50
+ ```python
51
+ # ✅ 好:Request / Response Schema 分开
52
+ class CreateUserRequest(BaseModel):
53
+ name: str = Field(..., min_length=1, max_length=100)
54
+ email: EmailStr
55
+ role: UserRole = UserRole.USER
56
+
57
+ class UserResponse(BaseModel):
58
+ id: int
59
+ name: str
60
+ email: EmailStr
61
+ role: UserRole
62
+ created_at: datetime
63
+
64
+ model_config = ConfigDict(from_attributes=True) # ORM 模式
65
+ ```
66
+
67
+ **规范:**
68
+ - Request Schema 用 `BaseModel`,加输入约束
69
+ - Response Schema 用 `ConfigDict(from_attributes=True)` 以支持 ORM 序列化
70
+ - CRUD 接口用 `Create*Request` / `Update*Request` / `*Response` 命名
71
+
72
+ ## 全局异常处理器
73
+
74
+ ```python
75
+ # ✅ 好:统一注册
76
+ from fastapi import FastAPI, Request
77
+ from fastapi.responses import JSONResponse
78
+
79
+ app = FastAPI()
80
+
81
+ @app.exception_handler(AppError)
82
+ async def app_error_handler(request: Request, exc: AppError):
83
+ return JSONResponse(
84
+ status_code=exc.status_code,
85
+ content={"code": exc.code, "message": exc.detail},
86
+ )
87
+ ```
88
+
89
+ ## 异步配置
90
+
91
+ ```python
92
+ # ✅ 好:FastAPI 原生异步
93
+ @app.get("/health")
94
+ async def health():
95
+ # 异步 HTTP 调用
96
+ async with httpx.AsyncClient() as client:
97
+ resp = await client.get("https://status.example.com")
98
+ return {"status": "ok"}
99
+ ```
100
+
101
+ **红线:** `async def` handler 内部不得调同步的 `time.sleep()` / `requests.get()` / 同步 ORM 操作。需要调同步操作时用 `asyncio.to_thread()` 包裹。
@@ -0,0 +1,135 @@
1
+ # LangChain 开发规范
2
+
3
+ ## 概述
4
+
5
+ LangChain 是 Python 生态中的 AI 集成框架,提供统一的 API 来调用 LLM、管理对话、构建 RAG 应用。
6
+
7
+ ## 核心概念
8
+
9
+ ### ChatModel
10
+ LangChain 的中央 API,用于与大语言模型交互。
11
+
12
+ ```python
13
+ from langchain_openai import ChatOpenAI
14
+ from langchain_core.messages import SystemMessage, HumanMessage
15
+
16
+ # 初始化
17
+ llm = ChatOpenAI(model="gpt-4o", temperature=0)
18
+
19
+ # 基础调用
20
+ response = llm.invoke([
21
+ SystemMessage(content="你是一个专业的 Python 开发者助手"),
22
+ HumanMessage(content="解释 FastAPI 的依赖注入")
23
+ ])
24
+ print(response.content)
25
+ ```
26
+
27
+ **最佳实践:**
28
+ - `ChatOpenAI` / `ChatAnthropic` 在应用初始化时创建一次,通过依赖注入传递
29
+ - 不要在每个请求中重新创建 LLM 实例
30
+ - `temperature=0` 用于确定性结果,`temperature>0` 用于创意场景
31
+ - API Key 通过环境变量读取,不硬编码
32
+
33
+ ### Prompt Template
34
+
35
+ 结构化提示词模板:
36
+
37
+ ```python
38
+ from langchain_core.prompts import ChatPromptTemplate
39
+
40
+ prompt = ChatPromptTemplate.from_messages([
41
+ ("system", "你是一个{role},请用{language}回答"),
42
+ ("human", "{question}"),
43
+ ])
44
+
45
+ chain = prompt | llm
46
+ result = chain.invoke({"role": "Python专家", "language": "中文", "question": "什么是异步编程?"})
47
+ ```
48
+
49
+ ### Tool / 函数调用
50
+
51
+ AI 模型调用外部函数的能力:
52
+
53
+ ```python
54
+ from langchain_core.tools import tool
55
+
56
+ @tool
57
+ def get_user_order(user_id: str) -> list[dict]:
58
+ """获取用户订单信息"""
59
+ # 实际业务逻辑调用 Service 层
60
+ return order_service.get_orders(user_id)
61
+
62
+
63
+ # 绑定工具后调用
64
+ llm_with_tools = llm.bind_tools([get_user_order])
65
+ ```
66
+
67
+ **最佳实践:**
68
+ - `@tool` 的 docstring 会被模型理解,写清楚参数含义和返回值格式
69
+ - 工具函数内部调用 Service 层,不在工具内写业务逻辑
70
+ - `bind_tools([])` 传工具数组,不逐个 `.bind()` 调用
71
+
72
+ ### Chain / LCEL
73
+
74
+ LangChain Expression Language — 声明式管道:
75
+
76
+ ```python
77
+ from langchain_core.output_parsers import StrOutputParser
78
+
79
+ # 链式组装
80
+ chain = prompt | llm | StrOutputParser()
81
+
82
+ # 执行
83
+ result = chain.invoke({"question": "Python 异步编程的最佳实践?"})
84
+ ```
85
+
86
+ ### RAG(检索增强生成)
87
+
88
+ ```python
89
+ from langchain_core.vectorstores import InMemoryVectorStore
90
+ from langchain_openai import OpenAIEmbeddings
91
+
92
+ # 创建向量存储
93
+ vector_store = InMemoryVectorStore.from_texts(
94
+ texts=["文档内容1", "文档内容2"],
95
+ embedding=OpenAIEmbeddings()
96
+ )
97
+
98
+ # 检索
99
+ retriever = vector_store.as_retriever(search_kwargs={"k": 3})
100
+
101
+ # RAG 链
102
+ from langchain.chains import create_retrieval_chain
103
+ from langchain.chains.combine_documents import create_stuff_documents_chain
104
+
105
+ combine_docs_chain = create_stuff_documents_chain(llm, prompt)
106
+ rag_chain = create_retrieval_chain(retriever, combine_docs_chain)
107
+ result = rag_chain.invoke({"question": "某个话题"})
108
+ ```
109
+
110
+ ## 依赖管理
111
+
112
+ ```bash
113
+ # 基础安装
114
+ uv add langchain langchain-openai
115
+
116
+ # 按需安装其他 provider
117
+ uv add langchain-anthropic # Claude
118
+ uv add langchain-google-ai # Gemini
119
+
120
+ # 向量存储
121
+ uv add langchain-community
122
+
123
+ # RAG 相关
124
+ uv add chromadb tiktoken
125
+ ```
126
+
127
+ ## 常见坑
128
+
129
+ | 场景 | 问题 | 正确做法 |
130
+ |------|------|---------|
131
+ | API Key 管理 | 硬编码在代码中 | 从 `os.getenv("OPENAI_API_KEY")` 读取 |
132
+ | 每次请求新建 LLM | 性能差、连接池耗尽 | 应用启动时创建单例,通过 DI 注入 |
133
+ | 工具函数逻辑过重 | 工具内包含全部业务逻辑 | 工具只做参数解析 + 调用 Service |
134
+ | 忽略 `async` 支持 | LCEL 链中调同步 IO | 使用 `ainvoke()` 和 async retriever |
135
+ | prompt 不设 system message | 模型行为不可控 | 始终设置 system message 定义角色和约束 |
@@ -0,0 +1,111 @@
1
+ # pytest 测试规范
2
+
3
+ ## 目录结构
4
+
5
+ ```
6
+ tests/
7
+ ├── conftest.py # 全局 fixture
8
+ ├── unit/ # 纯逻辑单元测试
9
+ │ ├── test_services/
10
+ │ └── test_schemas/
11
+ └── integration/ # 集成测试(DB / API)
12
+ ├── test_api/
13
+ └── conftest.py # 集成测试专用 fixture
14
+ ```
15
+
16
+ **规范:**
17
+ - `tests/unit/` — 不依赖外部服务的纯逻辑测试
18
+ - `tests/integration/` — 依赖数据库、HTTP 调用的测试
19
+ - 测试文件以 `test_` 开头,函数以 `test_` 开头
20
+
21
+ ## pytest fixture
22
+
23
+ ```python
24
+ # ✅ 好:conftest.py 中的共享 fixture
25
+ import pytest
26
+ import pytest_asyncio
27
+ from sqlalchemy.ext.asyncio import AsyncSession
28
+
29
+ @pytest_asyncio.fixture
30
+ async def db_session():
31
+ # 每个测试独立事务,测试后回滚
32
+ async with async_session() as session:
33
+ async with session.begin():
34
+ yield session
35
+ await session.rollback()
36
+
37
+ @pytest_asyncio.fixture
38
+ async def user_service(db_session: AsyncSession):
39
+ return UserService(db=db_session)
40
+ ```
41
+
42
+ **规范:**
43
+ - 测试用 `pytest-asyncio` 支持异步 fixture 和 test
44
+ - fixture 只在 `conftest.py` 中定义,不在测试文件中
45
+ - fixture 范围默认 `function`,仅共享资源用 `session` / `module`
46
+
47
+ ## 异步测试
48
+
49
+ ```python
50
+ # ✅ 好:pytest-asyncio 异步测试
51
+ import pytest
52
+
53
+ @pytest.mark.asyncio
54
+ async def test_create_user(user_service: UserService):
55
+ user = await user_service.create_user(name="Alice", email="alice@example.com")
56
+ assert user.id is not None
57
+ assert user.name == "Alice"
58
+ ```
59
+
60
+ ## 集成测试标记
61
+
62
+ ```python
63
+ import pytest
64
+
65
+ # ✅ 好:用 mark 区分测试类型
66
+ @pytest.mark.integration
67
+ @pytest.mark.asyncio
68
+ async def test_api_list_users(client: httpx.AsyncClient):
69
+ response = await client.get("/api/v1/users")
70
+ assert response.status_code == 200
71
+ data = response.json()
72
+ assert isinstance(data, list)
73
+ ```
74
+
75
+ ## HTTP 测试
76
+
77
+ ```python
78
+ # ✅ 好:用 httpx AsyncClient 配合 FastAPI TestClient
79
+ from httpx import AsyncClient, ASGITransport
80
+
81
+ @pytest_asyncio.fixture
82
+ async def client():
83
+ transport = ASGITransport(app=app)
84
+ async with AsyncClient(transport=transport, base_url="http://test") as ac:
85
+ yield ac
86
+
87
+ @pytest.mark.asyncio
88
+ async def test_get_user(client: AsyncClient):
89
+ response = await client.get("/api/v1/users/1")
90
+ assert response.status_code == 200
91
+ assert response.json()["id"] == 1
92
+ ```
93
+
94
+ ## 运行命令
95
+
96
+ ```bash
97
+ # 运行全部测试
98
+ python -m pytest
99
+
100
+ # 指定目录
101
+ python -m pytest tests/unit/
102
+
103
+ # 包含覆盖率
104
+ python -m pytest --cov=app --cov-report=term-missing
105
+
106
+ # 仅集成测试
107
+ python -m pytest -m integration
108
+
109
+ # 并行运行
110
+ python -m pytest -n auto
111
+ ```
@@ -0,0 +1,83 @@
1
+ # ruff + mypy 工具链规范
2
+
3
+ ## ruff 配置(pyproject.toml)
4
+
5
+ ```toml
6
+ [tool.ruff]
7
+ target-version = "py310"
8
+ line-length = 100
9
+
10
+ [tool.ruff.lint]
11
+ select = [
12
+ "F", # Pyflakes — 检测未使用导入、未定义变量等
13
+ "E", # pycodestyle — PEP 8 风格错误
14
+ "W", # pycodestyle — 警告
15
+ "I", # isort — 导入排序
16
+ "N", # pep8-naming — 命名规范
17
+ "UP", # pyupgrade — 新版语法建议
18
+ "B", # flake8-bugbear — 常见 bug 模式
19
+ ]
20
+ ignore = [
21
+ "E501", # 行长度交给 black,不在 ruff 中检查
22
+ ]
23
+
24
+ [tool.ruff.lint.per-file-ignores]
25
+ "tests/**" = ["N"] # 测试文件放宽命名规范
26
+
27
+ [tool.ruff.format]
28
+ quote-style = "double"
29
+ indent-style = "space"
30
+ line-ending = "lf"
31
+ ```
32
+
33
+ **规范:**
34
+ - ruff 负责 lint(`ruff check`)和 format(`ruff format`)双重职责
35
+ - 启用 `F` + `E` + `W` + `I` + `N` + `UP` + `B` 规则组
36
+ - 行长度检查(`E501`)交给 black / ruff format,不在 lint 中重复
37
+
38
+ ## mypy 配置(pyproject.toml)
39
+
40
+ ```toml
41
+ [tool.mypy]
42
+ strict = true
43
+ python_version = "3.10"
44
+ check_untyped_defs = true
45
+ warn_unused_ignores = true
46
+ warn_redundant_casts = true
47
+ warn_return_any = true
48
+ no_implicit_optional = true
49
+ disallow_any_unimported = true
50
+ disallow_untyped_defs = true
51
+ disallow_untyped_calls = true
52
+
53
+ [[tool.mypy.overrides]]
54
+ module = [
55
+ "tests.*",
56
+ ]
57
+ ignore_missing_imports = true
58
+ disallow_untyped_defs = false
59
+ ```
60
+
61
+ **规范:**
62
+ - 生产代码使用 `strict = true`
63
+ - 所有函数必须有类型注释
64
+ - 测试文件放宽类型要求
65
+
66
+ ## 常用命令
67
+
68
+ ```bash
69
+ # 快速 lint
70
+ ruff check .
71
+
72
+ # 自动修复
73
+ ruff check --fix .
74
+
75
+ # 格式化
76
+ ruff format
77
+
78
+ # 类型检查
79
+ mypy .
80
+
81
+ # 完整检查(推荐 CI 用)
82
+ ruff check . && ruff format --check && mypy .
83
+ ```
@@ -0,0 +1,207 @@
1
+ # 安全红线(Python / FastAPI)
2
+
3
+ ## 🔴 红线速查
4
+
5
+ | 红线 | 禁止行为 | 正确做法 | 违反后果 |
6
+ |------|---------|---------|---------|
7
+ | 密码存储 | 明文存储密码 | `hashlib` / `bcrypt` / `passlib` 哈希后存储 | P0 — 安全漏洞 |
8
+ | SQL 拼接 | f-string / format 拼接 SQL | SQLAlchemy ORM 或 参数化查询 | P0 — SQL 注入 |
9
+ | 硬编码密钥 | 代码中硬编码 Secret / Token | 环境变量 `os.getenv()` 或 `pydantic-settings` | P0 — 凭据泄露 |
10
+ | 敏感信息打印 | `print()` 敏感数据 | `logging` + 脱敏 + 生产级别过滤 | P1 — 信息泄露 |
11
+ | 输入验证缺失 | 不验证用户输入 | Pydantic model + 严格约束 | P1 — 注入/越权 |
12
+ | 不安全的 CORS | `allow_origins=["*"]` | 明确指定允许源列表 | P1 — CORS 安全 |
13
+ | 调试接口 | 生产暴露 `/docs` / 调试路由 | 根据环境变量控制开启 | P1 — 信息泄露 |
14
+
15
+ ## 🔴 红线详情
16
+
17
+ ### 1. 密码存储 — 禁止明文 (P0)
18
+
19
+ ```python
20
+ # ❌ 坏:明文存储
21
+ user.password = request.password
22
+
23
+ # ✅ 好:哈希后存储
24
+ from passlib.context import CryptContext
25
+
26
+ pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
27
+
28
+ class UserService:
29
+ def create_user(self, request: CreateUserRequest) -> User:
30
+ hashed = pwd_context.hash(request.password)
31
+ user = User(email=request.email, hashed_password=hashed)
32
+ return await user_repository.save(user)
33
+
34
+ def verify_password(self, plain: str, hashed: str) -> bool:
35
+ return pwd_context.verify(plain, hashed)
36
+
37
+ # 验证密码
38
+ is_valid = pwd_context.verify(plain_password, user.hashed_password)
39
+ ```
40
+
41
+ ### 2. SQL 拼接 — 禁止字符串拼接 (P0)
42
+
43
+ ```python
44
+ # ❌ 坏:f-string 拼接 — SQL 注入
45
+ query = f"SELECT * FROM users WHERE email = '{email}'"
46
+ result = await db.execute(query)
47
+
48
+ # ❌ 坏:format 拼接
49
+ query = "SELECT * FROM users WHERE email = '{}'".format(email)
50
+ result = await db.execute(query)
51
+
52
+ # ✅ 好:SQLAlchemy ORM(首选)
53
+ user = await db.query(User).filter(User.email == email).first()
54
+
55
+ # ✅ 好:参数化查询(原生 SQL 时)
56
+ from sqlalchemy import text
57
+ query = text("SELECT * FROM users WHERE email = :email")
58
+ result = await db.execute(query, {"email": email})
59
+
60
+ # ✅ 好:原始 SQL 用 bind 参数
61
+ cursor.execute("SELECT * FROM users WHERE email = %s", (email,))
62
+ ```
63
+
64
+ **红线规则:**
65
+ - 禁止在 SQL 中使用 `f"..."` 或 `"".format()` 拼接用户输入
66
+ - 优先使用 SQLAlchemy ORM 的查询构建器
67
+ - 必须使用原生 SQL 时,必须使用参数化查询(`:name` 或 `%s`)
68
+
69
+ ### 3. 硬编码密钥 — 禁止代码中内联 (P0)
70
+
71
+ ```python
72
+ # ❌ 坏:硬编码 Secret
73
+ SECRET_KEY = "sk-abc123..."
74
+ API_KEY = "my-secret-key-12345"
75
+
76
+ # ✅ 好:pydantic-settings 从环境变量读取
77
+ from pydantic_settings import BaseSettings
78
+
79
+ class Settings(BaseSettings):
80
+ secret_key: str
81
+ api_key: str
82
+ database_url: str
83
+
84
+ model_config = SettingsConfigDict(
85
+ env_file=".env",
86
+ env_file_encoding="utf-8",
87
+ )
88
+
89
+ settings = Settings() # 从 .env 或环境变量加载
90
+ ```
91
+
92
+ ```bash
93
+ # ❌ 坏:提交到 Git 的配置文件中有明文密钥
94
+ # config.py
95
+ SECRET_KEY = "my-secret-key" # ← 硬编码
96
+
97
+ # ✅ 好:.env 本地开发(不提交 Git)
98
+ SECRET_KEY=my-secret-key
99
+ API_KEY=sk-abc123...
100
+ DATABASE_URL=postgresql://localhost:5432/dev
101
+
102
+ # ✅ 好:CI/CD 中使用 CI Secrets 或 Vault
103
+ # GitHub Actions Secrets / GitLab CI Variables
104
+ ```
105
+
106
+ ### 4. 敏感脱敏 — 日志中禁止打印敏感信息 (P1)
107
+
108
+ ```python
109
+ import logging
110
+
111
+ logger = logging.getLogger(__name__)
112
+
113
+
114
+ # ❌ 坏:直接打印敏感信息
115
+ logger.info(f"User login: {email}, password: {password}") # ← 密码泄露
116
+ logger.info(f"Token: {token}") # ← Token 泄露
117
+ print(f"Auth token: {token}") # ← 禁止 print
118
+
119
+ # ✅ 好:脱敏后打印
120
+ def mask_email(email: str) -> str:
121
+ at = email.find("@")
122
+ if at <= 1:
123
+ return email
124
+ return email[0] + "***" + email[at:]
125
+
126
+ def mask_phone(phone: str) -> str:
127
+ if not phone or len(phone) < 7:
128
+ return phone
129
+ return phone[:3] + "****" + phone[-4:]
130
+
131
+ logger.info(f"User login: {mask_email(email)}")
132
+ logger.info(f"User [id={user_id}] logged in")
133
+ ```
134
+
135
+ **红线规则:**
136
+ - 禁止打印密码、Token、完整手机号、完整身份证号
137
+ - 响应体中密码字段不输出(Pydantic `model_config` 排除或 response schema 中忽略)
138
+ - 禁止 `print()` — 必须使用 `logging` 模块
139
+
140
+ ### 5. 输入验证红线 (P1)
141
+
142
+ ```python
143
+ # ✅ 好:Pydantic model 严格约束
144
+ from pydantic import BaseModel, Field, EmailStr, field_validator
145
+
146
+ class CreateUserRequest(BaseModel):
147
+ name: str = Field(..., min_length=1, max_length=100)
148
+ email: EmailStr
149
+ age: int = Field(..., ge=0, le=150)
150
+ role: UserRole = UserRole.USER
151
+
152
+ @field_validator("name")
153
+ @classmethod
154
+ def validate_name(cls, v: str) -> str:
155
+ if not v.strip():
156
+ raise ValueError("名称不能为纯空格")
157
+ return v.strip()
158
+
159
+ # ❌ 坏:不验证或用裸 dict
160
+ @app.post("/users")
161
+ async def create_user(name: str, email: str): # ← 无约束
162
+ ...
163
+
164
+ # ✅ 好:Router 中必须使用 Pydantic model
165
+ @app.post("/users", response_model=ApiResponse[UserResponse])
166
+ async def create_user(request: CreateUserRequest, ...):
167
+ ...
168
+ ```
169
+
170
+ ### 6. CORS 配置红线 (P1)
171
+
172
+ ```python
173
+ # ❌ 坏:允许所有来源
174
+ app.add_middleware(
175
+ CORSMiddleware,
176
+ allow_origins=["*"], # ← 生产环境禁止
177
+ allow_credentials=True, # ← allow_origins=["*"] 时配合 allow_credentials=True 无效且不安全
178
+ )
179
+
180
+ # ✅ 好:明确指定允许的来源
181
+ app.add_middleware(
182
+ CORSMiddleware,
183
+ allow_origins=[
184
+ "https://app.example.com",
185
+ "https://admin.example.com",
186
+ ],
187
+ allow_credentials=True,
188
+ allow_methods=["GET", "POST", "PUT", "DELETE", "PATCH"],
189
+ allow_headers=["Authorization", "Content-Type"],
190
+ )
191
+ ```
192
+
193
+ ### 7. 生产环境禁用调试接口 (P1)
194
+
195
+ ```python
196
+ # ✅ 好:根据环境控制
197
+ from app.core.config import settings
198
+
199
+ app = FastAPI(
200
+ title="My App",
201
+ docs_url="/api/docs" if settings.ENV != "production" else None,
202
+ redoc_url=None,
203
+ )
204
+
205
+ # ❌ 坏:生产环境也暴露
206
+ app = FastAPI(docs_url="/api/docs") # ← 生产环境暴露 API 文档
207
+ ```