From bf91f0973a9ac3536064bc304bbc17b9d286955b Mon Sep 17 00:00:00 2001 From: Ramesh Padmanabhaiah <22363102+codeforester@users.noreply.github.com> Date: Thu, 3 Sep 2026 23:57:23 +0530 Subject: [PATCH] fix(workspace): hide delegated log records from clone details --- cli/bash/commands/basectl/README.md | 4 ++- .../tests/test_workspace_clone.py | 27 +++++++++++++++++++ .../base_projects/workspace_clone_command.py | 15 ++++++++++- docs/command-reference.md | 4 ++- docs/workspace-manifest.md | 2 ++ 5 files changed, 49 insertions(+), 3 deletions(-) diff --git a/cli/bash/commands/basectl/README.md b/cli/bash/commands/basectl/README.md index 01b64271..0f774577 100644 --- a/cli/bash/commands/basectl/README.md +++ b/cli/bash/commands/basectl/README.md @@ -263,7 +263,9 @@ such command directories exist. Optional utility CLIs such as `caff` and `--manifest ` takes precedence over `workspace.manifest`. Interactive output uses a repository/action/result table with present, cloned, skipped, planned, and failed results plus aggregate counts; successful delegated output - is suppressed and failure details remain visible. + is suppressed and failure details remain visible. Timestamped delegated Base + log records are kept out of the normal detail block and remain available in + debug diagnostics. - `basectl workspace init ` bootstraps a workspace from a workspace configuration repository. The source may be a local path, GitHub URL, `owner/repo`, or a short repository name resolved by `--owner ` or diff --git a/cli/python/base_projects/tests/test_workspace_clone.py b/cli/python/base_projects/tests/test_workspace_clone.py index 1d539fb9..cdb7fd1d 100644 --- a/cli/python/base_projects/tests/test_workspace_clone.py +++ b/cli/python/base_projects/tests/test_workspace_clone.py @@ -9,6 +9,7 @@ from unittest import mock from base_projects import engine +from base_projects.workspace_clone_command import clone_detail def write_workspace_manifest(path: Path, body: str) -> None: @@ -41,6 +42,7 @@ def write_fake_basectl(base_home: Path, state_file: Path) -> None: done if [[ "$repo" == "codeforester/conflict" ]]; then printf 'simulated clone conflict for %s\\n' "$repo" >&2 + printf '2026-09-03 23:29:23 +0530 ERROR subcommands/repo.sh:2179 Failed to clone repository.\\n' >&2 exit 1 fi if [[ "$dry_run" != "1" && -n "$path" ]]; then @@ -84,6 +86,30 @@ def workspace_clone_row(stdout: str, repo_name: str) -> list[str]: class WorkspaceCloneTests(unittest.TestCase): + def test_clone_detail_filters_timestamped_base_log_records(self) -> None: + detail = clone_detail( + "Cloning GitHub repository 'codeforester/bleach'.\n", + "\n".join( + ( + "HTTP 401: Bad credentials (https://api.github.com/graphql)", + "Try authenticating with: gh auth refresh -h github.com", + "2026-09-03 23:29:23 +0530 ERROR subcommands/repo.sh:2179 Failed to clone repository.", + "2026-06-10 10:15:33 WARN repo.sh:100 retrying", + ) + ), + ) + + self.assertEqual( + detail, + "\n".join( + ( + "HTTP 401: Bad credentials (https://api.github.com/graphql)", + "Try authenticating with: gh auth refresh -h github.com", + "Cloning GitHub repository 'codeforester/bleach'.", + ) + ), + ) + def test_workspace_clone_dry_run_materializes_missing_required_repositories(self) -> None: with tempfile.TemporaryDirectory() as tmpdir: root = Path(tmpdir) @@ -200,6 +226,7 @@ def test_workspace_clone_include_optional_continues_after_clone_failures(self) - ["conflict", "CLONE", "failed", "(exit", "1)"], ) self.assertIn("simulated clone conflict for codeforester/conflict", stdout) + self.assertNotIn("subcommands/repo.sh:2179", stdout) self.assertEqual(workspace_clone_row(stdout, "api"), ["api", "CHECK", "present"]) self.assertEqual( workspace_clone_row(stdout, "optional-tool"), diff --git a/cli/python/base_projects/workspace_clone_command.py b/cli/python/base_projects/workspace_clone_command.py index 7d5e6f0d..0494c6b6 100644 --- a/cli/python/base_projects/workspace_clone_command.py +++ b/cli/python/base_projects/workspace_clone_command.py @@ -1,5 +1,6 @@ from __future__ import annotations +import re from dataclasses import dataclass from pathlib import Path from typing import Literal, Protocol @@ -54,6 +55,13 @@ class WorkspaceCloneCounts: failed: int = 0 +BASE_LOG_RECORD_RE = re.compile( + r"^\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2}" + r"(?:\s+(?:[+-]\d{4}|UTC))?\s+" + r"(?:TRACE|DEBUG|INFO|WARN|WARNING|ERROR|FATAL)\s+" +) + + def workspace_clone_command(ctx: base_cli.Context, options: WorkspaceCloneOptions) -> int: if options.output_format != "text": raise ProjectUsageError(f"Unsupported output format '{options.output_format}'. Expected: text.") @@ -248,7 +256,12 @@ def clone_workspace_repo( def clone_detail(stdout: str, stderr: str) -> str: - details = [part.strip() for part in (stderr, stdout) if part.strip()] + details = [ + line.strip() + for stream in (stderr, stdout) + for line in stream.splitlines() + if line.strip() and not BASE_LOG_RECORD_RE.match(line.strip()) + ] return "\n".join(details) or "clone failed without diagnostic output" diff --git a/docs/command-reference.md b/docs/command-reference.md index 582ccf9c..73463b97 100644 --- a/docs/command-reference.md +++ b/docs/command-reference.md @@ -644,7 +644,9 @@ a no-write preview. Text output uses a stable repository/action/result table: existing repositories are `present`, newly materialized repositories are `cloned`, optional omissions are `skipped`, dry-run operations are `planned`, and failures include concise details and exit codes. Successful delegated clone -output is suppressed in normal interactive mode. Optional repositories are +output is suppressed in normal interactive mode; timestamped delegated Base log +records remain in debug diagnostics rather than being rendered as detail lines. +Optional repositories are reported but skipped unless `--include-optional` is supplied. Workspace manifests may list non-GitHub Git URLs for reporting, but automatic materialization through `workspace clone` is GitHub-only today; clone GitLab, Bitbucket, internal Git, or local repositories diff --git a/docs/workspace-manifest.md b/docs/workspace-manifest.md index e0aef581..c431984f 100644 --- a/docs/workspace-manifest.md +++ b/docs/workspace-manifest.md @@ -575,6 +575,8 @@ table: existing repositories are `present`, newly materialized repositories are and failures include concise details and exit codes. Successful delegated clone output is suppressed in normal interactive mode, while the completion summary reports aggregate present, cloned, skipped, and failed counts. +Timestamped delegated Base log records remain available in debug diagnostics +rather than being rendered as indented failure details. `basectl workspace configure --manifest ` configures present Base-managed expected repositories through `basectl repo configure`. It skips missing