Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
642 changes: 642 additions & 0 deletions TODO/design-eliminate-registration-lists.md

Large diffs are not rendered by default.

91 changes: 76 additions & 15 deletions TODO/tasks.md

Large diffs are not rendered by default.

102 changes: 33 additions & 69 deletions core/daglang/daglang-cli/src/pipeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -770,65 +770,22 @@ mod tests {
))
}

fn expected_real_corpus_module_order() -> Vec<&'static str> {
vec![
"examples.rich_types",
"infra.aws.services",
"infra.azure.services",
"infra.core",
"infra.gcp.services",
"infra.spec",
"infra.aws.config",
"infra.aws.resources",
"infra.azure.config",
"infra.azure.resources",
"infra.gcp.config",
"infra.gcp.resources",
"examples.integration_tests",
"infra.sdlc.deploy",
"services.agent.codex",
"services.cargo",
"services.gcp.iam",
"services.gcp.secret_manager",
"services.gcp.sts",
"services.github.gist",
"services.github.issues",
"funcs.test_control_flow",
"services.github.pull_request",
"services.llm.anthropic",
"services.llm.openai",
"services.sdlc.control_plane",
"funcs.agent_feedback",
"std.resources",
"std.types",
"cloud.aws.credential",
"cloud.azure.credential",
"examples.abstract_services",
"services.git",
"services.shell",
"std.patterns",
"cloud.gcp.credential",
"shared.dag_util",
"shared.gist_modes",
"tools.bootstrap",
"tools.build",
"tools.clippy",
"tools.codegen",
"tools.dag_viz",
"tools.deps",
"tools.design",
"pipelines.sdlc",
"funcs.sdlc_worker",
"tools.docgen",
"tools.gist",
"examples.deployment",
"tools.infra",
"tools.makegen",
"tools.pragma",
"tools.review",
"tools.testgen",
"pipelines.ci",
]
/// Discover expected module IDs from the filesystem instead of a hardcoded list.
/// Globs `dsl/**/*.dag`, converts paths to module IDs, and returns a sorted set.
fn discover_expected_module_ids(dsl_root: &PathBuf) -> Vec<String> {
let files = daglang_resolve::discover_dag_files(dsl_root)
.expect("discover_dag_files should succeed");
let mut module_ids: Vec<String> = files
.iter()
.filter_map(|path| {
let rel = path.strip_prefix(dsl_root).ok()?;
let segments = daglang_resolve::relative_path_to_module_path(rel);
Some(segments.join("."))
})
.collect();
module_ids.sort();
module_ids.dedup();
module_ids
}

fn reported_modules_in_order(report: &str) -> Vec<String> {
Expand Down Expand Up @@ -1067,6 +1024,9 @@ mod tests {
#[test]
fn parse_pipeline_parses_full_real_dsl_corpus_without_diagnostics() {
let dsl_root = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../../dsl");
let expected_count = daglang_resolve::discover_dag_files(&dsl_root)
.expect("discover_dag_files should succeed")
.len();
let result = run_pipeline(
&PipelineContext {
roots: vec![dsl_root],
Expand All @@ -1076,7 +1036,7 @@ mod tests {
)
.expect("pipeline should execute");

assert_eq!(result.parsed_count(), 56);
assert_eq!(result.parsed_count(), expected_count);
assert!(
result.diagnostics().is_empty(),
"real corpus parse stop should not emit parse diagnostics: {:?}",
Expand Down Expand Up @@ -1111,6 +1071,9 @@ mod tests {
#[test]
fn report_pipeline_real_corpus_retains_parsed_count() {
let dsl_root = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../../dsl");
let expected_count = daglang_resolve::discover_dag_files(&dsl_root)
.expect("discover_dag_files should succeed")
.len();
let result = run_pipeline(
&PipelineContext {
roots: vec![dsl_root],
Expand All @@ -1121,29 +1084,30 @@ mod tests {
.expect("pipeline should execute");
assert_eq!(
result.parsed_count(),
56,
expected_count,
"report stop should retain parse-stage file count for real corpus"
);
}

#[test]
fn report_pipeline_real_corpus_module_order_matches_expected_snapshot() {
fn report_pipeline_real_corpus_module_set_matches_filesystem() {
let dsl_root = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../../dsl");
let result = run_pipeline(
&PipelineContext {
roots: vec![dsl_root],
roots: vec![dsl_root.clone()],
target_file: None,
},
PipelineStop::Report,
)
.expect("pipeline should execute");
let report = result.report().expect("report should be available");
let actual = reported_modules_in_order(report);
let expected: Vec<String> = expected_real_corpus_module_order()
.into_iter()
.map(String::from)
.collect();
assert_eq!(actual, expected);
let mut actual = reported_modules_in_order(report);
actual.sort();
let expected = discover_expected_module_ids(&dsl_root);
assert_eq!(
actual, expected,
"compiler-discovered modules should match filesystem-discovered .dag files"
);
}

#[test]
Expand Down
5 changes: 2 additions & 3 deletions core/daglang/daglang-syntax/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1271,9 +1271,8 @@ impl Parser {
self.expect(&TokenKind::Eq)?;
let expr = self.parse_expr(0)?;
props.push((name, expr));
if !self.eat(&TokenKind::Comma) {
break;
}
// Comma is optional; newline-separated properties are valid.
self.eat(&TokenKind::Comma);
}
Ok(props)
}
Expand Down
38 changes: 35 additions & 3 deletions gunbc-dag/src/bin/sdlc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1734,6 +1734,23 @@ fn run_ci_validation(branch: &str, dry_run: bool) -> Result<CiValidationResult,
});
}

// Record the current branch so we can restore it after validation.
let original_branch = Command::new("git")
.args(["rev-parse", "--abbrev-ref", "HEAD"])
.output()
.map_err(|e| format!("git rev-parse failed: {e}"))?;
let original_branch = String::from_utf8_lossy(&original_branch.stdout).trim().to_string();

Comment thread
briansrls marked this conversation as resolved.
// Checkout the agent branch so CI runs against the correct code.
let checkout = Command::new("git")
.args(["checkout", branch])
.output()
.map_err(|e| format!("git checkout {branch} failed: {e}"))?;
if !checkout.status.success() {
let stderr = String::from_utf8_lossy(&checkout.stderr);
return Err(format!("git checkout {branch} failed: {stderr}"));
}

let test_output = Command::new("cargo")
.args(["test", "--workspace", "--quiet"])
.output()
Comment thread
briansrls marked this conversation as resolved.
Expand All @@ -1744,6 +1761,11 @@ fn run_ci_validation(branch: &str, dry_run: bool) -> Result<CiValidationResult,
.output()
.map_err(|e| format!("cargo clippy failed to start: {e}"))?;

// Restore the original branch regardless of CI outcome.
let _ = Command::new("git")
.args(["checkout", &original_branch])
.output();

let tests_passed = test_output.status.success();
let clippy_passed = clippy_output.status.success();

Expand All @@ -1768,8 +1790,6 @@ fn run_ci_validation(branch: &str, dry_run: bool) -> Result<CiValidationResult,
parts.push(format!("clippy errors: {last_lines}"));
}

let _ = branch;

Ok(CiValidationResult {
success: tests_passed && clippy_passed,
summary: parts.join("; "),
Expand Down Expand Up @@ -1830,7 +1850,19 @@ fn parse_github_owner_repo(url: &str) -> Option<(String, String)> {
.trim()
.trim_end_matches(".git")
.trim_end_matches('/');
let parts: Vec<&str> = cleaned.rsplitn(3, '/').collect();
// Handle SSH URLs like git@github.com:org/repo
// by normalizing the colon separator to a slash.
let normalized = if let Some(colon_pos) = cleaned.find(':') {
// Only treat as SSH if there's no "://" (which would be HTTPS).
if !cleaned[..colon_pos + 1].ends_with("://") {
cleaned.replacen(':', "/", 1)
} else {
cleaned.to_string()
}
} else {
cleaned.to_string()
};
let parts: Vec<&str> = normalized.rsplitn(3, '/').collect();
if parts.len() >= 2 {
Some((parts[1].to_string(), parts[0].to_string()))
} else {
Expand Down
146 changes: 57 additions & 89 deletions gunbc-dag/src/binaries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,104 +3,72 @@
//! This is the single source of truth for how repo-local binaries are
//! invoked via cargo (package + bin name). Centralizing this mapping
//! prevents drift when binaries move between packages.
//!
//! **Adding a new binary**: add one line to the `workspace_binaries!` table
//! below. The enum variant, `ALL` array, `tool_name()`, `from_tool_name()`,
//! and `component()` are all derived automatically.

use gunbc_ir::CargoInvocation;
use gunbc_tool_registry::iter_tool_targets;

/// Repo-local workspace binaries (all live in gunbc-dag).
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum WorkspaceBinary {
Build,
Bootstrap,
Ci,
Codegen,
CodegenDag,
DepsConfig,
Docgen,
Infra,
Makegen,
Pragma,
Sdlc,
Testgen,
}

impl WorkspaceBinary {
/// Canonical ordered registry of all workspace binaries.
pub const ALL: [Self; 12] = [
Self::Build,
Self::Bootstrap,
Self::Ci,
Self::Codegen,
Self::CodegenDag,
Self::DepsConfig,
Self::Docgen,
Self::Infra,
Self::Makegen,
Self::Pragma,
Self::Sdlc,
Self::Testgen,
];

/// Iterate all known workspace binaries.
pub fn all() -> &'static [Self] {
&Self::ALL
}

/// Tool registry name for this binary when present.
pub fn tool_name(self) -> &'static str {
match self {
WorkspaceBinary::Build => "build",
WorkspaceBinary::Bootstrap => "bootstrap",
WorkspaceBinary::Ci => "ci",
WorkspaceBinary::Codegen => "codegen",
WorkspaceBinary::CodegenDag => "codegen-dag",
WorkspaceBinary::DepsConfig => "deps-config",
WorkspaceBinary::Docgen => "docgen",
WorkspaceBinary::Infra => "infra",
WorkspaceBinary::Makegen => "makegen",
WorkspaceBinary::Pragma => "pragma",
WorkspaceBinary::Sdlc => "sdlc",
WorkspaceBinary::Testgen => "testgen",
/// Generates the `WorkspaceBinary` enum and its core accessors from a single
/// definition table. Each entry is `VariantName => "tool-name"`.
macro_rules! workspace_binaries {
( $( $variant:ident => $tool_name:expr ),* $(,)? ) => {
/// Repo-local workspace binaries (all live in gunbc-dag).
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum WorkspaceBinary {
$( $variant, )*
}
}

/// Resolve enum variant from tool registry name.
pub fn from_tool_name(name: &str) -> Option<Self> {
match name {
"build" => Some(Self::Build),
"bootstrap" => Some(Self::Bootstrap),
"ci" => Some(Self::Ci),
"codegen" => Some(Self::Codegen),
"codegen-dag" => Some(Self::CodegenDag),
"deps-config" => Some(Self::DepsConfig),
"docgen" => Some(Self::Docgen),
"infra" => Some(Self::Infra),
"makegen" => Some(Self::Makegen),
"pragma" => Some(Self::Pragma),
"sdlc" => Some(Self::Sdlc),
"testgen" => Some(Self::Testgen),
_ => None,
impl WorkspaceBinary {
/// Canonical ordered registry of all workspace binaries.
pub const ALL: &[Self] = &[ $( Self::$variant, )* ];

/// Iterate all known workspace binaries.
pub fn all() -> &'static [Self] {
Self::ALL
}

/// Tool registry name for this binary.
pub fn tool_name(self) -> &'static str {
match self {
$( Self::$variant => $tool_name, )*
}
}

/// Resolve enum variant from tool registry name.
pub fn from_tool_name(name: &str) -> Option<Self> {
match name {
$( $tool_name => Some(Self::$variant), )*
_ => None,
}
}

/// Component name used to compose the binary name.
pub fn component(self) -> &'static str {
self.tool_name()
}
}
}
};
}

/// Component name used to compose the binary name.
pub fn component(self) -> &'static str {
match self {
WorkspaceBinary::Build => "build",
WorkspaceBinary::Bootstrap => "bootstrap",
WorkspaceBinary::Ci => "ci",
WorkspaceBinary::Codegen => "codegen",
WorkspaceBinary::CodegenDag => "codegen-dag",
WorkspaceBinary::DepsConfig => "deps-config",
WorkspaceBinary::Docgen => "docgen",
WorkspaceBinary::Infra => "infra",
WorkspaceBinary::Makegen => "makegen",
WorkspaceBinary::Pragma => "pragma",
WorkspaceBinary::Sdlc => "sdlc",
WorkspaceBinary::Testgen => "testgen",
}
}
workspace_binaries! {
Build => "build",
Bootstrap => "bootstrap",
Ci => "ci",
Codegen => "codegen",
CodegenDag => "codegen-dag",
DepsConfig => "deps-config",
Docgen => "docgen",
Infra => "infra",
Makegen => "makegen",
Pragma => "pragma",
Sdlc => "sdlc",
Testgen => "testgen",
}

impl WorkspaceBinary {
/// Whether this binary corresponds to a DSL pipeline module.
pub fn is_dsl_pipeline_module(self) -> bool {
matches!(self, Self::Ci)
Expand Down
Loading