закрыты известные остатки

- документ самоуправления объявляется ключом governance, а не угадывается
  по «он один и без ключей оси»: конвенция, потерявшая topic, была от него
  неотличима и тихо теряла все проверки об отъезде к потребителю
- проверка путей канона больше не ловит README.md и READING.md — эти два
  имени значат что-то и на стороне потребителя
- lang.Recognize требует совпадения и слов, и номера версии; директории
  компонентов сверяются на вложенность, а не только на равенство
- у обеих проверок появился --json, а convy sync называет ссылки на темы,
  которых компонент не взял
This commit is contained in:
av
2026-07-28 10:16:27 +03:00
parent 29d29740f5
commit 600aba5ee3
16 changed files with 413 additions and 70 deletions
+26 -8
View File
@@ -158,21 +158,39 @@ func components(env Env, m *manifest.Project, name string) ([]string, ExitCode)
return m.Names(), OK
}
// distinctDirs checks that no two components write into the same directory.
// Two copies of one topic would otherwise collide by name, and that is an error
// of the manifest rather than a reason to rename files.
// distinctDirs checks that no two components write into the same place. Two
// copies of one topic would otherwise collide by name, and that is an error of
// the manifest rather than a reason to rename files.
//
// One directory inside another is the same error told less plainly: whatever
// walks the outer one finds the copies of the inner, and every command that
// counts files counts them twice.
func distinctDirs(m *manifest.Project) error {
seen := make(map[string]string)
dirs := make(map[string]string, len(m.Components))
for _, name := range m.Names() {
dir := filepath.ToSlash(filepath.Clean(m.Components[name].Dir))
if other, taken := seen[dir]; taken {
return fmt.Errorf("the components %q and %q share the directory %s: copies of one topic would collide there", other, name, dir)
dirs[name] = filepath.ToSlash(filepath.Clean(m.Components[name].Dir))
}
names := m.Names()
for i, a := range names {
for _, b := range names[i+1:] {
switch {
case dirs[a] == dirs[b]:
return fmt.Errorf("the components %q and %q share the directory %s: copies of one topic would collide there", a, b, dirs[a])
case within(dirs[a], dirs[b]):
return fmt.Errorf("the directory of the component %q (%s) lies inside the one of %q (%s): whatever walks the outer one finds the copies of the inner", b, dirs[b], a, dirs[a])
case within(dirs[b], dirs[a]):
return fmt.Errorf("the directory of the component %q (%s) lies inside the one of %q (%s): whatever walks the outer one finds the copies of the inner", a, dirs[a], b, dirs[b])
}
}
seen[dir] = name
}
return nil
}
// within reports whether inner lies under outer.
func within(outer, inner string) bool {
return strings.HasPrefix(inner, outer+"/")
}
// exists reports whether a path is there.
func exists(name string) bool {
_, err := os.Stat(name)