Merge pull request #2050 from pikasTech/feat/pikaoa-test-runtime
Pipelines as Code CI / hwlab-web-probe-sentinel-nc01- Success
Pipelines as Code CI / platform-infra-gitea-nc01- Success
Pipelines as Code CI / unidesk-host- Success

PikaOA:增加 YAML-first 专用测试集群临时运行面
This commit is contained in:
Lyon
2026-07-15 00:27:27 +08:00
committed by GitHub
9 changed files with 1293 additions and 1 deletions
+1
View File
@@ -145,5 +145,6 @@
- OTel/可观测性: `docs/reference/observability.md`
- YAML-first: `docs/reference/yaml-first-ops.md`
- Platform infrastructure: `docs/reference/platform-infra.md`
- PikaOA 临时测试运行面: `docs/reference/pikaoa.md`
- Webterm 配置与受控部署: `docs/reference/webterm.md`
- Secretary: `docs/reference/secretary-reference.md`
+88
View File
@@ -0,0 +1,88 @@
version: 1
kind: pikaoa-platform-delivery
metadata:
id: pikaoa-enterprise-fixture
owner: unidesk
repository: pikainc/pikaoa
spec: PJ2026-03
testRuntime:
defaultTargetId: TEST01
targets:
TEST01:
enabled: true
validationOnly: true
dedicated: true
node: TEST01
route: TEST01:k3s
namespacePrefix: pikaoa-test
ttlSeconds: 7200
waitTimeoutSeconds: 180
fieldManager: unidesk-pikaoa-test-target
cleanup:
mode: delete-namespace
requireOwnershipLabels: true
source:
mode: prebuilt-images
repository: pikainc/pikaoa
commitPolicy: cli-required
images:
api: registry.invalid/pikaoa/api:${commit}
worker: registry.invalid/pikaoa/worker:${commit}
pullPolicy: IfNotPresent
database:
image: postgres:16-alpine
name: pikaoa_test
username: pikaoa_test
port: 5432
password:
sourceRef: ./secrets/pikaoa-test-database-password.txt
targetKey: PIKAOA_DATABASE_PASSWORD
runtime:
secretName: pikaoa-test-runtime
apiPort: 8080
workerMetricsPort: 8081
workerInterval: 2s
outbox:
batchSize: 50
maxAttempts: 4
retryDelay: 2s
sessionTTL: 8h
shutdownTimeout: 10s
administrator:
username: admin
password:
sourceRef: ./secrets/pikaoa-test-admin-password.txt
targetKey: PIKAOA_BOOTSTRAP_ADMIN_PASSWORD
employee:
username: employee
password:
sourceRef: ./secrets/pikaoa-test-employee-password.txt
targetKey: PIKAOA_BOOTSTRAP_EMPLOYEE_PASSWORD
sessionSecret:
sourceRef: ./secrets/pikaoa-test-session-secret.txt
targetKey: PIKAOA_SESSION_SECRET
observability:
otlpEndpoint: http://otel-collector.observability.svc.cluster.local:4318
prometheus:
scrape: true
apiPath: /metrics
workerPath: /metrics
exporterFailure:
warning: true
blocking: false
probes:
api:
livenessPath: /healthz
readinessPath: /readyz
worker:
livenessPath: /healthz
readinessPath: /readyz
delivery:
targets:
PROD:
namespace: pikaoa
ci:
namespace: pikaoa-ci
+5
View File
@@ -26,6 +26,11 @@ modules:
events: transactional-outbox
workers: named-consumer
testRuntime:
# 专用测试集群 target 由用户明确声明后才能启用;空对象表示当前不允许远端测试运行。
defaultTargetId: null
targets: {}
delivery:
targets:
NC01:
@@ -0,0 +1,36 @@
# R1.5.7 任务报告
## 结果
- Issue`pikasTech/unidesk#2046`
- PR`pikasTech/unidesk#2050`
- 实现提交:`c6fafc15`
- `config/pikaoa.yaml#testRuntime` 成为 PikaOA 专用测试集群临时运行面的唯一配置真相;当前 `targets={}`,不猜测 NC01、JD01 或其他节点。
- 新增 `pikaoa test-target plan|status|start|stop` 受控入口,不经过 PaC、Tekton、GitOps 或正式 CI/CD。
- `start` 按 target YAML 渲染运行实例 namespace、API、Worker、临时 PostgreSQL、Secret、OTel、Prometheus、TTL 和探针;`stop` 只删除标签匹配的精确实例 namespace。
- 正式 `delivery.targets.*.namespace``delivery.targets.*.ci.namespace` 进入保护集合,测试入口不得探测、渲染或删除 `pikaoa``pikaoa-ci`
- Secret 只披露 `sourceRef``targetKey`、presence 和 fingerprint,输出保持 `valuesPrinted=false`
## 验证
- `bun --check scripts/src/pikaoa-test-target.ts` 通过。
- `bun scripts/cli.ts check` 通过,11 项成功、0 项失败。
- scoped help 通过;默认文本紧凑,`--output json` 输出单一 JSON 文档。
- 默认 `plan` 连续两次输出完全一致;`plan/status` 均为 `configured=false``mutation=false`
- fixture 渲染 `pikaoa-test-issue-2046`、7 个 k8s 对象、实例选择器、TTL、Secret 引用、OTel 和 Prometheus;保护集合包含 `pikaoa``pikaoa-ci`
- fixture 固定 `validationOnly=true``status/start/stop` 均在本地返回 `remoteQueried=false`,未连接虚构 route。
- 所有配置、版本和 OTel 提示均为 `blocking=false` warning;损害预防 blocker 仅用于未配置 target、Secret 缺失、namespace 不安全和归属不匹配。
- `git diff --check` 通过;CLI 模块 2000 行以下,`AGENTS.md` 10 KiB 以下。
## 运行面
- 未启动 host server。
- 未连接或探测任何真实集群。
- 未操作正式 `pikaoa``pikaoa-ci` namespace。
- 未修改 `/root/pikaoa` 产品代码。
- 未执行 CI/CD 或正式 rollout`rollout=not-applicable`
- 真实 `start/status/CLI/stop` 等待用户在 owning YAML 中明确声明专用测试 target 后,再从原入口执行。
## Post-task
- 当前未发现需要扩展为独立工具、文档或 skill issue 的新反馈。
+1 -1
View File
@@ -83,7 +83,7 @@
#### R1.5.6 [completed]
修复 GitHub repository override 对 owner/repo 大小写敏感,确保 pikaInc/pikaoa 与 canonical 名称选择同一受控 token,执行记录见 [pikasTech/unidesk#1980](https://github.com/pikasTech/unidesk/issues/1980),完成任务后将详细报告写入[任务报告](./details/pikaoa-enterprise-platform/R1.5.6_Task_Report.md)。
#### R1.5.7 [in_progress]
#### R1.5.7 [completed]
增加 YAML-first PikaOA 专用测试集群临时 k8s 运行面和 pikaoa test-target plan/status/start/stop 受控入口,不经过 CI/CD、不触碰正式 namespace,未声明测试 target 时返回 configured=false,执行记录见 [pikasTech/unidesk#2046](https://github.com/pikasTech/unidesk/issues/2046),完成任务后将详细报告写入[任务报告](./details/pikaoa-enterprise-platform/R1.5.7_Task_Report.md)。
### R1.6
+72
View File
@@ -0,0 +1,72 @@
# PikaOA 开发与测试运行面
## 配置真相
- PikaOA 平台配置唯一真相是 `config/pikaoa.yaml`
- 正式交付与临时测试运行面职责分离:
- `delivery.targets` 只描述正式 CI/CD、GitOps、运行 namespace、数据库和公网暴露;
- `testRuntime.targets` 只描述专用测试集群上的临时后端运行面。
- 禁止把现有节点推断成 PikaOA 专用测试集群。
- `testRuntime.targets` 为空时表示当前没有获准的测试运行面:
- `plan``status` 返回 `configured=false``mutation=false`
- `start``stop` 在任何远端调用前失败。
## 受控入口
```bash
bun scripts/cli.ts pikaoa test-target plan
bun scripts/cli.ts pikaoa test-target status
bun scripts/cli.ts pikaoa test-target start --target <id> --instance <run-id> --commit <sha> --confirm
bun scripts/cli.ts pikaoa test-target stop --target <id> --instance <run-id> --confirm
```
- `plan` 是本地只读渲染入口,不接受 `--dry-run`
- `start``stop` 只在显式 `--confirm` 后执行。
- `--config <path>` 可用于 fixture 或本地 override`validationOnly=true` 的 target 永远不连接 route。
- 默认输出是紧凑文本;`--output json``--json` 输出单一 JSON 文档。
## 临时运行面
- namespace 固定由 YAML `namespacePrefix` 和 CLI `--instance` 组成。
- namespace、API、Worker、临时 PostgreSQL、Service 和 Secret 都带以下可审计标签:
- `app.kubernetes.io/managed-by=unidesk-pikaoa-test-target`
- `pikaoa.unidesk.io/test-runtime=true`
- `pikaoa.unidesk.io/target=<target-id>`
- `pikaoa.unidesk.io/instance=<run-id>`
- `stop` 删除前重新校验 target、instance 和 managed-by 标签,只删除精确实例 namespace。
- 正式 namespace 保护集合来自以下 YAML 字段:
- `delivery.targets.*.namespace`
- `delivery.targets.*.ci.namespace`
- 测试入口不得渲染或删除保护集合中的任何对象。
- PostgreSQL 使用 namespace 内的临时存储,namespace 删除后测试数据随之清理。
- TTL 和清理策略来自 target YAML
- TTL 写入 namespace annotation
- 当前 `cleanup.mode=delete-namespace`,由显式 `stop` 执行;
- `cleanup.requireOwnershipLabels=true` 时,删除前必须校验归属标签;
- 不建立额外控制器或长期状态库。
## Secret 与可观测性
- Secret 值只从 YAML `sourceRef` 读取并写入目标 Secret。
- CLI 输出只显示 `sourceRef``targetKey`、presence 和 fingerprint,始终保持 `valuesPrinted=false`
- Secret 缺失会在连接测试集群前阻塞 `start`
- API 和 Worker 从同一 Secret 挂载严格的 PikaOA runtime YAML。
- OTel endpoint、Prometheus scrape、指标路径和探针全部由 target YAML 声明。
- 配置一致性、镜像/commit 漂移和 OTel exporter 失败只产生 `blocking=false` warning,不作为 MVP 门禁。
## 本地验收
`config/fixtures/pikaoa-test-target.yaml` 只用于本地验证:
- 证明 namespace、选择器、工作负载和 Secret 引用的渲染结果;
- 证明 TTL、OTel、Prometheus 和正式 namespace 保护;
- fixture target 固定为 `validationOnly=true`,不得用于真实集群操作。
```bash
bun scripts/cli.ts pikaoa test-target plan \
--config config/fixtures/pikaoa-test-target.yaml \
--target TEST01 \
--instance issue-2046 \
--commit 0123456789abcdef \
--output json
```
+10
View File
@@ -428,6 +428,16 @@ async function main(): Promise<void> {
return;
}
if (top === "pikaoa") {
const { runPikaoaCommand } = await import("./src/pikaoa-test-target");
const result = await runPikaoaCommand(readConfig(), args.slice(1));
if (isRenderedCliResult(result)) {
emitText(result.renderedText, result.command || commandName);
if (!result.ok) process.exitCode = 1;
return;
}
}
if (top === "platform-db") {
const result = await runPlatformDbCommand(readConfig(), args.slice(1));
const ok = (result as { ok?: unknown }).ok !== false;
+2
View File
@@ -75,6 +75,7 @@ export function rootHelp(): unknown {
{ command: "hwlab nodes control-plane|git-mirror|hwpod-preinstall|secret|test-accounts --node NC01 --lane v03", description: "Operate the NC01 HWLAB v0.3 development and deployment lane from YAML source truth." },
{ command: "agentrun get|describe|events|logs|result|ack|cancel|dispatch|create|apply|send|control-plane|git-mirror", description: "Use AgentRun v0.1 resource primitives with low-noise human output by default; session follow-up uses send only and the server decides internal steer vs turn." },
{ command: "platform-infra sub2api|langbot|n8n|webterm|wechat-archive ...", description: "Deploy platform-infra services such as Sub2API, LangBot, n8n and Web Terminal, manage YAML-controlled public Caddy/FRP exposure and WeChat archive workflows, and inspect status/logs without printing secrets." },
{ command: "pikaoa test-target plan|status|start|stop", description: "管理 YAML-first 专用测试集群中的 PikaOA 临时后端运行面;不经过 CI/CD,也不触碰正式 namespace。" },
{ command: "secrets plan|sync|status", description: "Plan, push and inspect YAML-declared local secret source keys to Kubernetes Secrets without printing or reverse-engineering values." },
{ command: "platform-db postgres plan|status|export-secrets|apply", description: "Manage YAML-declared host-native PostgreSQL 16 on PK01 for Sub2API/platform state, with PostgreSQL native TLS and redacted secret exports." },
{ command: "hwlab cd audit --env dev | hwlab cd status --env dev | hwlab cd apply --env dev --dry-run", description: "Archived HWLAB DEV CD diagnostics only; current HWLAB rollout uses NC01 GitOps." },
@@ -1048,6 +1049,7 @@ export async function staticNamespaceHelp(args: string[]): Promise<unknown | nul
);
}
if (top === "platform-infra") return loadHelp(async () => (await import("./platform-infra")).platformInfraHelp(), platformInfraHelpSummary());
if (top === "pikaoa") return loadHelp(async () => (await import("./pikaoa-test-target")).pikaoaTestTargetHelp(), { command: "pikaoa test-target", source: "config/pikaoa.yaml#testRuntime" });
if (top === "platform-db") return platformDbHelp();
if (top === "secrets") return secretsHelp();
if (top === "web-probe") return loadHelp(async () => (await import("./web-probe")).webProbeHelp(), webProbeHelpSummary());
File diff suppressed because it is too large Load Diff