Compare commits

5 Commits

Author SHA1 Message Date
acb77a821c docs(skill): solidify routing, ws validation, pm2 port migration and slot rebind rules
Some checks failed
preview-slot-reclaim / reclaim (push) Has been cancelled
2026-03-10 08:16:35 +08:00
2fcd4d093a docs: sync wiki content and add skills parameter guide
Some checks failed
preview-slot-reclaim / reclaim (push) Has been cancelled
publish-site / deploy-public-site (push) Has been cancelled
2026-03-08 20:25:20 +08:00
fcecce0595 feat: publish standalone product landing page as rendered svg
Some checks failed
preview-slot-reclaim / reclaim (push) Has been cancelled
publish-site / deploy-public-site (push) Has been cancelled
2026-03-06 22:58:48 +08:00
fbe92a4e7c chore: add svg raw rendering probe
Some checks failed
publish-site / deploy-public-site (push) Has been cancelled
2026-03-06 22:55:48 +08:00
4df29b70cd docs: point official website to rendered wiki page
Some checks failed
publish-site / deploy-public-site (push) Has been cancelled
2026-03-06 22:46:50 +08:00
3 changed files with 438 additions and 22 deletions

234
README.md
View File

@@ -1,31 +1,47 @@
# DevOps Skills ![JUST FOR FUN - Linus Torvalds](https://kwize.com/pics/Linus-Torvalds-quote-about-fun-1a9823.jpg)
Issue-Driven DevOps 平台技能仓库,核心产品是 `gitea-issue-devops-agent` # gitea-issue-devops-agent
它把交付流程固化为: > **Issue-Driven DevOps 产品官网**
> 把 `Issue -> Branch -> Preview Slot -> Test Loop -> Human-Confirmed Merge` 变成标准交付引擎。
`Issue -> Branch -> Preview Slot -> Test Loop -> Human-Confirmed Merge`
## 公网产品页 ## 公网产品页
- 产品官网:`https://fun-md.com/Fun_MD/devops-skills/raw/branch/main/site/index.html` - 产品官网(独立前端渲染页,非 Wiki`https://fun-md.com/Fun_MD/devops-skills/raw/branch/main/site/index.svg`
- 仓库入口:`https://fun-md.com/Fun_MD/devops-skills` - 仓库入口:`https://fun-md.com/Fun_MD/devops-skills`
- HTML 设计稿源码:`site/index.html`
- SVG 官网文件:`site/index.svg`
## 核心价值
### 1) 分支隔离提测
每个 issue 固定独立分支和预览槽位,主干保持稳定回归,避免提测互相覆盖。
### 2) 智能节省资源
按改动自动识别部署策略:`skip / client_only / server_only / full_stack / infra_only`
**服务端未变更就不重启服务端**
### 3) 证据化闭环
提测沉淀 commit、测试链接、环境 URL、验证步骤最终合并必须工程师人工确认。
## 一键安装 ## 一键安装
Linux: ### Linux
```bash ```bash
curl -fsSL https://fun-md.com/Fun_MD/devops-skills/raw/branch/main/install/install.sh | bash curl -fsSL https://fun-md.com/Fun_MD/devops-skills/raw/branch/main/install/install.sh | bash
``` ```
macOS: ### macOS
```bash ```bash
curl -fsSL https://fun-md.com/Fun_MD/devops-skills/raw/branch/main/install/install.sh | bash curl -fsSL https://fun-md.com/Fun_MD/devops-skills/raw/branch/main/install/install.sh | bash
``` ```
Windows (PowerShell): ### Windows (PowerShell)
```powershell ```powershell
powershell -NoProfile -ExecutionPolicy Bypass -Command "iwr -useb https://fun-md.com/Fun_MD/devops-skills/raw/branch/main/install/install.ps1 | iex" powershell -NoProfile -ExecutionPolicy Bypass -Command "iwr -useb https://fun-md.com/Fun_MD/devops-skills/raw/branch/main/install/install.ps1 | iex"
@@ -35,25 +51,199 @@ powershell -NoProfile -ExecutionPolicy Bypass -Command "iwr -useb https://fun-md
- `~/.codex/skills/gitea-issue-devops-agent` - `~/.codex/skills/gitea-issue-devops-agent`
## 工具使用说明
### issue_audit.py
```bash
python skills/gitea-issue-devops-agent/scripts/issue_audit.py \
--base-url https://fun-md.com \
--repo FunMD/document-collab \
--token <TOKEN> \
--state all \
--download-attachments \
--output-dir .tmp/issue-audit
```
### change_scope.py
```bash
python skills/gitea-issue-devops-agent/scripts/change_scope.py --repo-path . --base-ref origin/main --head-ref HEAD
```
### preview_slot_allocator.py
```bash
python skills/gitea-issue-devops-agent/scripts/preview_slot_allocator.py --state-file .tmp/preview-slots.json --slots preview-a,preview-b --repo FunMD/document-collab --issue 48 --branch dev --ttl-hours 24 --url-template https://{slot}.qa.example.com --evict-oldest
```
## 工作流模板
- `.gitea/workflows/issue-branch-preview.yml`
- `.gitea/workflows/preview-slot-reclaim.yml`
- `.gitea/workflows/publish-site.yml`
## Skills 调用前置信息Claude Code / Codex / OpenCode
统一建议先准备这组参数:
- `repo_url`
- `api_key`Gitea token需 issue 读写权限)
- `mode``automatic` / `semi-automatic` / `manual`
- 可选:`reviewers``test_entry``deploy_env``health_endpoint``min_quality_score`
### Claude Code
Skills 目录(官方支持):
- 用户级:`~/.claude/skills/<skill-name>/SKILL.md`
- 项目级:`.claude/skills/<skill-name>/SKILL.md`
唤起方式:
- 显式调用:`/<skill-name> [args]`
- 对话调用:直接说“使用某个 skill 处理任务”
示例:
```text
/gitea-issue-devops-agent repo_url=https://fun-md.com/FunMD/document-collab mode=automatic
```
```text
请使用 gitea-issue-devops-agent连接 repo_url=...api_key=...,以 semi-automatic 模式处理 issue #48
```
### Codex
Skills 安装目录(当前方案):
- `~/.codex/skills/gitea-issue-devops-agent`
唤起方式:
- 对话显式点名:`$gitea-issue-devops-agent`
- 或自然语言明确要求:`使用 gitea-issue-devops-agent skill`
示例:
```text
$gitea-issue-devops-agent
repo_url: https://fun-md.com/FunMD/document-collab
api_key: <TOKEN>
mode: automatic
```
### OpenCode
Skills 目录Claude skill 兼容):
- 项目级:`.opencode/skills/<skill-name>/SKILL.md`
- 全局级:`~/.config/opencode/skills/<skill-name>/SKILL.md`
唤起方式:
- 对话里明确要求使用目标 skill推荐
- Agent 内部会通过原生 `skill` 工具加载(`skill({name: "..."})`
示例:
```text
Use skill gitea-issue-devops-agent.
repo_url=https://fun-md.com/FunMD/document-collab
api_key=<TOKEN>
mode=manual
```
## `skills` 命令参数释义(重点补充)
> 本节把“`skills` 命令”统一理解为:在 Claude/Codex/OpenCode 中显式调用 `gitea-issue-devops-agent` 时提交的参数块。
> 建议参数名如下,便于团队协作时统一模板和自动化脚本对接。
### 必填参数
| 参数 | 说明 | 典型值 | 使用场景 |
| --- | --- | --- | --- |
| `repo_url` | 目标仓库完整地址。优先使用完整 URL。 | `https://fun-md.com/Fun_MD/devops-skills` | 常规接入,避免 `base_url + owner/repo` 组合歧义 |
| `api_key` | Gitea token至少具备 issue 读写权限。 | `gta_xxx` | 需要读取 issue、评论、附件并回写提测证据 |
| `mode` | 执行模式:`automatic` / `semi-automatic` / `manual`。 | `automatic` | 决定自动化程度和人工审批点 |
### 重要可选参数
| 参数 | 说明 | 典型值 | 使用场景 |
| --- | --- | --- | --- |
| `reviewers` | 指定评审人列表(逗号分隔)。 | `alice,bob` | `semi-automatic` 模式下提交后等待人工评审 |
| `test_entry` | 分支提测入口CI 命令或 job 名)。 | `gitea workflow run issue-branch-preview` | 多条流水线并存时明确提测入口 |
| `main_env_url` | 主干稳定环境 URL。 | `https://main.qa.example.com` | 回归对比、基线验证 |
| `shared_qa_url` | 共享 QA 环境 URL可选。 | `https://qa.example.com` | 需要跨分支集成验证 |
| `preview_slots` | 预览槽位池。 | `preview-a,preview-b` | 多 issue 并行时的环境隔离与复用 |
| `preview_url_template` | 槽位 URL 模板。 | `https://{slot}.qa.example.com` | 自动生成 issue 分支预览地址 |
| `deploy_env` | 部署环境标识。 | `k8s-staging` | 一套技能同时驱动多环境 |
| `health_endpoint` | 健康检查接口。 | `/healthz` | 提测后自动做可用性验证 |
| `min_quality_score` | issue 最低质量分(默认 70。 | `70` | 低质量 issue 先补充信息再进入开发 |
| `skip_asset_endpoints` | 跳过 `/issues/*/assets` 端点抓图。 | `true` | 自建 Gitea 禁用了 assets API 时兜底 |
| `target_base` | 变更比较基线分支。 | `origin/main` | 用于 `change_scope` 判断部署范围 |
### 参数组合示例(按场景)
#### 场景 1日常 bug 修复,端到端自动执行
```text
/gitea-issue-devops-agent \
repo_url=https://fun-md.com/Fun_MD/devops-skills \
api_key=<TOKEN> \
mode=automatic \
test_entry="issue-branch-preview" \
main_env_url=https://main.qa.example.com \
preview_slots=preview-a,preview-b \
preview_url_template=https://{slot}.qa.example.com \
min_quality_score=70
```
适用:问题描述完整、团队希望最大化自动化吞吐。
#### 场景 2生产敏感仓库人工确认每一步
```text
$gitea-issue-devops-agent
repo_url: https://fun-md.com/Fun_MD/devops-skills
api_key: <TOKEN>
mode: manual
deploy_env: prod-like-staging
health_endpoint: /healthz
```
适用:高风险改动、强合规流程、需要逐步确认分支/提交/提测/关闭。
#### 场景 3半自动协作先评审后提测
```text
Use skill gitea-issue-devops-agent.
repo_url=https://fun-md.com/Fun_MD/devops-skills
api_key=<TOKEN>
mode=semi-automatic
reviewers=alice,bob
test_entry=issue-branch-preview
shared_qa_url=https://qa.example.com
preview_slots=preview-a,preview-b,preview-c
```
适用:多人协作项目,需要评审人显式批准后再进入提测和环境分配。
#### 场景 4仅文档改动或轻量改动资源最省策略
```text
/gitea-issue-devops-agent repo_url=... api_key=... mode=automatic target_base=origin/main
```
配合 `change_scope.py` 可自动得到 `skip``client_only`,避免不必要的服务端重启和环境开销。
## 技能路径 ## 技能路径
- `skills/gitea-issue-devops-agent/SKILL.md` - `skills/gitea-issue-devops-agent/SKILL.md`
## 核心能力
- 三种执行模式:`automatic` / `semi-automatic` / `manual`
- issue 图片证据抓取(含 attachments/assets 三路兜底)
- 按变更范围部署(`skip` / `client_only` / `server_only` / `full_stack` / `infra_only`
- 预览槽位池分配与自动回收TTL + 关闭释放)
- 最终代码合并必须人工确认
## 核心脚本 ## 核心脚本
- `skills/gitea-issue-devops-agent/scripts/issue_audit.py` - `skills/gitea-issue-devops-agent/scripts/issue_audit.py`
- `skills/gitea-issue-devops-agent/scripts/change_scope.py` - `skills/gitea-issue-devops-agent/scripts/change_scope.py`
- `skills/gitea-issue-devops-agent/scripts/preview_slot_allocator.py` - `skills/gitea-issue-devops-agent/scripts/preview_slot_allocator.py`
## .gitea/workflows 模板
- `.gitea/workflows/issue-branch-preview.yml`
- `.gitea/workflows/preview-slot-reclaim.yml`

213
site/index.svg Normal file
View File

@@ -0,0 +1,213 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1600" height="2500" viewBox="0 0 1600 2500">
<defs>
<linearGradient id="bgGrad" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#070c19"/>
<stop offset="55%" stop-color="#102347"/>
<stop offset="100%" stop-color="#0a3a4f"/>
</linearGradient>
<linearGradient id="accentGrad" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#29d8ff"/>
<stop offset="100%" stop-color="#49f2c7"/>
</linearGradient>
<linearGradient id="cardGrad" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#122445" stop-opacity="0.95"/>
<stop offset="100%" stop-color="#0f1d38" stop-opacity="0.95"/>
</linearGradient>
<filter id="softGlow" x="-40%" y="-40%" width="180%" height="180%">
<feGaussianBlur stdDeviation="18" result="blur"/>
<feMerge>
<feMergeNode in="blur"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
<style>
.title {
font-family: "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
fill: #ecf4ff;
font-weight: 700;
}
.subtitle {
font-family: "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
fill: #b8c9e9;
font-size: 30px;
}
.section-title {
font-family: "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
fill: #e7f1ff;
font-size: 44px;
font-weight: 700;
}
.card-title {
font-family: "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
fill: #def2ff;
font-size: 32px;
font-weight: 700;
}
.card-text {
font-family: "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
fill: #bad0ee;
font-size: 24px;
}
.mono {
font-family: "Cascadia Mono", Consolas, Menlo, monospace;
fill: #d8f1ff;
font-size: 21px;
}
.label {
font-family: "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
fill: #88a6d4;
font-size: 20px;
}
.badge {
font-family: "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
fill: #0a2b3f;
font-size: 21px;
font-weight: 700;
}
.btn {
font-family: "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
fill: #06253a;
font-size: 24px;
font-weight: 700;
}
.btn-muted {
font-family: "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
fill: #d2e8ff;
font-size: 22px;
font-weight: 600;
}
</style>
</defs>
<rect x="0" y="0" width="1600" height="2500" fill="url(#bgGrad)"/>
<circle cx="1350" cy="240" r="260" fill="#29d8ff" opacity="0.13" filter="url(#softGlow)"/>
<circle cx="180" cy="320" r="220" fill="#49f2c7" opacity="0.11" filter="url(#softGlow)"/>
<circle cx="420" cy="2100" r="260" fill="#2fd8f0" opacity="0.10" filter="url(#softGlow)"/>
<rect x="70" y="70" width="1460" height="500" rx="32" fill="url(#cardGrad)" stroke="#345a90" stroke-width="2"/>
<rect x="110" y="120" width="390" height="56" rx="28" fill="url(#accentGrad)"/>
<text x="145" y="158" class="badge">Issue-Driven DevOps Platform</text>
<text x="110" y="255" class="title" font-size="74">gitea-issue-devops-agent</text>
<text x="110" y="325" class="subtitle">把 Issue → Branch → Preview Slot → Test Loop → Human-Confirmed Merge 变成标准交付引擎</text>
<text x="110" y="372" class="subtitle">不是临时脚本,而是可规模化的研发基础设施产品。</text>
<rect x="110" y="415" width="260" height="82" rx="14" fill="url(#accentGrad)"/>
<text x="152" y="468" class="btn">访问仓库</text>
<a href="https://fun-md.com/Fun_MD/devops-skills">
<rect x="110" y="415" width="260" height="82" rx="14" fill="#ffffff" opacity="0.001"/>
</a>
<rect x="390" y="415" width="330" height="82" rx="14" fill="#1b3157" stroke="#4068a5"/>
<text x="445" y="468" class="btn-muted">SKILL 规范文档</text>
<a href="https://fun-md.com/Fun_MD/devops-skills/raw/branch/main/skills/gitea-issue-devops-agent/SKILL.md">
<rect x="390" y="415" width="330" height="82" rx="14" fill="#ffffff" opacity="0.001"/>
</a>
<rect x="820" y="150" width="660" height="340" rx="22" fill="#0c1a34" stroke="#34598e"/>
<text x="860" y="215" class="label">核心指标</text>
<text x="860" y="285" class="title" font-size="58">3 Modes</text>
<text x="860" y="325" class="label">automatic / semi-automatic / manual</text>
<text x="1160" y="285" class="title" font-size="58">5 Scopes</text>
<text x="1160" y="325" class="label">skip / client / server / full / infra</text>
<text x="860" y="390" class="title" font-size="58">1 : 1 Binding</text>
<text x="860" y="430" class="label">Issue - Branch - Preview Slot 精确绑定</text>
<text x="1160" y="390" class="title" font-size="58">TTL Reclaim</text>
<text x="1160" y="430" class="label">空闲槽位自动回收</text>
<text x="80" y="680" class="section-title">产品核心价值</text>
<rect x="80" y="715" width="460" height="280" rx="20" fill="url(#cardGrad)" stroke="#35598f"/>
<text x="110" y="785" class="card-title">1. 分支隔离提测</text>
<text x="110" y="835" class="card-text">每个 issue 固定分支和预览环境,</text>
<text x="110" y="872" class="card-text">主干环境稳定回归,提测互不覆盖。</text>
<text x="110" y="909" class="card-text">团队并行效率显著提升。</text>
<rect x="570" y="715" width="460" height="280" rx="20" fill="url(#cardGrad)" stroke="#35598f"/>
<text x="600" y="785" class="card-title">2. 资源智能节流</text>
<text x="600" y="835" class="card-text">按改动自动识别部署范围,</text>
<text x="600" y="872" class="card-text">前端-only 改动不重启服务端,</text>
<text x="600" y="909" class="card-text">节省机器和运维成本。</text>
<rect x="1060" y="715" width="460" height="280" rx="20" fill="url(#cardGrad)" stroke="#35598f"/>
<text x="1090" y="785" class="card-title">3. 证据化闭环</text>
<text x="1090" y="835" class="card-text">提测结果、commit、环境链接、</text>
<text x="1090" y="872" class="card-text">验证步骤统一沉淀,</text>
<text x="1090" y="909" class="card-text">最终合并始终人工确认。</text>
<text x="80" y="1090" class="section-title">流程拓扑Issue 到交付)</text>
<rect x="80" y="1125" width="1440" height="220" rx="20" fill="url(#cardGrad)" stroke="#35598f"/>
<rect x="112" y="1170" width="250" height="130" rx="14" fill="#13284b" stroke="#3d639b"/>
<text x="138" y="1223" class="card-title" font-size="26">1. 引导连接</text>
<text x="138" y="1263" class="card-text" font-size="20">repo_url + api_key + mode</text>
<rect x="402" y="1170" width="250" height="130" rx="14" fill="#13284b" stroke="#3d639b"/>
<text x="430" y="1223" class="card-title" font-size="26">2. 质量审计</text>
<text x="430" y="1263" class="card-text" font-size="20">Issue + 图片附件 + 去重评分</text>
<rect x="692" y="1170" width="250" height="130" rx="14" fill="#13284b" stroke="#3d639b"/>
<text x="720" y="1223" class="card-title" font-size="26">3. 分支修复</text>
<text x="720" y="1263" class="card-text" font-size="20">严格在 issue 分支迭代</text>
<rect x="982" y="1170" width="250" height="130" rx="14" fill="#13284b" stroke="#3d639b"/>
<text x="1007" y="1223" class="card-title" font-size="26">4. 按范围部署</text>
<text x="1007" y="1263" class="card-text" font-size="20">skip/client/server/full/infra</text>
<rect x="1272" y="1170" width="216" height="130" rx="14" fill="#13284b" stroke="#3d639b"/>
<text x="1300" y="1223" class="card-title" font-size="26">5. 自动回收</text>
<text x="1300" y="1263" class="card-text" font-size="20">TTL + Close Release</text>
<line x1="362" y1="1235" x2="402" y2="1235" stroke="#58d8ff" stroke-width="4"/>
<line x1="652" y1="1235" x2="692" y2="1235" stroke="#58d8ff" stroke-width="4"/>
<line x1="942" y1="1235" x2="982" y2="1235" stroke="#58d8ff" stroke-width="4"/>
<line x1="1232" y1="1235" x2="1272" y2="1235" stroke="#58d8ff" stroke-width="4"/>
<text x="80" y="1440" class="section-title">一键安装命令Windows / macOS / Linux</text>
<rect x="80" y="1480" width="470" height="300" rx="18" fill="url(#cardGrad)" stroke="#35598f"/>
<text x="110" y="1542" class="card-title">Linux</text>
<rect x="110" y="1568" width="410" height="178" rx="12" fill="#091327" stroke="#406aa6"/>
<text x="130" y="1620" class="mono">curl -fsSL https://fun-md.com/Fun_MD/</text>
<text x="130" y="1656" class="mono">devops-skills/raw/branch/main/install/</text>
<text x="130" y="1692" class="mono">install.sh | bash</text>
<rect x="565" y="1480" width="470" height="300" rx="18" fill="url(#cardGrad)" stroke="#35598f"/>
<text x="595" y="1542" class="card-title">macOS</text>
<rect x="595" y="1568" width="410" height="178" rx="12" fill="#091327" stroke="#406aa6"/>
<text x="615" y="1620" class="mono">curl -fsSL https://fun-md.com/Fun_MD/</text>
<text x="615" y="1656" class="mono">devops-skills/raw/branch/main/install/</text>
<text x="615" y="1692" class="mono">install.sh | bash</text>
<rect x="1050" y="1480" width="470" height="300" rx="18" fill="url(#cardGrad)" stroke="#35598f"/>
<text x="1080" y="1542" class="card-title">Windows PowerShell</text>
<rect x="1080" y="1568" width="410" height="178" rx="12" fill="#091327" stroke="#406aa6"/>
<text x="1100" y="1610" class="mono">powershell -NoProfile -ExecutionPolicy</text>
<text x="1100" y="1646" class="mono">Bypass -Command "iwr -useb https://</text>
<text x="1100" y="1682" class="mono">fun-md.com/Fun_MD/devops-skills/raw/</text>
<text x="1100" y="1718" class="mono">branch/main/install/install.ps1 | iex"</text>
<text x="80" y="1878" class="section-title">核心工具</text>
<rect x="80" y="1915" width="470" height="360" rx="18" fill="url(#cardGrad)" stroke="#35598f"/>
<text x="110" y="1976" class="card-title">issue_audit.py</text>
<text x="110" y="2014" class="card-text">拉取 issue / 评论 / 图片附件,去重并评分。</text>
<rect x="110" y="2042" width="410" height="205" rx="12" fill="#091327" stroke="#406aa6"/>
<text x="130" y="2091" class="mono">python .../issue_audit.py --base-url</text>
<text x="130" y="2127" class="mono">https://fun-md.com --repo FunMD/</text>
<text x="130" y="2163" class="mono">document-collab --token &lt;TOKEN&gt;</text>
<text x="130" y="2199" class="mono">--download-attachments</text>
<rect x="565" y="1915" width="470" height="360" rx="18" fill="url(#cardGrad)" stroke="#35598f"/>
<text x="595" y="1976" class="card-title">change_scope.py</text>
<text x="595" y="2014" class="card-text">识别部署范围,决定是否重启服务端。</text>
<rect x="595" y="2042" width="410" height="205" rx="12" fill="#091327" stroke="#406aa6"/>
<text x="615" y="2091" class="mono">python .../change_scope.py</text>
<text x="615" y="2127" class="mono">--repo-path . --base-ref origin/main</text>
<text x="615" y="2163" class="mono">--head-ref HEAD</text>
<rect x="1050" y="1915" width="470" height="360" rx="18" fill="url(#cardGrad)" stroke="#35598f"/>
<text x="1080" y="1976" class="card-title">preview_slot_allocator.py</text>
<text x="1080" y="2014" class="card-text">分配 / 复用 / 释放预览槽位。</text>
<rect x="1080" y="2042" width="410" height="205" rx="12" fill="#091327" stroke="#406aa6"/>
<text x="1100" y="2091" class="mono">python .../preview_slot_allocator.py</text>
<text x="1100" y="2127" class="mono">--state-file .tmp/preview-slots.json</text>
<text x="1100" y="2163" class="mono">--slots preview-a,preview-b --evict-oldest</text>
<rect x="80" y="2330" width="1440" height="110" rx="18" fill="#0b1730" stroke="#395d96"/>
<text x="120" y="2383" class="label">官网入口: https://fun-md.com/Fun_MD/devops-skills/raw/branch/main/site/index.svg</text>
<text x="120" y="2418" class="label">仓库地址: https://fun-md.com/Fun_MD/devops-skills</text>
</svg>

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -25,6 +25,8 @@ Run this interaction before any coding or issue action:
- optional shared QA URL - optional shared QA URL
- preview slot pool (for issue branches), e.g. `preview-a,preview-b` - preview slot pool (for issue branches), e.g. `preview-a,preview-b`
- preview URL template, e.g. `https://{slot}.qa.example.com` - preview URL template, e.g. `https://{slot}.qa.example.com`
- public routing mode: `port-based` or `virtual-host`
- websocket public entry: explicit WS URL (`wss://...`) or same-origin path (`/ws`)
- deployment environment + health endpoint - deployment environment + health endpoint
- minimum issue quality score (default `70`) - minimum issue quality score (default `70`)
5. Validate connectivity by running: 5. Validate connectivity by running:
@@ -93,6 +95,14 @@ Always avoid `main` and issue branches overwriting each other.
Never deploy different branches to the same fixed URL unless user explicitly approves override. Never deploy different branches to the same fixed URL unless user explicitly approves override.
### Routing Strategy (Recommended)
- Prefer `virtual-host` over raw ports for multi-branch testing:
- `main.example.com`, `preview-a.example.com`, `preview-b.example.com`
- Keep internal process ports private; expose only 80/443.
- Use same-origin WS path for frontend (`VITE_WS_URL=/ws`) and route `/ws/*` to the slot server.
- If `port-based` is used, every active env must have unique client/server ports; never reuse one public URL for two branches.
## Issue -> Branch -> Environment Binding ## Issue -> Branch -> Environment Binding
- Binding key: `<repo>#<issue>#<branch>` - Binding key: `<repo>#<issue>#<branch>`
@@ -171,6 +181,7 @@ Hard rule:
- Submit testing on the issue branch (CI pipeline + branch preview env). - Submit testing on the issue branch (CI pipeline + branch preview env).
- Allocate/reuse branch slot before submission. - Allocate/reuse branch slot before submission.
- Apply resource-aware deployment decision from change scope. - Apply resource-aware deployment decision from change scope.
- Verify websocket handshake is healthy on the published preview URL/path before asking QA to test.
- Post evidence in issue comment: - Post evidence in issue comment:
- commit SHA - commit SHA
- test run URL and result - test run URL and result
@@ -232,3 +243,5 @@ Close issue only when all are true:
- Never bypass engineer merge confirmation. - Never bypass engineer merge confirmation.
- Never allow branch previews to overwrite main stable env. - Never allow branch previews to overwrite main stable env.
- Never start dedicated branch server when scope indicates client-only changes. - Never start dedicated branch server when scope indicates client-only changes.
- When changing public service ports under PM2, do not rely on `pm2 restart --update-env` alone; delete and recreate the process so CLI args (for example `--port`) actually change.
- If a branch must be rebound to a specific preview slot (for example `preview-a`), release the existing issue allocation first, then redeploy; reuse logic otherwise keeps the previous slot by design.