Merge pull request 'view_reach answers one module's delivery reach' (#136) from fix/view-reach-filter into main
This commit was merged in pull request #136.
This commit is contained in:
@@ -33,12 +33,15 @@ func Tools(r view.Reader, u view.UsageReader) []stdio.Tool {
|
|||||||
"containing, as the view derives it from the indexer's modules, the wants and the haves; a merge at or before it is on the " +
|
"containing, as the view derives it from the indexer's modules, the wants and the haves; a merge at or before it is on the " +
|
||||||
"machines), what holds the next one (a module on a node, the indexer's lag), and per module its fingerprint, the nodes " +
|
"machines), what holds the next one (a module on a node, the indexer's lag), and per module its fingerprint, the nodes " +
|
||||||
"that want it, its changes (fingerprint, the merge it first appeared at, its source revision), its floor and the nodes " +
|
"that want it, its changes (fingerprint, the merge it first appeared at, its source revision), its floor and the nodes " +
|
||||||
"where it is not settled or not healthy (novox/hq ADR 0556). Give a repository (owner/name) for one, none for every one. Read-only.",
|
"where it is not settled or not healthy (novox/hq ADR 0556). Give a repository (owner/name) for one, none for every one; give a module to see only that module of each " +
|
||||||
|
"repository and the holds that name it. Answers the delivery reach of a repository and of a module. Read-only.",
|
||||||
Input: map[string]any{"type": "object", "properties": map[string]any{
|
Input: map[string]any{"type": "object", "properties": map[string]any{
|
||||||
"repository": map[string]any{"type": "string", "description": "the repository, owner/name; none for every repository"}}},
|
"repository": map[string]any{"type": "string", "description": "the repository, owner/name; none for every repository"},
|
||||||
|
"module": map[string]any{"type": "string", "description": "the module name; none for every module"}}},
|
||||||
Run: func(args map[string]any) (any, error) {
|
Run: func(args map[string]any) (any, error) {
|
||||||
repository, _ := args["repository"].(string)
|
repository, _ := args["repository"].(string)
|
||||||
return r.Reach(repository)
|
module, _ := args["module"].(string)
|
||||||
|
return r.Reach(repository, module)
|
||||||
}},
|
}},
|
||||||
{Name: view.ModuleHistoryTool,
|
{Name: view.ModuleHistoryTool,
|
||||||
Description: "The history of one module's value in the indexer's modules state (modules.<module>), with times: each value it " +
|
Description: "The history of one module's value in the indexer's modules state (modules.<module>), with times: each value it " +
|
||||||
|
|||||||
@@ -89,6 +89,19 @@ func TestModuleToolAnswersTheStoredValue(t *testing.T) {
|
|||||||
if _, err := run(t, r, view.ReachReadTool, map[string]any{"repository": "novox/gone"}); err == nil || !strings.Contains(err.Error(), "no reach of novox/gone") {
|
if _, err := run(t, r, view.ReachReadTool, map[string]any{"repository": "novox/gone"}); err == nil || !strings.Contains(err.Error(), "no reach of novox/gone") {
|
||||||
t.Errorf("a repository with no reach: %v", err)
|
t.Errorf("a repository with no reach: %v", err)
|
||||||
}
|
}
|
||||||
|
// The module argument keeps only that module of each repository, and only the holds that name it.
|
||||||
|
const reach2 = `{"v":1,"repository":"novox/web","reached":"c1","reached_src":12,"modules":{"web":{"nodes":1},"api":{"nodes":2}},"holds":["web on g14 runs 1","api on g14 runs 2","the indexer has not yet taken in x"]}`
|
||||||
|
r.ReachState = fake{"novox/web": reach2, "novox/other": `{"v":1,"repository":"novox/other","modules":{"o":{"nodes":1}}}`}.reader().State
|
||||||
|
if out, err := run(t, r, view.ReachReadTool, map[string]any{"repository": "novox/web", "module": "web"}); err != nil ||
|
||||||
|
!strings.Contains(string(out.(json.RawMessage)), `"web"`) || strings.Contains(string(out.(json.RawMessage)), `"api"`) || strings.Contains(string(out.(json.RawMessage)), "api on") || !strings.Contains(string(out.(json.RawMessage)), "web on g14") {
|
||||||
|
t.Errorf("view_reach novox/web module web = %s, %v", out, err)
|
||||||
|
}
|
||||||
|
if out, err := run(t, r, view.ReachReadTool, map[string]any{"module": "api"}); err != nil || !strings.Contains(fmt.Sprint(out), "api") || strings.Contains(fmt.Sprint(out), "novox/other") {
|
||||||
|
t.Errorf("view_reach module api over every repository = %v, %v", out, err)
|
||||||
|
}
|
||||||
|
if _, err := run(t, r, view.ReachReadTool, map[string]any{"repository": "novox/web", "module": "nope"}); err == nil || !strings.Contains(err.Error(), "no module nope") {
|
||||||
|
t.Errorf("a module the repository does not hold: %v", err)
|
||||||
|
}
|
||||||
// Issue 1126 note 76: indexer_module_history answers modules.<module>'s kept values newest first, each with its
|
// Issue 1126 note 76: indexer_module_history answers modules.<module>'s kept values newest first, each with its
|
||||||
// revision, time, commit, source revision and fp2; indexer_heard the FORGE messages the indexer handled, newest first.
|
// revision, time, commit, source revision and fp2; indexer_heard the FORGE messages the indexer handled, newest first.
|
||||||
at := time.Date(2026, 10, 28, 13, 7, 46, 0, time.UTC)
|
at := time.Date(2026, 10, 28, 13, 7, 46, 0, time.UTC)
|
||||||
|
|||||||
@@ -102,8 +102,61 @@ func (r Reader) Status(now time.Time) Status {
|
|||||||
// ReachReadTool reads the view's reach: per repository, how far each module reached and the reached head (ADR 0556).
|
// ReachReadTool reads the view's reach: per repository, how far each module reached and the reached head (ADR 0556).
|
||||||
const ReachReadTool = "view_reach"
|
const ReachReadTool = "view_reach"
|
||||||
|
|
||||||
// Reach is the stored reach of one repository, or of every repository when none is named, as the view wrote it.
|
// Reach is the stored reach of one repository, or of every repository when none is named, as the view wrote it. With a
|
||||||
func (r Reader) Reach(repository string) (any, error) {
|
// module, each repository's value holds only that module and the holds that name it, and a repository without it is left out.
|
||||||
|
func (r Reader) Reach(repository, module string) (any, error) {
|
||||||
|
out, err := r.reach(repository)
|
||||||
|
if module = strings.TrimSpace(module); module == "" || err != nil {
|
||||||
|
return out, err
|
||||||
|
}
|
||||||
|
if raw, ok := out.(json.RawMessage); ok {
|
||||||
|
one, err := onlyModule(raw, module)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if one == nil {
|
||||||
|
return nil, fmt.Errorf("no module %s in the reach of %s", module, repository)
|
||||||
|
}
|
||||||
|
return one, nil
|
||||||
|
}
|
||||||
|
var kept []json.RawMessage
|
||||||
|
for _, raw := range out.(map[string]any)["reach"].([]json.RawMessage) {
|
||||||
|
one, err := onlyModule(raw, module)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if one != nil {
|
||||||
|
kept = append(kept, one)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map[string]any{"reach": kept}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// onlyModule is a reach value with only module among its modules and only the holds that name it, nil when it holds none.
|
||||||
|
func onlyModule(raw json.RawMessage, module string) (json.RawMessage, error) {
|
||||||
|
if len(raw) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
rc, err := delivery.DecodeReach(raw)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
mr, ok := rc.Modules[module]
|
||||||
|
if !ok {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
rc.Modules = map[string]delivery.ModuleReach{module: mr}
|
||||||
|
var holds []string
|
||||||
|
for _, h := range rc.Holds {
|
||||||
|
if strings.HasPrefix(h, module+" ") {
|
||||||
|
holds = append(holds, h)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rc.Holds = holds
|
||||||
|
return json.Marshal(rc)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r Reader) reach(repository string) (any, error) {
|
||||||
if r.ReachState.Get == nil || r.ReachState.Keys == nil {
|
if r.ReachState.Get == nil || r.ReachState.Keys == nil {
|
||||||
return nil, fmt.Errorf("the reach state cannot be read here")
|
return nil, fmt.Errorf("the reach state cannot be read here")
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user