Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add buf migrate MVP #2655

Merged
merged 12 commits into from
Dec 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions TODO.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,27 @@
- document behavior of file searching, config override
- fix tamper-proofing
- go through all todos
- `buf mod init` should create a v2 buf.yaml.
- Is `buf build` supposed to call the API when there is already a buf.lock? On the same
directory, the released version (main) doesn't make that API call but `bufmod` calls
`buf.registry.module.v1beta1.CommitService/GetCommitNodes`. Is it for computing digest? Or is it because the buf.lock is in v1?

- Migrate buf.work.yaml, buf.yaml and buf.lock
- It would be nice if the bufconfig.File exposes a Path() method. Or a path() method and
I can move bufmigrate into bufconfig.
- A strategy to resolve dependencies for buf.lock
- A strategy to resolve dependencies declared. i.e. foo depends on common:commit_1,
bar depends on common:commit_2 and baz depends on common (without pinning a commit).
- Refine migration behavior and flags.
- Relative paths in lint/breaking ignore rules (and similar fields requiring paths), what
are they relative to? Their module root or the workspace root?
- Group lint rules, right now lint rules are expanded to a list of individual rules.
- Going from v1beta1 there are many changes to the rules.
- Going from v1 to v2, the only change is that PACKAGE_NO_IMPORT_CYCLE is added to MINIMAL, BASIC, DEFAULT.
- Breaking rules:
- FIELD_SAME_TYPE from v1beta1 is split into 3 rules in v1: FIELD_SAME_TYPE,
FIELD_WIRE_COMPATIBLE_TYPE, FIELD_WIRE_JSON_COMPATIBLE_TYPE.
- Rule `FILE_SAME_PACKAGE` has been added to PACKAGE, WIRE and WIRE_JSON in v1.
- Un-skip relevant tests in generate_test.go and generate_unix_test.go, when
- the behavior of --path and --exclude-path is updated to match what's on main
- Debug the skipped test with proto file ref in generate_test.go
Expand Down
134 changes: 134 additions & 0 deletions private/buf/bufmigrate/bufmigrate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
// Copyright 2020-2023 Buf Technologies, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package bufmigrate

import (
"context"
"errors"
"fmt"
"io"
"path/filepath"

"buf.build/gen/go/bufbuild/registry/connectrpc/go/buf/registry/module/v1beta1/modulev1beta1connect"
"github.com/bufbuild/buf/private/pkg/slicesext"
"github.com/bufbuild/buf/private/pkg/storage/storageos"
)

// Migrate migrate buf configuration files.
func Migrate(
ctx context.Context,
storageProvider storageos.Provider,
commitService modulev1beta1connect.CommitServiceClient,
options ...MigrateOption,
) (retErr error) {
migrateOptions := newMigrateOptions()
for _, option := range options {
option(migrateOptions)
}
if migrateOptions.bufWorkYAMLFilePath == "" && len(migrateOptions.bufYAMLFilePaths) == 0 {
return errors.New("unimplmented")
}
// TODO: decide on behavior for where to write this file
destinationDir := "."
// Alternatively, we could do the following: (but "." is probably better, since we want users to
// have buf.yaml v2 at their repository root and they are likely running this command from there)
//
// if migrateOptions.bufWorkYAMLFilePath != "" {
// destinationDir = filepath.Base(migrateOptions.bufWorkYAMLFilePath)
// } else if len(migrateOptions.bufYAMLFilePaths) == 1 {
// // TODO: maybe use "." (or maybe add --dest flag)
// destinationDir = filepath.Base(migrateOptions.bufYAMLFilePaths[0])
// } else {
// destinationDir = "."
// }
bucket, err := storageProvider.NewReadWriteBucket(
".",
storageos.ReadWriteBucketWithSymlinksIfSupported(),
)
if err != nil {
return err
}
migrator := newMigrator(
bucket,
destinationDir,
)
if migrateOptions.bufWorkYAMLFilePath != "" {
if err := migrator.addWorkspaceDirectory(
ctx,
filepath.Dir(migrateOptions.bufWorkYAMLFilePath),
); err != nil {
return err
}
}
for _, bufYAMLPath := range migrateOptions.bufYAMLFilePaths {
// TODO: read upwards to make sure it's not in a workspace
// i.e. for ./foo/bar/buf.yaml, check none of "./foo", ".", "../", "../..", and etc. is a workspace.
if err := migrator.addModuleDirectory(
ctx,
filepath.Dir(bufYAMLPath),
); err != nil {
return err
}
}
if migrateOptions.dryRun {
return migrator.migrateAsDryRun(migrateOptions.dryRunWriter)
}
return migrator.migrate(ctx)
}

// MigrateOption is a migrate option.
type MigrateOption func(*migrateOptions)

// MigrateAsDryRun write to the writer the summary of the changes to be made, without writing to the disk.
func MigrateAsDryRun(writer io.Writer) MigrateOption {
return func(migrateOptions *migrateOptions) {
migrateOptions.dryRun = true
migrateOptions.dryRunWriter = writer
}
}

// MigrateBufWorkYAMLFile migrates a v1 buf.work.yaml.
func MigrateBufWorkYAMLFile(path string) (MigrateOption, error) {
// TODO: Looking at IsLocal's doc, it seems to validate for what we want: relative and does not jump context.
if !filepath.IsLocal(path) {
return nil, fmt.Errorf("%s is not a relative path", path)
}
return func(migrateOptions *migrateOptions) {
migrateOptions.bufWorkYAMLFilePath = filepath.Clean(path)
}, nil
}

// MigrateBufYAMLFile migrates buf.yaml files.
func MigrateBufYAMLFile(paths []string) (MigrateOption, error) {
for _, path := range paths {
if !filepath.IsLocal(path) {
return nil, fmt.Errorf("%s is not a relative path", path)
}
}
return func(migrateOptions *migrateOptions) {
migrateOptions.bufYAMLFilePaths = slicesext.Map(paths, filepath.Clean)
}, nil
}

type migrateOptions struct {
dryRun bool
dryRunWriter io.Writer
bufWorkYAMLFilePath string
bufYAMLFilePaths []string
}

func newMigrateOptions() *migrateOptions {
return &migrateOptions{}
}
22 changes: 22 additions & 0 deletions private/buf/bufmigrate/bufmigrate_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright 2020-2023 Buf Technologies, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package bufmigrate

import "testing"

func TestMigrate(t *testing.T) {
// TODO
t.Parallel()
}
Loading
Loading