blob: 8e7ca220ddb5d9705bee5cc1c0461db4eea1ba68 [file] [log] [blame]
// Copyright 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package cli
import (
"context"
"strings"
"github.com/maruel/subcommands"
"go.chromium.org/luci/common/cli"
"go.chromium.org/luci/common/data/text"
"go.chromium.org/luci/common/errors"
"go.chromium.org/luci/common/system/signals"
"infra/tools/dirmd"
dirmdpb "infra/tools/dirmd/proto"
)
func cmdRead() *subcommands.Command {
return &subcommands.Command{
UsageLine: `read [DIR1 [DIR2]...]`,
ShortDesc: "read metadata from the specified directories",
Advanced: true,
LongDesc: text.Doc(`
Read metadata from the specified directories and print to stdout or a file.
Each directory must reside in a git checkout.
One of the repos must be the root repo, while other repos must be its
sub-repos. In other words, all git repos referred to by the directories
must be subdirectories of one of the repos.
The root dir of the root repo becomes the metadata root.
Unless -form is sparse, the output includes metadata of ancestors and
descendants of the specified directories.
In the sparse form, metadata of only the specified directories is
returned, which is usually much faster.
Descendants of the specified directories are discovered using
"git ls-files <dir>" and not FS walk.
This means files outside of the repo are ignored, as well as files
matched by .gitignore files.
Note that when reading ancestors of the specified directories,
the .gitignore files are not respected.
This inconsistency should not make a difference in
the vast majority of cases because it is confusing to have
git-ignored DIR_METADATA in the middle of the ancestry chain.
Such a case might indicate that DIR_METADATA files are used incorrectly.
This behavior can be changed, but it would come with a performance penalty.
The output format is JSON form of chrome.dir_metadata.Mapping protobuf message.
`),
CommandRun: func() subcommands.CommandRun {
r := &readRun{}
r.RegisterOutputFlag()
r.Flags.StringVar(&r.formString, "form", "original", text.Doc(`
The form of the returned mapping.
Valid values: "original", "reduced", "computed", "sparse", "full".
See chrome.dir_meta enum MappingForm for their descriptions.
https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/proto/mapping.proto;l=51?q=mappingform
`))
return r
},
}
}
type readRun struct {
baseCommandRun
formString string
}
func (r *readRun) Run(a subcommands.Application, args []string, env subcommands.Env) int {
ctx := cli.GetContext(a, r, env)
return r.done(ctx, r.run(ctx, args))
}
func (r *readRun) run(ctx context.Context, dirs []string) error {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
defer signals.HandleInterrupt(cancel)()
if len(dirs) == 0 {
dirs = []string{"."}
}
formInt, ok := dirmdpb.MappingForm_value[strings.ToUpper(r.formString)]
if !ok {
return errors.Reason("invalid value of -form").Err()
}
form := dirmdpb.MappingForm(formInt)
mapping, err := dirmd.ReadMapping(ctx, form, dirs...)
if err != nil {
return err
}
return r.writeMapping(mapping)
}