blob: 1ed3f11f803c921fdddf29ae48c6aff07e9a4be8 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You 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 bufbreakingconfig
import (
"encoding/json"
"sort"
)
import (
breakingv1 "github.com/apache/dubbo-kubernetes/pkg/bufman/gen/proto/go/breaking/v1"
)
const (
// These versions match the versions in bufconfig. We cannot take an explicit dependency
// on bufconfig without creating a circular dependency.
v1Beta1Version = "v1beta1"
v1Version = "v1"
)
// Config is the breaking check config.
type Config struct {
// Use is a list of the rule and/or category IDs that are included in the breaking change check.
Use []string
// Except is a list of the rule and/or category IDs that are excluded from the breaking change check.
Except []string
// IgnoreRootPaths is a list of the paths of directories and/or files that should be ignored by the breaking change check.
// All paths are relative to the root of the module.
IgnoreRootPaths []string
// IgnoreIDOrCategoryToRootPaths is a map of rule and/or category IDs to directory and/or file paths to exclude from the
// breaking change check.
IgnoreIDOrCategoryToRootPaths map[string][]string
// IgnoreUnstablePackages ignores packages with a last component that is one of the unstable forms recognised
// by the PACKAGE_VERSION_SUFFIX:
// v\d+test.*
// v\d+(alpha|beta)\d+
// v\d+p\d+(alpha|beta)\d+
IgnoreUnstablePackages bool
// Version represents the version of the breaking change rule and category IDs that should be used with this config.
Version string
}
// NewConfigV1Beta1 returns a new Config.
func NewConfigV1Beta1(externalConfig ExternalConfigV1Beta1) *Config {
return &Config{
Use: externalConfig.Use,
Except: externalConfig.Except,
IgnoreRootPaths: externalConfig.Ignore,
IgnoreIDOrCategoryToRootPaths: externalConfig.IgnoreOnly,
IgnoreUnstablePackages: externalConfig.IgnoreUnstablePackages,
Version: v1Beta1Version,
}
}
// NewConfigV1 returns a new Config.
func NewConfigV1(externalConfig ExternalConfigV1) *Config {
return &Config{
Use: externalConfig.Use,
Except: externalConfig.Except,
IgnoreRootPaths: externalConfig.Ignore,
IgnoreIDOrCategoryToRootPaths: externalConfig.IgnoreOnly,
IgnoreUnstablePackages: externalConfig.IgnoreUnstablePackages,
Version: v1Version,
}
}
// ConfigForProto returns the Config given the proto.
func ConfigForProto(protoConfig *breakingv1.Config) *Config {
return &Config{
Use: protoConfig.GetUseIds(),
Except: protoConfig.GetExceptIds(),
IgnoreRootPaths: protoConfig.GetIgnorePaths(),
IgnoreIDOrCategoryToRootPaths: ignoreIDOrCategoryToRootPathsForProto(protoConfig.GetIgnoreIdPaths()),
IgnoreUnstablePackages: protoConfig.GetIgnoreUnstablePackages(),
Version: protoConfig.GetVersion(),
}
}
// ProtoForConfig takes a *Config and returns the proto representation.
func ProtoForConfig(config *Config) *breakingv1.Config {
return &breakingv1.Config{
UseIds: config.Use,
ExceptIds: config.Except,
IgnorePaths: config.IgnoreRootPaths,
IgnoreIdPaths: protoForIgnoreIDOrCategoryToRootPaths(config.IgnoreIDOrCategoryToRootPaths),
IgnoreUnstablePackages: config.IgnoreUnstablePackages,
Version: config.Version,
}
}
// ExternalConfigV1Beta1 is an external config.
type ExternalConfigV1Beta1 struct {
Use []string `json:"use,omitempty" yaml:"use,omitempty"`
Except []string `json:"except,omitempty" yaml:"except,omitempty"`
// IgnoreRootPaths
Ignore []string `json:"ignore,omitempty" yaml:"ignore,omitempty"`
// IgnoreIDOrCategoryToRootPaths
IgnoreOnly map[string][]string `json:"ignore_only,omitempty" yaml:"ignore_only,omitempty"`
IgnoreUnstablePackages bool `json:"ignore_unstable_packages,omitempty" yaml:"ignore_unstable_packages,omitempty"`
}
// ExternalConfigV1 is an external config.
type ExternalConfigV1 struct {
Use []string `json:"use,omitempty" yaml:"use,omitempty"`
Except []string `json:"except,omitempty" yaml:"except,omitempty"`
// IgnoreRootPaths
Ignore []string `json:"ignore,omitempty" yaml:"ignore,omitempty"`
// IgnoreIDOrCategoryToRootPaths
IgnoreOnly map[string][]string `json:"ignore_only,omitempty" yaml:"ignore_only,omitempty"`
IgnoreUnstablePackages bool `json:"ignore_unstable_packages,omitempty" yaml:"ignore_unstable_packages,omitempty"`
}
// ExternalConfigV1Beta1ForConfig takes a *Config and returns the v1beta1 external config representation.
func ExternalConfigV1Beta1ForConfig(config *Config) ExternalConfigV1Beta1 {
return ExternalConfigV1Beta1{
Use: config.Use,
Except: config.Except,
Ignore: config.IgnoreRootPaths,
IgnoreOnly: config.IgnoreIDOrCategoryToRootPaths,
IgnoreUnstablePackages: config.IgnoreUnstablePackages,
}
}
// ExternalConfigV1ForConfig takes a *Config and returns the v1 external config representation.
func ExternalConfigV1ForConfig(config *Config) ExternalConfigV1 {
return ExternalConfigV1{
Use: config.Use,
Except: config.Except,
Ignore: config.IgnoreRootPaths,
IgnoreOnly: config.IgnoreIDOrCategoryToRootPaths,
IgnoreUnstablePackages: config.IgnoreUnstablePackages,
}
}
// BytesForConfig takes a *Config and returns the deterministic []byte representation.
// We use an unexported intermediary JSON form and sort all fields to ensure that the bytes
// associated with the *Config are deterministic.
func BytesForConfig(config *Config) ([]byte, error) {
if config == nil {
return nil, nil
}
return json.Marshal(configToJSON(config))
}
type configJSON struct {
Use []string `json:"use,omitempty"`
Except []string `json:"except,omitempty"`
IgnoreRootPaths []string `json:"ignore_root_paths,omitempty"`
IgnoreIDOrCategoryToRootPaths []idPathsJSON `json:"ignore_id_to_root_paths,omitempty"`
IgnoreUnstablePackages bool `json:"ignore_unstable_packages,omitempty"`
Version string `json:"version,omitempty"`
}
type idPathsJSON struct {
ID string `json:"id,omitempty"`
Paths []string `json:"paths,omitempty"`
}
func configToJSON(config *Config) *configJSON {
ignoreIDPathsJSON := make([]idPathsJSON, 0, len(config.IgnoreIDOrCategoryToRootPaths))
for ignoreID, rootPaths := range config.IgnoreIDOrCategoryToRootPaths {
rootPathsCopy := make([]string, len(rootPaths))
copy(rootPathsCopy, rootPaths)
sort.Strings(rootPathsCopy)
ignoreIDPathsJSON = append(ignoreIDPathsJSON, idPathsJSON{
ID: ignoreID,
Paths: rootPathsCopy,
})
}
sort.Slice(ignoreIDPathsJSON, func(i, j int) bool { return ignoreIDPathsJSON[i].ID < ignoreIDPathsJSON[j].ID })
// We should not be sorting in place for the config structure, since it will mutate the
// underlying config ordering.
use := make([]string, len(config.Use))
copy(use, config.Use)
except := make([]string, len(config.Except))
copy(except, config.Except)
ignoreRootPaths := make([]string, len(config.IgnoreRootPaths))
copy(ignoreRootPaths, config.IgnoreRootPaths)
sort.Strings(use)
sort.Strings(except)
sort.Strings(ignoreRootPaths)
return &configJSON{
Use: use,
Except: except,
IgnoreRootPaths: ignoreRootPaths,
IgnoreIDOrCategoryToRootPaths: ignoreIDPathsJSON,
IgnoreUnstablePackages: config.IgnoreUnstablePackages,
Version: config.Version,
}
}
func ignoreIDOrCategoryToRootPathsForProto(protoIgnoreIDPaths []*breakingv1.IDPaths) map[string][]string {
if protoIgnoreIDPaths == nil {
return nil
}
ignoreIDOrCategoryToRootPaths := make(map[string][]string)
for _, protoIgnoreIDPath := range protoIgnoreIDPaths {
ignoreIDOrCategoryToRootPaths[protoIgnoreIDPath.GetId()] = protoIgnoreIDPath.GetPaths()
}
return ignoreIDOrCategoryToRootPaths
}
func protoForIgnoreIDOrCategoryToRootPaths(ignoreIDOrCategoryToRootPaths map[string][]string) []*breakingv1.IDPaths {
if ignoreIDOrCategoryToRootPaths == nil {
return nil
}
idPathsProto := make([]*breakingv1.IDPaths, 0, len(ignoreIDOrCategoryToRootPaths))
for id, paths := range ignoreIDOrCategoryToRootPaths {
idPathsProto = append(idPathsProto, &breakingv1.IDPaths{
Id: id,
Paths: paths,
})
}
return idPathsProto
}