@pzy560117/opentest 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.
- package/LICENSE +21 -0
- package/README.md +51 -0
- package/assets/manifest.json +30 -0
- package/assets/skills/opentest/SKILL.md +60 -0
- package/assets/skills/opentest/references/acceptance-evidence.md +22 -0
- package/assets/skills/opentest/references/codex-harness-coverage-heuristics.md +64 -0
- package/assets/skills/opentest/references/command-routing.md +9 -0
- package/assets/skills/opentest/references/lifecycle.md +16 -0
- package/assets/skills/opentest/references/matrix-format.md +16 -0
- package/assets/skills/opentest/references/quality-gate.md +21 -0
- package/assets/skills/opentest/scripts/opentest-detect.sh +56 -0
- package/assets/skills/opentest/scripts/opentest-guard.sh +189 -0
- package/assets/skills/opentest/scripts/opentest-state.sh +277 -0
- package/assets/skills/opentest/templates/acceptance-template.md +23 -0
- package/assets/skills/opentest/templates/archive-layout.md +14 -0
- package/assets/skills/opentest/templates/matrix-template.md +5 -0
- package/assets/skills/opentest/templates/plan-template.md +18 -0
- package/assets/skills/opentest/templates/report-template.md +23 -0
- package/assets/skills/opentest-accept/SKILL.md +23 -0
- package/assets/skills/opentest-archive/SKILL.md +8 -0
- package/assets/skills/opentest-author/SKILL.md +23 -0
- package/assets/skills/opentest-heal/SKILL.md +8 -0
- package/assets/skills/opentest-plan/SKILL.md +23 -0
- package/assets/skills/opentest-run/SKILL.md +25 -0
- package/assets/skills/opentest-verify/SKILL.md +16 -0
- package/bin/opentest.js +122 -0
- package/package.json +36 -0
- package/scripts/prepublish-check.js +58 -0
- package/scripts/smoke-test.js +64 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 benym
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# OpenTest
|
|
2
|
+
|
|
3
|
+
OpenTest is a Codex skill bundle for planning, running, verifying, and archiving quality evidence for code changes.
|
|
4
|
+
|
|
5
|
+
It provides a small lifecycle:
|
|
6
|
+
|
|
7
|
+
```text
|
|
8
|
+
plan -> author -> run -> accept -> verify -> archive
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
`heal` is a recovery phase for stale test assets and must not hide product behavior failures.
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
Install into the current project:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npx @pzy560117/opentest install
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Install globally for Codex:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npx @pzy560117/opentest install --global
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Overwrite an existing OpenTest install:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npx @pzy560117/opentest install --force
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Restart Codex or open a new session after installation so the new skills are loaded.
|
|
34
|
+
|
|
35
|
+
## Included Skills
|
|
36
|
+
|
|
37
|
+
- `opentest`
|
|
38
|
+
- `opentest-plan`
|
|
39
|
+
- `opentest-author`
|
|
40
|
+
- `opentest-run`
|
|
41
|
+
- `opentest-accept`
|
|
42
|
+
- `opentest-verify`
|
|
43
|
+
- `opentest-heal`
|
|
44
|
+
- `opentest-archive`
|
|
45
|
+
|
|
46
|
+
## Package Contents
|
|
47
|
+
|
|
48
|
+
- `assets/skills/`: Codex skill files
|
|
49
|
+
- `assets/manifest.json`: published asset manifest
|
|
50
|
+
- `bin/opentest.js`: installer CLI
|
|
51
|
+
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "0.1.0",
|
|
3
|
+
"skills": [
|
|
4
|
+
"opentest/SKILL.md",
|
|
5
|
+
"opentest/scripts/opentest-state.sh",
|
|
6
|
+
"opentest/scripts/opentest-detect.sh",
|
|
7
|
+
"opentest/scripts/opentest-guard.sh",
|
|
8
|
+
"opentest/references/lifecycle.md",
|
|
9
|
+
"opentest/references/matrix-format.md",
|
|
10
|
+
"opentest/references/quality-gate.md",
|
|
11
|
+
"opentest/references/acceptance-evidence.md",
|
|
12
|
+
"opentest/references/command-routing.md",
|
|
13
|
+
"opentest/references/codex-harness-coverage-heuristics.md",
|
|
14
|
+
"opentest/templates/plan-template.md",
|
|
15
|
+
"opentest/templates/matrix-template.md",
|
|
16
|
+
"opentest/templates/acceptance-template.md",
|
|
17
|
+
"opentest/templates/report-template.md",
|
|
18
|
+
"opentest/templates/archive-layout.md",
|
|
19
|
+
"opentest-plan/SKILL.md",
|
|
20
|
+
"opentest-author/SKILL.md",
|
|
21
|
+
"opentest-run/SKILL.md",
|
|
22
|
+
"opentest-accept/SKILL.md",
|
|
23
|
+
"opentest-verify/SKILL.md",
|
|
24
|
+
"opentest-heal/SKILL.md",
|
|
25
|
+
"opentest-archive/SKILL.md"
|
|
26
|
+
],
|
|
27
|
+
"platforms": [
|
|
28
|
+
{ "id": "codex", "skillsDir": ".codex/skills" }
|
|
29
|
+
]
|
|
30
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: opentest
|
|
3
|
+
description: "OpenTest 路由入口。自动检测 .opentest.yaml、初始化测试生命周期状态,并分发到 plan/author/run/accept/verify/heal/archive 阶段。"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# OpenTest 路由入口
|
|
7
|
+
|
|
8
|
+
## 产出语言契约
|
|
9
|
+
|
|
10
|
+
- 面向用户输出和生成文档默认使用中文。
|
|
11
|
+
- 命令、路径、frontmatter key、代码标识符和 API 名称保持原文。
|
|
12
|
+
|
|
13
|
+
## Step 0: 定位脚本
|
|
14
|
+
|
|
15
|
+
运行:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
OPENTEST_SEARCH_ROOTS=("." "$HOME/.claude/skills" "$HOME/.codex/skills" "$HOME/.cursor/skills")
|
|
19
|
+
OPENTEST_STATE="${OPENTEST_STATE:-$(find "${OPENTEST_SEARCH_ROOTS[@]}" -path '*/opentest/scripts/opentest-state.sh' -type f -print -quit 2>/dev/null)}"
|
|
20
|
+
OPENTEST_GUARD="${OPENTEST_GUARD:-$(find "${OPENTEST_SEARCH_ROOTS[@]}" -path '*/opentest/scripts/opentest-guard.sh' -type f -print -quit 2>/dev/null)}"
|
|
21
|
+
OPENTEST_DETECT="${OPENTEST_DETECT:-$(find "${OPENTEST_SEARCH_ROOTS[@]}" -path '*/opentest/scripts/opentest-detect.sh' -type f -print -quit 2>/dev/null)}"
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
若任一脚本缺失,停止并提示安装或复制 OpenTest 分发包。
|
|
25
|
+
|
|
26
|
+
## Step 1: 状态检测
|
|
27
|
+
|
|
28
|
+
如果 `.opentest.yaml` 不存在,运行:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
bash "$OPENTEST_STATE" init
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
然后运行:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
bash "$OPENTEST_STATE" check
|
|
38
|
+
bash "$OPENTEST_DETECT" summary
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Step 2: 阶段分发
|
|
42
|
+
|
|
43
|
+
读取 `phase`:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
bash "$OPENTEST_STATE" get phase
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
分发规则:
|
|
50
|
+
|
|
51
|
+
- `archived: true`:报告 OpenTest 已完成。
|
|
52
|
+
- `phase: plan`:调用 `opentest-plan`。
|
|
53
|
+
- `phase: author`:调用 `opentest-author`。
|
|
54
|
+
- `phase: run`:调用 `opentest-run`。
|
|
55
|
+
- `phase: accept`:调用 `opentest-accept`。
|
|
56
|
+
- `phase: verify`:调用 `opentest-verify`。
|
|
57
|
+
- `phase: heal`:调用 `opentest-heal`。
|
|
58
|
+
- `phase: archive`:调用 `opentest-archive`。
|
|
59
|
+
|
|
60
|
+
若状态与可验证文件冲突,以文件状态为准,并用 `opentest-state.sh set` 修正。
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Acceptance Evidence
|
|
2
|
+
|
|
3
|
+
Natural language acceptance cases must include:
|
|
4
|
+
|
|
5
|
+
- ID
|
|
6
|
+
- intent
|
|
7
|
+
- context
|
|
8
|
+
- actor or role
|
|
9
|
+
- steps
|
|
10
|
+
- expected outcome
|
|
11
|
+
- execution surface
|
|
12
|
+
- evidence status
|
|
13
|
+
|
|
14
|
+
Allowed evidence status:
|
|
15
|
+
|
|
16
|
+
- pending
|
|
17
|
+
- pass
|
|
18
|
+
- fail
|
|
19
|
+
- blocked
|
|
20
|
+
- risk-accepted
|
|
21
|
+
|
|
22
|
+
Blocked evidence must include a reason such as unavailable MCP, unavailable environment, missing seed data, auth requirement, or external service failure.
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Codex Harness 覆盖启发参考
|
|
2
|
+
|
|
3
|
+
## 用途
|
|
4
|
+
|
|
5
|
+
本文件随 OpenTest 分发,用于给 `/opentest-plan` 和 `/opentest-accept` 提供轻量覆盖启发。它不是强制 checklist,也不是 Codex Harness 全量内容副本。
|
|
6
|
+
|
|
7
|
+
OpenTest 应根据变更类型、风险和项目事实选择适用覆盖面。适用但缺少证据的点应记录为 `gap`;不适用的点应在覆盖摘要中说明,不需要展开用例。
|
|
8
|
+
|
|
9
|
+
## 来源
|
|
10
|
+
|
|
11
|
+
本参考从本地 Codex Harness 知识库中抽取短规则,主要对应:
|
|
12
|
+
|
|
13
|
+
- `tdd-workflow`
|
|
14
|
+
- `e2e-runner`
|
|
15
|
+
- `browser-e2e-testing`
|
|
16
|
+
- `verification-loop`
|
|
17
|
+
- `code-reviewer`
|
|
18
|
+
- `speckit-checklist`
|
|
19
|
+
|
|
20
|
+
## 选择规则
|
|
21
|
+
|
|
22
|
+
| 变更类型 | 默认考虑的证据 |
|
|
23
|
+
| --- | --- |
|
|
24
|
+
| 纯函数、工具函数、状态计算 | unit evidence |
|
|
25
|
+
| 服务、模块协作、数据库读写 | integration evidence |
|
|
26
|
+
| API、契约、错误处理 | contract、integration、error evidence |
|
|
27
|
+
| 前端渲染或交互 | UI acceptance,必要时 component 或 E2E evidence |
|
|
28
|
+
| 权限、支付、安全、数据写入、跨页面闭环 | high-risk acceptance 或 E2E evidence |
|
|
29
|
+
| 文案、配置、小范围无行为变更 | targeted review 或 light evidence |
|
|
30
|
+
|
|
31
|
+
## 前端验收维度
|
|
32
|
+
|
|
33
|
+
前端或真实链路验收可从以下维度中选择适用项,不要求每次全部覆盖:
|
|
34
|
+
|
|
35
|
+
| 维度 | 含义 |
|
|
36
|
+
| --- | --- |
|
|
37
|
+
| D1 页面渲染 | 标题、文案、结构、主要元素存在 |
|
|
38
|
+
| D2 交互功能 | 点击、输入、选择、弹窗、菜单 |
|
|
39
|
+
| D3 数据展示 | API 数据、格式化、状态展示 |
|
|
40
|
+
| D4 表单验证 | 必填、格式、边界、关联校验 |
|
|
41
|
+
| D5 导航流转 | 菜单、链接、返回、跨页面传参 |
|
|
42
|
+
| D6 状态管理 | loading、empty、error、retry |
|
|
43
|
+
| D7 权限控制 | 角色、路由守卫、越权拦截 |
|
|
44
|
+
| D8 UI 反馈 | toast、modal、loading 指示 |
|
|
45
|
+
| D9 组件联动 | 筛选、tab、数量、级联变化 |
|
|
46
|
+
| D10 边界情况 | 空数据、长文本、特殊输入、前置数据缺失 |
|
|
47
|
+
|
|
48
|
+
## 输出约束
|
|
49
|
+
|
|
50
|
+
`/opentest-plan` 应优先输出窄矩阵:
|
|
51
|
+
|
|
52
|
+
```markdown
|
|
53
|
+
| ID | 意图 | 风险 | 必需证据 | 状态 |
|
|
54
|
+
| --- | --- | --- | --- | --- |
|
|
55
|
+
| ACC-001 | 用户保存后看到成功反馈 | medium | UI 验收 | pending |
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
只有当风险或变更类型需要时,才增加覆盖维度、命令、证据路径和阻塞原因列。
|
|
59
|
+
|
|
60
|
+
## 质量门启发
|
|
61
|
+
|
|
62
|
+
验证建议按 build、type、lint、test、security、diff 的顺序组织。构建、类型、lint、必需测试或必需验收失败时应阻塞通过;非必需证据缺口可以作为可解释风险记录。
|
|
63
|
+
|
|
64
|
+
工具、环境、前置数据或 MCP 不可用时,记录 `blocked` evidence,不得声称验收通过。
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# OpenTest Command Routing
|
|
2
|
+
|
|
3
|
+
`/opentest` is the only automatic router.
|
|
4
|
+
|
|
5
|
+
If `.opentest.yaml` is missing, initialize it and set `phase: plan`.
|
|
6
|
+
|
|
7
|
+
The router does not write tests or run product verification directly. It detects state and invokes the next phase skill.
|
|
8
|
+
|
|
9
|
+
When metadata conflicts with files, prefer verifiable files and repair `.opentest.yaml`.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# OpenTest Lifecycle
|
|
2
|
+
|
|
3
|
+
## Phases
|
|
4
|
+
|
|
5
|
+
`plan -> author -> run -> accept -> verify -> archive`
|
|
6
|
+
|
|
7
|
+
`heal` 是失败恢复阶段,只在测试资产过期或验收执行路径失效时进入。
|
|
8
|
+
|
|
9
|
+
## Exit Conditions
|
|
10
|
+
|
|
11
|
+
- plan: `docs/opentest/plans/*.md` and `docs/opentest/matrices/*.md` exist.
|
|
12
|
+
- author: required test assets or acceptance cases exist, or gaps are explicitly recorded.
|
|
13
|
+
- run: run report exists and records commands, exit codes, and summaries.
|
|
14
|
+
- accept: required acceptance evidence is pass or blocked with reason.
|
|
15
|
+
- verify: verification report exists and result is pass, fail, or risk-accepted.
|
|
16
|
+
- archive: verification passed before archive is marked complete.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Acceptance-to-Test Matrix Format
|
|
2
|
+
|
|
3
|
+
## Minimal Columns
|
|
4
|
+
|
|
5
|
+
| ID | 意图 | 风险 | 必需证据 | 状态 |
|
|
6
|
+
| --- | --- | --- | --- | --- |
|
|
7
|
+
|
|
8
|
+
## Optional Columns
|
|
9
|
+
|
|
10
|
+
- 覆盖面
|
|
11
|
+
- 命令
|
|
12
|
+
- 证据路径
|
|
13
|
+
- 阻塞原因
|
|
14
|
+
- 风险接受说明
|
|
15
|
+
|
|
16
|
+
Only add optional columns when risk or change type needs them.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# OpenTest Quality Gate
|
|
2
|
+
|
|
3
|
+
## Blocking
|
|
4
|
+
|
|
5
|
+
- build failure
|
|
6
|
+
- type failure
|
|
7
|
+
- lint failure
|
|
8
|
+
- required test failure
|
|
9
|
+
- required acceptance failure
|
|
10
|
+
- required evidence gap for high-risk behavior
|
|
11
|
+
|
|
12
|
+
## Risk Accepted
|
|
13
|
+
|
|
14
|
+
- non-critical evidence gap with written reason
|
|
15
|
+
- unavailable optional tool with fallback evidence
|
|
16
|
+
- low-risk behavior covered by review rather than execution
|
|
17
|
+
|
|
18
|
+
## Non-Blocking Gap
|
|
19
|
+
|
|
20
|
+
- evidence not applicable to this change
|
|
21
|
+
- optional coverage dimension omitted with reason
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
usage() {
|
|
5
|
+
printf 'Usage: opentest-detect.sh summary\n' >&2
|
|
6
|
+
exit 1
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
package_script_present() {
|
|
10
|
+
local name="$1"
|
|
11
|
+
[ -f package.json ] || return 1
|
|
12
|
+
grep -Eq "\"$name\"[[:space:]]*:" package.json
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
summary() {
|
|
16
|
+
printf '=== OpenTest Project Facts ===\n'
|
|
17
|
+
|
|
18
|
+
if [ -f package.json ]; then
|
|
19
|
+
printf 'package_json: present\n'
|
|
20
|
+
if package_script_present test; then printf 'npm_test: present\n'; else printf 'npm_test: missing\n'; fi
|
|
21
|
+
if package_script_present build; then printf 'npm_build: present\n'; else printf 'npm_build: missing\n'; fi
|
|
22
|
+
if package_script_present lint; then printf 'npm_lint: present\n'; else printf 'npm_lint: missing\n'; fi
|
|
23
|
+
else
|
|
24
|
+
printf 'package_json: missing\n'
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
if [ -f pyproject.toml ] || [ -f pytest.ini ] || [ -f setup.cfg ]; then
|
|
28
|
+
printf 'python_tests: possible\n'
|
|
29
|
+
else
|
|
30
|
+
printf 'python_tests: missing\n'
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
if [ -f pom.xml ]; then printf 'maven_project: present\n'; else printf 'maven_project: missing\n'; fi
|
|
34
|
+
if [ -f Cargo.toml ]; then printf 'cargo_project: present\n'; else printf 'cargo_project: missing\n'; fi
|
|
35
|
+
if [ -d tests ]; then printf 'tests_dir: present\n'; else printf 'tests_dir: missing\n'; fi
|
|
36
|
+
if [ -d docs/opentest ]; then printf 'opentest_docs: present\n'; else printf 'opentest_docs: missing\n'; fi
|
|
37
|
+
if [ -d docs/opentest/acceptance ]; then printf 'acceptance_docs: present\n'; else printf 'acceptance_docs: missing\n'; fi
|
|
38
|
+
|
|
39
|
+
if git rev-parse --git-dir >/dev/null 2>&1; then
|
|
40
|
+
printf 'git: present\n'
|
|
41
|
+
printf 'changed_files: %s\n' "$(git status --porcelain 2>/dev/null | wc -l | tr -d ' ')"
|
|
42
|
+
else
|
|
43
|
+
printf 'git: missing\n'
|
|
44
|
+
fi
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
case "${1:-summary}" in
|
|
48
|
+
summary)
|
|
49
|
+
shift
|
|
50
|
+
[ $# -eq 0 ] || usage
|
|
51
|
+
summary
|
|
52
|
+
;;
|
|
53
|
+
*)
|
|
54
|
+
usage
|
|
55
|
+
;;
|
|
56
|
+
esac
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
PHASE="${1:-}"
|
|
5
|
+
APPLY="${2:-}"
|
|
6
|
+
STATE_FILE="${OPENTEST_STATE_FILE:-.opentest.yaml}"
|
|
7
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
8
|
+
BLOCK=0
|
|
9
|
+
|
|
10
|
+
red() { printf '\033[31m%s\033[0m\n' "$1" >&2; }
|
|
11
|
+
green() { printf '\033[32m%s\033[0m\n' "$1" >&2; }
|
|
12
|
+
|
|
13
|
+
usage() {
|
|
14
|
+
red "Usage: opentest-guard.sh <plan|author|run|accept|verify|heal|archive> [--apply]"
|
|
15
|
+
exit 1
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
yaml_field() {
|
|
19
|
+
local field="$1"
|
|
20
|
+
awk -v key="$field" '
|
|
21
|
+
$1 == key ":" {
|
|
22
|
+
sub(/^[^:]+:[[:space:]]*/, "", $0)
|
|
23
|
+
print
|
|
24
|
+
exit
|
|
25
|
+
}
|
|
26
|
+
' "$STATE_FILE" | tr -d '"' | tr -d "'"
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
file_nonempty() {
|
|
30
|
+
[ -n "${1:-}" ] && [ "$1" != "null" ] && [ -f "$1" ] && [ -s "$1" ]
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
check_path() {
|
|
34
|
+
local description="$1"
|
|
35
|
+
local path_value="$2"
|
|
36
|
+
if file_nonempty "$path_value"; then
|
|
37
|
+
green " [PASS] $description"
|
|
38
|
+
else
|
|
39
|
+
red " [FAIL] $description"
|
|
40
|
+
BLOCK=1
|
|
41
|
+
fi
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
check_result() {
|
|
45
|
+
local description="$1"
|
|
46
|
+
shift
|
|
47
|
+
if "$@"; then
|
|
48
|
+
green " [PASS] $description"
|
|
49
|
+
else
|
|
50
|
+
red " [FAIL] $description"
|
|
51
|
+
BLOCK=1
|
|
52
|
+
fi
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
result_is_archivable() {
|
|
56
|
+
local result
|
|
57
|
+
result=$(yaml_field verification_result)
|
|
58
|
+
[ "$result" = "pass" ] || [ "$result" = "risk-accepted" ]
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
guard_plan() {
|
|
62
|
+
check_path "plan exists" "$(yaml_field plan)"
|
|
63
|
+
check_path "matrix exists" "$(yaml_field matrix)"
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
guard_author() {
|
|
67
|
+
local acceptance matrix
|
|
68
|
+
acceptance=$(yaml_field acceptance)
|
|
69
|
+
matrix=$(yaml_field matrix)
|
|
70
|
+
|
|
71
|
+
if [ -n "$acceptance" ] && [ "$acceptance" != "null" ]; then
|
|
72
|
+
check_path "acceptance asset exists" "$acceptance"
|
|
73
|
+
else
|
|
74
|
+
check_path "matrix exists for recorded gaps" "$matrix"
|
|
75
|
+
fi
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
guard_run() {
|
|
79
|
+
check_path "run report exists" "$(yaml_field run_report)"
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
guard_accept() {
|
|
83
|
+
local acceptance run_report
|
|
84
|
+
acceptance=$(yaml_field acceptance)
|
|
85
|
+
run_report=$(yaml_field run_report)
|
|
86
|
+
|
|
87
|
+
if [ -n "$acceptance" ] && [ "$acceptance" != "null" ]; then
|
|
88
|
+
check_path "acceptance evidence exists" "$acceptance"
|
|
89
|
+
else
|
|
90
|
+
check_path "run report exists for recorded no-acceptance path" "$run_report"
|
|
91
|
+
fi
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
guard_verify() {
|
|
95
|
+
check_path "verification report exists" "$(yaml_field verification_report)"
|
|
96
|
+
check_result "verification result is archivable" result_is_archivable
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
guard_heal() {
|
|
100
|
+
green " [PASS] heal can return to run after targeted assets are updated"
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
guard_archive() {
|
|
104
|
+
check_path "verification report exists" "$(yaml_field verification_report)"
|
|
105
|
+
check_result "verification result is archivable" result_is_archivable
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
apply_transition() {
|
|
109
|
+
case "$PHASE" in
|
|
110
|
+
plan)
|
|
111
|
+
bash "$SCRIPT_DIR/opentest-state.sh" transition plan-complete
|
|
112
|
+
;;
|
|
113
|
+
author)
|
|
114
|
+
bash "$SCRIPT_DIR/opentest-state.sh" transition author-complete
|
|
115
|
+
;;
|
|
116
|
+
run)
|
|
117
|
+
bash "$SCRIPT_DIR/opentest-state.sh" transition run-complete
|
|
118
|
+
;;
|
|
119
|
+
accept)
|
|
120
|
+
bash "$SCRIPT_DIR/opentest-state.sh" transition accept-complete
|
|
121
|
+
;;
|
|
122
|
+
verify)
|
|
123
|
+
local result
|
|
124
|
+
result=$(yaml_field verification_result)
|
|
125
|
+
if [ "$result" = "pass" ]; then
|
|
126
|
+
bash "$SCRIPT_DIR/opentest-state.sh" transition verify-pass
|
|
127
|
+
elif [ "$result" = "risk-accepted" ]; then
|
|
128
|
+
bash "$SCRIPT_DIR/opentest-state.sh" transition verify-risk-accepted
|
|
129
|
+
else
|
|
130
|
+
bash "$SCRIPT_DIR/opentest-state.sh" transition verify-fail
|
|
131
|
+
fi
|
|
132
|
+
;;
|
|
133
|
+
heal)
|
|
134
|
+
bash "$SCRIPT_DIR/opentest-state.sh" transition heal-complete
|
|
135
|
+
;;
|
|
136
|
+
archive)
|
|
137
|
+
bash "$SCRIPT_DIR/opentest-state.sh" transition archived
|
|
138
|
+
;;
|
|
139
|
+
*)
|
|
140
|
+
usage
|
|
141
|
+
;;
|
|
142
|
+
esac
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if [ -z "$PHASE" ]; then
|
|
146
|
+
usage
|
|
147
|
+
fi
|
|
148
|
+
|
|
149
|
+
if [ ! -f "$STATE_FILE" ]; then
|
|
150
|
+
red "FATAL: $STATE_FILE not found"
|
|
151
|
+
exit 1
|
|
152
|
+
fi
|
|
153
|
+
|
|
154
|
+
case "$PHASE" in
|
|
155
|
+
plan)
|
|
156
|
+
guard_plan
|
|
157
|
+
;;
|
|
158
|
+
author)
|
|
159
|
+
guard_author
|
|
160
|
+
;;
|
|
161
|
+
run)
|
|
162
|
+
guard_run
|
|
163
|
+
;;
|
|
164
|
+
accept)
|
|
165
|
+
guard_accept
|
|
166
|
+
;;
|
|
167
|
+
verify)
|
|
168
|
+
guard_verify
|
|
169
|
+
;;
|
|
170
|
+
heal)
|
|
171
|
+
guard_heal
|
|
172
|
+
;;
|
|
173
|
+
archive)
|
|
174
|
+
guard_archive
|
|
175
|
+
;;
|
|
176
|
+
*)
|
|
177
|
+
usage
|
|
178
|
+
;;
|
|
179
|
+
esac
|
|
180
|
+
|
|
181
|
+
if [ "$BLOCK" -ne 0 ]; then
|
|
182
|
+
red "BLOCKED"
|
|
183
|
+
exit 1
|
|
184
|
+
fi
|
|
185
|
+
|
|
186
|
+
green "ALL CHECKS PASSED"
|
|
187
|
+
if [ "$APPLY" = "--apply" ]; then
|
|
188
|
+
apply_transition
|
|
189
|
+
fi
|