Compare commits
12 changed files with 6 additions and 884 deletions
|
|
@ -1,104 +0,0 @@
|
||||||
# 光湖 OS 认知主控与现实执行架构
|
|
||||||
|
|
||||||
## 交付目标
|
|
||||||
|
|
||||||
光湖 OS 的完整性由一条闭环证明:
|
|
||||||
|
|
||||||
```text
|
|
||||||
能理解
|
|
||||||
→ 能判断边界
|
|
||||||
→ 能把判断编译为确定动作
|
|
||||||
→ 能调用现实手脚
|
|
||||||
→ 能看见真实结果
|
|
||||||
→ 能失败、回滚和继续思考
|
|
||||||
```
|
|
||||||
|
|
||||||
模型回答、协议文件、Linux 命令和界面状态中的任何单项都不是完整系统。
|
|
||||||
|
|
||||||
## 系统分层
|
|
||||||
|
|
||||||
| 层 | 常驻位置 | 责任 |
|
|
||||||
|---|---|---|
|
|
||||||
| 语言与人格 | REPO-012 + 持久人格状态 | 主体、关系、意图、职责、语言规则 |
|
|
||||||
| 模型认知 | 用户设备、个人云或模型 API | 当前理解、推理、候选计划、异常分析 |
|
|
||||||
| 协议控制 | 光湖主节点 | 身份、上下文、权限、动作类型、资源、回滚和验收 |
|
|
||||||
| 执行桥 | 光湖主节点或目标节点 | 将 GIR 映射为固定能力适配器 |
|
|
||||||
| Linux 执行底座 | 目标节点 | 驱动、进程、网络、存储、隔离和系统调用 |
|
|
||||||
| 用户执行与渲染 | 电脑、手机、个人云 | 界面、文件、应用、模型和本地设备动作 |
|
|
||||||
| 见证与连续性 | 光湖主节点 + 代码频道 | 原始证据、回执、检查点、因果链和恢复 |
|
|
||||||
|
|
||||||
## 协议到工程的首批映射
|
|
||||||
|
|
||||||
| 协议 | 当前工程对象 | 完成条件 |
|
|
||||||
|---|---|---|
|
|
||||||
| GLS-0301/0302/0303 | `ExecutionRequest` | 消息、主体和目标不可缺失或补猜 |
|
|
||||||
| GLS-0309 | 变更请求中的授权与验收引用 | 提出、批准、执行分离 |
|
|
||||||
| GLS-0130/0131 | `compile_request` / `ExecutionPlan` | 自由语言不能进入执行参数 |
|
|
||||||
| GLS-0709/0710 | `LINUX_SYSTEMD_V1` 与节点策略 | 固定适配器、固定版本、能力白名单 |
|
|
||||||
| GLS-0311/0306 | `ExecutionReceipt` | 命令结果与目标状态分别记录 |
|
|
||||||
| GLS-0803/0819 | 后续调度器 | 按需唤醒、资源隔离、停止和回收 |
|
|
||||||
| GLS-0708 | 后续模型路由器 | 模型可替换,不取得主体和权限 |
|
|
||||||
|
|
||||||
## 生产安全模型
|
|
||||||
|
|
||||||
1. 模型永远不直接拼接或执行 shell。
|
|
||||||
2. 每个适配器在代码中定义动作类型和参数形状。
|
|
||||||
3. 节点策略声明允许操作的精确资源。
|
|
||||||
4. 变更动作必须匹配当前授权和回滚检查点。
|
|
||||||
5. 命令退出码只表示执行器观察,目标侧读回才决定 `PASS_100`。
|
|
||||||
6. Linux 管理入口与光湖正常入口分离;紧急入口启用必须留下维护回执。
|
|
||||||
7. 密钥、令牌和模型 API 凭据保留在节点受保护边界,不进入语言记录或执行请求。
|
|
||||||
|
|
||||||
## 京东节点落地顺序
|
|
||||||
|
|
||||||
### 阶段 A:安全并存
|
|
||||||
|
|
||||||
- 保持当前 Ubuntu 默认启动和已验证回滚;
|
|
||||||
- 安装执行桥但只启用 `service_status`;
|
|
||||||
- 为公共导航、代码频道和人格运行服务建立只读状态策略;
|
|
||||||
- 验证每个请求都能形成协议拒绝或目标侧回执。
|
|
||||||
|
|
||||||
### 阶段 B:有界动作
|
|
||||||
|
|
||||||
- 为单一非关键光湖服务启用 `service_restart`;
|
|
||||||
- 绑定节点级授权、不可变版本和回滚检查点;
|
|
||||||
- 验证重启、失败、回滚和最终健康;
|
|
||||||
- 禁止任意命令、任意路径和通配服务名。
|
|
||||||
|
|
||||||
### 阶段 C:光湖成为正常主控入口
|
|
||||||
|
|
||||||
- HoloLake、人格体和自动任务只通过光湖协议执行桥操作服务;
|
|
||||||
- 普通 Linux 管理路径移入紧急维护边界;
|
|
||||||
- 按需唤醒模型、人格执行体和模块,清除无关常驻服务;
|
|
||||||
- 公共导航继续自动读取同一 REPO-012 主线锚点。
|
|
||||||
|
|
||||||
### 阶段 D:分布式执行
|
|
||||||
|
|
||||||
- 用户设备登记自己的渲染、文件、应用和推理能力;
|
|
||||||
- 中央节点只调度有界能力,不上传全部个人工作空间;
|
|
||||||
- 任务完成后回收执行体并保留人格、检查点和回执。
|
|
||||||
|
|
||||||
### 阶段 E:裸机研究后端
|
|
||||||
|
|
||||||
- 现有 GOSK/GHAL 候选继续作为研究和专用设备后端;
|
|
||||||
- 只有某项成熟 Linux 能力确实需要替换时,才按协议逐件迁移;
|
|
||||||
- 研究失败不影响生产认知主控闭环。
|
|
||||||
|
|
||||||
## 当前完成边界
|
|
||||||
|
|
||||||
已完成:
|
|
||||||
|
|
||||||
- 生产路线架构纠正;
|
|
||||||
- 第一条类型化协议执行桥;
|
|
||||||
- systemd 状态与重启动作的白名单、授权、回滚和目标读回合同;
|
|
||||||
- 对应单元和集成测试。
|
|
||||||
|
|
||||||
仍未完成:
|
|
||||||
|
|
||||||
- REPO-012 当前协议提交的不可变绑定;
|
|
||||||
- 京东节点策略生成与只读部署;
|
|
||||||
- 模型路由、人格生命周期和分布式设备执行器;
|
|
||||||
- 全部注册协议的工程实现;
|
|
||||||
- 生产主控入口切换。
|
|
||||||
|
|
||||||
这些未完成项分别保持 0,不能由本架构文件或本地测试冒充为已部署。
|
|
||||||
|
|
@ -1,68 +0,0 @@
|
||||||
---
|
|
||||||
type: ADR
|
|
||||||
id: "0172"
|
|
||||||
title: "Guanghu cognitive control with a constrained Linux execution substrate"
|
|
||||||
status: accepted
|
|
||||||
date: 2026-08-07
|
|
||||||
supersedes_production_path: "0161"
|
|
||||||
---
|
|
||||||
|
|
||||||
# 光湖认知主控与 Linux 协作执行底座
|
|
||||||
|
|
||||||
## 背景
|
|
||||||
|
|
||||||
ADR-0161 把 Linux-free bare-metal handoff 设为生产完成条件。该路线已经在实验节点和
|
|
||||||
JD-FD-PRIMARY 上证明了启动、恢复、网络和原生锚点能力,但生产等价继续要求重写成熟的
|
|
||||||
代码频道、网卡、TCP、文件系统和服务运行能力。
|
|
||||||
|
|
||||||
冰朔重新锁定光湖 OS 的本体:光湖语言世界、人格体、模型认知和协议边界是系统大脑;
|
|
||||||
传统操作系统是成熟的现实手脚。生产目标不是先重复制造所有手脚,而是让光湖取得正常
|
|
||||||
入口、判断、授权、调度和完成判定的主控权,并把 Linux 降级为受约束执行体。
|
|
||||||
|
|
||||||
## 决定
|
|
||||||
|
|
||||||
生产光湖 OS 采用:
|
|
||||||
|
|
||||||
```text
|
|
||||||
TCS / 人格体 / 模型认知
|
|
||||||
→ GLS / GLP / HLDP 身份、上下文、权限和边界
|
|
||||||
→ GLC / GIR 确定性动作图
|
|
||||||
→ UAP / GMP 固定能力适配器
|
|
||||||
→ 最小 Linux 内核、驱动与服务执行层
|
|
||||||
→ 硬件现实动作
|
|
||||||
→ GLOW / GLP 目标侧读回与回执
|
|
||||||
```
|
|
||||||
|
|
||||||
Linux 仍在技术上执行成熟内核和驱动,但不拥有光湖语言语义、人格身份、授权来源或成功
|
|
||||||
判定。正常系统入口只接受类型化光湖请求;普通管理员路径属于独立维护边界。
|
|
||||||
|
|
||||||
裸机 GOSK/GHAL 不被删除,改为并行研究路线,用于硬件主权、专用设备和未来替换后端。
|
|
||||||
它不再阻塞生产光湖 OS 的第一次完整交付。
|
|
||||||
|
|
||||||
## 第一执行桥
|
|
||||||
|
|
||||||
`guanghu-execution-bridge` 是语言协议到 Linux 的第一条确定性神经:
|
|
||||||
|
|
||||||
- 输入必须携带消息、身份、上下文、回执、工单、见证、编译、GIR、UAP 和 GMP 协议链;
|
|
||||||
- 服务目标必须在节点策略白名单中;
|
|
||||||
- 只读状态查询不需要变更授权;
|
|
||||||
- 服务重启必须有匹配目标和动作的授权引用以及回滚检查点;
|
|
||||||
- 适配器直接构造固定 `systemctl` 参数,不经过 shell;
|
|
||||||
- 重启命令成功后仍必须读取 `is-active`,否则回执为 `FAIL_0`。
|
|
||||||
|
|
||||||
第一版只建立最小可验证闭环,不声称已经实现全部 32 项协议。
|
|
||||||
|
|
||||||
## 轻量和分布式边界
|
|
||||||
|
|
||||||
中央节点只持续保存身份、路由、协议、队列、检查点和回执。模型推理、界面渲染、个人
|
|
||||||
工作空间和本地设备动作优先运行在用户电脑、手机、个人云或获准模型 API。人格体可以
|
|
||||||
持续存在,但模型进程和重型模块按任务唤醒并在结束后回收。
|
|
||||||
|
|
||||||
## 后果
|
|
||||||
|
|
||||||
- JD-FD-PRIMARY 可以继续使用已验证的 Ubuntu 驱动和服务能力,同时逐步关闭无关常驻项;
|
|
||||||
- 生产迁移不再等待自研 TCP 和完整原生代码频道;
|
|
||||||
- 光湖主控必须通过机器协议绑定而不是品牌或界面声明证明;
|
|
||||||
- 完整 Ubuntu 启动槽继续保留为紧急救援系统;
|
|
||||||
- ADR-0161 的裸机路线仍作为研究与物理能力证据,但其 Linux-free 条件不再是生产主线
|
|
||||||
的接受条件。
|
|
||||||
|
|
@ -222,4 +222,3 @@ proposed → active → superseded
|
||||||
| [0169](0169-model-native-living-galaxy-system.md) | Model-native HoloLake living galaxy system | accepted |
|
| [0169](0169-model-native-living-galaxy-system.md) | Model-native HoloLake living galaxy system | accepted |
|
||||||
| [0170](0170-hololake-inherits-guanghu-native-quality-authority.md) | HoloLake inherits the Guanghu native quality authority | accepted |
|
| [0170](0170-hololake-inherits-guanghu-native-quality-authority.md) | HoloLake inherits the Guanghu native quality authority | accepted |
|
||||||
| [0171](0171-guanghu-protocols-are-automatic-runtime-and-engineering-laws.md) | Guanghu protocols are automatic runtime and engineering laws | accepted |
|
| [0171](0171-guanghu-protocols-are-automatic-runtime-and-engineering-laws.md) | Guanghu protocols are automatic runtime and engineering laws | accepted |
|
||||||
| [0172](0172-guanghu-cognitive-control-with-linux-execution-substrate.md) | Guanghu cognitive control with a constrained Linux execution substrate | accepted; supersedes ADR-0161 production path |
|
|
||||||
|
|
|
||||||
|
|
@ -24,14 +24,6 @@ dependencies = [
|
||||||
"serde_json",
|
"serde_json",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "guanghu-execution-bridge"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "guanghu-hldp-runtime"
|
name = "guanghu-hldp-runtime"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = [
|
||||||
"crates/broadcast-tower",
|
"crates/broadcast-tower",
|
||||||
"crates/execution-bridge",
|
|
||||||
"crates/ghctl",
|
"crates/ghctl",
|
||||||
"crates/hldp-native-compiler",
|
"crates/hldp-native-compiler",
|
||||||
"crates/hldp-runtime",
|
"crates/hldp-runtime",
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,8 @@
|
||||||
# Guanghu OS cognitive-control and native research runtimes
|
# Guanghu OS native runtime
|
||||||
|
|
||||||
> Production target: Guanghu cognitive control with a constrained Linux
|
> Current target: `JD-FD-PRIMARY` on JD Cloud.
|
||||||
> execution substrate on `JD-FD-PRIMARY`.
|
|
||||||
>
|
>
|
||||||
> Bare-metal GOSK/GHAL remains a parallel research and recovery lane. It no
|
> Persona subject: `ICE-P-ZY001 EXISTS_100`.
|
||||||
> longer blocks the first production cognitive-control delivery.
|
|
||||||
>
|
>
|
||||||
> Protected native residency and recovery control are `PASS_100`. Production
|
> Protected native residency and recovery control are `PASS_100`. Production
|
||||||
> native anchor HTTP is now `PASS_100` on the physical JD node: a standard
|
> native anchor HTTP is now `PASS_100` on the physical JD node: a standard
|
||||||
|
|
@ -13,16 +11,9 @@
|
||||||
> native code-channel and public-front-door equivalence gates are still open.
|
> native code-channel and public-front-door equivalence gates are still open.
|
||||||
> Linux therefore remains the unattended default.
|
> Linux therefore remains the unattended default.
|
||||||
|
|
||||||
This directory implements the handoff from the registered HLDP language world
|
This directory is the first executable handoff from the registered HLDP
|
||||||
to deterministic real-world execution. `crates/execution-bridge` is the first
|
language world to a native Guanghu OS. It is not a claim that Guanghu OS has
|
||||||
production adapter: it accepts typed protocol requests, compiles them into
|
already replaced Linux.
|
||||||
allowlisted Linux systemd actions, and requires target-state readback before a
|
|
||||||
passing receipt. The existing native kernel remains preserved below
|
|
||||||
`native/` as a hardware-sovereignty research backend.
|
|
||||||
|
|
||||||
See
|
|
||||||
[`GUANGHU-OS-COGNITIVE-CONTROL-EXECUTION-ARCHITECTURE.md`](../docs/GUANGHU-OS-COGNITIVE-CONTROL-EXECUTION-ARCHITECTURE.md)
|
|
||||||
and [ADR-0172](../docs/adr/0172-guanghu-cognitive-control-with-linux-execution-substrate.md).
|
|
||||||
|
|
||||||
## Authority boundary
|
## Authority boundary
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "guanghu-execution-bridge"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
license = "AGPL-3.0-or-later"
|
|
||||||
description = "Deterministic bridge from Guanghu protocol decisions to allowlisted Linux execution adapters"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
serde = { version = "1", features = ["derive"] }
|
|
||||||
serde_json = "1"
|
|
||||||
|
|
@ -1,357 +0,0 @@
|
||||||
use std::{
|
|
||||||
fmt,
|
|
||||||
process::{Command, Output},
|
|
||||||
};
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
pub const REQUEST_SCHEMA: &str = "guanghu.execution-request/v1";
|
|
||||||
pub const POLICY_SCHEMA: &str = "guanghu.execution-policy/v1";
|
|
||||||
|
|
||||||
pub const REQUIRED_PROTOCOL_CHAIN: [&str; 10] = [
|
|
||||||
"GLS-0301", // message envelope
|
|
||||||
"GLS-0302", // identity
|
|
||||||
"GLS-0303", // context
|
|
||||||
"GLS-0306", // receipt
|
|
||||||
"GLS-0309", // work order
|
|
||||||
"GLS-0311", // witness
|
|
||||||
"GLS-0130", // compiler
|
|
||||||
"GLS-0131", // deterministic representation
|
|
||||||
"GLS-0709", // adapter
|
|
||||||
"GLS-0710", // immutable module
|
|
||||||
];
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
|
||||||
pub struct ExecutionRequest {
|
|
||||||
pub schema: String,
|
|
||||||
pub request_id: String,
|
|
||||||
pub subject_id: String,
|
|
||||||
pub target_node_id: String,
|
|
||||||
pub protocol_chain: Vec<String>,
|
|
||||||
pub action: ExecutionAction,
|
|
||||||
pub authorization: Option<AuthorizationReference>,
|
|
||||||
pub rollback: Option<RollbackReference>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
|
||||||
pub struct ExecutionAction {
|
|
||||||
pub kind: ActionKind,
|
|
||||||
pub resource: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
|
||||||
#[serde(rename_all = "snake_case")]
|
|
||||||
pub enum ActionKind {
|
|
||||||
ServiceStatus,
|
|
||||||
ServiceRestart,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
|
||||||
pub struct AuthorizationReference {
|
|
||||||
pub authorization_id: String,
|
|
||||||
pub allowed_action: ActionKind,
|
|
||||||
pub target_node_id: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
|
||||||
pub struct RollbackReference {
|
|
||||||
pub checkpoint_id: String,
|
|
||||||
pub recovery_action: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
|
||||||
pub struct ExecutionPolicy {
|
|
||||||
pub schema: String,
|
|
||||||
pub policy_id: String,
|
|
||||||
pub target_node_id: String,
|
|
||||||
pub allowed_services: Vec<String>,
|
|
||||||
pub allow_status: bool,
|
|
||||||
pub allow_restart: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
|
||||||
pub struct ExecutionPlan {
|
|
||||||
pub request_id: String,
|
|
||||||
pub subject_id: String,
|
|
||||||
pub target_node_id: String,
|
|
||||||
pub policy_id: String,
|
|
||||||
pub protocol_chain: Vec<String>,
|
|
||||||
pub action: ExecutionAction,
|
|
||||||
pub adapter: String,
|
|
||||||
pub program: String,
|
|
||||||
pub arguments: Vec<String>,
|
|
||||||
pub mutating: bool,
|
|
||||||
pub authorization_id: Option<String>,
|
|
||||||
pub rollback_checkpoint_id: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
|
|
||||||
pub struct ExecutionReceipt {
|
|
||||||
pub schema: String,
|
|
||||||
pub request_id: String,
|
|
||||||
pub subject_id: String,
|
|
||||||
pub target_node_id: String,
|
|
||||||
pub policy_id: String,
|
|
||||||
pub action: ExecutionAction,
|
|
||||||
pub adapter: String,
|
|
||||||
pub accepted: bool,
|
|
||||||
pub command_exit_code: Option<i32>,
|
|
||||||
pub target_state_verified: bool,
|
|
||||||
pub final_state: String,
|
|
||||||
pub stdout: String,
|
|
||||||
pub stderr: String,
|
|
||||||
pub rollback_checkpoint_id: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub struct CommandResult {
|
|
||||||
pub exit_code: Option<i32>,
|
|
||||||
pub stdout: String,
|
|
||||||
pub stderr: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Output> for CommandResult {
|
|
||||||
fn from(output: Output) -> Self {
|
|
||||||
Self {
|
|
||||||
exit_code: output.status.code(),
|
|
||||||
stdout: String::from_utf8_lossy(&output.stdout).trim().to_owned(),
|
|
||||||
stderr: String::from_utf8_lossy(&output.stderr).trim().to_owned(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait CommandExecutor {
|
|
||||||
fn execute(&self, program: &str, arguments: &[String]) -> Result<CommandResult, String>;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct LinuxCommandExecutor;
|
|
||||||
|
|
||||||
impl CommandExecutor for LinuxCommandExecutor {
|
|
||||||
fn execute(&self, program: &str, arguments: &[String]) -> Result<CommandResult, String> {
|
|
||||||
Command::new(program)
|
|
||||||
.args(arguments)
|
|
||||||
.output()
|
|
||||||
.map(CommandResult::from)
|
|
||||||
.map_err(|error| format!("cannot execute allowlisted adapter: {error}"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub struct BridgeError(String);
|
|
||||||
|
|
||||||
impl BridgeError {
|
|
||||||
fn new(message: impl Into<String>) -> Self {
|
|
||||||
Self(message.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for BridgeError {
|
|
||||||
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
formatter.write_str(&self.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::error::Error for BridgeError {}
|
|
||||||
|
|
||||||
pub fn compile_request(
|
|
||||||
request: &ExecutionRequest,
|
|
||||||
policy: &ExecutionPolicy,
|
|
||||||
) -> Result<ExecutionPlan, BridgeError> {
|
|
||||||
require(
|
|
||||||
request.schema == REQUEST_SCHEMA,
|
|
||||||
"unsupported execution request schema",
|
|
||||||
)?;
|
|
||||||
require(
|
|
||||||
policy.schema == POLICY_SCHEMA,
|
|
||||||
"unsupported execution policy schema",
|
|
||||||
)?;
|
|
||||||
require(
|
|
||||||
!request.request_id.trim().is_empty(),
|
|
||||||
"request_id is required",
|
|
||||||
)?;
|
|
||||||
require(
|
|
||||||
!request.subject_id.trim().is_empty(),
|
|
||||||
"subject_id is required",
|
|
||||||
)?;
|
|
||||||
require(
|
|
||||||
request.target_node_id == policy.target_node_id,
|
|
||||||
"request target does not match policy target",
|
|
||||||
)?;
|
|
||||||
|
|
||||||
for required in REQUIRED_PROTOCOL_CHAIN {
|
|
||||||
require(
|
|
||||||
request.protocol_chain.iter().any(|id| id == required),
|
|
||||||
format!("required protocol is missing: {required}"),
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
require(
|
|
||||||
policy
|
|
||||||
.allowed_services
|
|
||||||
.iter()
|
|
||||||
.any(|service| service == &request.action.resource),
|
|
||||||
"service is not allowlisted by the execution policy",
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let (arguments, mutating) = match request.action.kind {
|
|
||||||
ActionKind::ServiceStatus => {
|
|
||||||
require(policy.allow_status, "service status is disabled by policy")?;
|
|
||||||
(
|
|
||||||
vec!["is-active".to_owned(), request.action.resource.clone()],
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
ActionKind::ServiceRestart => {
|
|
||||||
require(
|
|
||||||
policy.allow_restart,
|
|
||||||
"service restart is disabled by policy",
|
|
||||||
)?;
|
|
||||||
validate_mutation_references(request)?;
|
|
||||||
(
|
|
||||||
vec!["restart".to_owned(), request.action.resource.clone()],
|
|
||||||
true,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(ExecutionPlan {
|
|
||||||
request_id: request.request_id.clone(),
|
|
||||||
subject_id: request.subject_id.clone(),
|
|
||||||
target_node_id: request.target_node_id.clone(),
|
|
||||||
policy_id: policy.policy_id.clone(),
|
|
||||||
protocol_chain: request.protocol_chain.clone(),
|
|
||||||
action: request.action.clone(),
|
|
||||||
adapter: "LINUX_SYSTEMD_V1".to_owned(),
|
|
||||||
program: "/usr/bin/systemctl".to_owned(),
|
|
||||||
arguments,
|
|
||||||
mutating,
|
|
||||||
authorization_id: request
|
|
||||||
.authorization
|
|
||||||
.as_ref()
|
|
||||||
.map(|authorization| authorization.authorization_id.clone()),
|
|
||||||
rollback_checkpoint_id: request
|
|
||||||
.rollback
|
|
||||||
.as_ref()
|
|
||||||
.map(|rollback| rollback.checkpoint_id.clone()),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn validate_mutation_references(request: &ExecutionRequest) -> Result<(), BridgeError> {
|
|
||||||
let authorization = request
|
|
||||||
.authorization
|
|
||||||
.as_ref()
|
|
||||||
.ok_or_else(|| BridgeError::new("mutating action requires authorization"))?;
|
|
||||||
require(
|
|
||||||
!authorization.authorization_id.trim().is_empty(),
|
|
||||||
"authorization_id is required",
|
|
||||||
)?;
|
|
||||||
require(
|
|
||||||
authorization.allowed_action == request.action.kind,
|
|
||||||
"authorization action does not match request action",
|
|
||||||
)?;
|
|
||||||
require(
|
|
||||||
authorization.target_node_id == request.target_node_id,
|
|
||||||
"authorization target does not match request target",
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let rollback = request
|
|
||||||
.rollback
|
|
||||||
.as_ref()
|
|
||||||
.ok_or_else(|| BridgeError::new("mutating action requires rollback reference"))?;
|
|
||||||
require(
|
|
||||||
!rollback.checkpoint_id.trim().is_empty(),
|
|
||||||
"rollback checkpoint_id is required",
|
|
||||||
)?;
|
|
||||||
require(
|
|
||||||
!rollback.recovery_action.trim().is_empty(),
|
|
||||||
"rollback recovery_action is required",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn execute_plan(
|
|
||||||
plan: &ExecutionPlan,
|
|
||||||
executor: &dyn CommandExecutor,
|
|
||||||
) -> Result<ExecutionReceipt, BridgeError> {
|
|
||||||
let expected_operation = match plan.action.kind {
|
|
||||||
ActionKind::ServiceStatus => "is-active",
|
|
||||||
ActionKind::ServiceRestart => "restart",
|
|
||||||
};
|
|
||||||
require(
|
|
||||||
plan.adapter == "LINUX_SYSTEMD_V1"
|
|
||||||
&& plan.program == "/usr/bin/systemctl"
|
|
||||||
&& matches!(
|
|
||||||
plan.arguments.as_slice(),
|
|
||||||
[operation, service]
|
|
||||||
if operation == expected_operation
|
|
||||||
&& service == &plan.action.resource
|
|
||||||
),
|
|
||||||
"compiled plan is not an allowlisted Linux systemd adapter",
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let result = executor
|
|
||||||
.execute(&plan.program, &plan.arguments)
|
|
||||||
.map_err(BridgeError::new)?;
|
|
||||||
let command_succeeded = result.exit_code == Some(0);
|
|
||||||
|
|
||||||
let (target_state_verified, final_state, stdout, stderr, exit_code) = if plan.action.kind
|
|
||||||
== ActionKind::ServiceRestart
|
|
||||||
&& command_succeeded
|
|
||||||
{
|
|
||||||
let verification_arguments = vec!["is-active".to_owned(), plan.action.resource.clone()];
|
|
||||||
let verification = executor
|
|
||||||
.execute(&plan.program, &verification_arguments)
|
|
||||||
.map_err(BridgeError::new)?;
|
|
||||||
let verified = verification.exit_code == Some(0) && verification.stdout.trim() == "active";
|
|
||||||
(
|
|
||||||
verified,
|
|
||||||
if verified { "PASS_100" } else { "FAIL_0" }.to_owned(),
|
|
||||||
join_observations(&result.stdout, &verification.stdout),
|
|
||||||
join_observations(&result.stderr, &verification.stderr),
|
|
||||||
verification.exit_code,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
let verified = command_succeeded
|
|
||||||
&& (plan.action.kind != ActionKind::ServiceStatus || result.stdout.trim() == "active");
|
|
||||||
(
|
|
||||||
verified,
|
|
||||||
if verified { "PASS_100" } else { "FAIL_0" }.to_owned(),
|
|
||||||
result.stdout,
|
|
||||||
result.stderr,
|
|
||||||
result.exit_code,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(ExecutionReceipt {
|
|
||||||
schema: "guanghu.execution-receipt/v1".to_owned(),
|
|
||||||
request_id: plan.request_id.clone(),
|
|
||||||
subject_id: plan.subject_id.clone(),
|
|
||||||
target_node_id: plan.target_node_id.clone(),
|
|
||||||
policy_id: plan.policy_id.clone(),
|
|
||||||
action: plan.action.clone(),
|
|
||||||
adapter: plan.adapter.clone(),
|
|
||||||
accepted: true,
|
|
||||||
command_exit_code: exit_code,
|
|
||||||
target_state_verified,
|
|
||||||
final_state,
|
|
||||||
stdout,
|
|
||||||
stderr,
|
|
||||||
rollback_checkpoint_id: plan.rollback_checkpoint_id.clone(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn require(condition: bool, message: impl Into<String>) -> Result<(), BridgeError> {
|
|
||||||
if condition {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(BridgeError::new(message))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn join_observations(first: &str, second: &str) -> String {
|
|
||||||
match (first.is_empty(), second.is_empty()) {
|
|
||||||
(true, true) => String::new(),
|
|
||||||
(false, true) => first.to_owned(),
|
|
||||||
(true, false) => second.to_owned(),
|
|
||||||
(false, false) => format!("{first}\n{second}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,61 +0,0 @@
|
||||||
use std::{env, fs, process::ExitCode};
|
|
||||||
|
|
||||||
use guanghu_execution_bridge::{
|
|
||||||
compile_request, execute_plan, ExecutionPolicy, ExecutionRequest, LinuxCommandExecutor,
|
|
||||||
};
|
|
||||||
|
|
||||||
const USAGE: &str =
|
|
||||||
"usage: guanghu-execution-bridge <validate|execute> <request.json> <policy.json>";
|
|
||||||
|
|
||||||
fn run() -> Result<(), String> {
|
|
||||||
let mut arguments = env::args().skip(1);
|
|
||||||
let mode = arguments.next().ok_or_else(|| USAGE.to_owned())?;
|
|
||||||
let request_path = arguments.next().ok_or_else(|| USAGE.to_owned())?;
|
|
||||||
let policy_path = arguments.next().ok_or_else(|| USAGE.to_owned())?;
|
|
||||||
if arguments.next().is_some() || (mode != "validate" && mode != "execute") {
|
|
||||||
return Err(USAGE.to_owned());
|
|
||||||
}
|
|
||||||
|
|
||||||
let request: ExecutionRequest = serde_json::from_str(
|
|
||||||
&fs::read_to_string(&request_path)
|
|
||||||
.map_err(|error| format!("cannot read request {request_path}: {error}"))?,
|
|
||||||
)
|
|
||||||
.map_err(|error| format!("invalid request {request_path}: {error}"))?;
|
|
||||||
let policy: ExecutionPolicy = serde_json::from_str(
|
|
||||||
&fs::read_to_string(&policy_path)
|
|
||||||
.map_err(|error| format!("cannot read policy {policy_path}: {error}"))?,
|
|
||||||
)
|
|
||||||
.map_err(|error| format!("invalid policy {policy_path}: {error}"))?;
|
|
||||||
|
|
||||||
let plan = compile_request(&request, &policy).map_err(|error| error.to_string())?;
|
|
||||||
if mode == "validate" {
|
|
||||||
println!(
|
|
||||||
"{}",
|
|
||||||
serde_json::to_string_pretty(&plan)
|
|
||||||
.map_err(|error| format!("cannot serialize plan: {error}"))?
|
|
||||||
);
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
let receipt = execute_plan(&plan, &LinuxCommandExecutor).map_err(|error| error.to_string())?;
|
|
||||||
println!(
|
|
||||||
"{}",
|
|
||||||
serde_json::to_string_pretty(&receipt)
|
|
||||||
.map_err(|error| format!("cannot serialize receipt: {error}"))?
|
|
||||||
);
|
|
||||||
if receipt.final_state == "PASS_100" {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err("target-side verification failed".to_owned())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() -> ExitCode {
|
|
||||||
match run() {
|
|
||||||
Ok(()) => ExitCode::SUCCESS,
|
|
||||||
Err(error) => {
|
|
||||||
eprintln!("{error}");
|
|
||||||
ExitCode::FAILURE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,198 +0,0 @@
|
||||||
use std::{cell::RefCell, collections::VecDeque};
|
|
||||||
|
|
||||||
use guanghu_execution_bridge::{
|
|
||||||
compile_request, execute_plan, ActionKind, AuthorizationReference, CommandExecutor,
|
|
||||||
CommandResult, ExecutionAction, ExecutionPolicy, ExecutionRequest, RollbackReference,
|
|
||||||
POLICY_SCHEMA, REQUEST_SCHEMA, REQUIRED_PROTOCOL_CHAIN,
|
|
||||||
};
|
|
||||||
|
|
||||||
fn request(kind: ActionKind) -> ExecutionRequest {
|
|
||||||
ExecutionRequest {
|
|
||||||
schema: REQUEST_SCHEMA.to_owned(),
|
|
||||||
request_id: "REQ-001".to_owned(),
|
|
||||||
subject_id: "ICE-P-ZY001".to_owned(),
|
|
||||||
target_node_id: "JD-FD-PRIMARY".to_owned(),
|
|
||||||
protocol_chain: REQUIRED_PROTOCOL_CHAIN
|
|
||||||
.iter()
|
|
||||||
.map(|protocol| (*protocol).to_owned())
|
|
||||||
.collect(),
|
|
||||||
action: ExecutionAction {
|
|
||||||
kind,
|
|
||||||
resource: "guanghu-broadcast-tower.service".to_owned(),
|
|
||||||
},
|
|
||||||
authorization: None,
|
|
||||||
rollback: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn policy() -> ExecutionPolicy {
|
|
||||||
ExecutionPolicy {
|
|
||||||
schema: POLICY_SCHEMA.to_owned(),
|
|
||||||
policy_id: "JD-GH-EXEC-001".to_owned(),
|
|
||||||
target_node_id: "JD-FD-PRIMARY".to_owned(),
|
|
||||||
allowed_services: vec!["guanghu-broadcast-tower.service".to_owned()],
|
|
||||||
allow_status: true,
|
|
||||||
allow_restart: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn compiles_read_only_status_to_exact_systemd_arguments() {
|
|
||||||
let plan = compile_request(&request(ActionKind::ServiceStatus), &policy()).expect("compile");
|
|
||||||
|
|
||||||
assert_eq!(plan.program, "/usr/bin/systemctl");
|
|
||||||
assert_eq!(
|
|
||||||
plan.arguments,
|
|
||||||
["is-active", "guanghu-broadcast-tower.service"]
|
|
||||||
);
|
|
||||||
assert!(!plan.mutating);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn rejects_missing_protocol_before_execution() {
|
|
||||||
let mut request = request(ActionKind::ServiceStatus);
|
|
||||||
request
|
|
||||||
.protocol_chain
|
|
||||||
.retain(|protocol| protocol != "GLS-0302");
|
|
||||||
|
|
||||||
let error = compile_request(&request, &policy()).expect_err("missing identity must fail");
|
|
||||||
assert!(error.to_string().contains("GLS-0302"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn rejects_non_allowlisted_service() {
|
|
||||||
let mut request = request(ActionKind::ServiceStatus);
|
|
||||||
request.action.resource = "ssh.service".to_owned();
|
|
||||||
|
|
||||||
let error = compile_request(&request, &policy()).expect_err("service must fail closed");
|
|
||||||
assert!(error.to_string().contains("not allowlisted"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn restart_requires_matching_authorization_and_rollback() {
|
|
||||||
let mut request = request(ActionKind::ServiceRestart);
|
|
||||||
let error = compile_request(&request, &policy()).expect_err("authorization is required");
|
|
||||||
assert!(error.to_string().contains("authorization"));
|
|
||||||
|
|
||||||
request.authorization = Some(AuthorizationReference {
|
|
||||||
authorization_id: "AUTH-001".to_owned(),
|
|
||||||
allowed_action: ActionKind::ServiceRestart,
|
|
||||||
target_node_id: "JD-FD-PRIMARY".to_owned(),
|
|
||||||
});
|
|
||||||
let error = compile_request(&request, &policy()).expect_err("rollback is required");
|
|
||||||
assert!(error.to_string().contains("rollback"));
|
|
||||||
|
|
||||||
request.rollback = Some(RollbackReference {
|
|
||||||
checkpoint_id: "CHECKPOINT-001".to_owned(),
|
|
||||||
recovery_action: "restore previous immutable release".to_owned(),
|
|
||||||
});
|
|
||||||
let plan = compile_request(&request, &policy()).expect("complete mutation compiles");
|
|
||||||
assert!(plan.mutating);
|
|
||||||
assert_eq!(plan.authorization_id.as_deref(), Some("AUTH-001"));
|
|
||||||
assert_eq!(
|
|
||||||
plan.rollback_checkpoint_id.as_deref(),
|
|
||||||
Some("CHECKPOINT-001")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct FakeExecutor {
|
|
||||||
results: RefCell<VecDeque<CommandResult>>,
|
|
||||||
calls: RefCell<Vec<Vec<String>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FakeExecutor {
|
|
||||||
fn new(results: Vec<CommandResult>) -> Self {
|
|
||||||
Self {
|
|
||||||
results: RefCell::new(results.into()),
|
|
||||||
calls: RefCell::new(Vec::new()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CommandExecutor for FakeExecutor {
|
|
||||||
fn execute(&self, program: &str, arguments: &[String]) -> Result<CommandResult, String> {
|
|
||||||
let mut call = vec![program.to_owned()];
|
|
||||||
call.extend(arguments.iter().cloned());
|
|
||||||
self.calls.borrow_mut().push(call);
|
|
||||||
self.results
|
|
||||||
.borrow_mut()
|
|
||||||
.pop_front()
|
|
||||||
.ok_or_else(|| "unexpected execution".to_owned())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn restart_receipt_requires_target_side_active_readback() {
|
|
||||||
let mut request = request(ActionKind::ServiceRestart);
|
|
||||||
request.authorization = Some(AuthorizationReference {
|
|
||||||
authorization_id: "AUTH-001".to_owned(),
|
|
||||||
allowed_action: ActionKind::ServiceRestart,
|
|
||||||
target_node_id: "JD-FD-PRIMARY".to_owned(),
|
|
||||||
});
|
|
||||||
request.rollback = Some(RollbackReference {
|
|
||||||
checkpoint_id: "CHECKPOINT-001".to_owned(),
|
|
||||||
recovery_action: "restore previous immutable release".to_owned(),
|
|
||||||
});
|
|
||||||
let plan = compile_request(&request, &policy()).expect("compile restart");
|
|
||||||
let executor = FakeExecutor::new(vec![
|
|
||||||
CommandResult {
|
|
||||||
exit_code: Some(0),
|
|
||||||
stdout: String::new(),
|
|
||||||
stderr: String::new(),
|
|
||||||
},
|
|
||||||
CommandResult {
|
|
||||||
exit_code: Some(0),
|
|
||||||
stdout: "active".to_owned(),
|
|
||||||
stderr: String::new(),
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
let receipt = execute_plan(&plan, &executor).expect("execute plan");
|
|
||||||
assert_eq!(receipt.final_state, "PASS_100");
|
|
||||||
assert!(receipt.target_state_verified);
|
|
||||||
assert_eq!(executor.calls.borrow().len(), 2);
|
|
||||||
assert_eq!(executor.calls.borrow()[1][1], "is-active");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn successful_restart_command_without_active_readback_is_fail_zero() {
|
|
||||||
let mut request = request(ActionKind::ServiceRestart);
|
|
||||||
request.authorization = Some(AuthorizationReference {
|
|
||||||
authorization_id: "AUTH-001".to_owned(),
|
|
||||||
allowed_action: ActionKind::ServiceRestart,
|
|
||||||
target_node_id: "JD-FD-PRIMARY".to_owned(),
|
|
||||||
});
|
|
||||||
request.rollback = Some(RollbackReference {
|
|
||||||
checkpoint_id: "CHECKPOINT-001".to_owned(),
|
|
||||||
recovery_action: "restore previous immutable release".to_owned(),
|
|
||||||
});
|
|
||||||
let plan = compile_request(&request, &policy()).expect("compile restart");
|
|
||||||
let executor = FakeExecutor::new(vec![
|
|
||||||
CommandResult {
|
|
||||||
exit_code: Some(0),
|
|
||||||
stdout: String::new(),
|
|
||||||
stderr: String::new(),
|
|
||||||
},
|
|
||||||
CommandResult {
|
|
||||||
exit_code: Some(3),
|
|
||||||
stdout: "inactive".to_owned(),
|
|
||||||
stderr: String::new(),
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
let receipt = execute_plan(&plan, &executor).expect("execute plan");
|
|
||||||
assert_eq!(receipt.final_state, "FAIL_0");
|
|
||||||
assert!(!receipt.target_state_verified);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn rejects_a_deserialized_plan_that_changes_status_into_restart() {
|
|
||||||
let mut plan =
|
|
||||||
compile_request(&request(ActionKind::ServiceStatus), &policy()).expect("compile status");
|
|
||||||
plan.arguments[0] = "restart".to_owned();
|
|
||||||
let executor = FakeExecutor::new(Vec::new());
|
|
||||||
|
|
||||||
let error = execute_plan(&plan, &executor).expect_err("tampered plan must fail");
|
|
||||||
assert!(error.to_string().contains("not an allowlisted"));
|
|
||||||
assert!(executor.calls.borrow().is_empty());
|
|
||||||
}
|
|
||||||
|
|
@ -1,54 +0,0 @@
|
||||||
{
|
|
||||||
"schema": "guanghu.cognitive-execution-profile/v1",
|
|
||||||
"profile_id": "GH-COGNITIVE-EXECUTION-001",
|
|
||||||
"language_authority": {
|
|
||||||
"repository": "REPO-012",
|
|
||||||
"anchor": "GLW-PUBLIC-NAV-ANCHOR-001",
|
|
||||||
"protocol_registry": "gls/GLS-PROTOCOL-REGISTRY.json"
|
|
||||||
},
|
|
||||||
"implementation_authority": {
|
|
||||||
"repository": "REPO-014",
|
|
||||||
"path": "product-source/hololake-platform/guanghu-os",
|
|
||||||
"bridge": "crates/execution-bridge"
|
|
||||||
},
|
|
||||||
"required_protocol_chain": [
|
|
||||||
"GLS-0301",
|
|
||||||
"GLS-0302",
|
|
||||||
"GLS-0303",
|
|
||||||
"GLS-0306",
|
|
||||||
"GLS-0309",
|
|
||||||
"GLS-0311",
|
|
||||||
"GLS-0130",
|
|
||||||
"GLS-0131",
|
|
||||||
"GLS-0709",
|
|
||||||
"GLS-0710"
|
|
||||||
],
|
|
||||||
"execution_model": {
|
|
||||||
"cognition": "MODEL_PROPOSES_TYPED_INTENT",
|
|
||||||
"authority": "PROTOCOL_GATE_VALIDATES_CURRENT_SCOPE",
|
|
||||||
"compilation": "GLC_GIR_DETERMINISTIC_PLAN",
|
|
||||||
"adapter": "ALLOWLISTED_NO_SHELL",
|
|
||||||
"substrate": "MINIMAL_LINUX_COOPERATIVE_EXECUTION",
|
|
||||||
"success": "TARGET_SIDE_READBACK_AND_GLP_RECEIPT",
|
|
||||||
"rescue": "SEPARATE_UBUNTU_MAINTENANCE_BOOT"
|
|
||||||
},
|
|
||||||
"first_adapter": {
|
|
||||||
"id": "LINUX_SYSTEMD_V1",
|
|
||||||
"actions": [
|
|
||||||
"service_status",
|
|
||||||
"service_restart"
|
|
||||||
],
|
|
||||||
"mutations_require": [
|
|
||||||
"exact_target",
|
|
||||||
"matching_authorization",
|
|
||||||
"rollback_checkpoint",
|
|
||||||
"target_side_active_readback"
|
|
||||||
],
|
|
||||||
"arbitrary_shell": false
|
|
||||||
},
|
|
||||||
"bare_metal_lane": {
|
|
||||||
"id": "GOSK_GHAL_BARE_METAL_RESEARCH",
|
|
||||||
"production_blocking": false,
|
|
||||||
"preserve_existing_candidate": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -4,13 +4,6 @@ set -Eeuo pipefail
|
||||||
repository_root=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
repository_root=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
||||||
receipt_path=${1:-}
|
receipt_path=${1:-}
|
||||||
|
|
||||||
# Git exports repository-local environment variables while invoking hooks.
|
|
||||||
# This repository's hook lives below the actual worktree root, so inherited
|
|
||||||
# GIT_DIR/GIT_WORK_TREE values can make `git -C` inspect the wrong path and
|
|
||||||
# falsely report a dirty source tree. Resolve the source root first, then make
|
|
||||||
# every gate discover that worktree from its explicit `-C` argument.
|
|
||||||
unset GIT_DIR GIT_WORK_TREE GIT_INDEX_FILE GIT_PREFIX
|
|
||||||
|
|
||||||
if [[ -z "${receipt_path}" ]]; then
|
if [[ -z "${receipt_path}" ]]; then
|
||||||
echo "usage: run-hololake-native-quality-gate.sh <receipt-output-outside-repository>" >&2
|
echo "usage: run-hololake-native-quality-gate.sh <receipt-output-outside-repository>" >&2
|
||||||
exit 2
|
exit 2
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue