Skip to content

Commit 3a6e7b7

Browse files
authored
Fix writing contents to ephemeral repo after sync (#466)
* Try to add writing contents to ephemeral repo * Add copying and building buf image * Comments
1 parent d4da020 commit 3a6e7b7

File tree

3 files changed

+122
-13
lines changed

3 files changed

+122
-13
lines changed

pkg/repo/contents.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,43 @@ import (
2323
"google.golang.org/protobuf/proto"
2424
)
2525

26+
// TODO: Diffing method
27+
28+
// Take the namespace contents of `other` into `dst`.
29+
// Namespaces and features in `other` that are not in `dst` are added.
30+
// Conflicting features are overwritten with ones from `other`.
31+
// Namespaces in `dst` that are not in `other` are NOT removed.
32+
// Returns the modified `dst`. `other` should not be used after being passed.
33+
func TakeNamespaceContents(dst *featurev1beta1.RepositoryContents, other *featurev1beta1.RepositoryContents) *featurev1beta1.RepositoryContents {
34+
for _, otherNs := range other.Namespaces {
35+
replaced := false
36+
for i, dstNs := range dst.Namespaces {
37+
if dstNs.Name == otherNs.Name {
38+
dst.Namespaces[i] = otherNs
39+
replaced = true
40+
}
41+
}
42+
if !replaced {
43+
dst.Namespaces = append(dst.Namespaces, otherNs)
44+
}
45+
}
46+
// Need to also overwrite appropriate file descriptors
47+
// Assumes that file descriptors corresponding to namespaces have deterministic names
48+
for _, otherFD := range other.FileDescriptorSet.File {
49+
replaced := false
50+
for i, dstFD := range dst.FileDescriptorSet.File {
51+
if dstFD.Name == otherFD.Name {
52+
dst.FileDescriptorSet.File[i] = otherFD
53+
replaced = true
54+
}
55+
}
56+
if !replaced {
57+
dst.FileDescriptorSet.File = append(dst.FileDescriptorSet.File, otherFD)
58+
}
59+
}
60+
return dst
61+
}
62+
2663
// Expects base64 encoded serialized RepositoryContents message
2764
func DecodeRepositoryContents(encoded []byte) (*featurev1beta1.RepositoryContents, error) {
2865
// Because Protobuf is not self-describing, we have to jump through some hoops here for deserialization.

pkg/star/prototypes/proto.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,10 @@ func ReBuildDynamicTypeRegistry(ctx context.Context, protoDir string, useExterna
7272
}
7373
// Lint before generating the new buf image
7474
fullProtoDir := cw.GetFullPath(protoDir)
75-
if err := lint(fullProtoDir); err != nil {
75+
if err := BufLint(fullProtoDir); err != nil {
7676
return nil, errors.Wrap(err, "buf lint")
7777
}
78-
_, err := newBufImage(fullProtoDir)
78+
_, err := NewBufImage(fullProtoDir)
7979
if err != nil {
8080
return nil, errors.Wrap(err, "new buf image")
8181
}
@@ -198,15 +198,15 @@ func (st *SerializableTypes) AddFileDescriptor(fd protoreflect.FileDescriptor, c
198198
return nil
199199
}
200200

201-
type bufImage struct {
202-
filename string
201+
type BufImage struct {
202+
Filename string
203203
}
204204

205205
// Generates a buf image, which is compatible with
206206
// protobuf's native FileDescriptorSet type. This allows us to build a
207207
// registry of protobuf types, adding any user-defined types.
208208
// Note: expects that buf cmd line exists.
209-
func newBufImage(protoDir string) (*bufImage, error) {
209+
func NewBufImage(protoDir string) (*BufImage, error) {
210210
outputFile := bufImageFilepath(protoDir)
211211
args := []string{
212212
"build",
@@ -220,13 +220,13 @@ func newBufImage(protoDir string) (*bufImage, error) {
220220
if err := cmd.Run(); err != nil {
221221
return nil, errors.Wrap(err, "buf build")
222222
}
223-
return &bufImage{
224-
filename: outputFile,
223+
return &BufImage{
224+
Filename: outputFile,
225225
}, nil
226226
}
227227

228228
// Note: expects that buf cmd lint exists.
229-
func lint(protoDir string) error {
229+
func BufLint(protoDir string) error {
230230
cmd := exec.Command("buf", "lint", protoDir)
231231
cmd.Stdout = os.Stdout
232232
cmd.Stderr = os.Stderr

pkg/sync/repo.go

Lines changed: 77 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ package sync
1717
import (
1818
"context"
1919
"io"
20+
"os"
2021
"path/filepath"
2122
"strings"
2223

@@ -25,6 +26,7 @@ import (
2526
"github.com/lekkodev/cli/pkg/proto"
2627
"github.com/lekkodev/cli/pkg/repo"
2728
"github.com/lekkodev/cli/pkg/star"
29+
"github.com/lekkodev/cli/pkg/star/prototypes"
2830
"github.com/lekkodev/cli/pkg/star/static"
2931
"github.com/lekkodev/go-sdk/pkg/eval"
3032
"github.com/pkg/errors"
@@ -46,11 +48,17 @@ func WriteContentsToLocalRepo(ctx context.Context, contents *featurev1beta1.Repo
4648
if err != nil {
4749
return errors.Wrap(err, "prepare repo")
4850
}
51+
return WriteContentsToRepo(ctx, contents, r)
52+
}
53+
54+
func WriteContentsToRepo(ctx context.Context, contents *featurev1beta1.RepositoryContents, r repo.ConfigurationRepository) error {
4955
// Discard logs, mainly for silencing compilation later
50-
// TODO: Maybe a verbose flag
51-
r.ConfigureLogger(&repo.LoggingConfiguration{
52-
Writer: io.Discard,
56+
// TODO: Allow passing in writer
57+
clearFn := r.ConfigureLogger(&repo.LoggingConfiguration{
58+
Writer: io.Discard,
59+
ColorsDisabled: true,
5360
})
61+
defer clearFn()
5462
rootMD, _, err := r.ParseMetadata(ctx)
5563
if err != nil {
5664
return err
@@ -159,8 +167,35 @@ func WriteContentsToLocalRepo(ctx context.Context, contents *featurev1beta1.Repo
159167
if err := WriteTypesToRepo(ctx, contents.FileDescriptorSet, r); err != nil {
160168
return errors.Wrap(err, "write type files")
161169
}
162-
if _, err := r.ReBuildDynamicTypeRegistry(ctx, rootMD.ProtoDirectory, false); err != nil {
163-
return errors.Wrap(err, "final rebuild type registry")
170+
// NOTE: This is a workaround to correctly rebuild the type registry in ephemeral repositories.
171+
// Because ephemeral repos use an in-mem fs, we can't run the buf cmd line on its files.
172+
// So we copy the proto dir to a temp dir in the real fs, build there, then copy the image back.
173+
// Still does require the buf CLI to be available.
174+
tmpProtoDir, err := os.MkdirTemp("", "proto")
175+
if err != nil {
176+
return errors.Wrap(err, "create tmp proto dir")
177+
}
178+
defer os.RemoveAll(tmpProtoDir)
179+
if err := copyDirFiles(ctx, r, rootMD.ProtoDirectory, tmpProtoDir); err != nil {
180+
return errors.Wrap(err, "copy proto files")
181+
}
182+
bufIn := filepath.Join(tmpProtoDir, rootMD.ProtoDirectory)
183+
if err := prototypes.BufLint(bufIn); err != nil {
184+
return errors.Wrap(err, "lint protos")
185+
}
186+
bufImage, err := prototypes.NewBufImage(bufIn)
187+
if err != nil {
188+
return errors.Wrap(err, "build protos")
189+
}
190+
bufContents, err := os.ReadFile(bufImage.Filename)
191+
if err != nil {
192+
return errors.Wrap(err, "read buf image")
193+
}
194+
if err := r.WriteFile(filepath.Join(rootMD.ProtoDirectory, filepath.Base(bufImage.Filename)), bufContents, 0644); err != nil {
195+
return errors.Wrap(err, "copy back buf image")
196+
}
197+
if _, err := r.BuildDynamicTypeRegistry(ctx, rootMD.ProtoDirectory); err != nil {
198+
return errors.Wrap(err, "final build type registry")
164199
}
165200

166201
// Final compile to verify overall health
@@ -206,3 +241,40 @@ func WriteTypesToRepo(ctx context.Context, fds *descriptorpb.FileDescriptorSet,
206241
})
207242
return writeErr
208243
}
244+
245+
// Copies a directory in a configuration repository to a directory on the OS filesystem.
246+
// e.g. <dir> -> <dst>/<dir>
247+
func copyDirFiles(ctx context.Context, r repo.ConfigurationRepository, dir string, dst string) error {
248+
pfs, err := r.GetDirContents(ctx, dir)
249+
if err != nil {
250+
return errors.Wrap(err, "get dir contents")
251+
}
252+
dstDir := filepath.Join(dst, dir)
253+
if err := os.MkdirAll(dstDir, 0777); err != nil {
254+
return errors.Wrapf(err, "mkdir %s", dstDir)
255+
}
256+
for _, pf := range pfs {
257+
dstPath := filepath.Join(dst, pf.Path)
258+
if pf.IsDir {
259+
if err := os.MkdirAll(dstPath, 0777); err != nil {
260+
return errors.Wrapf(err, "mkdir %s", pf.Path)
261+
}
262+
if err := copyDirFiles(ctx, r, pf.Path, dst); err != nil {
263+
return err
264+
}
265+
continue
266+
}
267+
b, err := r.Read(pf.Path)
268+
if err != nil {
269+
return errors.Wrapf(err, "read %s", pf.Path)
270+
}
271+
f, err := os.Create(dstPath)
272+
if err != nil {
273+
return errors.Wrapf(err, "create %s", dstPath)
274+
}
275+
if _, err := f.Write(b); err != nil {
276+
return errors.Wrapf(err, "copy %s", dstPath)
277+
}
278+
}
279+
return nil
280+
}

0 commit comments

Comments
 (0)