Files
devops-skills/site/index.html

420 lines
14 KiB
HTML
Raw Normal View History

<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Issue-Driven DevOps Agent | Branch-Scoped Delivery Platform</title>
<style>
:root {
--bg-0: #070b18;
--bg-1: #101936;
--bg-2: #13274f;
--line: #30406b;
--text: #ecf3ff;
--text-soft: #b9c7e6;
--brand: #36d1ff;
--brand-2: #56f6c8;
--warn: #ffd86b;
--panel: rgba(16, 25, 54, 0.76);
--panel-strong: rgba(10, 17, 38, 0.82);
--ok: #56f6c8;
--danger: #ff7f8f;
--shadow: 0 20px 60px rgba(3, 8, 26, 0.45);
}
* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
color: var(--text);
font-family: "Space Grotesk", "PingFang SC", "Microsoft YaHei", sans-serif;
background:
radial-gradient(circle at 0% 0%, rgba(54, 209, 255, 0.22), transparent 30%),
radial-gradient(circle at 100% 10%, rgba(86, 246, 200, 0.18), transparent 28%),
linear-gradient(140deg, var(--bg-0) 0%, var(--bg-1) 42%, var(--bg-2) 100%);
line-height: 1.58;
}
.page {
max-width: 1140px;
margin: 0 auto;
padding: 24px 20px 80px;
}
.nav {
display: flex;
justify-content: space-between;
align-items: center;
gap: 16px;
margin-bottom: 20px;
}
.logo {
display: inline-flex;
align-items: center;
gap: 10px;
font-weight: 700;
font-size: 15px;
letter-spacing: 0.2px;
}
.logo-dot {
width: 12px;
height: 12px;
border-radius: 50%;
background: linear-gradient(120deg, var(--brand), var(--brand-2));
box-shadow: 0 0 0 5px rgba(86, 246, 200, 0.18);
}
.nav-links {
display: flex;
gap: 14px;
flex-wrap: wrap;
}
.nav a {
color: var(--text-soft);
text-decoration: none;
border: 1px solid transparent;
padding: 8px 12px;
border-radius: 999px;
}
.nav a:hover {
color: var(--text);
border-color: var(--line);
background: rgba(255, 255, 255, 0.05);
}
.hero {
border: 1px solid var(--line);
background:
linear-gradient(145deg, rgba(86, 246, 200, 0.12), transparent 35%),
linear-gradient(325deg, rgba(54, 209, 255, 0.18), transparent 40%),
var(--panel-strong);
border-radius: 24px;
padding: 34px 28px 28px;
box-shadow: var(--shadow);
position: relative;
overflow: hidden;
}
.hero::after {
content: "";
position: absolute;
right: -60px;
top: -65px;
width: 180px;
height: 180px;
background: radial-gradient(circle, rgba(86, 246, 200, 0.35), transparent 70%);
pointer-events: none;
}
.hero h1 {
margin: 0 0 10px;
font-size: clamp(30px, 4.4vw, 52px);
line-height: 1.05;
letter-spacing: -0.6px;
max-width: 880px;
}
.hero p {
margin: 0;
font-size: clamp(15px, 2.2vw, 19px);
max-width: 880px;
color: var(--text-soft);
}
.hero-cta {
margin-top: 18px;
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.btn {
border: 1px solid var(--line);
border-radius: 12px;
padding: 10px 14px;
color: var(--text);
text-decoration: none;
font-size: 14px;
display: inline-flex;
align-items: center;
gap: 8px;
}
.btn.primary {
border-color: transparent;
background: linear-gradient(120deg, #2db6ff, #49f2ca);
color: #06253a;
font-weight: 700;
}
.btn:hover { filter: brightness(1.06); }
.stats {
margin-top: 16px;
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 10px;
}
.stat {
border: 1px solid var(--line);
border-radius: 14px;
padding: 12px;
background: rgba(18, 30, 60, 0.58);
}
.stat .v {
font-size: 24px;
font-weight: 700;
letter-spacing: -0.3px;
}
.stat .k {
color: var(--text-soft);
font-size: 12px;
margin-top: 2px;
}
section {
margin-top: 16px;
border: 1px solid var(--line);
border-radius: 18px;
padding: 20px;
background: var(--panel);
box-shadow: var(--shadow);
}
h2 {
margin: 0 0 12px;
font-size: 26px;
line-height: 1.12;
letter-spacing: -0.25px;
}
h3 {
margin: 0 0 8px;
font-size: 18px;
line-height: 1.2;
}
p { margin: 0; color: var(--text-soft); }
.value-grid, .install-grid, .tool-grid {
display: grid;
gap: 12px;
}
.value-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.install-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.tool-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.card {
border: 1px solid #385082;
background: rgba(20, 34, 68, 0.82);
border-radius: 14px;
padding: 14px;
}
.card p { margin-top: 4px; }
.flow {
display: grid;
gap: 10px;
grid-template-columns: repeat(5, minmax(0, 1fr));
}
.step {
border: 1px dashed #4f6796;
border-radius: 12px;
padding: 10px;
background: rgba(20, 34, 68, 0.55);
}
.step strong {
display: inline-block;
margin-bottom: 6px;
color: #dff5ff;
}
.step p { font-size: 13px; }
.install-card code, pre, .tool-card code {
font-family: "Cascadia Code", Consolas, Menlo, monospace;
font-size: 12.8px;
}
pre {
margin: 8px 0 0;
border: 1px solid #415983;
border-radius: 10px;
padding: 10px;
overflow: auto;
background: #081126;
color: #dcf3ff;
white-space: pre-wrap;
word-break: break-all;
}
.copy {
margin-top: 8px;
border: 1px solid #4e6899;
background: rgba(255, 255, 255, 0.03);
color: var(--text);
border-radius: 8px;
padding: 6px 10px;
cursor: pointer;
font-size: 12px;
}
.copy.ok { border-color: #4fcd99; color: var(--ok); }
.list {
margin: 10px 0 0;
padding: 0;
list-style: none;
display: grid;
gap: 8px;
}
.list li {
border: 1px solid #3b4f78;
border-radius: 10px;
padding: 9px 10px;
color: #d2e0f9;
background: rgba(15, 25, 49, 0.7);
}
.tagline {
margin-top: 10px;
color: var(--warn);
font-weight: 600;
}
.footer {
margin-top: 16px;
border: 1px solid var(--line);
border-radius: 14px;
padding: 14px;
background: rgba(6, 11, 25, 0.66);
display: flex;
justify-content: space-between;
gap: 10px;
flex-wrap: wrap;
}
.footer a {
color: var(--brand);
text-decoration: none;
font-weight: 600;
}
.footer a:hover { text-decoration: underline; }
@media (max-width: 1040px) {
.stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.value-grid, .install-grid, .tool-grid { grid-template-columns: 1fr; }
.flow { grid-template-columns: 1fr; }
}
</style>
</head>
<body>
<main class="page">
<nav class="nav">
<div class="logo"><span class="logo-dot"></span> Issue-Driven DevOps Agent</div>
<div class="nav-links">
<a href="#install">安装</a>
<a href="#tools">工具</a>
<a href="#workflow">工作流</a>
</div>
</nav>
<header class="hero">
<h1>把 Issue 变成可追踪、可复用、可规模化的交付流水线</h1>
<p>
我们不是“会修 bug 的脚本”,而是一个面向真实研发组织的 DevOps 交付平台:
<strong>Issue → Branch → Preview Slot → 提测闭环</strong>,并且始终保留工程师对最终合并的控制权。
</p>
<div class="hero-cta">
<a class="btn primary" href="https://fun-md.com/Fun_MD/devops-skills" target="_blank" rel="noopener noreferrer">访问仓库</a>
<a class="btn" href="#install">一键安装</a>
<a class="btn" href="../skills/gitea-issue-devops-agent/SKILL.md">查看 Skill 规范</a>
</div>
<div class="stats">
<article class="stat"><div class="v">3</div><div class="k">执行模式(自动/半自动/人工)</div></article>
<article class="stat"><div class="v">5</div><div class="k">部署范围策略skip→full_stack</div></article>
<article class="stat"><div class="v">1:1</div><div class="k">Issue/Branch/Preview Slot 绑定</div></article>
<article class="stat"><div class="v">24h+</div><div class="k">可配置 TTL 自动回收</div></article>
</div>
</header>
<section id="value">
<h2>核心价值</h2>
<div class="value-grid">
<article class="card">
<h3>分支隔离,主干稳定</h3>
<p>每个 issue 分配独立分支和预览槽位,不再发生“提测相互覆盖”,主干环境用于稳定回归。</p>
</article>
<article class="card">
<h3>资源智能节流</h3>
<p>根据代码变更范围自动判定部署策略。仅前端改动时不重启服务端,直接复用共享后端。</p>
</article>
<article class="card">
<h3>证据驱动闭环</h3>
<p>自动沉淀 commit、测试结果、环境链接、验证步骤。Issue 可关闭、可回溯、可审计。</p>
</article>
</div>
<p class="tagline">这是一套用于长期演进的研发基础设施,不是临时脚本集合。</p>
</section>
<section id="workflow">
<h2>工作流拓扑</h2>
<div class="flow">
<div class="step"><strong>1. 引导连接</strong><p>输入 repo_url、api_key、mode完成连通性校验。</p></div>
<div class="step"><strong>2. 质量审计</strong><p>拉取 issue 与图片附件,做质量评分与去重分组。</p></div>
<div class="step"><strong>3. 分支执行</strong><p>严格在 issue 指定分支改动,保留变更可追踪性。</p></div>
<div class="step"><strong>4. 按范围部署</strong><p>change_scope 自动判断 skip/client/server/full_stack。</p></div>
<div class="step"><strong>5. 自动回收</strong><p>槽位 TTL + 关闭释放,减少预览环境资源占用。</p></div>
</div>
<ul class="list">
<li>工作流模板:<code>.gitea/workflows/issue-branch-preview.yml</code></li>
<li>回收模板:<code>.gitea/workflows/preview-slot-reclaim.yml</code></li>
<li>合并策略:所有模式下都要求工程师人工确认最终合并</li>
</ul>
</section>
<section id="install">
<h2>一键安装Windows / macOS / Linux</h2>
<p>以下命令会把技能安装到本机 <code>~/.codex/skills/gitea-issue-devops-agent</code></p>
<div class="install-grid">
<article class="card install-card">
<h3>Linux</h3>
<pre id="cmd-linux">curl -fsSL https://fun-md.com/Fun_MD/devops-skills/raw/branch/main/install/install.sh | bash</pre>
<button class="copy" data-target="cmd-linux">复制命令</button>
</article>
<article class="card install-card">
<h3>macOS</h3>
<pre id="cmd-macos">curl -fsSL https://fun-md.com/Fun_MD/devops-skills/raw/branch/main/install/install.sh | bash</pre>
<button class="copy" data-target="cmd-macos">复制命令</button>
</article>
<article class="card install-card">
<h3>Windows (PowerShell)</h3>
<pre id="cmd-win">powershell -NoProfile -ExecutionPolicy Bypass -Command "iwr -useb https://fun-md.com/Fun_MD/devops-skills/raw/branch/main/install/install.ps1 | iex"</pre>
<button class="copy" data-target="cmd-win">复制命令</button>
</article>
</div>
</section>
<section id="tools">
<h2>核心工具</h2>
<div class="tool-grid">
<article class="card tool-card">
<h3>issue_audit.py</h3>
<p>抓取 issue、评论和图片附件完成质量评分、去重与回归候选识别。</p>
<pre>python skills/gitea-issue-devops-agent/scripts/issue_audit.py --base-url https://fun-md.com --repo FunMD/document-collab --token &lt;TOKEN&gt; --state all --download-attachments --output-dir .tmp/issue-audit</pre>
</article>
<article class="card tool-card">
<h3>change_scope.py</h3>
<p>输出部署范围建议,决定是否需要服务端重启。</p>
<pre>python skills/gitea-issue-devops-agent/scripts/change_scope.py --repo-path . --base-ref origin/main --head-ref HEAD</pre>
</article>
<article class="card tool-card">
<h3>preview_slot_allocator.py</h3>
<p>管理 preview 槽位分配、复用、释放、TTL 回收。</p>
<pre>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</pre>
</article>
</div>
</section>
<footer class="footer">
<span>Skill Path: <code>skills/gitea-issue-devops-agent/SKILL.md</code></span>
<span><a href="https://fun-md.com/Fun_MD/devops-skills/raw/branch/main/site/index.html" target="_blank" rel="noopener noreferrer">公网页面链接</a></span>
</footer>
</main>
<script>
const copyButtons = document.querySelectorAll(".copy");
copyButtons.forEach((btn) => {
btn.addEventListener("click", async () => {
const targetId = btn.getAttribute("data-target");
const el = document.getElementById(targetId);
if (!el) return;
try {
await navigator.clipboard.writeText(el.textContent.trim());
btn.classList.add("ok");
btn.textContent = "已复制";
setTimeout(() => {
btn.classList.remove("ok");
btn.textContent = "复制命令";
}, 1400);
} catch (err) {
btn.textContent = "复制失败";
setTimeout(() => {
btn.textContent = "复制命令";
}, 1400);
}
});
});
</script>
</body>
</html>