mirror of
https://gitea.com/Lydanne/buildx.git
synced 2025-07-11 05:57:08 +08:00
vendor: update to compose-go 1.13.4
Signed-off-by: Nick Sieger <nick@nicksieger.com>
This commit is contained in:
153
vendor/github.com/compose-spec/compose-go/loader/loader.go
generated
vendored
153
vendor/github.com/compose-spec/compose-go/loader/loader.go
generated
vendored
@ -37,7 +37,7 @@ import (
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"gopkg.in/yaml.v2"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// Options supported by Load
|
||||
@ -69,7 +69,7 @@ type Options struct {
|
||||
}
|
||||
|
||||
func (o *Options) SetProjectName(name string, imperativelySet bool) {
|
||||
o.projectName = NormalizeProjectName(name)
|
||||
o.projectName = name
|
||||
o.projectNameImperativelySet = imperativelySet
|
||||
}
|
||||
|
||||
@ -138,6 +138,14 @@ func ParseYAML(source []byte) (map[string]interface{}, error) {
|
||||
if err := yaml.Unmarshal(source, &cfg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stringMap, ok := cfg.(map[string]interface{})
|
||||
if ok {
|
||||
converted, err := convertToStringKeysRecursive(stringMap, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return converted.(map[string]interface{}), nil
|
||||
}
|
||||
cfgMap, ok := cfg.(map[interface{}]interface{})
|
||||
if !ok {
|
||||
return nil, errors.Errorf("Top-level object must be a mapping")
|
||||
@ -185,7 +193,7 @@ func Load(configDetails types.ConfigDetails, options ...func(*Options)) (*types.
|
||||
}
|
||||
dict, err := parseConfig(file.Content, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("parsing %s: %w", file.Filename, err)
|
||||
}
|
||||
configDict = dict
|
||||
file.Config = dict
|
||||
@ -194,7 +202,7 @@ func Load(configDetails types.ConfigDetails, options ...func(*Options)) (*types.
|
||||
|
||||
if !opts.SkipValidation {
|
||||
if err := schema.Validate(configDict); err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("validating %s: %w", file.Filename, err)
|
||||
}
|
||||
}
|
||||
|
||||
@ -233,7 +241,7 @@ func Load(configDetails types.ConfigDetails, options ...func(*Options)) (*types.
|
||||
}
|
||||
|
||||
if !opts.SkipNormalization {
|
||||
err = normalize(project, opts.ResolvePaths)
|
||||
err = Normalize(project, opts.ResolvePaths)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -246,40 +254,82 @@ func Load(configDetails types.ConfigDetails, options ...func(*Options)) (*types.
|
||||
}
|
||||
}
|
||||
|
||||
if len(opts.Profiles) > 0 {
|
||||
project.ApplyProfiles(opts.Profiles)
|
||||
if profiles, ok := project.Environment[consts.ComposeProfiles]; ok && len(opts.Profiles) == 0 {
|
||||
opts.Profiles = strings.Split(profiles, ",")
|
||||
}
|
||||
project.ApplyProfiles(opts.Profiles)
|
||||
|
||||
err = project.ResolveServicesEnvironment(opts.discardEnvFiles)
|
||||
|
||||
return project, err
|
||||
}
|
||||
|
||||
func InvalidProjectNameErr(v string) error {
|
||||
return fmt.Errorf(
|
||||
"%q is not a valid project name: it must contain only "+
|
||||
"characters from [a-z0-9_-] and start with [a-z0-9]", v,
|
||||
)
|
||||
}
|
||||
|
||||
// projectName determines the canonical name to use for the project considering
|
||||
// the loader Options as well as `name` fields in Compose YAML fields (which
|
||||
// also support interpolation).
|
||||
//
|
||||
// TODO(milas): restructure loading so that we don't need to re-parse the YAML
|
||||
// here, as it's both wasteful and makes this code error-prone.
|
||||
func projectName(details types.ConfigDetails, opts *Options) (string, error) {
|
||||
projectName, projectNameImperativelySet := opts.GetProjectName()
|
||||
var pjNameFromConfigFile string
|
||||
|
||||
for _, configFile := range details.ConfigFiles {
|
||||
yml, err := ParseYAML(configFile.Content)
|
||||
if err != nil {
|
||||
return "", nil
|
||||
// if user did NOT provide a name explicitly, then see if one is defined
|
||||
// in any of the config files
|
||||
if !projectNameImperativelySet {
|
||||
var pjNameFromConfigFile string
|
||||
for _, configFile := range details.ConfigFiles {
|
||||
yml, err := ParseYAML(configFile.Content)
|
||||
if err != nil {
|
||||
// HACK: the way that loading is currently structured, this is
|
||||
// a duplicative parse just for the `name`. if it fails, we
|
||||
// give up but don't return the error, knowing that it'll get
|
||||
// caught downstream for us
|
||||
return "", nil
|
||||
}
|
||||
if val, ok := yml["name"]; ok && val != "" {
|
||||
sVal, ok := val.(string)
|
||||
if !ok {
|
||||
// HACK: see above - this is a temporary parsed version
|
||||
// that hasn't been schema-validated, but we don't want
|
||||
// to be the ones to actually report that, so give up,
|
||||
// knowing that it'll get caught downstream for us
|
||||
return "", nil
|
||||
}
|
||||
pjNameFromConfigFile = sVal
|
||||
}
|
||||
}
|
||||
if val, ok := yml["name"]; ok && val != "" {
|
||||
pjNameFromConfigFile = yml["name"].(string)
|
||||
if !opts.SkipInterpolation {
|
||||
interpolated, err := interp.Interpolate(
|
||||
map[string]interface{}{"name": pjNameFromConfigFile},
|
||||
*opts.Interpolate,
|
||||
)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
pjNameFromConfigFile = interpolated["name"].(string)
|
||||
}
|
||||
}
|
||||
if !opts.SkipInterpolation {
|
||||
interpolated, err := interp.Interpolate(map[string]interface{}{"name": pjNameFromConfigFile}, *opts.Interpolate)
|
||||
if err != nil {
|
||||
return "", err
|
||||
pjNameFromConfigFile = NormalizeProjectName(pjNameFromConfigFile)
|
||||
if pjNameFromConfigFile != "" {
|
||||
projectName = pjNameFromConfigFile
|
||||
}
|
||||
pjNameFromConfigFile = interpolated["name"].(string)
|
||||
}
|
||||
pjNameFromConfigFile = NormalizeProjectName(pjNameFromConfigFile)
|
||||
if !projectNameImperativelySet && pjNameFromConfigFile != "" {
|
||||
projectName = pjNameFromConfigFile
|
||||
}
|
||||
|
||||
if projectName == "" {
|
||||
return "", errors.New("project name must not be empty")
|
||||
}
|
||||
|
||||
if NormalizeProjectName(projectName) != projectName {
|
||||
return "", InvalidProjectNameErr(projectName)
|
||||
}
|
||||
|
||||
// TODO(milas): this should probably ALWAYS set (overriding any existing)
|
||||
if _, ok := details.Environment[consts.ComposeProjectName]; !ok && projectName != "" {
|
||||
details.Environment[consts.ComposeProjectName] = projectName
|
||||
}
|
||||
@ -304,6 +354,8 @@ func parseConfig(b []byte, opts *Options) (map[string]interface{}, error) {
|
||||
return yml, err
|
||||
}
|
||||
|
||||
const extensions = "#extensions" // Using # prefix, we prevent risk to conflict with an actual yaml key
|
||||
|
||||
func groupXFieldsIntoExtensions(dict map[string]interface{}) map[string]interface{} {
|
||||
extras := map[string]interface{}{}
|
||||
for key, value := range dict {
|
||||
@ -316,7 +368,7 @@ func groupXFieldsIntoExtensions(dict map[string]interface{}) map[string]interfac
|
||||
}
|
||||
}
|
||||
if len(extras) > 0 {
|
||||
dict["extensions"] = extras
|
||||
dict[extensions] = extras
|
||||
}
|
||||
return dict
|
||||
}
|
||||
@ -355,7 +407,7 @@ func loadSections(filename string, config map[string]interface{}, configDetails
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
extensions := getSection(config, "extensions")
|
||||
extensions := getSection(config, extensions)
|
||||
if len(extensions) > 0 {
|
||||
cfg.Extensions = extensions
|
||||
}
|
||||
@ -450,6 +502,22 @@ func createTransformHook(additionalTransformers ...Transformer) mapstructure.Dec
|
||||
|
||||
// keys need to be converted to strings for jsonschema
|
||||
func convertToStringKeysRecursive(value interface{}, keyPrefix string) (interface{}, error) {
|
||||
if mapping, ok := value.(map[string]interface{}); ok {
|
||||
for key, entry := range mapping {
|
||||
var newKeyPrefix string
|
||||
if keyPrefix == "" {
|
||||
newKeyPrefix = key
|
||||
} else {
|
||||
newKeyPrefix = fmt.Sprintf("%s.%s", keyPrefix, key)
|
||||
}
|
||||
convertedEntry, err := convertToStringKeysRecursive(entry, newKeyPrefix)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mapping[key] = convertedEntry
|
||||
}
|
||||
return mapping, nil
|
||||
}
|
||||
if mapping, ok := value.(map[interface{}]interface{}); ok {
|
||||
dict := make(map[string]interface{})
|
||||
for key, entry := range mapping {
|
||||
@ -501,7 +569,7 @@ func formatInvalidKeyError(keyPrefix string, key interface{}) error {
|
||||
func LoadServices(filename string, servicesDict map[string]interface{}, workingDir string, lookupEnv template.Mapping, opts *Options) ([]types.ServiceConfig, error) {
|
||||
var services []types.ServiceConfig
|
||||
|
||||
x, ok := servicesDict["extensions"]
|
||||
x, ok := servicesDict[extensions]
|
||||
if ok {
|
||||
// as a top-level attribute, "services" doesn't support extensions, and a service can be named `x-foo`
|
||||
for k, v := range x.(map[string]interface{}) {
|
||||
@ -541,16 +609,17 @@ func loadServiceWithExtends(filename, name string, servicesDict map[string]inter
|
||||
}
|
||||
|
||||
if serviceConfig.Extends != nil && !opts.SkipExtends {
|
||||
baseServiceName := *serviceConfig.Extends["service"]
|
||||
baseServiceName := serviceConfig.Extends.Service
|
||||
var baseService *types.ServiceConfig
|
||||
if file := serviceConfig.Extends["file"]; file == nil {
|
||||
file := serviceConfig.Extends.File
|
||||
if file == "" {
|
||||
baseService, err = loadServiceWithExtends(filename, baseServiceName, servicesDict, workingDir, lookupEnv, opts, ct)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
// Resolve the path to the imported file, and load it.
|
||||
baseFilePath := absPath(workingDir, *file)
|
||||
baseFilePath := absPath(workingDir, file)
|
||||
|
||||
b, err := os.ReadFile(baseFilePath)
|
||||
if err != nil {
|
||||
@ -569,10 +638,10 @@ func loadServiceWithExtends(filename, name string, servicesDict map[string]inter
|
||||
}
|
||||
|
||||
// Make paths relative to the importing Compose file. Note that we
|
||||
// make the paths relative to `*file` rather than `baseFilePath` so
|
||||
// that the resulting paths won't be absolute if `*file` isn't an
|
||||
// make the paths relative to `file` rather than `baseFilePath` so
|
||||
// that the resulting paths won't be absolute if `file` isn't an
|
||||
// absolute path.
|
||||
baseFileParent := filepath.Dir(*file)
|
||||
baseFileParent := filepath.Dir(file)
|
||||
if baseService.Build != nil {
|
||||
baseService.Build.Context = resolveBuildContextPath(baseFileParent, baseService.Build.Context)
|
||||
}
|
||||
@ -583,12 +652,17 @@ func loadServiceWithExtends(filename, name string, servicesDict map[string]inter
|
||||
}
|
||||
baseService.Volumes[i].Source = resolveMaybeUnixPath(vol.Source, baseFileParent, lookupEnv)
|
||||
}
|
||||
|
||||
for i, envFile := range baseService.EnvFile {
|
||||
baseService.EnvFile[i] = resolveMaybeUnixPath(envFile, baseFileParent, lookupEnv)
|
||||
}
|
||||
}
|
||||
|
||||
serviceConfig, err = _merge(baseService, serviceConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
serviceConfig.Extends = nil
|
||||
}
|
||||
|
||||
return serviceConfig, nil
|
||||
@ -996,14 +1070,15 @@ var transformDependsOnConfig TransformerFunc = func(data interface{}) (interface
|
||||
}
|
||||
}
|
||||
|
||||
var transformExtendsConfig TransformerFunc = func(data interface{}) (interface{}, error) {
|
||||
switch data.(type) {
|
||||
var transformExtendsConfig TransformerFunc = func(value interface{}) (interface{}, error) {
|
||||
switch value.(type) {
|
||||
case string:
|
||||
data = map[string]interface{}{
|
||||
"service": data,
|
||||
}
|
||||
return map[string]interface{}{"service": value}, nil
|
||||
case map[string]interface{}:
|
||||
return value, nil
|
||||
default:
|
||||
return value, errors.Errorf("invalid type %T for extends", value)
|
||||
}
|
||||
return transformMappingOrListFunc("=", true)(data)
|
||||
}
|
||||
|
||||
var transformServiceVolumeConfig TransformerFunc = func(data interface{}) (interface{}, error) {
|
||||
|
Reference in New Issue
Block a user