From 08f2e75cbde9dc775a61012a02f7954ea3d1540a Mon Sep 17 00:00:00 2001 From: "benjamin.747" Date: Sat, 25 Jul 2026 09:48:40 +0800 Subject: [PATCH] fix: persist path-attach .gitkeep blobs and stop panicking on missing objects GitHub sync / import attach created trees that referenced a placeholder .gitkeep but never wrote it to object storage, so clone/fetch hit S3 404 and upload-pack unwrap turned that into HTTP 502. Save the blob on attach and map pack failures to protocol errors instead. --- Cargo.lock | 2 +- .../src/application/api_service/mono/sync.rs | 10 +- ceres/src/application/api_service/tree_ops.rs | 11 +- .../code_edit/post_receive/import.rs | 9 +- ceres/src/transport/protocol/smart.rs | 77 ++--- common/src/errors.rs | 36 +++ orion/Cargo.toml | 2 +- orion/docs/TARGET_DISCOVERY_SCOPE.md | 269 +++++++----------- 8 files changed, 205 insertions(+), 211 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 26df3c3b3..724afe16e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5768,7 +5768,7 @@ dependencies = [ [[package]] name = "orion" -version = "0.1.2" +version = "0.1.3" dependencies = [ "anyhow", "api-model", diff --git a/ceres/src/application/api_service/mono/sync.rs b/ceres/src/application/api_service/mono/sync.rs index 50a794b53..5150ee68d 100644 --- a/ceres/src/application/api_service/mono/sync.rs +++ b/ceres/src/application/api_service/mono/sync.rs @@ -46,7 +46,8 @@ impl MonoApiService { let expected_tree = root_ref.ref_tree_hash.clone(); let root_ref_id = root_ref.id; - let save_trees = tree_ops::search_and_create_tree(self, &path_buf).await?; + let (save_trees, gitkeep_blob) = + tree_ops::search_and_create_tree(self, &path_buf).await?; let leaf_tree = save_trees .back() .ok_or_else(|| MegaError::Other("no tree generated".to_string()))?; @@ -60,6 +61,13 @@ impl MonoApiService { &commit_msg, ); + // Persist .gitkeep before attaching trees. Previously only trees/commits + // were saved, leaving object storage without the blob and breaking clone. + self.storage() + .mono_service + .save_blobs(&new_commit.id.to_string(), vec![gitkeep_blob]) + .await?; + let txn = self.storage().begin_db_transaction().await?; match storage .attach_to_monorepo_parent_in_txn( diff --git a/ceres/src/application/api_service/tree_ops.rs b/ceres/src/application/api_service/tree_ops.rs index c12c65ea7..ae996374a 100644 --- a/ceres/src/application/api_service/tree_ops.rs +++ b/ceres/src/application/api_service/tree_ops.rs @@ -9,6 +9,7 @@ use git_internal::{ errors::GitError, internal::object::{ ObjectTrait, + blob::Blob, tree::{Tree, TreeItem, TreeItemMode}, }, }; @@ -112,11 +113,15 @@ pub async fn search_tree_by_path( /// /// # Errors /// -/// Returns a `GitError` if an error occurs during the search or tree creation process. +/// Returns a `MegaError` if an error occurs during the search or tree creation process. +/// +/// The returned [`Blob`] is the placeholder `.gitkeep` referenced by the new leaf tree. +/// Callers **must** persist it via object storage (`save_blobs` / `put_objects`) before +/// or with the trees; otherwise clone/fetch will 404 on that object. pub async fn search_and_create_tree( handler: &T, path: &Path, -) -> Result, MegaError> { +) -> Result<(VecDeque, Blob), MegaError> { let relative_path = handler.strip_relative(path)?; let root_tree = handler.get_root_tree(None).await?; let mut search_tree = root_tree.clone(); @@ -190,7 +195,7 @@ pub async fn search_and_create_tree( } } - Ok(saving_trees) + Ok((saving_trees, blob)) } /// return the dir's hash only diff --git a/ceres/src/application/code_edit/post_receive/import.rs b/ceres/src/application/code_edit/post_receive/import.rs index aa21b9ef6..6795af182 100644 --- a/ceres/src/application/code_edit/post_receive/import.rs +++ b/ceres/src/application/code_edit/post_receive/import.rs @@ -63,7 +63,8 @@ pub async fn dispatch_import_receive_pack_finalized( let expected_tree = root_ref.ref_tree_hash.clone(); let root_ref_id = root_ref.id; - let save_trees = tree_ops::search_and_create_tree(mono_api_service, &repo_path).await?; + let (save_trees, gitkeep_blob) = + tree_ops::search_and_create_tree(mono_api_service, &repo_path).await?; let new_commit = Commit::from_tree_id( save_trees @@ -74,6 +75,12 @@ pub async fn dispatch_import_receive_pack_finalized( &format!("\n{commit_msg}"), ); + // Persist placeholder .gitkeep referenced by newly created path trees. + storage + .mono_service + .save_blobs(&new_commit.id.to_string(), vec![gitkeep_blob]) + .await?; + let txn = storage.begin_db_transaction().await?; let git_db = storage.git_db_storage(); for cmd in &commands { diff --git a/ceres/src/transport/protocol/smart.rs b/ceres/src/transport/protocol/smart.rs index 268a92131..71f199cad 100644 --- a/ceres/src/transport/protocol/smart.rs +++ b/ceres/src/transport/protocol/smart.rs @@ -6,7 +6,7 @@ use std::{ use anyhow::Result; use bytes::{Buf, BufMut, Bytes, BytesMut}; use callisto::sea_orm_active_enums::RefTypeEnum; -use common::errors::{ProtocolError, mega_to_protocol_error}; +use common::errors::{ProtocolError, git_to_protocol_error, mega_to_protocol_error}; use tokio_stream::wrappers::ReceiverStream; use crate::{ @@ -153,48 +153,53 @@ impl SmartSession { let have: Vec = have.into_iter().collect(); if have.is_empty() { - pack_data = repo_handler.full_pack(want).await.unwrap(); + pack_data = repo_handler.full_pack(want).await.map_err(|e| { + tracing::error!(error = %e, "git upload-pack full_pack failed"); + git_to_protocol_error(e) + })?; add_pkt_line_string(&mut protocol_buf, String::from("NAK\n")); - } else { - if self.capabilities.contains(&Capability::MultiAckDetailed) { - // multi_ack_detailed mode, the server will differentiate the ACKs where it is signaling that - // it is ready to send data with ACK obj-id ready lines, - // and signals the identified common commits with ACK obj-id common lines - - for hash in &have { - if repo_handler.check_commit_exist(hash).await { - add_pkt_line_string(&mut protocol_buf, format!("ACK {hash} common\n")); - if last_common_commit.is_empty() { - last_common_commit = hash.to_string(); - } + } else if self.capabilities.contains(&Capability::MultiAckDetailed) { + // multi_ack_detailed mode, the server will differentiate the ACKs where it is signaling that + // it is ready to send data with ACK obj-id ready lines, + // and signals the identified common commits with ACK obj-id common lines + + for hash in &have { + if repo_handler.check_commit_exist(hash).await { + add_pkt_line_string(&mut protocol_buf, format!("ACK {hash} common\n")); + if last_common_commit.is_empty() { + last_common_commit = hash.to_string(); } } - pack_data = repo_handler - .incremental_pack(want.clone(), have) - .await - .unwrap(); - - if last_common_commit.is_empty() { - //send NAK if missing common commit - add_pkt_line_string(&mut protocol_buf, String::from("NAK\n")); - // need to handle rebase option, still need pack data when has no common commit - return Ok((pack_data, protocol_buf)); - } + } + pack_data = repo_handler + .incremental_pack(want.clone(), have) + .await + .map_err(|e| { + tracing::error!(error = %e, "git upload-pack incremental_pack failed"); + git_to_protocol_error(e) + })?; + + if last_common_commit.is_empty() { + //send NAK if missing common commit + add_pkt_line_string(&mut protocol_buf, String::from("NAK\n")); + // need to handle rebase option, still need pack data when has no common commit + return Ok((pack_data, protocol_buf)); + } - for hash in want { - if self.capabilities.contains(&Capability::NoDone) { - // If multi_ack_detailed and no-done are both present, then the sender is free to immediately send a pack - // following its first "ACK obj-id ready" message. - add_pkt_line_string(&mut protocol_buf, format!("ACK {hash} ready\n")); - } + for hash in want { + if self.capabilities.contains(&Capability::NoDone) { + // If multi_ack_detailed and no-done are both present, then the sender is free to immediately send a pack + // following its first "ACK obj-id ready" message. + add_pkt_line_string(&mut protocol_buf, format!("ACK {hash} ready\n")); } - } else { - tracing::error!("capability unsupported"); - // init a empty receiverstream - let (_, rx) = tokio::sync::mpsc::channel::>(1); - pack_data = ReceiverStream::new(rx); } add_pkt_line_string(&mut protocol_buf, format!("ACK {last_common_commit} \n")); + } else { + tracing::error!("capability unsupported"); + // init a empty receiverstream + let (_, rx) = tokio::sync::mpsc::channel::>(1); + pack_data = ReceiverStream::new(rx); + add_pkt_line_string(&mut protocol_buf, format!("ACK {last_common_commit} \n")); } Ok((pack_data, protocol_buf)) } diff --git a/common/src/errors.rs b/common/src/errors.rs index c69797752..3059be656 100644 --- a/common/src/errors.rs +++ b/common/src/errors.rs @@ -352,16 +352,36 @@ pub fn protocol_error_is_client_safe(err: &ProtocolError) -> bool { pub fn mega_to_protocol_error(err: MegaError) -> ProtocolError { match err { MegaError::NotFound(msg) => ProtocolError::NotFound(msg), + MegaError::ObjStorageNotFound(msg) => { + // Missing blob/object content: return a typed client error instead of + // panicking in upload-pack (which surfaces to git as HTTP 502). + ProtocolError::NotFound(format!("git object missing from object storage: {msg}")) + } + MegaError::ObjStorageInconsistent(msg) => { + ProtocolError::InvalidInput(format!("git object storage inconsistent: {msg}")) + } MegaError::BadRequest(msg) => ProtocolError::InvalidInput(msg), MegaError::Unauthorized(msg) => ProtocolError::Deny(msg), MegaError::Forbidden(msg) => ProtocolError::InvalidInput(msg), MegaError::Unavailable(msg) => ProtocolError::InvalidInput(msg), MegaError::Conflict(msg) => ProtocolError::InvalidInput(msg), + MegaError::Git(e) => git_to_protocol_error(e), MegaError::Io(e) => ProtocolError::IO(e), other => ProtocolError::InvalidInput(other.to_string()), } } +/// Map [`GitError`] into a protocol-layer error for upload/receive-pack handlers. +pub fn git_to_protocol_error(err: GitError) -> ProtocolError { + let msg = err.to_string(); + match git_error_http_status(&err) { + 404 => ProtocolError::NotFound(msg), + 401 => ProtocolError::Deny(msg), + 400 | 403 | 409 | 413 => ProtocolError::InvalidInput(msg), + _ => ProtocolError::IO(std::io::Error::other(msg)), + } +} + #[cfg(test)] mod tests { use super::*; @@ -414,4 +434,20 @@ mod tests { let err = mega_to_protocol_error(MegaError::NotFound("repo".into())); assert!(matches!(err, ProtocolError::NotFound(_))); } + + #[test] + fn mega_to_protocol_error_maps_objstorage_not_found() { + let err = mega_to_protocol_error(MegaError::ObjStorageNotFound("missing key".into())); + assert!(matches!(err, ProtocolError::NotFound(_))); + assert_eq!(protocol_error_http_status(&err), 404); + } + + #[test] + fn git_to_protocol_error_maps_objstorage_custom_error() { + let err = git_to_protocol_error(GitError::CustomError( + "ObjStorage not found: Object at location git/ab/cd/ef/123 not found".into(), + )); + assert!(matches!(err, ProtocolError::NotFound(_))); + assert_eq!(protocol_error_http_status(&err), 404); + } } diff --git a/orion/Cargo.toml b/orion/Cargo.toml index aa080d65f..56d971cd8 100644 --- a/orion/Cargo.toml +++ b/orion/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "orion" -version = "0.1.2" +version = "0.1.3" edition = "2024" [[bin]] diff --git a/orion/docs/TARGET_DISCOVERY_SCOPE.md b/orion/docs/TARGET_DISCOVERY_SCOPE.md index 162d2a174..5d2298148 100644 --- a/orion/docs/TARGET_DISCOVERY_SCOPE.md +++ b/orion/docs/TARGET_DISCOVERY_SCOPE.md @@ -1,211 +1,147 @@ -# Target Discovery 扫描范围过大问题分析 +# Target Discovery / CL Overlay 问题归档 -本文档记录 Orion worker 在做 Buck2 增量构建时,**target discovery(目标发现)与影响传播范围过大**的问题:一个纯 Rust 改动(如 `rk8s`)曾拉起无关 JVM/Android toolchain,或扫入整个 `third-party/**`,导致构建失败或耗时过长。 +本文分两块,互不混写: -涉及代码主要在 `orion` crate(worker 端)。本文作为问题归档、修改方案与**落地进展**说明。 +1. **Part I — 发现范围(方案 A–E)**:incremental discovery 扫太大 / 拉无关 toolchain / all-added 爆炸(历史,已基本落地)。 +2. **Part II — Antares CL overlay(方案 G)**:`repo=/` 时 overlay 路径错位 → 0 target(2026-07,已落地 0.1.3)。 -> 重要澄清:`project/buck2_test/toolchains:jdk_system_image`、`__android_sdk_tools__` 等 **不代表 `buck2_test` 业务代码依赖 JVM/Android**。它们只是 JVM/Android toolchain / platform helper target 的**定义位置**恰好在 `project/buck2_test/toolchains` 这个包下。这些 helper 被拉进来,通常是因为默认 platform 为 `prelude//platforms:default`,且影响传播曾不过滤 toolchain/platform 节点——而不是 `rk8s` 真的需要 Java/Android。 +涉及代码均在 `orion` crate(worker)。开放项见文末。 --- -## 进展摘要(截至 2026-06-23) - -| 方案 | 内容 | 状态 | 说明 | -|------|------|------|------| -| **B** | 过滤 toolchain/platform helper 的传播与 build 选集 | **已落地** | `buck_controller.rs`:`is_toolchain_or_platform_*` | -| **C1** | Orion 不再 CLI 强制 `--target-platforms` | **已落地** | platform 由 `.buckconfig` + Orion `--config` 决定 | -| **C2** | 补全 platform 映射 + Orion `--config` 兜底 | **已落地** | `orion/buck/platform.rs`;挂载只读时覆盖不完整 `.buckconfig` | -| **A** | 按改动路径收窄 `buck2 targets` 查询范围 | **已落地** | `orion/buck/discovery_scope.rs`;`ORION_DISCOVERY_SCOPE=0` 可关 | -| **D** | A + rdeps 补齐反向依赖 | **部分落地** | narrow scope 用 `uquery rdeps`;all-added 子项目用**图 rdeps** | -| **子项目根** | 检测 `rk8s/.buckconfig` 等,从子目录跑 buck2 | **已落地** | `detect_subproject_buck_root()` | -| **All-added 子项目** | 全新增 CL 只 build `project/` crate | **已落地** | owner 播种 + `normalize_owner_targets_to_rust` | -| **E** | 收敛 all-added 空 base 的 SelectAll | **已落地** | 任意 all-added CL 跳过图 `SelectAll`,改 `owner()` 播种 | - -### CL `UYXIYYNJ`(`rk8s/**` 全量导入)构建对照 - -| Build | Task ID(前缀) | Discovery | Build 规模 | 结果 | -|-------|----------------|-----------|------------|------| -| #35–#36 | — | 0 target | 跳过 build | 假成功(`finish_without_build` exit 0) | -| #37 | — | 重试 | — | 从 monorepo 根跑 buck2,`set_cfg_constructor` 失败 | -| #39 | `019ef246` | `SelectAll` 全图 | ~39k action | 失败(范围过大) | -| #40 | `019ef252` | `ScopedNew` | ~18k action,真 `rustc` | 失败(FUSE ENOENT) | -| #42 | `019ef325` | owner → **17× `:vendor`** | ~113 action,无 `rustc` | exit 0,**浅层成功** | -| **#45** | `019ef333` | **28× `rust_*`**(owner 归一化 + 图 rdeps) | **23,270 action**;**8,035** local commands;**0%** cache | **exit 0,真编译通过** | - -#### Build #45 时间线(`019ef333-db97-70e2-8960-40bcc5cc2496`) - -| 阶段 | 时间 (UTC) | 耗时 | -|------|------------|------| -| 收到任务 | 06:38:42 | — | -| CL overlay 就绪 | 06:39:33 | ~51s | -| Discovery 完成(28 targets,子项目 `rk8s/`) | 06:39:56 | ~23s | -| `buck2 build` 开始 | 06:39:56 | — | -| `BUILD SUCCEEDED` | 07:51:54 | **~72 min** | -| 上报 exit 0 | 07:52:00 | 总计 **~73 min** | - -日志要点:从 `mount/.../rk8s` 跑 buck2;discovery 后卸载 `old` mount;末尾为 `root//project/rkl:rkl`、`rkforge`、`slayerfs` 等 **`rustc link`**;**无** `uquery rdeps` / `cxx_no_default_deps` 报错(all-added 走图 rdeps)。 +# Part I — 发现范围过大(方案 A–E) ---- - -## 1. 问题现象(历史) - -CL(例如 `#UYXIYYNJ`)只新增 `rk8s` 下 Rust 代码与 `third-party/**`,早期构建曾出现: - -``` -Action failed: root//project/buck2_test/toolchains:jdk_system_image (create_jdk_system_image) - FileNotFoundError: ... jlink -``` - -以及 `__android_sdk_tools__` 缺失、`` platform 等。 - -几个关键事实: - -- monorepo 根 `.buckconfig` 下,`rk8s` 与 `project/buck2_test` 同在 **`root` cell**。 -- 仅「按 cell 收窄」不够:扫 `root//...` 仍会带入无关 `project/**` 与 `third-party/**`。 -- `rk8s` 作为**子项目**有自己的 `.buckconfig`(`root = .`),必须从 `mount/.../rk8s` 跑 buck2,不能从 monorepo 根跑。 - ---- +## 问题是什么 -## 2. 根因分析 +Buck2 增量构建时,纯 Rust 改动(如 `rk8s`)曾拉起无关 JVM/Android toolchain,或扫入整个 `third-party/**`,导致失败或耗时过长。 -worker 端流程:`get_build_targets()` → `collect_impacted_targets()` / owner 回退 → `buck2 build`(见 [buck_controller.rs](../src/buck_controller.rs))。 +> 澄清:`project/buck2_test/toolchains:jdk_*` 等只是 helper 的**定义位置**;被拉入是因为默认 platform + 未过滤 toolchain 传播,不是业务依赖 Java/Android。 -### 2.1 发现阶段曾扫全 cell(已由方案 A 缓解) +## 进展(已落地) -`get_repo_targets()` 默认通过 `get_all_cell_patterns()` 查询 `root//...`、`toolchains//...` 等。方案 A 根据改动路径收窄为例如 `root//rk8s/...`(见 `compute_discovery_scope()`)。 - -### 2.2 默认 platform + toolchain 传播(已由 B + C 缓解) - -1. 默认 platform `prelude//platforms:default` 会解析多语言 toolchain。 -2. 历史上 `recursive_target_changes(..., |_| true)` 不过滤 toolchain/platform。 -3. **B**:传播与 build 选集跳过 toolchain/platform helper。 -4. **C2**:Orion 对每次 `buck2 targets` / `buck2 build` 注入 `default_target_platforms` 与完整 `target_platform_detector_spec`(含 `buckal//...`),避免 FUSE 只读挂载上旧 `.buckconfig` 导致 ``。 - -### 2.3 All-added + 空 base 曾 SelectAll(#39) - -`base` 图为空时,`EmptyBasePolicy::SelectAll` 会把 diff 图里**所有** target 标为 impacted,包括 `third-party/**` 下每个 crate 的 vendor 根,action 爆炸。 - -**任意 all-added CL**(含子项目导入与普通「只新增文件」CL)现统一: - -1. **跳过** 图 `collect_impacted_targets` + `SelectAll`(避免空 base 全选)。 -2. **`owner()` 播种**:改动中的源文件路径(排除 `BUCK`、`Cargo.toml`、`vendor/` 等)。 - -**All-added 子项目**(改动全在带 `.buckconfig` 的子目录,如 `rk8s/**`)在此基础上还有: - -1. **发现范围**:仅 `root//project/...`(不含 `third-party/`)。 -2. **`normalize_owner_targets_to_rust`**:buckal 的 `filegroup :vendor` 拥有包内几乎所有文件,`owner()` 常返回 `:vendor`;映射为同 package 的 `rust_library` / `rust_binary` / `rust_test`。 -3. **图 rdeps**:在 `project/` 内用 `diff::recursive_target_changes` 扩展反向依赖,**不用** `buck2 uquery rdeps`(遍历 `aardvark-dns` 等会碰到缺失的 `toolchains//:cxx_no_default_deps`)。 - -### 2.4 owner 误选 `:vendor`(#42) +| 方案 | 内容 | 状态 | +|------|------|------| +| **A** | 按改动路径收窄 `buck2 targets`(`discovery_scope.rs`;`ORION_DISCOVERY_SCOPE=0` 可关) | 已落地 | +| **B** | 过滤 toolchain/platform helper 的传播与 build 选集 | 已落地 | +| **C1/C2** | 去掉 CLI `--target-platforms`;`platform.rs` 注入 `--config` | 已落地 | +| **D** | narrow:`uquery rdeps`(失败则图 rdeps);all-added 子项目:仅图 rdeps | 部分落地 | +| **子项目根** | `detect_subproject_buck_root()`,从 `rk8s/` 等跑 buck2 | 已落地 | +| **E / All-added** | 跳过空 base `SelectAll`;`owner()` + `normalize_owner_targets_to_rust` + 图 rdeps | 已落地 | -#42 在 ~30s 内 exit 0,但 buck2 只执行了 17 个 `symlinked_dir vendor`,**无 `rustc`**。根因是 build 列表为 `root//project/*:vendor` 而非 `root//project/*:`。`normalize_owner_targets_to_rust` 已修复。 +**验证(CL `UYXIYYNJ` / rk8s 全量导入)**:Build **#45**(`019ef333…`)discovery 28× `rust_*`,约 23k action / ~73 min,exit 0 真编译。中间失败(SelectAll 爆炸、`:vendor` 浅成功、FUSE ENOENT 等)从略。 -### 2.5 0 target 假成功(未修复) +## 根因与对策(精简) -`finish_without_build_if_no_targets()` 在 0 target 时仍返回 exit 0(#35–#36)。与扫描范围无关,但会掩盖发现失败。 +worker:`get_build_targets()` → 图 diff / `owner()` → `buck2 build`。 ---- +| 问题 | 表现 | 对策 | +|------|------|------| +| 扫全 cell | `root//...` 带入无关树 | **A** | +| toolchain 传播 | 默认 platform 拉 JVM/Android helper | **B** + **C2** | +| all-added 空 base | `SelectAll` → 数万 action | **E**:`owner()` + 限 `project/` + rust 归一化 + 图 rdeps | +| owner → `:vendor` | 无 `rustc` | `normalize_owner_targets_to_rust` | +| 子项目 `.buckconfig` | 从 monorepo 根跑 buck2 失败 | `detect_subproject_buck_root()` | -## 3. 当前 discovery 流程(简图) +### Discovery 流程 ```mermaid flowchart TD - CL["CL 改动列表"] --> Sub{"全在子目录且含\n.buckconfig?"} - Sub -->|是| Strip["strip 前缀\n从子项目根跑 buck2"] - Sub -->|否| Root["monorepo 根"] - Strip --> Scope - Root --> Scope - - Scope["compute_discovery_scope (A)\nORION_DISCOVERY_SCOPE"] - Scope --> AllAdded{"all-added?"} - - AllAdded -->|是| AllAddedKind{"子项目\n.buckconfig?"} - AllAdded -->|否| Patterns["收窄或全 cell 模式"] + CL["CL changes"] --> Sub{"子目录 .buckconfig?"} + Sub -->|是| Strip["strip + 子项目根"] + Sub -->|否| Root["monorepo 根"] + Strip --> Scope + Root --> Scope + Scope["A: discovery_scope"] --> AllAdded{"all-added?"} + AllAdded -->|是| Owner["owner + normalize rust_*"] + AllAdded -->|否| Graph["collect_impacted_targets B"] + Owner --> RdepsG["图 rdeps"] + Graph --> RdepsU{"narrow?"} + RdepsU -->|是| Uquery["uquery rdeps / fallback"] + RdepsU -->|否| Build + RdepsG --> Build["buck2 build + C2"] + Uquery --> Build +``` - AllAddedKind -->|是| ProjOnly["buck2 targets\nroot//project/..."] - AllAddedKind -->|否| Patterns +上图假定挂载上的源码路径已正确;路径错位见 Part II。 - ProjOnly --> Owner["owner() 播种\n+ normalize → rust_*"] - Patterns --> GraphCheck{"all-added?"} - GraphCheck -->|是| Owner - GraphCheck -->|否| Graph["collect_impacted_targets (B)"] +## Part I 代码索引 - Owner --> RdepsG["图 rdeps (project/)"] - Graph --> RdepsU{"narrow?"} - RdepsU -->|是| RdepsTry["uquery rdeps\n失败则图 rdeps fallback"] - RdepsU -->|否| Build +| 文件 | 内容 | +|------|------| +| [buck_controller.rs](../src/buck_controller.rs) | discovery、B、all-added、owner 归一化、图 rdeps | +| [discovery_scope.rs](../buck/discovery_scope.rs) | 方案 A、子项目检测 | +| [platform.rs](../buck/platform.rs) | C2 `--config` | +| [diff.rs](../src/repo/diff.rs) | `EmptyBasePolicy`、图 rdeps | +| [run.rs](../buck/run.rs) | `uquery_rdeps`、`owners` | +| [FUSE_MOUNT_ISSUES.md](./FUSE_MOUNT_ISSUES.md) | FUSE / ENOENT(#45 相关) | - RdepsG --> Build["buck2 build\n+ platform --config (C2)"] - RdepsTry --> Build -``` +| 变量 | 默认 | 作用 | +|------|------|------| +| `ORION_DISCOVERY_SCOPE` | 开 | 关方案 A | +| `ORION_BUCK_REMOTE_CACHE` | 关 | `1` 允许读 remote cache | --- -## 4. 方案说明与状态 +# Part II — Antares CL overlay 路径错位(方案 G) -### 方案 A:按改动路径收窄发现(**已落地**) +与 Part I 无关:discovery 算法可正确,但 **CL 文件没铺到 monorepo 对应路径**,`owner()` / `buck2 targets` 仍看到空 package → 0 target。 -- 落点:`orion/buck/discovery_scope.rs` → `compute_discovery_scope()`。 -- 行为:改动在 `rk8s/**` 时查询 `root//rk8s/...`,而非整个 `root//...`。 -- 环境变量:`ORION_DISCOVERY_SCOPE=0|false|no|off` 关闭。 +## 现象 -### 方案 B:过滤 toolchain / platform helper(**已落地**) +CL [LADRHWDL](https://app.rk8s.xuanwu.openatom.cn/mega/cl/LADRHWDL)(CL 路径 `/project/dagrs-derive`,task `repo=/`): -见 `is_toolchain_or_platform_*` 与 `collect_impacted_targets()`;单元测试 `test_toolchain_helper_targets_are_excluded`。 +- Mega task `changes` 已是 `project/dagrs-derive/src/lib.rs` 等。 +- Orion:`owner() returned no build targets` → `0 targets` → 跳过 build、exit 0。 +- Overlay:`applied_files=["BUCK","src/lib.rs",…]`(**缺少** `project/dagrs-derive/`)。 -### 方案 C:收敛默认 platform(**C1 + C2 已落地**) +## 根因 -| 子项 | 内容 | 状态 | -|------|------|------| -| **C1** | 移除 Orion CLI `--target-platforms` | 已落地 | -| **C2** | `platform.rs` 注入 `default_target_platforms` + `target_platform_detector_spec` | 已落地 | - -仓库根 [.buckconfig](../../.buckconfig) 可与 Orion 注入不一致;**以 Orion `--config` 为准**保证 worker 行为一致。完整裁剪 `prelude//platforms:default`(按语言缩 toolchain)仍未做。 +```mermaid +flowchart LR + FilesList["files-list: src/lib.rs"] --> Resolve["resolve_overlay repo=/"] + Resolve --> Wrong["overlay 写到挂载根"] + Changes["task changes: project/dagrs-derive/..."] --> Owner["owner 查 package 路径"] + Wrong --> Miss["project/dagrs-derive 仅 .gitkeep"] + Miss --> Empty["0 targets"] + Owner --> Empty +``` -### 方案 D:A + rdeps(**部分落地**) +1. Mega `files-list` 路径相对 **CL 目录**。 +2. Task `changes` 相对 **Buck 根**(Mega 已 rebase)。 +3. 旧 Antares 在 `repo=/` 时原样用 CL 相对路径 → 文件在挂载根,package 无 BUCK。 +4. discovery 查 `project/dagrs-derive/...` → 无 owner / 无 `rust_*`。 -| 场景 | rdeps 实现 | -|------|------------| -| 普通 narrow scope | `buck2 uquery rdeps(seeds, universe)`;失败时 fallback 图 rdeps | -| all-added 子项目 | 仅用图 rdeps,universe = `root//project/...` | +VM:文件放到正确路径后,`owner(…/src/lib.rs)` → `:vendor`,`targets //project/dagrs-derive:` 含 `dagrs_derive`。 -### 子项目 buck 根(**已落地**) +## 修复(已落地,orion `0.1.3`) -`detect_subproject_buck_root()`:当所有改动路径在同一含 `.buckconfig` 的目录下(如 `rk8s/`),discovery 与 build 的 `current_dir` 设为 `mount/.../rk8s`,路径去掉 `rk8s/` 前缀。 +| 点 | 行为 | +|----|------| +| `rebase_cl_relative_path` | 与 Mega 同语义:CL 为 repo 子目录时拼前缀 | +| `infer_cl_path_from_changes` | 仅 `repo` 为空/`/`:changes 最长公共目录前缀 | +| `resolve_overlay_relative_path` | 先 rebase,再 repo-prefix / 安全检查 | +| 接线 | CL mount 传 `cl_path`;old mount 不传 | -### 环境变量 +落点:[antares.rs](../src/antares.rs)、[buck_controller.rs](../src/buck_controller.rs)(`mount_antares_fs`)。 -| 变量 | 默认 | 作用 | -|------|------|------| -| `ORION_DISCOVERY_SCOPE` | 开启 | `0`/`false`/`no`/`off` 关闭方案 A | -| `ORION_BUCK_REMOTE_CACHE` | 关闭 | `1` 时 buck2 build 允许读 remote cache;否则 `--no-remote-cache` | +部署后 Retry:`applied_files` 应含 `project/dagrs-derive/...`;discovery 非 0。 ---- +**不做**方案 F(heuristic vendor / Added-BUCK fallback):overlay 修对后 `owner(src/…)` 已够用。 -## 5. 现状小结 +## Part II 代码索引 -| 维度 | 当前行为 | 进展 | -|------|----------|------| -| 发现范围 | 方案 A 按改动子树;all-added 子项目限 `project/` | **已收窄** | -| 子项目 buck 根 | `rk8s/.buckconfig` 等 | **已落地** | -| toolchain 传播 / build 选集 | 过滤 helper | **B 已落地** | -| platform | Orion `--config` + `.buckconfig` | **C2 已落地** | -| all-added 子项目 | owner + rust 映射 + 图 rdeps | **已落地** | -| 0 target | 仍 exit 0 | **待修** | -| FUSE + 真 `rustc` | #45 单点通过(0× ENOENT) | 见 [FUSE_MOUNT_ISSUES.md](./FUSE_MOUNT_ISSUES.md) | +| 文件 | 内容 | +|------|------| +| [antares.rs](../src/antares.rs) | `rebase` / `infer_cl_path` / populate | +| [buck_controller.rs](../src/buck_controller.rs) | CL mount 传入推断的 `cl_path` | --- -## 6. 相关代码索引 +# 开放项(跨 Part) -| 文件 | 内容 | -|------|------| -| [orion/src/buck_controller.rs](../src/buck_controller.rs) | `get_build_targets()`、B、all-added 子项目、owner 归一化、图 rdeps、`ORION_BUCK_REMOTE_CACHE` | -| [orion/buck/discovery_scope.rs](../buck/discovery_scope.rs) | 方案 A、子项目检测、`ORION_DISCOVERY_SCOPE` | -| [orion/buck/run.rs](../buck/run.rs) | `uquery_rdeps`、`owners` | -| [orion/buck/platform.rs](../buck/platform.rs) | Scheme C2 `--config` | -| [orion/src/repo/diff.rs](../src/repo/diff.rs) | `EmptyBasePolicy`、`recursive_target_changes` | -| [.buckconfig](../../.buckconfig) | 仓库侧 platform(可被 Orion 覆盖) | +| 项 | 说明 | +|----|------| +| **0 target 仍 exit 0** | `finish_without_build_if_no_targets()` 掩盖 discovery / overlay 失败 | --- @@ -213,8 +149,5 @@ flowchart TD | 日期 | 说明 | |------|------| -| 2026-06-15 | 初稿:根因分析与方案 A–E | -| 2026-06-17 | 补充 B 与 Build #17 验证 | -| 2026-06-18 | 落地 C2 | -| 2026-06-23 | 更新:A/B/C1/D/子项目/all-added 已落地;#39–#42 对照;owner→rust 与图 rdeps | -| 2026-06-23 | **#45** 验证:28 targets、23k action、~73 min、exit 0 真编译 | +| 2026-06-15~23 | Part I:方案 A–E;UYXIYYNJ #45 | +| 2026-07-24 | Part II:方案 G(LADRHWDL overlay 路径);文档拆成发现范围 vs overlay 两块 |