feat(#143): fetch doc-map config from trusted VCS ref #153
@@ -174,9 +174,12 @@ func main() {
|
||||
os.Exit(1)
|
||||
|
|
||||
}
|
||||
|
||||
// Early validation of filesystem-path flags (fail fast before network I/O)
|
||||
// Early validation of filesystem-path flags (fail fast before network I/O).
|
||||
|
sonnet-review-bot
commented
[NIT] The early validation of **[NIT]** The early validation of `--doc-map` path now happens before client initialization (good for fail-fast), but when `--doc-map-trusted-ref` is set, the `resolvedDocMapFile` variable is never used (the trusted-ref path fetches from VCS, ignoring the local file). This is correct behavior but the variable name `resolvedDocMapFile` implies a local file will be used. A comment clarifying that this is only used in the local-workspace fallback path would improve readability.
|
||||
// Skip local-path validation when --doc-map-trusted-ref is set: the flag
|
||||
// value is used as a VCS API path, not a local filesystem path, and the
|
||||
// file may not exist in the local checkout (sparse, PR-deleted, etc.).
|
||||
var resolvedDocMapFile string
|
||||
if *docMapFile != "" {
|
||||
if *docMapFile != "" && *docMapTrustedRef == "" {
|
||||
resolved, err := validateWorkspacePath(*docMapFile, "doc-map")
|
||||
if err != nil {
|
||||
slog.Error("invalid doc-map path", "error", err)
|
||||
|
||||
@@ -1578,3 +1578,47 @@ func TestMainSubprocess_InvalidDocMapFile(t *testing.T) {
|
||||
t.Errorf("expected error about failed resolution, got: %s", output)
|
||||
}
|
||||
}
|
||||
|
||||
// TestMainSubprocess_DocMapTrustedRefSkipsLocalValidation confirms that
|
||||
// --doc-map-trusted-ref bypasses local filesystem validation for --doc-map.
|
||||
// When the trusted-ref flag is set, the doc-map value is used as a VCS API
|
||||
// path; a nonexistent local file must not cause an early exit before network I/O.
|
||||
func TestMainSubprocess_DocMapTrustedRefSkipsLocalValidation(t *testing.T) {
|
||||
if os.Getenv("TEST_SUBPROCESS_MAIN") == "1" {
|
||||
flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ExitOnError)
|
||||
os.Args = []string{"review-bot",
|
||||
"--vcs-url", "https://gitea.example.com",
|
||||
"--repo", "owner/repo",
|
||||
"--pr", "1",
|
||||
"--reviewer-token", "tok",
|
||||
"--llm-base-url", "https://api.example.com",
|
||||
"--llm-api-key", "key",
|
||||
"--llm-model", "gpt-4",
|
||||
"--doc-map", "nonexistent-local.yml",
|
||||
"--doc-map-trusted-ref", "main",
|
||||
}
|
||||
main()
|
||||
return
|
||||
}
|
||||
|
||||
cmd := exec.Command(os.Args[0], "-test.run=TestMainSubprocess_DocMapTrustedRefSkipsLocalValidation")
|
||||
cmd.Env = append(cleanEnv(),
|
||||
"TEST_SUBPROCESS_MAIN=1",
|
||||
"GITHUB_WORKSPACE="+t.TempDir(),
|
||||
)
|
||||
out, err := cmd.CombinedOutput()
|
||||
output := string(out)
|
||||
|
||||
// The test must fail (network I/O or VCS API failure) but must NOT
|
||||
// fail with the local filesystem validation error.
|
||||
// "failed to resolve" would indicate the early validateWorkspacePath ran —
|
||||
// that would be the bug this test is catching.
|
||||
if strings.Contains(output, "failed to resolve") {
|
||||
t.Errorf("--doc-map-trusted-ref should skip local path validation, but got filesystem error: %s", output)
|
||||
}
|
||||
|
||||
// It must still exit non-zero (real VCS call to example.com will fail).
|
||||
if err == nil {
|
||||
t.Fatal("expected non-zero exit when VCS API is unreachable, got success")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ func ParseDocMapConfig(localPath string) (*DocMapConfig, error) {
|
||||
|
||||
// ParseDocMapConfigContent parses a doc-map YAML config from an in-memory
|
||||
// string. The source parameter is used only for error messages and log entries
|
||||
// (e.g. "main:main@<ref>").
|
||||
// (e.g. "owner/repo@main:.review-bot/doc-map.yml").
|
||||
|
gpt-review-bot
commented
[NIT] The comment example for the **[NIT]** The comment example for the `source` parameter mentions a format like "main:main@<ref>", but the code constructs `owner/repo@ref:path`. Consider updating the example to match the actual format to avoid confusion.
|
||||
//
|
||||
// Use this when the config content has been fetched from a trusted VCS ref
|
||||
// rather than read from the local workspace.
|
||||
|
sonnet-review-bot
commented
[NIT] The doc comment for ParseDocMapConfigContent says 'e.g. "main:main@"' but the actual format used in main.go is 'owner/repo@ref:path'. Minor doc inconsistency, no functional impact. **[NIT]** The doc comment for ParseDocMapConfigContent says 'e.g. "main:main@<ref>"' but the actual format used in main.go is 'owner/repo@ref:path'. Minor doc inconsistency, no functional impact.
|
||||
|
||||
[NIT] The
resolvedDocMapFilevariable is declared at a wide scope and left as the zero value (empty string) when*docMapTrustedRef != "". It is then used later in theelsebranch of the trusted-ref check. This is correct — the empty string is only used in the else branch — but it is slightly fragile. A future refactor could accidentally useresolvedDocMapFilein the trusted-ref path. A comment noting thatresolvedDocMapFileis intentionally empty whendocMapTrustedRefis set would improve clarity, or the variable could be moved into theelseblock. No bug here.