Files
convy/internal/cli/pull.go
T
av b6b0976c19 исправлены находки ревью проектной стороны
- манифест читается так, как записан: решётка внутри строки не открывает
  комментарий, скобка внутри комментария не закрывает массив, имя внутри
  комментария не становится подпиской; новый ключ встаёт после массива,
  а не внутрь него
- всё записываемое проходит через manifest.Quote — обратный слэш в пути
  делал файл, который инструмент сам не читает
- маркер локальной части переехал в doc и пропускает огороженные блоки:
  процитированный в примере маркер больше не считается границей, а копия
  без маркера не перезаписывается молча
- лишний позиционный аргумент отсекается: flag прекращал разбор и прятал
  флаги после себя, из-за чего pull, list и check игнорировали --for
- заведены тесты проверок копий, включая молчание на исправной копии
2026-07-27 21:16:29 +03:00

108 lines
2.9 KiB
Go

package cli
import (
"flag"
"fmt"
"git.vakhrushev.me/av/convy/internal/project"
)
// convy pull reassembles what the manifest lists. It never reports what
// changed: after a pull that is shown by git diff, and the decision to accept,
// to fix or to roll back is taken by a person before the commit. A second
// mechanism for comparing files, standing next to git, would answer the same
// question worse.
func runPull(env Env, args []string) ExitCode {
fs := flag.NewFlagSet("convy pull", flag.ContinueOnError)
fs.SetOutput(env.Err)
root := fs.String("root", "", "root of the project; it is looked up upwards by default")
forComponent := fs.String("for", "", "component to reassemble; every one of them by default")
if err := fs.Parse(args); err != nil {
return Usage
}
if code := noStrayArgs(env, "convy pull", fs.Args()); code != OK {
return code
}
o, code := openProject(env, *root)
if code != OK {
return code
}
defer o.Close()
if err := distinctDirs(o.Manifest); err != nil {
fmt.Fprintln(env.Err, err)
return Usage
}
names, code := components(env, o.Manifest, *forComponent)
if code != OK {
return code
}
failed := 0
written := 0
for i, name := range names {
c := o.Manifest.Components[name]
if i > 0 {
fmt.Fprintln(env.Out)
}
fmt.Fprintf(env.Out, "%s → %s\n", name, c.Dir)
if c.Dir == "" {
fmt.Fprintln(env.Err, " the component names no dir, and a copy has to be written somewhere")
failed++
continue
}
if len(c.Topics) == 0 {
fmt.Fprintln(env.Out, " subscribed to nothing yet")
}
// The path column is sized to the component rather than guessed: a
// name that overruns a fixed width breaks every row below it.
width := len(c.Dir) + 1 + len(project.ReadingName)
for _, topic := range c.Topics {
width = max(width, len(c.Dir)+len(topic)+4)
}
for _, topic := range c.Topics {
made, err := project.Assemble(o.Suite, o.Root, c, topic)
if err != nil {
fmt.Fprintf(env.Err, " %s: %s\n", topic, err)
failed++
continue
}
written++
fmt.Fprintf(env.Out, " %-*s %s%s\n", width, made.Path, plural(len(made.Layers), "layer"), kept(made))
}
guide, err := project.Reading(o.LangRoot, o.Suite.Manifest.Language.Reading, o.Root, c.Dir)
if err != nil {
fmt.Fprintf(env.Err, " %s\n", err)
failed++
continue
}
fmt.Fprintf(env.Out, " %-*s the guide to reading a rule\n", width, guide)
}
fmt.Fprintf(env.Out, "\n%s assembled", plural(written, "file"))
if failed > 0 {
fmt.Fprintf(env.Out, ", %s\n", plural(failed, "failure"))
return Failed
}
fmt.Fprintln(env.Out)
fmt.Fprintln(env.Out, "git diff says what changed")
return OK
}
// kept notes that a local part was carried over, because that is the one thing
// a reassembly could have destroyed and did not.
func kept(c project.Copy) string {
switch {
case c.Created:
return ", new"
case c.Kept:
return ", local part kept"
}
return ""
}