# pipeloom
**Repository Path**: Adnois/pipeloom
## Basic Information
- **Project Name**: pipeloom
- **Description**: 自托管 DevOps 平台:流水线编排 · 代码扫描 · 制品晋级 · 部署发布 · 服务器/容器运维。
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: main
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 2
- **Forks**: 0
- **Created**: 2026-09-24
- **Last Updated**: 2026-10-12
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# Pipeloom
**自托管 DevOps 平台:流水线编排 · 代码扫描 · 制品晋级 · 部署发布 · 服务器/容器运维。**
Kotlin / Spring Boot 控制面 + Vue 3 控制台 + MySQL 单一状态存储,另配一个只出不进的 Go 探针。





[English](README.md) | 简体中文
---
## 为什么是 Pipeloom
内部交付链路通常是一堆工具的拼装:一台 CI、一套 SonarQube、几份 Ansible playbook、一个 Portainer 标签页、一张"这次发布谁批的"的表格,再加某个人写的告警 cron。每次工具之间交接,上下文就掉一次,最后没人能回答「生产现在跑的是哪个版本、谁放行的」。
Pipeloom 把这个闭环收进一个控制面:拉代码的流水线、给代码打分的扫描、卡环境的制品晋级、把包发出去的部署、出问题时要进的运维台,共用同一套节点目录、同一份扫描指标、同一个凭据保险库——质量门禁读的就是扫描节点写出的那些数字,制品没过该环境的审批部署就不会开始,所有写操作最后落进同一条防篡改审计流。
| | Pipeloom | Jenkins | GitLab CI + SonarQube + Ansible | 自研脚本 + cron |
|---|:---:|:---:|:---:|:---:|
| 可视化流水线编排(Stage→Job DAG) | ✅ 内置 | 插件 | 只有 `.gitlab-ci` | ❌ |
| 代码扫描 + 质量红线 | ✅ 14 个引擎 + 内置规则 | 插件 | 独立 SonarQube | ❌ |
| 制品晋级审批(按环境) | ✅ 服务端强制拦截 | ❌ | ❌ | ❌ |
| 多目标部署(SSH / Compose / Kubernetes) | ✅ 内置 | 插件 | Ansible | 自己写 |
| 服务器 / 容器运维 + Web 终端 | ✅ 内置 | ❌ | Portainer | ❌ |
| 免装 Agent 的 SSH **与** 只出不进的探针 | ✅ | ❌ | 只有 Ansible | ❌ |
| 插件市场(节点热插拔,无需重启) | ✅ | ✅ | ❌ | ❌ |
| 凭据保险库(AES-256-GCM,只存密文) | ✅ | 插件 | CI 变量 | 明文 env |
| append-only 审计(触发器硬拦 UPDATE/DELETE) | ✅ | 插件 | ❌ | ❌ |
| RBAC(ADMIN/OPERATOR/VIEWER)+ 作用域化 API 令牌 | ✅ | 插件 | ✅ | ❌ |
| 运维负担 | 1 个 jar + 1 个静态站 + MySQL | JVM + 插件 | 3+ 个服务 | 看你留了多少 |
## 界面导览
本仓库附带截图,实际会用到的页面:
- **概览** —— 流水线 / 服务器 / 探针数量、凭据数、24 小时运行量与成功率、趋势、最近运行。

- **流水线** —— 两级画布(阶段横向排列、任务在阶段内纵向堆叠)、节点配置抽屉的动态字段、YAML 导入、按版本的配置历史与回滚。

- **运行详情** —— 阶段时间线、逐步骤状态、实时日志(服务端每 2 秒落一次步骤日志,前端 2 秒轮询)、报告、制品、审批操作、重跑 / 取消 / 强杀。

- **服务器与容器** —— SSH 与 Kubernetes 目标、跨主机容器聚合视图、镜像 / 卷 / 网络 / Stack、`system df` 与 prune、实时 stats、Web 终端。

- **代码扫描与质量红线** —— 节点工具矩阵、扫描规则清单、质量指标、红线规则、制品晋级审批。


- **插件市场** —— 插件包、版本、按用户安装、评论、内置的插件开发指南与 Kotlin 模板。

- **设置** —— 用户与角色、通知渠道、API 令牌、审计日志、AI 提供商、账户,另有顶栏的站内消息中心。

## 能力总览
- **🔐 安全地基** —— argon2id 口令哈希 · 登录口令 RSA-OAEP 加密传输 · 每会话 CSRF 令牌 · 登录失败锁定(默认 5 次 / 15 分钟)· 会话 Cookie 可开 `Secure` · Bearer API 令牌(`pipeline:trigger` 作用域)· `ADMIN` / `OPERATOR` / `VIEWER` 三角色在服务端强制:**读请求登录即可,写请求默认需要 OPERATOR+**(`@RequireAuth` 不写 roles 也不再对 VIEWER 放行);凭据明文揭示、API 令牌签发、用户管理、插件与规则库、反代路由、定时调度、`docker system prune` 这类平台级动作**显式只给 ADMIN**;scope 已按路径命中的存量自动化 token 不受影响。`AuthContractTest` + `AuthInterceptorRoleTest` + `PipelineVisibilityTest` 把这套策略钉住(新增匿名端点必须登记理由;别人的 private 流水线要在列表、详情、运行历史里一起消失)。前端按钮同步按角色隐藏。
- **🗝 凭据保险库** —— git token / git 账号口令 / SSH 私钥 / SSH 口令 / 镜像仓库 / kubeconfig 六类凭据,用 `PIPELOOM_VAULT_KEY`(base64、32 字节)走 AES-256-GCM 封装,落库格式 `nonce ‖ 密文 ‖ tag`。明文既不进数据库,也不会在 GET 响应里回来。主密钥没配 → 相关能力统一返回 `503 vault_unconfigured`,而不是半死不活地跑。凭据过期每小时扫一次,提前 7 天预警。
- **🧩 流水线编排** —— Stage→Job 两级 DAG、`needs` 依赖、按拓扑分层调度(同层阶段并发执行;阶段内任务默认按声明顺序,`job.needs` 会做稳定拓扑排序并在上游失败/被跳过时连带跳过下游)、阶段级与任务级 `allowFailure`、阶段/任务 `when` 条件(分支通配、触发事件、表达式)、任务级重试(所有节点类型都生效,2 秒起指数退避;`approval` / `quality_gate` / `concurrency_group` 属于门禁控制流,不参与重试)、任务级超时(整任务预算、按调用粒度执法,见「已知边界」)、阶段级门禁与独立的 `approval` 节点、阶段级容器 `image` 与 `cachePaths` / `cacheKey` 依赖缓存、`isolatedWorkspace` 阶段独立工作目录、按 `agentId` 或 `agentLabels` 绑定构建机、`matrix` / `matrixExclude` / `matrixInclude` / `matrixFailFast` 矩阵构建。
- **🚦 触发方式** —— 手动、cron 定时(5 段表达式自动补成 Spring 的 6 段)、GitHub / GitLab / Gitea 的 Git Webhook(解析 push · tag · PR/MR · release 事件)、通用 Webhook 端点、流水线互相调用(`call_pipeline`,禁止自调用,子运行变量回传父运行)、带 Bearer 令牌的 REST 触发。入站 Webhook 强制验签(`X-Pipeloom-Signature` + 时间戳、`X-Hub-Signature-256`、或 `X-Gitlab-Token`),按 delivery id 做 5 分钟幂等去重,连续验签失败会按 IP 限流,累计 10 次自动停用该配置。运行进入终态后还会把结果回写成 GitHub / GitLab / Gitea 的 commit status(best-effort:回写失败绝不影响运行结果),开发者在 PR 上直接看到 ✓/✗ 而不必开平台。前提是这条流水线由 webhook 触发(带提交号),并且 `git_source` 节点填了 `repoUrl` 与一个 **git_token 类型**的凭据 —— 公开仓库能匿名克隆,但没有 token 就写不了状态;每种「没回写」的原因都会以独立结论写进服务端日志。
- **🏗 构建、制品与晋级** —— `git_source` 在工作区(或阶段容器)内 clone,凭据从保险库取出后注入 URL,并采集提交、作者、文件数与逐文件 diff;仓库浏览与远端 ref 列举走 JGit。`build_image` / `push_image` 以 `pipeloom-:` 打标签并推私有仓库;`archive_artifact` 保存构建产物的**真实字节**并可下载;运行进入终态后回收本次 workspace。搬运制品的部署节点(`deploy_ssh`、`file_distribute`)可开启**按(制品, 环境)的晋级审批**:批准的对象是制品而不是某次运行,开关没配全就**按拦截处理**,同时把被拦住的那次登记成待审批,审批人直接在运行页就能处理。
- **🔍 代码扫描与质量红线** —— `code_scan` 节点编排 14 个已注册引擎。内置规则引擎(24 条风格规则 + 16 条密钥规则)纯 JVM、始终可用;gitleaks · SpotBugs · Checkstyle · PMD · detekt · ktlint · ESLint · Pylint · cpplint · clang · gometalinter · StyleCop · OcCheck 各自走 `PATH → Docker 镜像 → 跳过` 的降级链,**跳过会被说出来**,不会静默产出空结果。`semgrep_scan` 与 `tca_scan` 单独成节点,正是因为它们需要外部环境(Semgrep 运行环境、可达的 TCA 服务端)。产出为 SARIF + 聚合问题数;扫描结果写成运行变量(`CODE_SCAN_TOTAL`、`CODE_SCAN_ERRORS`、`CODE_COMPLEXITY_MAX`、`CODE_DUPLICATION_RATIO`、`CODE_LOC_CODE` 等),`quality_gate` 节点按红线规则判定——直接失败,或阻塞等待审批。报告、逐文件下钻与 JSON 导出都挂在运行详情上。
- **🚀 部署发布** —— `deploy_ssh`(上传镜像包 → `docker load` → `docker run -d`,可配容器名、端口、启动参数)、`deploy_compose`、`file_distribute`、命令式运维的 `server_script`、`health_check`(HTTP 或命令行探测,支持重试间隔与 fatal / 非 fatal 两种结局)。Kubernetes 目标支持 `image_update`(rolling · recreate · blue-green)、`manifest_apply`(server-side apply,独立 `fieldManager`,集群级资源先应用)与 `helm_chart`(`helm upgrade --install` 并带 values)。
- **🖥 服务器与容器运维** —— SSH / Kubernetes / 本机 / 探针四类目标;跨主机容器聚合列表、创建 / inspect / prune,镜像 · 卷 · 网络 · Stack 管理,`system df` 与 prune,实时 stats,systemd 服务 start/stop/restart/status(走命令白名单),主机与容器 Web 终端,Pod exec,实时日志尾随。
- **🛰 探针(Agent)** —— 一个 `CGO_ENABLED=0` 的静态 Go 二进制,只主动外连 `GET /ws/agents/connect?token=…`,**本机不监听任何端口**。心跳上报 OS · 架构 · CPU 核数 · 内存 · 主机名,执行 `exec` / `upload` / `download` 任务并流式回传输出,按 `deploy` / `build` 分类、带 tags 与标签匹配调度。`SshExecutor` 在目标绑定探针时自动改走探针通道,探针离线就明确报错而不是悄悄回退。服务端在 `GET /api/agents/binary/{os}/{arch}` 提供对应平台的二进制。
- **📣 通知投递** —— 企业微信 · 钉钉 · 飞书 · Slack · Microsoft Teams · 通用 Webhook · 邮件,按**事件订阅**路由(`succeeded` / `failed` / `gate_pending` / `canceled` / `custom` / `concurrency_rejected` / `gate_rejected`;成功刻意默认关、失败与待审批默认开,避免通知疲劳),模板按运行变量渲染,留有投递台账,**一键重投会按当前渠道配置重新渲染**(重放旧 payload 只会把同一个错误再犯一次),另有测试发送。流水线内部还有 `notify` 节点。
- **📥 站内收件箱** —— 同一批事件会进站内消息中心,且**不依赖有没有配渠道**:一个渠道都没配的新实例照样通知得到人。分发给谁由事件语义决定,而不是由渠道订阅决定 —— 流水线归属人与运行触发者恒定收到一份,失败额外通知 ADMIN,审批类事件(`gate_pending` / `gate_rejected` / `ticket_open`)通知 ADMIN 与 OPERATOR,因为能点审批的就是这两类角色。**每个收件人各存一行、各自记已读**(甲读过一条待审批不该让乙的未读徽标消失);不是有效启用账号的名字(API 令牌、已删用户、Git 提交作者)会被丢掉;单次事件最多 200 份。顶栏铃铛与 `/messages` 页面负责呈现,站内信随运行被保留策略一并清理。
- **🧩 插件市场与 SDK** —— 插件 = `task.json` + Kotlin/Java fat JAR(或 shell handler 脚本)。安装即向 `JobHandlerRegistry` 注册一个新节点类型、或向 `ScanRuleRegistry` 注册一条新规则,**无需重启**;卸载会把类型标记为退役,让存量流水线**带原因失败**,而不是静默成功。安装按用户隔离,版本包做 SHA-256 校验并拒绝路径穿越段,保留节点类型不允许占用;插件通过一次性、按 run 作用域的 API 令牌访问平台,且只能取回本条流水线自己声明引用的凭据。内置 4 个 Kotlin 模板与一份站内插件开发指南。
- **🤖 AI 辅助(可选、可完全降级)** —— 任意 OpenAI 兼容端点(`PIPELOOM_AI_BASE_URL` / `MODEL` / `API_KEY`,因此 DeepSeek · Moonshot · vLLM 同样可用;密钥存保险库):按仓库生成流水线、运行失败诊断、问题修复建议,以及运维台的 NL→shell、命令解释、compose 生成、容器诊断。未配置或不可达就返回 `available=false`,前端优雅降级——CI/CD 主链路从不依赖 AI。
- **🧹 平台与日常治理** —— MySQL 8 + Flyway(`V0002`→`V0112`,共 68 个迁移)+ MyBatis-Plus,运行数据保留清理(默认 90 天),两层崩溃回收:启动时 `StaleRunRecoveryService` 把孤立的 `running` / `queued` / `canceling` 推入终态,一个 2 分钟周期的看门狗则处理心跳停更超 3 小时的步骤;另有门禁超时自动判失败、过期资源槽回收、控制台中英双语、`/api/health`、Actuator 暴露 `health,info,prometheus,metrics`、`/swagger-ui.html` 在线接口文档。
- **🩺 运行诊断与业务指标** —— 运行详情页的「运行诊断」把「为什么慢 / 卡在哪 / 是不是死了」算成结论:排队与执行时长分开、心跳静默判定失联、任务级时间预算用尽、门禁在等谁、第一个失败步骤带日志末行与是否被 `allowFailure` 容忍、瓶颈步骤占比、平台中断代号翻译成人话。判定阈值只存在于服务端一处(`RunDiagnosisService`),前端只展示。`/actuator/prometheus` 里除了框架自身的 JVM/HTTP 指标,现在有流水线业务指标:终态计数(按状态与触发方式)、排队与执行时长分布、重试次数、超时预算耗尽、`when`/`needs` 跳过、门禁批准与驳回、矩阵展开组合数、Git 回写结论、晋级拦截、活跃运行数 gauge。标签一律取代码里的有穷集合,**不含 pipelineId / runId / 用户名 / 步骤名**——那些走诊断接口看明细。
- **⚖️ 并发控制** —— 流水线级互斥组(`deploy-prod`、`deploy-${branch}` 支持插值)在触发时直接以 `409` 拒绝并点名占用者;阶段级资源槽由 `concurrency_group` 节点租约占用,租约到期自动清扫。
> 安全不打折:密文存储、命令白名单或数组化、Webhook 必验签、会话与令牌按作用域收敛、审计记录改不掉也删不掉。
## 安装 / 部署
前置:**服务端 Gradle toolchain 需要 JDK 25**(本地没有时 Gradle 会自动下载;插件 SDK 固定 17,保证插件可移植)、**MySQL 8.0+**、构建控制台需要 **Node.js 22+**;只有真要用到容器隔离构建、镜像部署、容器运维时才需要 Docker。
### ① Docker Compose(自托管推荐)
```bash
git clone https://gitee.com/Adnois/pipeloom.git && cd pipeloom
cp .env.example .env # 首次 up 之前先把 DB / 管理员 / vault 主密钥填好
docker compose up -d --build # mysql + server + web
open http://localhost:8092 # 登录:admin / $PIPELOOM_ADMIN_PASSWORD
```
`web` 服务是 nginx:静态控制台在 `8092`,`/api/` 反代到 `server:8888`,`/ws/` 带 upgrade 头反代并把读超时设为 24 小时。数据落在 `mysql-data`、`artifacts`、`plugins` 三个命名卷。服务端镜像以 UID `10001` 非 root 运行,与 K8s 清单里的 `runAsUser` 对齐。
### ② Kubernetes
```bash
kubectl apply -f k8s/namespace.yaml
# 先改 k8s/secret.yaml:数据库口令、管理员初始口令、vault 主密钥
kubectl apply -f k8s/secret.yaml
kubectl apply -f k8s/mysql.yaml
kubectl apply -f k8s/server.yaml # PVC:制品 20Gi、插件 5Gi
kubectl apply -f k8s/web.yaml
kubectl apply -f k8s/ingress.yaml # 换成自己的域名并按需打开 TLS
```
`k8s/server.yaml` 里显式写死 `SERVER_PORT=8888` 是必要的:名为 `server` 的 Service 会被 Kubernetes 注入 `SERVER_PORT=tcp://…`(Docker link 兼容行为),Spring Boot 的宽松绑定会把它当成 `server.port` 从而启动失败。存活/就绪探针都打 `/api/health`。`replicas` 保持 `1`——cron 调度与在跑的运行由进程自己驱动,见[已知边界](#已知边界诚实清单)。
### ③ 源码启动(开发调试)
```bash
# 1. 建库
mysql -u root -p -e "CREATE DATABASE pipeloom CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
# 2. 后端 —— http://localhost:8888,启动时 Flyway 自动迁移
cd server && ./gradlew bootRun
# 3. 控制台 —— http://localhost:5173,/api 与 /ws 自动代理到 :8888
cd web && npm ci && npm run dev
```
口令类配置一律走环境变量;本地开发也可以复制 `server/src/main/resources/application-local.yml.example` 为 `application-local.yml`(已在 `.gitignore` 里,勿提交),以 `--spring.profiles.active=local` 启动。构建产物:`./gradlew bootJar`(→ `server/target/pipeloom-server.jar`)与 `npm run build`(→ `web/dist`)。
探针是独立的 Go module:
```bash
cd agent && make # pipeloom-agent-linux-amd64 / -linux-arm64 / -darwin-amd64 / -darwin-arm64
# 控制台 → 探针管理 → 新建探针 → 复制连接命令
./pipeloom-agent-darwin-arm64 -server=http://localhost:8888 -token= -v
```
### 配置(环境变量)
日常部署真正要填的只有 `.env` 里那三个(DB 口令 / 管理员初始口令 / vault 主密钥),再加上对外地址 `PIPELOOM_API_URL`;其余变量都有可用默认值。
**核心**
| 变量 | 说明 | 默认值 |
|---|---|---|
| `PIPELOOM_ADMIN_PASSWORD` | **仅首次启动**用来创建管理员的初始口令(之后请在账户设置里改)。至少 8 位;无默认值,已有管理员时不会被使用 | 无(首启必填) |
| `PIPELOOM_ADMIN_USERNAME` | 首次启动创建的管理员用户名 | `admin` |
| `PIPELOOM_VAULT_KEY` | base64 编码的 32 字节保险库主密钥。不设不会阻断启动,但影响面远超「凭据」一项:凭据 CRUD、SSH/Web 终端、K8s exec、AI 配置与全部 AI 能力都不可用(`503 vault_unconfigured` / WS 连不上) | 无 |
| `PIPELOOM_API_URL` | 对外可访问的基础 URL,用于通知里的详情链接与 Webhook 的 `curl` 示例 | `http://localhost:8888` |
| `PIPELOOM_AUTH_COOKIE_SECURE` | 部署到 HTTPS 后置 `true` | `false` |
| `SERVER_PORT` | HTTP 监听端口 | `8888` |
**数据库**
| 变量 | 说明 | 默认值 |
|---|---|---|
| `PIPELOOM_DB_HOST` | MySQL 地址 | `localhost` |
| `PIPELOOM_DB_USERNAME` | MySQL 用户 | `root` |
| `PIPELOOM_DB_PASSWORD` | MySQL 口令。compose / K8s 都要显式提供;`application.yml` 目前仍留有一个本地开发兜底默认值,生产部署不要依赖它 | 环境变量(compose 从 `.env` 取) |
**运行、插件与清理**
| 变量 | 说明 | 默认值 |
|---|---|---|
| `PIPELOOM_RETENTION_RUNS_MAX_AGE_DAYS` | 终态运行保留天数,超期由清理任务连同步骤日志、制品、报告、通知记录与站内信一并删除 | `90` |
| `PIPELOOM_PLUGIN_STORAGE` | 插件包存储目录 | `./data/plugins` |
| `PIPELOOM_PLUGIN_EXEC_TIMEOUT_SECONDS` | 插件执行超时上限(单节点可用 `pluginTimeoutSeconds` 覆盖,夹在 1s–24h) | `3600` |
| `pipeloom.agent-binary-dir` | 服务端查找探针二进制的目录 | `./agent` |
| `pipeloom.server.local-bootstrap.enabled` | 启动时把自身所在主机注册为 `local` 目标(无需 SSH 凭据) | `true` |
| `pipeloom.security.ws.allowed-origins` | WebSocket 来源白名单(防 CSWSH),部署域名要加进来 | `localhost` / `127.0.0.1` 模式 |
**AI(可选)**
| 变量 | 说明 | 默认值 |
|---|---|---|
| `PIPELOOM_AI_BASE_URL` | OpenAI 兼容 base URL | `https://api.openai.com/v1` |
| `PIPELOOM_AI_API_KEY` | API Key;留空 = AI 能力未启用(不阻断启动) | 空 |
| `PIPELOOM_AI_MODEL` | 模型名 | `gpt-4o-mini` |
| `PIPELOOM_AI_TIMEOUT_SECONDS` | 请求超时 | `120` |
## 流水线参考
画布上可选的节点类型,以及谁来执行它们:
| 分类 | 节点 | 执行者 |
|---|---|---|
| 源码 | `trigger` | 引擎(固定首阶段:事件、cron、并发互斥) |
| 源码 | `git_source` | 引擎(`git clone` + 提交/diff 采集;仓库浏览走 JGit) |
| 构建 | `build_image`、`push_image`、`archive_artifact` | 引擎(Docker;镜像仓库凭据来自保险库) |
| 构建 | `script`、`custom`、`test` | **平台保留类型** —— 需安装市场里对应的插件 |
| 部署 | `deploy_ssh`、`deploy_compose`、`file_distribute`、`server_script` | 内置 Handler(SSH 或探针通道) |
| 部署 | `deploy_k8s` | 内置 Handler(fabric8:镜像滚动 / manifests / Helm) |
| 质量 | `code_scan`、`semgrep_scan`、`tca_scan`、`quality_gate`、`health_check` | 内置 Handler |
| 流程 | `approval`、`concurrency_group`、`call_pipeline` | 内置 Handler |
| 通知 | `notify` | 内置 Handler(走你配置的渠道) |
`script` / `custom` / `test` 是**刻意保留**的占位类型:留着是为了不让存量流水线一夜变红,但它们**什么都不做**,除非装了提供该类型的插件。画布、保存校验和运行详情页都会直接说明这点(占位类型的「成功」步骤带「空操作」徽标),而不是让你在跑到那一步时才发现。
可在命令、环境变量与节点配置里插值的内置变量(`${VAR}` 与 `${VAR:-默认值}`):`RUN_ID`、`PIPELINE_ID`、`PIPELINE_NAME`、`BRANCH`、`COMMIT_SHA`、`COMMIT_SHORT`、`TRIGGER_TYPE`、`TRIGGER_ACTOR`、`STAGE_NAME`、`JOB_NAME`、`WORKSPACE`,再加上节点通过运行变量发布的值(制品名、扫描指标、矩阵轴、子流水线输出)。
## 插件市场速览
```json
{
"name": "hello-world",
"description": "Hello World 示例插件",
"nodeType": "hello_world",
"version": "1.0.0",
"category": "custom",
"execution": { "language": "java", "packagePath": "build/libs/hello-world-1.0.0.jar" },
"fields": [{ "key": "message", "label": "消息内容", "kind": "text" }],
"outputs": [{ "name": "RESULT", "desc": "输出结果" }],
"defaultConfig": { "message": "Hello, Pipeloom!" }
}
```
```bash
gradle jar
zip hello-plugin.zip task.json build/libs/hello-world-1.0.0.jar
# 控制台 → 插件市场 → 上传插件包(支持拖拽/粘贴)→ 安装
```
插件实现 `server/plugin-sdk` 里的 `PluginTask`;平台把节点配置注入为 `PLG_*` 环境变量加 `$PLG_INPUT_FILE`,再从 `$PLG_OUTPUTS_FILE`(变量)、`$PLG_REPORT_FILE`(结构化报告)与 `$PLG_API_URL` + `$PLG_API_TOKEN`(一次性、run 结束即失效的令牌)读回结果。模板:`tpl-kotlin-basic`、`tpl-kotlin-deploy`、`tpl-kotlin-notify`、`tpl-kotlin-scan`。
## 技术栈
- **后端** —— Kotlin 2.3 · Spring Boot 4.1(web / websocket / mail / flyway / actuator)· MyBatis-Plus 3.5 · MySQL 8 + Flyway · BouncyCastle · sshj(SSH)· fabric8 `kubernetes-client` · Eclipse JGit · Jackson 3(`tools.jackson`)· springdoc OpenAPI · Micrometer/Prometheus · ArchUnit 分层守卫 · 构建期 ktlint + detekt。
- **控制台** —— Vue 3.5 `