[roller-configurator] Support jiri pkgs and projects
Add logic to resolve details about Jiri projects and packages based
solely on project/package names, as was as validation (e.g. checking that the referenced projects/packages actually exist in Jiri manifests).
Bug: b/42051371
Change-Id: Iea88be48bc97c383102bd7d38572dcc183b7d0dc
Reviewed-on: https://fuchsia-review.googlesource.com/c/infra/infra/+/964153
Reviewed-by: Carver Forbes <carverforbes@google.com>
Commit-Queue: Auto-Submit <auto-submit@fuchsia-infra.iam.gserviceaccount.com>
Fuchsia-Auto-Submit: Oliver Newman <olivernewman@google.com>
diff --git a/cmd/roller-configurator/jiri/manifest.go b/cmd/roller-configurator/jiri/manifest.go
new file mode 100644
index 0000000..363fb36
--- /dev/null
+++ b/cmd/roller-configurator/jiri/manifest.go
@@ -0,0 +1,61 @@
+// Copyright 2023 The Fuchsia 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 jiri contains jiri manifest schema definitions copied from Jiri's
+// source code.
+//
+// Only fields relevant to roller configuration are included, others are
+// omitted.
+package jiri
+
+import (
+ "encoding/xml"
+ "errors"
+ "fmt"
+ "io"
+ "os"
+)
+
+type Manifest struct {
+ Projects []Project `xml:"projects>project"`
+ Packages []Package `xml:"packages>package"`
+ XMLName struct{} `xml:"manifest"`
+}
+
+// Project represents a jiri project.
+type Project struct {
+ // Name is the project name.
+ Name string `xml:"name,attr,omitempty"`
+
+ // Remote is the project remote.
+ Remote string `xml:"remote,attr,omitempty"`
+
+ // RemoteBranch is the name of the remote branch to track.
+ RemoteBranch string `xml:"remotebranch,attr,omitempty"`
+}
+
+// Package struct represents the <package> tag in manifest files.
+type Package struct {
+ // Name represents the remote cipd path of the package.
+ Name string `xml:"name,attr"`
+
+ // Version represents the version tag of the cipd package.
+ Version string `xml:"version,attr"`
+}
+
+// LoadManifest reads a Jiri manifest from disk.
+func LoadManifest(path string) (*Manifest, error) {
+ b, err := os.ReadFile(path)
+ if err != nil {
+ return nil, err
+ }
+ var manifest Manifest
+ if err := xml.Unmarshal(b, &manifest); err != nil {
+ if errors.Is(err, io.EOF) {
+ return nil, fmt.Errorf("invalid XML in %s", path)
+ }
+ return nil, err
+ }
+ return &manifest, nil
+}
diff --git a/cmd/roller-configurator/proto/resolvers.go b/cmd/roller-configurator/proto/resolvers.go
index 1a94844..078aef8 100644
--- a/cmd/roller-configurator/proto/resolvers.go
+++ b/cmd/roller-configurator/proto/resolvers.go
@@ -6,18 +6,14 @@
import (
"context"
- "encoding/json"
-
- "google.golang.org/protobuf/encoding/protojson"
- "google.golang.org/protobuf/proto"
)
func (s *GitSubmodule) Resolve(ctx context.Context, repoRoot string) (map[string]any, error) {
- res, err := protoToMap(s)
+ res, err := ProtoToMap(s)
if err != nil {
return nil, err
}
- res["url"], err = s.url(ctx, repoRoot)
+ res["remote"], err = s.url(ctx, repoRoot)
if err != nil {
return nil, err
}
@@ -26,7 +22,7 @@
}
func (c *CIPDEnsureFile) Resolve(ctx context.Context, repoRoot string) (map[string]any, error) {
- res, err := protoToMap(c)
+ res, err := ProtoToMap(c)
if err != nil {
return nil, err
}
@@ -35,35 +31,28 @@
}
func (p *JiriProject) Resolve(ctx context.Context, repoRoot string) (map[string]any, error) {
- res, err := protoToMap(p)
+ me, err := p.manifestEntry(repoRoot)
if err != nil {
return nil, err
}
+ res, err := ProtoToMap(p)
+ if err != nil {
+ return nil, err
+ }
+ res["remote"] = me.Remote
+ res["remote_branch"] = me.RemoteBranch
res["type"] = "jiri_project"
return res, nil
}
func (p *JiriPackages) Resolve(ctx context.Context, repoRoot string) (map[string]any, error) {
- res, err := protoToMap(p)
+ if p.Ref == "" {
+ p.Ref = "latest"
+ }
+ res, err := ProtoToMap(p)
if err != nil {
return nil, err
}
res["type"] = "jiri_packages"
return res, nil
}
-
-func protoToMap(m proto.Message) (map[string]any, error) {
- opts := protojson.MarshalOptions{
- UseProtoNames: true,
- EmitUnpopulated: true,
- }
- b, err := opts.Marshal(m)
- if err != nil {
- return nil, err
- }
- res := make(map[string]any)
- if err := json.Unmarshal(b, &res); err != nil {
- return nil, err
- }
- return res, nil
-}
diff --git a/cmd/roller-configurator/proto/rollers_cfg.pb.go b/cmd/roller-configurator/proto/rollers_cfg.pb.go
index 7c88c2a..2c505ad 100644
--- a/cmd/roller-configurator/proto/rollers_cfg.pb.go
+++ b/cmd/roller-configurator/proto/rollers_cfg.pb.go
@@ -13,7 +13,6 @@
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
- structpb "google.golang.org/protobuf/types/known/structpb"
reflect "reflect"
sync "sync"
)
@@ -378,8 +377,8 @@
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
- // Mapping from Jiri manifest path to list of packages to roll within that
- PackagesByManifest map[string]*structpb.ListValue `protobuf:"bytes,1,rep,name=packages_by_manifest,json=packagesByManifest,proto3" json:"packages_by_manifest,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
+ // Jiri manifests containing packages to roll.
+ Manifests []*JiriPackages_Manifest `protobuf:"bytes,1,rep,name=manifests,proto3" json:"manifests,omitempty"`
// CIPD ref to track. Default is "latest".
Ref string `protobuf:"bytes,2,opt,name=ref,proto3" json:"ref,omitempty"`
}
@@ -416,9 +415,9 @@
return file_rollers_cfg_proto_rawDescGZIP(), []int{5}
}
-func (x *JiriPackages) GetPackagesByManifest() map[string]*structpb.ListValue {
+func (x *JiriPackages) GetManifests() []*JiriPackages_Manifest {
if x != nil {
- return x.PackagesByManifest
+ return x.Manifests
}
return nil
}
@@ -430,62 +429,110 @@
return ""
}
+type JiriPackages_Manifest struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"`
+ Packages []string `protobuf:"bytes,2,rep,name=packages,proto3" json:"packages,omitempty"`
+}
+
+func (x *JiriPackages_Manifest) Reset() {
+ *x = JiriPackages_Manifest{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_rollers_cfg_proto_msgTypes[6]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *JiriPackages_Manifest) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*JiriPackages_Manifest) ProtoMessage() {}
+
+func (x *JiriPackages_Manifest) ProtoReflect() protoreflect.Message {
+ mi := &file_rollers_cfg_proto_msgTypes[6]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use JiriPackages_Manifest.ProtoReflect.Descriptor instead.
+func (*JiriPackages_Manifest) Descriptor() ([]byte, []int) {
+ return file_rollers_cfg_proto_rawDescGZIP(), []int{5, 0}
+}
+
+func (x *JiriPackages_Manifest) GetPath() string {
+ if x != nil {
+ return x.Path
+ }
+ return ""
+}
+
+func (x *JiriPackages_Manifest) GetPackages() []string {
+ if x != nil {
+ return x.Packages
+ }
+ return nil
+}
+
var File_rollers_cfg_proto protoreflect.FileDescriptor
var file_rollers_cfg_proto_rawDesc = []byte{
0x0a, 0x11, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x73, 0x5f, 0x63, 0x66, 0x67, 0x2e, 0x70, 0x72,
- 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74,
- 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74,
- 0x6f, 0x22, 0x2b, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x21, 0x0a, 0x07, 0x72,
- 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x07, 0x2e, 0x52,
- 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x52, 0x07, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x73, 0x22, 0xa9,
- 0x02, 0x0a, 0x06, 0x52, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x12, 0x2d, 0x0a, 0x09, 0x73, 0x75, 0x62,
- 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x47,
- 0x69, 0x74, 0x53, 0x75, 0x62, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x48, 0x00, 0x52, 0x09, 0x73,
- 0x75, 0x62, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x3b, 0x0a, 0x10, 0x63, 0x69, 0x70, 0x64,
- 0x5f, 0x65, 0x6e, 0x73, 0x75, 0x72, 0x65, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01,
- 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x43, 0x49, 0x50, 0x44, 0x45, 0x6e, 0x73, 0x75, 0x72, 0x65, 0x46,
- 0x69, 0x6c, 0x65, 0x48, 0x00, 0x52, 0x0e, 0x63, 0x69, 0x70, 0x64, 0x45, 0x6e, 0x73, 0x75, 0x72,
- 0x65, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x31, 0x0a, 0x0c, 0x6a, 0x69, 0x72, 0x69, 0x5f, 0x70, 0x72,
- 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x4a, 0x69,
- 0x72, 0x69, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x48, 0x00, 0x52, 0x0b, 0x6a, 0x69, 0x72,
- 0x69, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x34, 0x0a, 0x0d, 0x6a, 0x69, 0x72, 0x69,
- 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32,
- 0x0d, 0x2e, 0x4a, 0x69, 0x72, 0x69, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x48, 0x00,
- 0x52, 0x0c, 0x6a, 0x69, 0x72, 0x69, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x12, 0x1a,
- 0x0a, 0x08, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09,
- 0x52, 0x08, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x6e, 0x6f,
- 0x74, 0x69, 0x66, 0x79, 0x5f, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28,
- 0x09, 0x52, 0x0c, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x73, 0x42,
- 0x09, 0x0a, 0x07, 0x74, 0x6f, 0x5f, 0x72, 0x6f, 0x6c, 0x6c, 0x22, 0x22, 0x0a, 0x0c, 0x47, 0x69,
- 0x74, 0x53, 0x75, 0x62, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61,
- 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x22, 0x36,
- 0x0a, 0x0e, 0x43, 0x49, 0x50, 0x44, 0x45, 0x6e, 0x73, 0x75, 0x72, 0x65, 0x46, 0x69, 0x6c, 0x65,
- 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
- 0x70, 0x61, 0x74, 0x68, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x65, 0x66, 0x18, 0x02, 0x20, 0x01, 0x28,
- 0x09, 0x52, 0x03, 0x72, 0x65, 0x66, 0x22, 0x43, 0x0a, 0x0b, 0x4a, 0x69, 0x72, 0x69, 0x50, 0x72,
- 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73,
- 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73,
- 0x74, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x02, 0x20, 0x01,
- 0x28, 0x09, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x22, 0xdc, 0x01, 0x0a, 0x0c,
- 0x4a, 0x69, 0x72, 0x69, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x12, 0x57, 0x0a, 0x14,
- 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x5f, 0x62, 0x79, 0x5f, 0x6d, 0x61, 0x6e, 0x69,
- 0x66, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x4a, 0x69, 0x72,
- 0x69, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67,
- 0x65, 0x73, 0x42, 0x79, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x45, 0x6e, 0x74, 0x72,
- 0x79, 0x52, 0x12, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x42, 0x79, 0x4d, 0x61, 0x6e,
- 0x69, 0x66, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x65, 0x66, 0x18, 0x02, 0x20, 0x01,
- 0x28, 0x09, 0x52, 0x03, 0x72, 0x65, 0x66, 0x1a, 0x61, 0x0a, 0x17, 0x50, 0x61, 0x63, 0x6b, 0x61,
- 0x67, 0x65, 0x73, 0x42, 0x79, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x45, 0x6e, 0x74,
- 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
- 0x03, 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20,
- 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
- 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52,
- 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x34, 0x5a, 0x32, 0x67, 0x6f,
- 0x2e, 0x66, 0x75, 0x63, 0x68, 0x73, 0x69, 0x61, 0x2e, 0x64, 0x65, 0x76, 0x2f, 0x69, 0x6e, 0x66,
- 0x72, 0x61, 0x2f, 0x63, 0x6d, 0x64, 0x2f, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2d, 0x63, 0x6f,
- 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
- 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+ 0x6f, 0x74, 0x6f, 0x22, 0x2b, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x21, 0x0a,
+ 0x07, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x07,
+ 0x2e, 0x52, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x52, 0x07, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x73,
+ 0x22, 0xa9, 0x02, 0x0a, 0x06, 0x52, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x12, 0x2d, 0x0a, 0x09, 0x73,
+ 0x75, 0x62, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d,
+ 0x2e, 0x47, 0x69, 0x74, 0x53, 0x75, 0x62, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x48, 0x00, 0x52,
+ 0x09, 0x73, 0x75, 0x62, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x3b, 0x0a, 0x10, 0x63, 0x69,
+ 0x70, 0x64, 0x5f, 0x65, 0x6e, 0x73, 0x75, 0x72, 0x65, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x02,
+ 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x43, 0x49, 0x50, 0x44, 0x45, 0x6e, 0x73, 0x75, 0x72,
+ 0x65, 0x46, 0x69, 0x6c, 0x65, 0x48, 0x00, 0x52, 0x0e, 0x63, 0x69, 0x70, 0x64, 0x45, 0x6e, 0x73,
+ 0x75, 0x72, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x31, 0x0a, 0x0c, 0x6a, 0x69, 0x72, 0x69, 0x5f,
+ 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e,
+ 0x4a, 0x69, 0x72, 0x69, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x48, 0x00, 0x52, 0x0b, 0x6a,
+ 0x69, 0x72, 0x69, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x34, 0x0a, 0x0d, 0x6a, 0x69,
+ 0x72, 0x69, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28,
+ 0x0b, 0x32, 0x0d, 0x2e, 0x4a, 0x69, 0x72, 0x69, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73,
+ 0x48, 0x00, 0x52, 0x0c, 0x6a, 0x69, 0x72, 0x69, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73,
+ 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x05, 0x20, 0x01,
+ 0x28, 0x09, 0x52, 0x08, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x23, 0x0a, 0x0d,
+ 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x5f, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x06, 0x20,
+ 0x03, 0x28, 0x09, 0x52, 0x0c, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x45, 0x6d, 0x61, 0x69, 0x6c,
+ 0x73, 0x42, 0x09, 0x0a, 0x07, 0x74, 0x6f, 0x5f, 0x72, 0x6f, 0x6c, 0x6c, 0x22, 0x22, 0x0a, 0x0c,
+ 0x47, 0x69, 0x74, 0x53, 0x75, 0x62, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04,
+ 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68,
+ 0x22, 0x36, 0x0a, 0x0e, 0x43, 0x49, 0x50, 0x44, 0x45, 0x6e, 0x73, 0x75, 0x72, 0x65, 0x46, 0x69,
+ 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
+ 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x65, 0x66, 0x18, 0x02, 0x20,
+ 0x01, 0x28, 0x09, 0x52, 0x03, 0x72, 0x65, 0x66, 0x22, 0x43, 0x0a, 0x0b, 0x4a, 0x69, 0x72, 0x69,
+ 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6d, 0x61, 0x6e, 0x69, 0x66,
+ 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6d, 0x61, 0x6e, 0x69, 0x66,
+ 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x02,
+ 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x22, 0x92, 0x01,
+ 0x0a, 0x0c, 0x4a, 0x69, 0x72, 0x69, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73, 0x12, 0x34,
+ 0x0a, 0x09, 0x6d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28,
+ 0x0b, 0x32, 0x16, 0x2e, 0x4a, 0x69, 0x72, 0x69, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x73,
+ 0x2e, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x52, 0x09, 0x6d, 0x61, 0x6e, 0x69, 0x66,
+ 0x65, 0x73, 0x74, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x65, 0x66, 0x18, 0x02, 0x20, 0x01, 0x28,
+ 0x09, 0x52, 0x03, 0x72, 0x65, 0x66, 0x1a, 0x3a, 0x0a, 0x08, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65,
+ 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
+ 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67,
+ 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67,
+ 0x65, 0x73, 0x42, 0x34, 0x5a, 0x32, 0x67, 0x6f, 0x2e, 0x66, 0x75, 0x63, 0x68, 0x73, 0x69, 0x61,
+ 0x2e, 0x64, 0x65, 0x76, 0x2f, 0x69, 0x6e, 0x66, 0x72, 0x61, 0x2f, 0x63, 0x6d, 0x64, 0x2f, 0x72,
+ 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74,
+ 0x6f, 0x72, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
@@ -502,14 +549,13 @@
var file_rollers_cfg_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
var file_rollers_cfg_proto_goTypes = []interface{}{
- (*Config)(nil), // 0: Config
- (*Roller)(nil), // 1: Roller
- (*GitSubmodule)(nil), // 2: GitSubmodule
- (*CIPDEnsureFile)(nil), // 3: CIPDEnsureFile
- (*JiriProject)(nil), // 4: JiriProject
- (*JiriPackages)(nil), // 5: JiriPackages
- nil, // 6: JiriPackages.PackagesByManifestEntry
- (*structpb.ListValue)(nil), // 7: google.protobuf.ListValue
+ (*Config)(nil), // 0: Config
+ (*Roller)(nil), // 1: Roller
+ (*GitSubmodule)(nil), // 2: GitSubmodule
+ (*CIPDEnsureFile)(nil), // 3: CIPDEnsureFile
+ (*JiriProject)(nil), // 4: JiriProject
+ (*JiriPackages)(nil), // 5: JiriPackages
+ (*JiriPackages_Manifest)(nil), // 6: JiriPackages.Manifest
}
var file_rollers_cfg_proto_depIdxs = []int32{
1, // 0: Config.rollers:type_name -> Roller
@@ -517,13 +563,12 @@
3, // 2: Roller.cipd_ensure_file:type_name -> CIPDEnsureFile
4, // 3: Roller.jiri_project:type_name -> JiriProject
5, // 4: Roller.jiri_packages:type_name -> JiriPackages
- 6, // 5: JiriPackages.packages_by_manifest:type_name -> JiriPackages.PackagesByManifestEntry
- 7, // 6: JiriPackages.PackagesByManifestEntry.value:type_name -> google.protobuf.ListValue
- 7, // [7:7] is the sub-list for method output_type
- 7, // [7:7] is the sub-list for method input_type
- 7, // [7:7] is the sub-list for extension type_name
- 7, // [7:7] is the sub-list for extension extendee
- 0, // [0:7] is the sub-list for field type_name
+ 6, // 5: JiriPackages.manifests:type_name -> JiriPackages.Manifest
+ 6, // [6:6] is the sub-list for method output_type
+ 6, // [6:6] is the sub-list for method input_type
+ 6, // [6:6] is the sub-list for extension type_name
+ 6, // [6:6] is the sub-list for extension extendee
+ 0, // [0:6] is the sub-list for field type_name
}
func init() { file_rollers_cfg_proto_init() }
@@ -604,6 +649,18 @@
return nil
}
}
+ file_rollers_cfg_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*JiriPackages_Manifest); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
}
file_rollers_cfg_proto_msgTypes[1].OneofWrappers = []interface{}{
(*Roller_Submodule)(nil),
diff --git a/cmd/roller-configurator/proto/rollers_cfg.proto b/cmd/roller-configurator/proto/rollers_cfg.proto
index a23477f..24e9672 100644
--- a/cmd/roller-configurator/proto/rollers_cfg.proto
+++ b/cmd/roller-configurator/proto/rollers_cfg.proto
@@ -4,8 +4,6 @@
syntax = "proto3";
-import "google/protobuf/struct.proto";
-
option go_package = "go.fuchsia.dev/infra/cmd/roller-configurator/proto";
message Config {
@@ -62,8 +60,13 @@
// CIPD packages pinned in a Jiri manifest to roll.
message JiriPackages {
- // Mapping from Jiri manifest path to list of packages to roll within that
- map<string, google.protobuf.ListValue> packages_by_manifest = 1;
+ message Manifest {
+ string path = 1;
+ repeated string packages = 2;
+ }
+
+ // Jiri manifests containing packages to roll.
+ repeated Manifest manifests = 1;
// CIPD ref to track. Default is "latest".
string ref = 2;
diff --git a/cmd/roller-configurator/proto/util.go b/cmd/roller-configurator/proto/util.go
new file mode 100644
index 0000000..9eca643
--- /dev/null
+++ b/cmd/roller-configurator/proto/util.go
@@ -0,0 +1,29 @@
+// Copyright 2023 The Fuchsia 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 proto
+
+import (
+ "encoding/json"
+
+ "google.golang.org/protobuf/encoding/protojson"
+ "google.golang.org/protobuf/proto"
+)
+
+// ProtoToMap converts a proto message to a Go map.
+func ProtoToMap(m proto.Message) (map[string]any, error) {
+ opts := protojson.MarshalOptions{
+ UseProtoNames: true,
+ EmitUnpopulated: true,
+ }
+ b, err := opts.Marshal(m)
+ if err != nil {
+ return nil, err
+ }
+ res := make(map[string]any)
+ if err := json.Unmarshal(b, &res); err != nil {
+ return nil, err
+ }
+ return res, nil
+}
diff --git a/cmd/roller-configurator/proto/validators.go b/cmd/roller-configurator/proto/validators.go
index 790f0a0..26a1dad 100644
--- a/cmd/roller-configurator/proto/validators.go
+++ b/cmd/roller-configurator/proto/validators.go
@@ -6,11 +6,15 @@
import (
"context"
+ "errors"
"fmt"
+ "io/fs"
"os"
"os/exec"
"path/filepath"
"strings"
+
+ "go.fuchsia.dev/infra/cmd/roller-configurator/jiri"
)
func (s *GitSubmodule) url(ctx context.Context, repoRoot string) (string, error) {
@@ -59,21 +63,53 @@
}
func (p *JiriProject) Validate(ctx context.Context, repoRoot string) error {
- if _, err := os.Stat(filepath.Join(repoRoot, p.Manifest)); err != nil {
- return fmt.Errorf("no such file: %s", p.Manifest)
+ _, err := p.manifestEntry(repoRoot)
+ return err
+}
+
+func (p *JiriProject) manifestEntry(repoRoot string) (jiri.Project, error) {
+ manifest, err := jiri.LoadManifest(filepath.Join(repoRoot, p.Manifest))
+ if err != nil {
+ if errors.Is(err, fs.ErrNotExist) {
+ err = fmt.Errorf("no such file: %s", p.Manifest)
+ }
+ return jiri.Project{}, err
}
- // TODO(olivernewman): Check that the project name refers to a valid entry
- // in the manifest.
- return nil
+ for _, proj := range manifest.Projects {
+ if proj.Name == p.Project {
+ return proj, nil
+ }
+ }
+ return jiri.Project{}, fmt.Errorf("no project %q in manifest %q", p.Project, p.Manifest)
}
func (p *JiriPackages) Validate(ctx context.Context, repoRoot string) error {
- for manifest := range p.PackagesByManifest {
- if _, err := os.Stat(filepath.Join(repoRoot, manifest)); err != nil {
- return fmt.Errorf("no such file: %s", manifest)
+ _, err := p.manifestEntries(repoRoot)
+ return err
+}
+
+func (p *JiriPackages) manifestEntries(repoRoot string) ([]jiri.Package, error) {
+ var entries []jiri.Package
+ for _, m := range p.Manifests {
+ manifest, err := jiri.LoadManifest(filepath.Join(repoRoot, m.Path))
+ if err != nil {
+ if errors.Is(err, fs.ErrNotExist) {
+ err = fmt.Errorf("no such file: %s", m.Path)
+ }
+ return nil, err
+ }
+ for _, pkg := range m.Packages {
+ var found bool
+ for _, entry := range manifest.Packages {
+ if entry.Name == pkg {
+ entries = append(entries, entry)
+ found = true
+ }
+ }
+ if !found {
+ return nil, fmt.Errorf("no package %q in manifest %q", pkg, m.Path)
+ }
}
}
- // TODO(olivernewman): Check that the package names refer to valid entries
- // in the manifest.
- return nil
+ return entries, nil
}
diff --git a/cmd/roller-configurator/resolve.go b/cmd/roller-configurator/resolve.go
index 6bfa281..1932405 100644
--- a/cmd/roller-configurator/resolve.go
+++ b/cmd/roller-configurator/resolve.go
@@ -87,11 +87,18 @@
log.Panicf("unknown to_roll type: %q", field.Name())
}
- resolved, err := toResolve.Resolve(ctx, repoRoot)
+ resolved, err := proto.ProtoToMap(roller)
if err != nil {
return err
}
-
+ delete(resolved, string(field.Name()))
+ addl, err := toResolve.Resolve(ctx, repoRoot)
+ if err != nil {
+ return err
+ }
+ for k, v := range addl {
+ resolved[k] = v
+ }
out = append(out, resolved)
}
enc := json.NewEncoder(output)
diff --git a/cmd/roller-configurator/resolve_test.go b/cmd/roller-configurator/resolve_test.go
index cb0f29a..68ed827 100644
--- a/cmd/roller-configurator/resolve_test.go
+++ b/cmd/roller-configurator/resolve_test.go
@@ -19,7 +19,23 @@
".gitmodules": `
[submodule "path/to/submodule"]
url = "https://example.com/lib"
- `,
+ `,
+ "path/to/jiri_manifest": `
+ <?xml version="1.0" encoding="UTF-8"?>
+ <manifest>
+ <projects>
+ <project name="foo-project"
+ remote="https://example.com/jiri-project"
+ anotherfield="bar"/>
+ </projects>
+ <packages>
+ <package name="foo/package1/${platform}"
+ anotherfield="baz"/>
+ <package name="foo/package2/${platform}"
+ anotherfield="baz"/>
+ </packages>
+ </manifest>
+ `,
}
func TestResolve(t *testing.T) {
@@ -42,13 +58,98 @@
submodule: {
path: "path/to/submodule"
}
+ schedule: "* * * * *"
}
]`,
want: `[
{
"type": "submodule",
"path": "path/to/submodule",
- "url": "https://example.com/lib"
+ "remote": "https://example.com/lib",
+ "schedule": "* * * * *",
+ "notify_emails": []
+ }
+ ]`,
+ },
+ {
+ name: "jiri project roller",
+ input: `rollers: [
+ {
+ jiri_project: {
+ manifest: "path/to/jiri_manifest"
+ project: "foo-project"
+ }
+ }
+ ]`,
+ want: `[
+ {
+ "type": "jiri_project",
+ "manifest": "path/to/jiri_manifest",
+ "project": "foo-project",
+ "remote": "https://example.com/jiri-project",
+ "remote_branch": "",
+ "schedule": "",
+ "notify_emails": []
+ }
+ ]`,
+ },
+ {
+ name: "jiri package roller",
+ input: `rollers: [
+ {
+ jiri_packages: {
+ ref: "foo"
+ manifests: [
+ {
+ path: "path/to/jiri_manifest"
+ packages: [
+ "foo/package1/${platform}"
+ ]
+ }
+ ]
+ }
+ },
+ {
+ jiri_packages: {
+ manifests: [
+ {
+ path: "path/to/jiri_manifest"
+ packages: [
+ "foo/package2/${platform}"
+ ]
+ }
+ ]
+ }
+ }
+ ]`,
+ want: `[
+ {
+ "type": "jiri_packages",
+ "manifests": [
+ {
+ "path": "path/to/jiri_manifest",
+ "packages": [
+ "foo/package1/${platform}"
+ ]
+ }
+ ],
+ "ref": "foo",
+ "schedule": "",
+ "notify_emails": []
+ },
+ {
+ "type": "jiri_packages",
+ "manifests": [
+ {
+ "path": "path/to/jiri_manifest",
+ "packages": [
+ "foo/package2/${platform}"
+ ]
+ }
+ ],
+ "ref": "latest",
+ "schedule": "",
+ "notify_emails": []
}
]`,
},
diff --git a/cmd/roller-configurator/validate_test.go b/cmd/roller-configurator/validate_test.go
index fa75322..b827581 100644
--- a/cmd/roller-configurator/validate_test.go
+++ b/cmd/roller-configurator/validate_test.go
@@ -11,7 +11,6 @@
"testing"
"go.fuchsia.dev/infra/cmd/roller-configurator/proto"
- "google.golang.org/protobuf/types/known/structpb"
)
var filesWithGitmodules = map[string]string{
@@ -21,6 +20,22 @@
`,
}
+var filesWithJiriManifest = map[string]string{
+ "path/to/jiri_manifest": `
+ <?xml version="1.0" encoding="UTF-8"?>
+ <manifest>
+ <projects>
+ <project name="foo-project"
+ remote="https://example.com/jiri-project"/>
+ </projects>
+ <packages>
+ <package name="package1"/>
+ <package name="package2"/>
+ </packages>
+ </manifest>
+ `,
+}
+
func TestValidate_valid(t *testing.T) {
t.Parallel()
@@ -70,8 +85,8 @@
Rollers: []*proto.Roller{
{
ToRoll: &proto.Roller_JiriProject{JiriProject: &proto.JiriProject{
- Manifest: "path/to/manifest",
- Project: "project-name",
+ Manifest: "path/to/jiri_manifest",
+ Project: "foo-project",
}},
NotifyEmails: []string{
"foo@example.com",
@@ -80,9 +95,7 @@
},
},
},
- files: map[string]string{
- "path/to/manifest": ``,
- },
+ files: filesWithJiriManifest,
},
{
name: "jiri packages",
@@ -90,11 +103,12 @@
Rollers: []*proto.Roller{
{
ToRoll: &proto.Roller_JiriPackages{JiriPackages: &proto.JiriPackages{
- PackagesByManifest: map[string]*structpb.ListValue{
- "path/to/manifest": {
- Values: []*structpb.Value{
- structpb.NewStringValue("package1"),
- structpb.NewStringValue("package2"),
+ Manifests: []*proto.JiriPackages_Manifest{
+ {
+ Path: "path/to/jiri_manifest",
+ Packages: []string{
+ "package1",
+ "package2",
},
},
},
@@ -102,9 +116,7 @@
},
},
},
- files: map[string]string{
- "path/to/manifest": ``,
- },
+ files: filesWithJiriManifest,
},
}
@@ -286,11 +298,12 @@
Rollers: []*proto.Roller{
{
ToRoll: &proto.Roller_JiriPackages{JiriPackages: &proto.JiriPackages{
- PackagesByManifest: map[string]*structpb.ListValue{
- "path/to/manifest": {
- Values: []*structpb.Value{
- structpb.NewStringValue("package1"),
- structpb.NewStringValue("package2"),
+ Manifests: []*proto.JiriPackages_Manifest{
+ {
+ Path: "path/to/manifest",
+ Packages: []string{
+ "package1",
+ "package2",
},
},
},
@@ -300,6 +313,43 @@
},
err: "no such file: path/to/manifest",
},
+ {
+ name: "invalid jiri project",
+ config: &proto.Config{
+ Rollers: []*proto.Roller{
+ {
+ ToRoll: &proto.Roller_JiriProject{JiriProject: &proto.JiriProject{
+ Manifest: "path/to/jiri_manifest",
+ Project: "not-a-project",
+ }},
+ },
+ },
+ },
+ files: filesWithJiriManifest,
+ err: `no project "not-a-project" in manifest "path/to/jiri_manifest"`,
+ },
+ {
+ name: "invalid jiri package",
+ config: &proto.Config{
+ Rollers: []*proto.Roller{
+ {
+ ToRoll: &proto.Roller_JiriPackages{JiriPackages: &proto.JiriPackages{
+ Manifests: []*proto.JiriPackages_Manifest{
+ {
+ Path: "path/to/jiri_manifest",
+ Packages: []string{
+ "bad-package1",
+ "bad-package2",
+ },
+ },
+ },
+ }},
+ },
+ },
+ },
+ files: filesWithJiriManifest,
+ err: `no package "bad-package1" in manifest "path/to/jiri_manifest"`,
+ },
}
for _, tc := range testCases {