Build a module from a repository and a path within it
The builder cloned a repository and read the manifest at its root, which means one repository per module. Nothing we have is shaped that way, so the builder could be asked to build nothing that exists (novox/hq ADR 0069). The path travels the whole way — named when asking, carried in the request, used to read the manifest and as the context everything is produced from, echoed back in the result, and recorded as part of where a module came from. Without that last part the mesh could notice a module was behind its source and then be unable to rebuild it, which is the worst of both. A path climbing out of the clone is refused: a machine whose job is building other people's repositories must not read whatever else is on its disk. Claude-Session: https://claude.ai/code/session_01D6qtiYU3P9jk3pnAXyAFyx
This commit is contained in:
@@ -23,7 +23,10 @@ var ErrStillAssigned = errors.New("that module is still assigned to nodes")
|
||||
// Source is where a module comes from and what has been built from it.
|
||||
type Source struct {
|
||||
Repository string
|
||||
Ref string
|
||||
// Path is the module's directory inside that repository (novox/hq ADR 0069). Empty is the
|
||||
// repository's root, which is a real answer rather than a missing one.
|
||||
Path string
|
||||
Ref string
|
||||
// BuiltFrom is the commit the manifest the mesh holds was read at.
|
||||
BuiltFrom string
|
||||
// Head is the newest commit the source is known to have.
|
||||
@@ -57,17 +60,19 @@ func (i *Inventory) RegisterModule(ctx context.Context, m catalogue.Manifest, fr
|
||||
// record of where the module normally comes from — which is the only thing that would say,
|
||||
// afterwards, that the machine is running something nobody can rebuild.
|
||||
_, err = i.store.Pool().Exec(ctx,
|
||||
`insert into module (name, manifest, version, source, ref, built_from, source_head)
|
||||
values ($1, $2, nullif($3,''), nullif($4,''), nullif($5,''), nullif($6,''), nullif($6,''))
|
||||
`insert into module (name, manifest, version, source, source_path, ref, built_from, source_head)
|
||||
values ($1, $2, nullif($3,''), nullif($4,''), $7, nullif($5,''), nullif($6,''), nullif($6,''))
|
||||
on conflict (name) do update set
|
||||
manifest = excluded.manifest,
|
||||
version = excluded.version,
|
||||
registered = now(),
|
||||
source = coalesce(excluded.source, module.source),
|
||||
source_path = case when excluded.source is null then module.source_path
|
||||
else excluded.source_path end,
|
||||
ref = coalesce(excluded.ref, module.ref),
|
||||
built_from = coalesce(excluded.built_from, module.built_from),
|
||||
source_head = coalesce(excluded.built_from, module.source_head)`,
|
||||
m.Module, raw, m.Version, from.Repository, from.Ref, from.BuiltFrom)
|
||||
m.Module, raw, m.Version, from.Repository, from.Ref, from.BuiltFrom, from.Path)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -92,9 +97,10 @@ func (i *Inventory) SourceMoved(ctx context.Context, module, head string) error
|
||||
func (i *Inventory) SourceOf(ctx context.Context, module string) (Source, error) {
|
||||
var s Source
|
||||
var repo, ref, built, head *string
|
||||
var path string
|
||||
err := i.store.Pool().QueryRow(ctx,
|
||||
`select source, ref, built_from, source_head from module where name = $1`,
|
||||
module).Scan(&repo, &ref, &built, &head)
|
||||
`select source, source_path, ref, built_from, source_head from module where name = $1`,
|
||||
module).Scan(&repo, &path, &ref, &built, &head)
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return Source{}, fmt.Errorf("%w: %s", ErrNoSuchModule, module)
|
||||
}
|
||||
@@ -109,6 +115,9 @@ func (i *Inventory) SourceOf(ctx context.Context, module string) (Source, error)
|
||||
*pair.to = *pair.from
|
||||
}
|
||||
}
|
||||
// Not in the loop above: the path is never null, because "the repository's root" is an answer
|
||||
// rather than an absence.
|
||||
s.Path = path
|
||||
return s, nil
|
||||
}
|
||||
|
||||
@@ -746,13 +755,13 @@ type Entry struct {
|
||||
func (i *Inventory) Catalogued(ctx context.Context) ([]Entry, error) {
|
||||
rows, err := i.store.Pool().Query(ctx,
|
||||
`select m.name, m.manifest,
|
||||
coalesce(m.source, ''), coalesce(m.ref, ''),
|
||||
coalesce(m.source, ''), m.source_path, coalesce(m.ref, ''),
|
||||
coalesce(m.built_from, ''), coalesce(m.source_head, ''),
|
||||
coalesce(array_agg(n.name order by n.name) filter (where n.name is not null), '{}')
|
||||
from module m
|
||||
left join assignment a on a.module = m.name
|
||||
left join node n on n.id = a.node
|
||||
group by m.name, m.manifest, m.source, m.ref, m.built_from, m.source_head
|
||||
group by m.name, m.manifest, m.source, m.source_path, m.ref, m.built_from, m.source_head
|
||||
order by m.name`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -765,7 +774,7 @@ func (i *Inventory) Catalogued(ctx context.Context) ([]Entry, error) {
|
||||
var name string
|
||||
var source Source
|
||||
var on []string
|
||||
if err := rows.Scan(&name, &raw, &source.Repository, &source.Ref,
|
||||
if err := rows.Scan(&name, &raw, &source.Repository, &source.Path, &source.Ref,
|
||||
&source.BuiltFrom, &source.Head, &on); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user