Compare commits

...

11 Commits
master ... v0.8

Author SHA1 Message Date
Tõnis Tiigi 6224def4dd
Merge pull request #1042 from tonistiigi/update-buildkit-220403-v0.8
[v0.8] vendor: update buildkit to 10e6f94b
3 years ago
Tõnis Tiigi 5abee699a6
Merge pull request #1022 from crazy-max/v0.8-bake-fix-visited-group
[v0.8] bake: fix skipped group when already visited by another one
3 years ago
Tonis Tiigi 54e497dfba vendor: update buildkit to 10e6f94b
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
3 years ago
Tõnis Tiigi dfbd226285
Merge pull request #1034 from crazy-max/v0.8-update-compose-go
[v0.8] update github.com/compose-spec/compose-go to v1.2.1
3 years ago
CrazyMax a78c2957a2
compose: add test for port mapping
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
(cherry picked from commit 243b428a58)
3 years ago
CrazyMax e80890ec89
update github.com/compose-spec/compose-go to v1.2.1
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
(cherry picked from commit 785dc17f13)
3 years ago
CrazyMax 10fe8f380c
bake: fix skipped group when already visited by another one
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
(cherry picked from commit 0b8dde1071)
3 years ago
Tõnis Tiigi 5fac64c2c4
Merge pull request #1018 from tonistiigi/v0.8-compose-target-dot
[v0.8] bake: allow dot in target names for compose
3 years ago
Tonis Tiigi 24ad37a5d2 bake: allow dot in target names for compose
This is a hotfix for v0.8 to unblock release and
restore backward compatibility. More proper fix
coming later.

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
3 years ago
Tõnis Tiigi 106651877d
Merge pull request #1005 from tonistiigi/v0.8-update-fsutil-220315
[v0.8] vendor: update fsutil to 9ed61262
3 years ago
Tonis Tiigi 35bcd88f08 vendor: update fsutil to 9ed61262
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
(cherry picked from commit 111ea95629)
3 years ago

@ -29,7 +29,9 @@ var (
gitURLPathWithFragmentSuffix = regexp.MustCompile(`\.git(?:#.+)?$`)
validTargetNameChars = `[a-zA-Z0-9_-]+`
validTargetNameCharsCompose = `[a-zA-Z0-9._-]+`
targetNamePattern = regexp.MustCompile(`^` + validTargetNameChars + `$`)
targetNamePatternCompose = regexp.MustCompile(`^` + validTargetNameCharsCompose + `$`)
)
type File struct {
@ -412,12 +414,12 @@ func (c Config) newOverrides(v []string) (map[string]map[string]Override, error)
}
func (c Config) ResolveGroup(name string) []string {
return c.group(name, map[string]struct{}{})
return dedupString(c.group(name, map[string][]string{}))
}
func (c Config) group(name string, visited map[string]struct{}) []string {
func (c Config) group(name string, visited map[string][]string) []string {
if _, ok := visited[name]; ok {
return nil
return visited[name]
}
var g *Group
for _, group := range c.Groups {
@ -429,7 +431,7 @@ func (c Config) group(name string, visited map[string]struct{}) []string {
if g == nil {
return []string{name}
}
visited[name] = struct{}{}
visited[name] = []string{}
targets := make([]string, 0, len(g.Targets))
for _, t := range g.Targets {
tgroup := c.group(t, visited)
@ -439,6 +441,7 @@ func (c Config) group(name string, visited map[string]struct{}) []string {
targets = append(targets, t)
}
}
visited[name] = targets
return targets
}
@ -968,6 +971,13 @@ func validateTargetName(name string) error {
return nil
}
func validateTargetNameCompose(name string) error {
if !targetNamePatternCompose.MatchString(name) {
return errors.Errorf("only %q are allowed", validTargetNameCharsCompose)
}
return nil
}
func sliceEqual(s1, s2 []string) bool {
if len(s1) != len(s2) {
return false

@ -1045,3 +1045,79 @@ func TestTargetName(t *testing.T) {
})
}
}
func TestNestedGroupsWithSameTarget(t *testing.T) {
ctx := context.TODO()
f := File{
Name: "docker-bake.hcl",
Data: []byte(`
group "a" {
targets = ["b", "c"]
}
group "b" {
targets = ["d"]
}
group "c" {
targets = ["b"]
}
target "d" {
context = "."
dockerfile = "./testdockerfile"
}
group "e" {
targets = ["a", "f"]
}
target "f" {
context = "./foo"
}`)}
cases := []struct {
name string
targets []string
ntargets int
}{
{
name: "a",
targets: []string{"b", "c"},
ntargets: 1,
},
{
name: "b",
targets: []string{"d"},
ntargets: 1,
},
{
name: "c",
targets: []string{"b"},
ntargets: 1,
},
{
name: "d",
targets: []string{"d"},
ntargets: 1,
},
{
name: "e",
targets: []string{"a", "f"},
ntargets: 2,
},
}
for _, tt := range cases {
tt := tt
t.Run(tt.name, func(t *testing.T) {
m, g, err := ReadTargets(ctx, []File{f}, []string{tt.name}, nil, nil)
require.NoError(t, err)
require.Equal(t, 1, len(g))
require.Equal(t, tt.targets, g[0].Targets)
require.Equal(t, tt.ntargets, len(m))
require.Equal(t, ".", *m["d"].Context)
require.Equal(t, "./testdockerfile", *m["d"].Dockerfile)
})
}
}

@ -60,7 +60,7 @@ func ParseCompose(dt []byte) (*Config, error) {
continue
}
if err = validateTargetName(s.Name); err != nil {
if err = validateTargetNameCompose(s.Name); err != nil {
return nil, errors.Wrapf(err, "invalid service name %q", s.Name)
}

@ -310,6 +310,27 @@ services:
require.Equal(t, c.Targets[0].Args, map[string]string{"CT_ECR": "foo", "FOO": "bsdf -csdf", "NODE_ENV": "test"})
}
func TestPorts(t *testing.T) {
var dt = []byte(`
services:
foo:
build:
context: .
ports:
- 3306:3306
bar:
build:
context: .
ports:
- mode: ingress
target: 3306
published: "3306"
protocol: tcp
`)
_, err := ParseCompose(dt)
require.NoError(t, err)
}
func newBool(val bool) *bool {
b := val
return &b
@ -330,6 +351,10 @@ func TestServiceName(t *testing.T) {
},
{
svc: "a.b",
wantErr: false,
},
{
svc: "a?b",
wantErr: true,
},
{

@ -8,7 +8,7 @@ require (
github.com/bugsnag/panicwrap v1.2.0 // indirect
github.com/cenkalti/backoff v2.1.1+incompatible // indirect
github.com/cloudflare/cfssl v0.0.0-20181213083726-b94e044bb51e // indirect
github.com/compose-spec/compose-go v1.0.8
github.com/compose-spec/compose-go v1.2.1
github.com/containerd/console v1.0.3
github.com/containerd/containerd v1.6.1
github.com/docker/cli v20.10.12+incompatible
@ -30,7 +30,7 @@ require (
github.com/jinzhu/gorm v1.9.2 // indirect
github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a // indirect
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect
github.com/moby/buildkit v0.10.0-rc2.0.20220308185020-fdecd0ae108b
github.com/moby/buildkit v0.10.1-0.20220403220257-10e6f94bf90d
github.com/morikuni/aec v1.0.0
github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/image-spec v1.0.2-0.20211117181255-693428a734f5
@ -42,6 +42,7 @@ require (
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.7.0
github.com/theupdateframework/notary v0.6.1 // indirect
github.com/tonistiigi/fsutil v0.0.0-20220315205639-9ed612626da3 // indirect
github.com/zclconf/go-cty v1.10.0
go.opentelemetry.io/otel v1.4.1
go.opentelemetry.io/otel/trace v1.4.1

@ -280,10 +280,8 @@ github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h
github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA=
github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI=
github.com/codahale/hdrhistogram v0.0.0-20160425231609-f8ad88b59a58/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
github.com/compose-spec/compose-go v1.0.8 h1:fgT7mYYu5Sp37i2lUIAAvwJpkAHk6dP5ITHy/LlutUk=
github.com/compose-spec/compose-go v1.0.8/go.mod h1:REnCbBugoIdHB7S1sfkN/aJ7AJpNApGNjNiVjA9L8x4=
github.com/compose-spec/godotenv v1.1.1 h1:lp+WpAInnw06YN9sV/XLUOV/9z4C+6wjJdWlrdVac7o=
github.com/compose-spec/godotenv v1.1.1/go.mod h1:zF/3BOa18Z24tts5qnO/E9YURQanJTBUf7nlcCTNsyc=
github.com/compose-spec/compose-go v1.2.1 h1:8+DAP7Mt/Ohl5y6YbZdilLMvIhMxvuSZcNZyywjQmJE=
github.com/compose-spec/compose-go v1.2.1/go.mod h1:pAy7Mikpeft4pxkFU565/DRHEbDfR84G6AQuiL+Hdg8=
github.com/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABAEopAHvB6j7cnP5zJ+/3aVcE7hCYqvIwAHyE=
github.com/containerd/aufs v0.0.0-20201003224125-76a6863f2989/go.mod h1:AkGGQs9NM2vtYHaUen+NljV0/baGCAPELGm2q9ZXpWU=
github.com/containerd/aufs v0.0.0-20210316121734-20793ff83c97/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU=
@ -966,12 +964,12 @@ github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:F
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.3.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/mapstructure v1.4.2 h1:6h7AQ0yhTcIsmFmnAwQls75jp2Gzs4iB8W7pjMO+rqo=
github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs=
github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A=
github.com/moby/buildkit v0.8.1/go.mod h1:/kyU1hKy/aYCuP39GZA9MaKioovHku57N6cqlKZIaiQ=
github.com/moby/buildkit v0.10.0-rc2.0.20220308185020-fdecd0ae108b h1:plbnJxjht8Z6D3c/ga79D1+VaA/IUfNVp08J3lcDgI8=
github.com/moby/buildkit v0.10.0-rc2.0.20220308185020-fdecd0ae108b/go.mod h1:WvwAZv8aRScHkqc/+X46cRC2CKMKpqcaX+pRvUTtPes=
github.com/moby/buildkit v0.10.1-0.20220403220257-10e6f94bf90d h1:6pLVBJO3V/lMegbVD5kh2QrpZwqS4ZrxEm/MyifCPaY=
github.com/moby/buildkit v0.10.1-0.20220403220257-10e6f94bf90d/go.mod h1:WvwAZv8aRScHkqc/+X46cRC2CKMKpqcaX+pRvUTtPes=
github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg=
github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=
github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8=
@ -1286,8 +1284,9 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1
github.com/tommy-muehle/go-mnd v1.1.1/go.mod h1:dSUh0FtTP8VhvkL1S+gUR1OKd9ZnSaozuI6r3m6wOig=
github.com/tommy-muehle/go-mnd v1.3.1-0.20200224220436-e6f9a994e8fa/go.mod h1:dSUh0FtTP8VhvkL1S+gUR1OKd9ZnSaozuI6r3m6wOig=
github.com/tonistiigi/fsutil v0.0.0-20201103201449-0834f99b7b85/go.mod h1:a7cilN64dG941IOXfhJhlH0qB92hxJ9A1ewrdUmJ6xo=
github.com/tonistiigi/fsutil v0.0.0-20220115021204-b19f7f9cb274 h1:wbyZxD6IPFp0sl5uscMOJRsz5UKGFiNiD16e+MVfKZY=
github.com/tonistiigi/fsutil v0.0.0-20220115021204-b19f7f9cb274/go.mod h1:oPAfvw32vlUJSjyDcQ3Bu0nb2ON2B+G0dtVN/SZNJiA=
github.com/tonistiigi/fsutil v0.0.0-20220315205639-9ed612626da3 h1:T1pEe+WB3SCPVAfVquvfPfagKZU2Z8c1OP3SuGB+id0=
github.com/tonistiigi/fsutil v0.0.0-20220315205639-9ed612626da3/go.mod h1:oPAfvw32vlUJSjyDcQ3Bu0nb2ON2B+G0dtVN/SZNJiA=
github.com/tonistiigi/go-actions-cache v0.0.0-20211202175116-9642704158ff/go.mod h1:qqvyZqkfwkoJuPU/bw61bItaoO0SJ8YSW0vSVRRvsRg=
github.com/tonistiigi/go-archvariant v1.0.0/go.mod h1:TxFmO5VS6vMq2kvs3ht04iPXtu2rUT/erOnGFYfk5Ho=
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea h1:SXhTLE6pb6eld/v/cCndK0AMpt1wiVFb/YYmqB3/QG0=
@ -2041,8 +2040,9 @@ gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0=
gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
gotest.tools/v3 v3.1.0 h1:rVV8Tcg/8jHUkPUorwjaMTtemIMVXfIPKiOqnhEhakk=
gotest.tools/v3 v3.1.0/go.mod h1:fHy7eyTmJFO5bQbUsEGQ1v4m2J3Jz9eWL54TP2/ZuYQ=
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20180920025451-e3ad64cb4ed3/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

@ -0,0 +1,23 @@
/*
Copyright 2020 The Compose Specification Authors.
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 consts
const (
ComposeProjectName = "COMPOSE_PROJECT_NAME"
ComposePathSeparator = "COMPOSE_PATH_SEPARATOR"
ComposeFilePath = "COMPOSE_FILE"
)

@ -20,4 +20,3 @@ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

@ -1,4 +1,4 @@
// Package godotenv is a go port of the ruby dotenv library (https://github.com/bkeepers/dotenv)
// Package dotenv is a go port of the ruby dotenv library (https://github.com/bkeepers/dotenv)
//
// Examples/readme can be found on the github page at https://github.com/joho/godotenv
//
@ -11,7 +11,7 @@
// godotenv.Load()
//
// and all the env vars declared in .env will be available through os.Getenv("SOME_ENV_VAR")
package godotenv
package dotenv
import (
"errors"

@ -1,4 +1,4 @@
package godotenv
package dotenv
import (
"bytes"
@ -41,10 +41,10 @@ func parseBytes(src []byte, out map[string]string, lookupFn LookupFn) error {
value, ok := lookupFn(key)
if ok {
out[key] = value
}
cutset = left
continue
}
}
value, left, err := extractVarValue(left, out, lookupFn)
if err != nil {
@ -132,14 +132,14 @@ func extractVarValue(src []byte, envMap map[string]string, lookupFn LookupFn) (v
// unquoted value - read until new line
end := bytes.IndexFunc(src, isNewLine)
var rest []byte
var value string
if end < 0 {
value := strings.TrimRightFunc(string(src), unicode.IsSpace)
rest = nil
return expandVariables(value, envMap, lookupFn), rest, nil
value := strings.Split(string(src), "#")[0] // Remove inline comments on unquoted lines
value = strings.TrimRightFunc(value, unicode.IsSpace)
return expandVariables(value, envMap, lookupFn), nil, nil
}
value = strings.TrimRightFunc(string(src[0:end]), unicode.IsSpace)
value := strings.Split(string(src[0:end]), "#")[0]
value = strings.TrimRightFunc(value, unicode.IsSpace)
rest = src[end:]
return expandVariables(value, envMap, lookupFn), rest, nil
}
@ -228,7 +228,6 @@ func isSpace(r rune) bool {
return false
}
// isNewLine reports whether the rune is a new line character
func isNewLine(r rune) bool {
return r == '\n'

@ -115,7 +115,7 @@ func newPathError(path Path, err error) error {
return nil
case *template.InvalidTemplateError:
return errors.Errorf(
"invalid interpolation format for %s: %#v. You may need to escape any $ with another $.",
"invalid interpolation format for %s: %#v. You may need to escape any $ with another $",
path, err.Template)
default:
return errors.Wrapf(err, "error while interpolating %s", path)

@ -1,3 +1,4 @@
name: Full_Example_project_name
services:
foo:
@ -6,6 +7,8 @@ services:
dockerfile: Dockerfile
args:
foo: bar
ssh:
- default
target: foo
network: foo
cache_from:
@ -85,6 +88,10 @@ services:
- spread: node.labels.az
endpoint_mode: dnsrr
device_cgroup_rules:
- "c 1:3 mr"
- "a 7:* rmw"
devices:
- "/dev/ttyUSB0:/dev/ttyUSB0"

@ -21,7 +21,6 @@ import (
"strings"
interp "github.com/compose-spec/compose-go/interpolation"
"github.com/compose-spec/compose-go/types"
"github.com/pkg/errors"
)
@ -53,7 +52,6 @@ var interpolateTypeCastMapping = map[interp.Path]interp.Cast{
servicePath("oom_score_adj"): toInt64,
servicePath("pids_limit"): toInt64,
servicePath("ports", interp.PathMatchList, "target"): toInt,
servicePath("ports", interp.PathMatchList, "published"): toInt,
servicePath("privileged"): toBoolean,
servicePath("read_only"): toBoolean,
servicePath("scale"): toInt,
@ -94,19 +92,11 @@ func toInt64(value string) (interface{}, error) {
}
func toUnitBytes(value string) (interface{}, error) {
i, err := strconv.ParseInt(value, 10, 64)
if err != nil {
return nil, err
}
return types.UnitBytes(i), nil
return transformSize(value)
}
func toDuration(value string) (interface{}, error) {
i, err := strconv.ParseInt(value, 10, 64)
if err != nil {
return nil, err
}
return types.Duration(i), nil
return transformStringToDuration(value)
}
func toFloat(value string) (interface{}, error) {

@ -23,15 +23,18 @@ import (
"path"
"path/filepath"
"reflect"
"regexp"
"sort"
"strconv"
"strings"
"time"
"github.com/compose-spec/compose-go/consts"
"github.com/compose-spec/compose-go/dotenv"
interp "github.com/compose-spec/compose-go/interpolation"
"github.com/compose-spec/compose-go/schema"
"github.com/compose-spec/compose-go/template"
"github.com/compose-spec/compose-go/types"
"github.com/compose-spec/godotenv"
"github.com/docker/go-units"
"github.com/mattn/go-shellwords"
"github.com/mitchellh/mapstructure"
@ -58,8 +61,19 @@ type Options struct {
Interpolate *interp.Options
// Discard 'env_file' entries after resolving to 'environment' section
discardEnvFiles bool
// Set project name
Name string
// Set project projectName
projectName string
// Indicates when the projectName was imperatively set or guessed from path
projectNameImperativelySet bool
}
func (o *Options) SetProjectName(name string, imperativelySet bool) {
o.projectName = normalizeProjectName(name)
o.projectNameImperativelySet = imperativelySet
}
func (o Options) GetProjectName() (string, bool) {
return o.projectName, o.projectNameImperativelySet
}
// serviceRef identifies a reference to a service. It's used to detect cyclic
@ -192,8 +206,17 @@ func Load(configDetails types.ConfigDetails, options ...func(*Options)) (*types.
s.EnvFile = newEnvFiles
}
projectName, projectNameImperativelySet := opts.GetProjectName()
model.Name = normalizeProjectName(model.Name)
if !projectNameImperativelySet && model.Name != "" {
projectName = model.Name
}
if projectName != "" {
configDetails.Environment[consts.ComposeProjectName] = projectName
}
project := &types.Project{
Name: opts.Name,
Name: projectName,
WorkingDir: configDetails.WorkingDir,
Services: model.Services,
Networks: model.Networks,
@ -221,6 +244,13 @@ func Load(configDetails types.ConfigDetails, options ...func(*Options)) (*types.
return project, nil
}
func normalizeProjectName(s string) string {
r := regexp.MustCompile("[a-z0-9_-]")
s = strings.ToLower(s)
s = strings.Join(r.FindAllString(s, -1), "")
return strings.TrimLeft(s, "_-")
}
func parseConfig(b []byte, opts *Options) (map[string]interface{}, error) {
yml, err := ParseYAML(b)
if err != nil {
@ -254,7 +284,14 @@ func loadSections(filename string, config map[string]interface{}, configDetails
cfg := types.Config{
Filename: filename,
}
name := ""
if n, ok := config["name"]; ok {
name, ok = n.(string)
if !ok {
return nil, errors.New("project name must be a string")
}
}
cfg.Name = name
cfg.Services, err = LoadServices(filename, getSection(config, "services"), configDetails.WorkingDir, configDetails.LookupEnv, opts)
if err != nil {
return nil, err
@ -280,10 +317,6 @@ func loadSections(filename string, config map[string]interface{}, configDetails
if len(extensions) > 0 {
cfg.Extensions = extensions
}
if err != nil {
return nil, err
}
return &cfg, nil
}
@ -357,6 +390,7 @@ func createTransformHook(additionalTransformers ...Transformer) mapstructure.Dec
reflect.TypeOf(types.DependsOnConfig{}): transformDependsOnConfig,
reflect.TypeOf(types.ExtendsConfig{}): transformExtendsConfig,
reflect.TypeOf(types.DeviceRequest{}): transformServiceDeviceRequest,
reflect.TypeOf(types.SSHConfig{}): transformSSHConfig,
}
for _, transformer := range additionalTransformers {
@ -559,7 +593,7 @@ func resolveEnvironment(serviceConfig *types.ServiceConfig, workingDir string, l
return err
}
defer file.Close()
fileVars, err := godotenv.ParseWithLookup(file, godotenv.LookupFn(lookupEnv))
fileVars, err := dotenv.ParseWithLookup(file, dotenv.LookupFn(lookupEnv))
if err != nil {
return err
}
@ -817,6 +851,10 @@ var transformServicePort TransformerFunc = func(data interface{}) (interface{},
ports = append(ports, v)
}
case map[string]interface{}:
published := value["published"]
if v, ok := published.(int); ok {
value["published"] = strconv.Itoa(v)
}
ports = append(ports, groupXFieldsIntoExtensions(value))
default:
return data, errors.Errorf("invalid type %T for port", value)
@ -891,7 +929,7 @@ var transformDependsOnConfig TransformerFunc = func(data interface{}) (interface
for _, serviceIntf := range value {
service, ok := serviceIntf.(string)
if !ok {
return data, errors.Errorf("invalid type %T for service depends_on element. Expected string.", value)
return data, errors.Errorf("invalid type %T for service depends_on elementn, expected string", value)
}
transformed[service] = map[string]interface{}{"condition": types.ServiceConditionStarted}
}
@ -941,6 +979,35 @@ var transformServiceNetworkMap TransformerFunc = func(value interface{}) (interf
return value, nil
}
var transformSSHConfig TransformerFunc = func(data interface{}) (interface{}, error) {
switch value := data.(type) {
case map[string]interface{}:
var result []types.SSHKey
for key, val := range value {
if val == nil {
val = ""
}
result = append(result, types.SSHKey{ID: key, Path: val.(string)})
}
return result, nil
case []interface{}:
var result []types.SSHKey
for _, v := range value {
key, val := transformValueToMapEntry(v.(string), "=", false)
result = append(result, types.SSHKey{ID: key, Path: val.(string)})
}
return result, nil
case string:
if value == "" {
value = "default"
}
key, val := transformValueToMapEntry(value, "=", false)
result := []types.SSHKey{{ID: key, Path: val.(string)}}
return result, nil
}
return nil, errors.Errorf("expected a sting, map or a list, got %T: %#v", data, data)
}
var transformStringOrNumberList TransformerFunc = func(value interface{}) (interface{}, error) {
list := value.([]interface{})
result := make([]string, len(list))
@ -963,47 +1030,52 @@ var transformStringList TransformerFunc = func(data interface{}) (interface{}, e
func transformMappingOrListFunc(sep string, allowNil bool) TransformerFunc {
return func(data interface{}) (interface{}, error) {
return transformMappingOrList(data, sep, allowNil), nil
return transformMappingOrList(data, sep, allowNil)
}
}
func transformListOrMappingFunc(sep string, allowNil bool) TransformerFunc {
return func(data interface{}) (interface{}, error) {
return transformListOrMapping(data, sep, allowNil), nil
return transformListOrMapping(data, sep, allowNil)
}
}
func transformListOrMapping(listOrMapping interface{}, sep string, allowNil bool) interface{} {
func transformListOrMapping(listOrMapping interface{}, sep string, allowNil bool) (interface{}, error) {
switch value := listOrMapping.(type) {
case map[string]interface{}:
return toStringList(value, sep, allowNil)
return toStringList(value, sep, allowNil), nil
case []interface{}:
return listOrMapping
return listOrMapping, nil
}
panic(errors.Errorf("expected a map or a list, got %T: %#v", listOrMapping, listOrMapping))
return nil, errors.Errorf("expected a map or a list, got %T: %#v", listOrMapping, listOrMapping)
}
func transformMappingOrList(mappingOrList interface{}, sep string, allowNil bool) interface{} {
func transformMappingOrList(mappingOrList interface{}, sep string, allowNil bool) (interface{}, error) {
switch value := mappingOrList.(type) {
case map[string]interface{}:
return toMapStringString(value, allowNil)
return toMapStringString(value, allowNil), nil
case []interface{}:
result := make(map[string]interface{})
for _, value := range value {
parts := strings.SplitN(value.(string), sep, 2)
key, val := transformValueToMapEntry(value.(string), sep, allowNil)
result[key] = val
}
return result, nil
}
return nil, errors.Errorf("expected a map or a list, got %T: %#v", mappingOrList, mappingOrList)
}
func transformValueToMapEntry(value string, separator string, allowNil bool) (string, interface{}) {
parts := strings.SplitN(value, separator, 2)
key := parts[0]
switch {
case len(parts) == 1 && allowNil:
result[key] = nil
return key, nil
case len(parts) == 1 && !allowNil:
result[key] = ""
return key, ""
default:
result[key] = parts[1]
}
return key, parts[1]
}
return result
}
panic(errors.Errorf("expected a map or a list, got %T: %#v", mappingOrList, mappingOrList))
}
var transformShellCommand TransformerFunc = func(value interface{}) (interface{}, error) {
@ -1045,6 +1117,8 @@ var transformStringToDuration TransformerFunc = func(value interface{}) (interfa
return value, err
}
return types.Duration(d), nil
case types.Duration:
return value, nil
default:
return value, errors.Errorf("invalid type %T for duration", value)
}

@ -53,6 +53,7 @@ func merge(configs []*types.Config) (*types.Config, error) {
base := configs[0]
for _, override := range configs[1:] {
var err error
base.Name = mergeNames(base.Name, override.Name)
base.Services, err = mergeServices(base.Services, override.Services)
if err != nil {
return base, errors.Wrapf(err, "cannot merge services from %s", override.Filename)
@ -81,6 +82,13 @@ func merge(configs []*types.Config) (*types.Config, error) {
return base, nil
}
func mergeNames(base, override string) string {
if override != "" {
return override
}
return base
}
func mergeServices(base, override []types.ServiceConfig) ([]types.ServiceConfig, error) {
baseServices := mapByName(base)
overrideServices := mapByName(override)
@ -154,7 +162,7 @@ func toServicePortConfigsMap(s interface{}) (map[interface{}]interface{}, error)
m := map[interface{}]interface{}{}
type port struct {
target uint32
published uint32
published string
ip string
protocol string
}
@ -291,7 +299,7 @@ func mergeLoggingConfig(dst, src reflect.Value) error {
return nil
}
//nolint: unparam
// nolint: unparam
func mergeUlimitsConfig(dst, src reflect.Value) error {
if src.Interface() != reflect.Zero(reflect.TypeOf(src.Interface())).Interface() {
dst.Elem().Set(src.Elem())
@ -299,7 +307,7 @@ func mergeUlimitsConfig(dst, src reflect.Value) error {
return nil
}
//nolint: unparam
// nolint: unparam
func mergeServiceNetworkConfig(dst, src reflect.Value) error {
if src.Interface() != reflect.Zero(reflect.TypeOf(src.Interface())).Interface() {
dst.Elem().FieldByName("Aliases").Set(src.Elem().FieldByName("Aliases"))

@ -79,7 +79,7 @@ func normalize(project *types.Project, resolvePaths bool) error {
if resolvePaths {
s.Build.Context = localContext
}
} else {
// } else {
// might be a remote http/git context. Unfortunately supported "remote" syntax is highly ambiguous
// in moby/moby and not defined by compose-spec, so let's assume runtime will check
}

@ -92,7 +92,7 @@ func populateFieldFromBuffer(char rune, buffer []rune, volume *types.ServiceVolu
volume.Volume = &types.ServiceVolumeVolume{NoCopy: true}
default:
if isBindOption(option) {
volume.Bind = &types.ServiceVolumeBind{Propagation: option}
setBindOption(volume, option)
}
// ignore unknown options
}
@ -109,13 +109,39 @@ var Propagations = []string{
types.PropagationSlave,
}
type setBindOptionFunc func(bind *types.ServiceVolumeBind, option string)
var bindOptions = map[string]setBindOptionFunc{
types.PropagationRPrivate: setBindPropagation,
types.PropagationPrivate: setBindPropagation,
types.PropagationRShared: setBindPropagation,
types.PropagationShared: setBindPropagation,
types.PropagationRSlave: setBindPropagation,
types.PropagationSlave: setBindPropagation,
types.SELinuxShared: setBindSELinux,
types.SELinuxPrivate: setBindSELinux,
}
func setBindPropagation(bind *types.ServiceVolumeBind, option string) {
bind.Propagation = option
}
func setBindSELinux(bind *types.ServiceVolumeBind, option string) {
bind.SELinux = option
}
func isBindOption(option string) bool {
for _, propagation := range Propagations {
if option == propagation {
return true
}
_, ok := bindOptions[option]
return ok
}
func setBindOption(volume *types.ServiceVolumeConfig, option string) {
if volume.Bind == nil {
volume.Bind = &types.ServiceVolumeBind{}
}
return false
bindOptions[option](volume.Bind, option)
}
func populateType(volume *types.ServiceVolumeConfig) {

@ -8,7 +8,12 @@
"properties": {
"version": {
"type": "string",
"description": "Version of the Compose specification used. Tools not implementing required version MUST reject the configuration file."
"description": "declared for backward compatibility, ignored."
},
"name": {
"type": "string",
"description": "define the Compose project name, until user defines one explicitly."
},
"services": {
@ -86,9 +91,13 @@
"context": {"type": "string"},
"dockerfile": {"type": "string"},
"args": {"$ref": "#/definitions/list_or_dict"},
"ssh": {"$ref": "#/definitions/list_or_dict"},
"labels": {"$ref": "#/definitions/list_or_dict"},
"cache_from": {"type": "array", "items": {"type": "string"}},
"cache_to": {"type": "array", "items": {"type": "string"}},
"no_cache": {"type": "boolean"},
"network": {"type": "string"},
"pull": {"type": "boolean"},
"target": {"type": "string"},
"shm_size": {"type": ["integer", "string"]},
"extra_hosts": {"$ref": "#/definitions/list_or_dict"},
@ -318,7 +327,7 @@
"mode": {"type": "string"},
"host_ip": {"type": "string"},
"target": {"type": "integer"},
"published": {"type": "integer"},
"published": {"type": ["string", "integer"]},
"protocol": {"type": "string"}
},
"additionalProperties": false,
@ -410,7 +419,8 @@
"type": "object",
"properties": {
"propagation": {"type": "string"},
"create_host_path": {"type": "boolean"}
"create_host_path": {"type": "boolean"},
"selinux": {"type": "string", "enum": ["z", "Z"]}
},
"additionalProperties": false,
"patternProperties": {"^x-": {}}
@ -519,7 +529,8 @@
"type": "object",
"properties": {
"cpus": {"type": ["number", "string"]},
"memory": {"type": "string"}
"memory": {"type": "string"},
"pids": {"type": "integer"}
},
"additionalProperties": false,
"patternProperties": {"^x-": {}}

@ -27,11 +27,6 @@ import (
_ "embed"
)
const (
defaultVersion = "1.0"
versionField = "version"
)
type portsFormatChecker struct{}
func (checker portsFormatChecker) IsFormat(input interface{}) bool {

@ -26,6 +26,7 @@ import (
var delimiter = "\\$"
var substitutionNamed = "[_a-z][_a-z0-9]*"
var substitutionBraced = "[_a-z][_a-z0-9]*(?::?[-?](.*}|[^}]*))?"
var patternString = fmt.Sprintf(
@ -60,15 +61,17 @@ type SubstituteFunc func(string, Mapping) (string, bool, error)
// It accepts additional substitute function.
func SubstituteWith(template string, mapping Mapping, pattern *regexp.Regexp, subsFuncs ...SubstituteFunc) (string, error) {
if len(subsFuncs) == 0 {
subsFuncs = []SubstituteFunc{
softDefault,
hardDefault,
requiredNonEmpty,
required,
}
subsFuncs = getDefaultSortedSubstitutionFunctions(template)
}
var err error
result := pattern.ReplaceAllStringFunc(template, func(substring string) string {
closingBraceIndex := getFirstBraceClosingIndex(substring)
rest := ""
if closingBraceIndex > -1 {
rest = substring[closingBraceIndex+1:]
substring = substring[0 : closingBraceIndex+1]
}
matches := pattern.FindStringSubmatch(substring)
groups := matchGroups(matches, pattern)
if escaped := groups["escaped"]; escaped != "" {
@ -100,7 +103,11 @@ func SubstituteWith(template string, mapping Mapping, pattern *regexp.Regexp, su
if !applied {
continue
}
return value
interpolatedNested, err := SubstituteWith(rest, mapping, pattern, subsFuncs...)
if err != nil {
return ""
}
return value + interpolatedNested
}
}
@ -114,6 +121,42 @@ func SubstituteWith(template string, mapping Mapping, pattern *regexp.Regexp, su
return result, err
}
func getDefaultSortedSubstitutionFunctions(template string, fns ...SubstituteFunc) []SubstituteFunc {
hyphenIndex := strings.IndexByte(template, '-')
questionIndex := strings.IndexByte(template, '?')
if hyphenIndex < 0 || hyphenIndex > questionIndex {
return []SubstituteFunc{
requiredNonEmpty,
required,
softDefault,
hardDefault,
}
}
return []SubstituteFunc{
softDefault,
hardDefault,
requiredNonEmpty,
required,
}
}
func getFirstBraceClosingIndex(s string) int {
openVariableBraces := 0
for i := 0; i < len(s); i++ {
if s[i] == '}' {
openVariableBraces--
if openVariableBraces == 0 {
return i
}
}
if strings.HasPrefix(s[i:], "${") {
openVariableBraces++
i++
}
}
return -1
}
// Substitute variables in the string with their values
func Substitute(template string, mapping Mapping) (string, error) {
return SubstituteWith(template, mapping, defaultPattern)

@ -49,6 +49,7 @@ type ConfigFile struct {
// Config is a full compose file configuration and model
type Config struct {
Filename string `yaml:"-" json:"-"`
Name string `yaml:",omitempty" json:"name,omitempty"`
Services Services `json:"services"`
Networks Networks `yaml:",omitempty" json:"networks,omitempty"`
Volumes Volumes `yaml:",omitempty" json:"volumes,omitempty"`

@ -29,7 +29,7 @@ import (
// Project is the result of loading a set of compose files
type Project struct {
Name string `yaml:"-" json:"-"`
Name string `yaml:"name,omitempty" json:"name,omitempty"`
WorkingDir string `yaml:"-" json:"-"`
Services Services `json:"services"`
Networks Networks `yaml:",omitempty" json:"networks,omitempty"`

@ -108,6 +108,7 @@ type ServiceConfig struct {
CredentialSpec *CredentialSpecConfig `mapstructure:"credential_spec" yaml:"credential_spec,omitempty" json:"credential_spec,omitempty"`
DependsOn DependsOnConfig `mapstructure:"depends_on" yaml:"depends_on,omitempty" json:"depends_on,omitempty"`
Deploy *DeployConfig `yaml:",omitempty" json:"deploy,omitempty"`
DeviceCgroupRules []string `mapstructure:"device_cgroup_rules" yaml:"device_cgroup_rules,omitempty" json:"device_cgroup_rules,omitempty"`
Devices []string `yaml:",omitempty" json:"devices,omitempty"`
DNS StringList `yaml:",omitempty" json:"dns,omitempty"`
DNSOpts []string `mapstructure:"dns_opt" yaml:"dns_opt,omitempty" json:"dns_opt,omitempty"`
@ -129,6 +130,7 @@ type ServiceConfig struct {
Ipc string `yaml:",omitempty" json:"ipc,omitempty"`
Isolation string `mapstructure:"isolation" yaml:"isolation,omitempty" json:"isolation,omitempty"`
Labels Labels `yaml:",omitempty" json:"labels,omitempty"`
CustomLabels Labels `yaml:"-" json:"-"`
Links []string `yaml:",omitempty" json:"links,omitempty"`
Logging *LoggingConfig `yaml:",omitempty" json:"logging,omitempty"`
LogDriver string `mapstructure:"log_driver" yaml:"log_driver,omitempty" json:"log_driver,omitempty"`
@ -292,8 +294,12 @@ type BuildConfig struct {
Context string `yaml:",omitempty" json:"context,omitempty"`
Dockerfile string `yaml:",omitempty" json:"dockerfile,omitempty"`
Args MappingWithEquals `yaml:",omitempty" json:"args,omitempty"`
SSH SSHConfig `yaml:"ssh,omitempty" json:"ssh,omitempty"`
Labels Labels `yaml:",omitempty" json:"labels,omitempty"`
CacheFrom StringList `mapstructure:"cache_from" yaml:"cache_from,omitempty" json:"cache_from,omitempty"`
CacheTo StringList `mapstructure:"cache_to" yaml:"cache_to,omitempty" json:"cache_to,omitempty"`
NoCache bool `mapstructure:"no_cache" yaml:"no_cache,omitempty" json:"no_cache,omitempty"`
Pull bool `mapstructure:"pull" yaml:"pull,omitempty" json:"pull,omitempty"`
ExtraHosts HostsList `mapstructure:"extra_hosts" yaml:"extra_hosts,omitempty" json:"extra_hosts,omitempty"`
Isolation string `yaml:",omitempty" json:"isolation,omitempty"`
Network string `yaml:",omitempty" json:"network,omitempty"`
@ -305,11 +311,11 @@ type BuildConfig struct {
// BlkioConfig define blkio config
type BlkioConfig struct {
Weight uint16 `yaml:",omitempty" json:"weight,omitempty"`
WeightDevice []WeightDevice `yaml:",omitempty" json:"weight_device,omitempty"`
DeviceReadBps []ThrottleDevice `yaml:",omitempty" json:"device_read_bps,omitempty"`
DeviceReadIOps []ThrottleDevice `yaml:",omitempty" json:"device_read_iops,omitempty"`
DeviceWriteBps []ThrottleDevice `yaml:",omitempty" json:"device_write_bps,omitempty"`
DeviceWriteIOps []ThrottleDevice `yaml:",omitempty" json:"device_write_iops,omitempty"`
WeightDevice []WeightDevice `mapstructure:"weight_device" yaml:",omitempty" json:"weight_device,omitempty"`
DeviceReadBps []ThrottleDevice `mapstructure:"device_read_bps" yaml:",omitempty" json:"device_read_bps,omitempty"`
DeviceReadIOps []ThrottleDevice `mapstructure:"device_read_iops" yaml:",omitempty" json:"device_read_iops,omitempty"`
DeviceWriteBps []ThrottleDevice `mapstructure:"device_write_bps" yaml:",omitempty" json:"device_write_bps,omitempty"`
DeviceWriteIOps []ThrottleDevice `mapstructure:"device_write_iops" yaml:",omitempty" json:"device_write_iops,omitempty"`
Extensions map[string]interface{} `yaml:",inline" json:"-"`
}
@ -423,6 +429,39 @@ func (l Labels) Add(key, value string) Labels {
return l
}
type SSHKey struct {
ID string
Path string
}
// SSHConfig is a mapping type for SSH build config
type SSHConfig []SSHKey
func (s SSHConfig) Get(id string) (string, error) {
for _, sshKey := range s {
if sshKey.ID == id {
return sshKey.Path, nil
}
}
return "", fmt.Errorf("ID %s not found in SSH keys", id)
}
// MarshalYAML makes SSHKey implement yaml.Marshaller
func (s SSHKey) MarshalYAML() (interface{}, error) {
if s.Path == "" {
return s.ID, nil
}
return fmt.Sprintf("%s: %s", s.ID, s.Path), nil
}
// MarshalJSON makes SSHKey implement json.Marshaller
func (s SSHKey) MarshalJSON() ([]byte, error) {
if s.Path == "" {
return []byte(fmt.Sprintf(`"%s"`, s.ID)), nil
}
return []byte(fmt.Sprintf(`"%s": %s`, s.ID, s.Path)), nil
}
// MappingWithColon is a mapping type that can be converted from a list of
// 'key: value' strings
type MappingWithColon map[string]string
@ -493,6 +532,7 @@ type Resource struct {
// TODO: types to convert from units and ratios
NanoCPUs string `mapstructure:"cpus" yaml:"cpus,omitempty" json:"cpus,omitempty"`
MemoryBytes UnitBytes `mapstructure:"memory" yaml:"memory,omitempty" json:"memory,omitempty"`
PIds int64 `mapstructure:"pids" yaml:"pids,omitempty" json:"pids,omitempty"`
Devices []DeviceRequest `mapstructure:"devices" yaml:"devices,omitempty" json:"devices,omitempty"`
GenericResources []GenericResource `mapstructure:"generic_resources" yaml:"generic_resources,omitempty" json:"generic_resources,omitempty"`
@ -579,7 +619,7 @@ type ServicePortConfig struct {
Mode string `yaml:",omitempty" json:"mode,omitempty"`
HostIP string `mapstructure:"host_ip" yaml:"host_ip,omitempty" json:"host_ip,omitempty"`
Target uint32 `yaml:",omitempty" json:"target,omitempty"`
Published uint32 `yaml:",omitempty" json:"published,omitempty"`
Published string `yaml:",omitempty" json:"published,omitempty"`
Protocol string `yaml:",omitempty" json:"protocol,omitempty"`
Extensions map[string]interface{} `yaml:",inline" json:"-"`
@ -613,22 +653,14 @@ func ParsePortConfig(value string) ([]ServicePortConfig, error) {
func convertPortToPortConfig(port nat.Port, portBindings map[nat.Port][]nat.PortBinding) ([]ServicePortConfig, error) {
var portConfigs []ServicePortConfig
for _, binding := range portBindings[port] {
startHostPort, endHostPort, err := nat.ParsePortRange(binding.HostPort)
if err != nil && binding.HostPort != "" {
return nil, fmt.Errorf("invalid hostport binding (%s) for port (%s)", binding.HostPort, port.Port())
}
for i := startHostPort; i <= endHostPort; i++ {
portConfigs = append(portConfigs, ServicePortConfig{
HostIP: binding.HostIP,
Protocol: strings.ToLower(port.Proto()),
Target: uint32(port.Int()),
Published: uint32(i),
Published: binding.HostPort,
Mode: "ingress",
})
}
}
return portConfigs, nil
}
@ -655,16 +687,30 @@ const (
VolumeTypeTmpfs = "tmpfs"
// VolumeTypeNamedPipe is the type for mounting Windows named pipes
VolumeTypeNamedPipe = "npipe"
// SElinuxShared share the volume content
SElinuxShared = "z"
// SElinuxUnshared label content as private unshared
SElinuxUnshared = "Z"
)
// ServiceVolumeBind are options for a service volume of type bind
type ServiceVolumeBind struct {
SELinux string `mapstructure:"selinux" yaml:",omitempty" json:"selinux,omitempty"`
Propagation string `yaml:",omitempty" json:"propagation,omitempty"`
CreateHostPath bool `mapstructure:"create_host_path" yaml:"create_host_path,omitempty" json:"create_host_path,omitempty"`
Extensions map[string]interface{} `yaml:",inline" json:"-"`
}
// SELinux represents the SELinux re-labeling options.
const (
// SELinuxShared option indicates that the bind mount content is shared among multiple containers
SELinuxShared string = "z"
// SELinuxPrivate option indicates that the bind mount content is private and unshared
SELinuxPrivate string = "Z"
)
// Propagation represents the propagation of a mount.
const (
// PropagationRPrivate RPRIVATE

@ -1,27 +0,0 @@
ARG GOLANG_VERSION=1.17.1
ARG ALPINE_VERSION=3.14
FROM golang:${GOLANG_VERSION}-alpine${ALPINE_VERSION}
WORKDIR /code
code:
FROM +base
COPY . .
golangci:
ARG GOLANGCI_VERSION=v1.40.1
FROM golangci/golangci-lint:${GOLANGCI_VERSION}-alpine
SAVE ARTIFACT /usr/bin/golangci-lint
lint:
FROM +code
COPY +golangci/golangci-lint /usr/bin/golangci-lint
RUN golangci-lint run --timeout 5m ./...
test:
FROM +code
RUN go test ./...
all:
BUILD +lint
BUILD +test

@ -1,18 +0,0 @@
# GoDotEnv
A Go (golang) port of the Ruby dotenv project (which loads env vars from a .env file)
From the original Library:
> Storing configuration in the environment is one of the tenets of a twelve-factor app. Anything that is likely to change between deployment environmentssuch as resource handles for databases or credentials for external servicesshould be extracted from the code into environment variables.
>
> But it is not always practical to set environment variables on development machines or continuous integration servers where multiple projects are run. Dotenv load variables from a .env file into ENV when the environment is bootstrapped.
This is a fork of [joho/godotenv](https://github.com/joho/godotenv) focussing on `.env` file support by the compose specification
To run linter and tests, please install [Earthly](https://earthly.dev/get-earthly) and run:
```sh
earthly +all
```

@ -1,3 +0,0 @@
module github.com/compose-spec/godotenv
go 1.16

@ -1,3 +1,7 @@
## 1.4.3
* Fix cases where `json.Number` didn't decode properly [GH-261]
## 1.4.2
* Custom name matchers to support any sort of casing, formatting, etc. for

@ -684,16 +684,12 @@ func (d *Decoder) decodeUint(name string, data interface{}, val reflect.Value) e
}
case dataType.PkgPath() == "encoding/json" && dataType.Name() == "Number":
jn := data.(json.Number)
i, err := jn.Int64()
i, err := strconv.ParseUint(string(jn), 0, 64)
if err != nil {
return fmt.Errorf(
"error decoding json.Number into %s: %s", name, err)
}
if i < 0 && !d.config.WeaklyTypedInput {
return fmt.Errorf("cannot parse '%s', %d overflows uint",
name, i)
}
val.SetUint(uint64(i))
val.SetUint(i)
default:
return fmt.Errorf(
"'%s' expected type '%s', got unconvertible type '%s', value: '%v'",

@ -0,0 +1,32 @@
package imageutil
import (
"encoding/base64"
"encoding/json"
binfotypes "github.com/moby/buildkit/util/buildinfo/types"
"github.com/pkg/errors"
)
// BuildInfo returns build info from image config.
func BuildInfo(dt []byte) (*binfotypes.BuildInfo, error) {
if len(dt) == 0 {
return nil, nil
}
var config binfotypes.ImageConfig
if err := json.Unmarshal(dt, &config); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal image config")
}
if len(config.BuildInfo) == 0 {
return nil, nil
}
dtbi, err := base64.StdEncoding.DecodeString(config.BuildInfo)
if err != nil {
return nil, err
}
var bi binfotypes.BuildInfo
if err = json.Unmarshal(dtbi, &bi); err != nil {
return nil, errors.Wrap(err, "failed to decode buildinfo from image config")
}
return &bi, nil
}

@ -256,7 +256,7 @@ func (p *textMux) print(t *trace) {
}
// make any open vertex active
for dgst, v := range t.byDigest {
if v.isStarted() && !v.isCompleted() {
if v.isStarted() && !v.isCompleted() && v.ProgressGroup == nil && !v.hidden {
p.printVtx(t, dgst)
return
}
@ -281,6 +281,10 @@ func (p *textMux) print(t *trace) {
if !ok {
continue
}
if v.lastBlockTime == nil {
// shouldn't happen, but not worth crashing over
continue
}
tm := now.Sub(*v.lastBlockTime)
speed := float64(v.count) / tm.Seconds()
overLimit := tm > maxDelay && dgst != current

@ -123,7 +123,13 @@ func Walk(ctx context.Context, p string, opt *WalkOpt, fn filepath.WalkFunc) err
return nil
}
var dir visitedDir
var (
dir visitedDir
isDir bool
)
if fi != nil {
isDir = fi.IsDir()
}
if includeMatcher != nil || excludeMatcher != nil {
for len(parentDirs) != 0 {
@ -134,7 +140,7 @@ func Walk(ctx context.Context, p string, opt *WalkOpt, fn filepath.WalkFunc) err
parentDirs = parentDirs[:len(parentDirs)-1]
}
if fi.IsDir() {
if isDir {
dir = visitedDir{
fi: fi,
path: path,
@ -156,12 +162,12 @@ func Walk(ctx context.Context, p string, opt *WalkOpt, fn filepath.WalkFunc) err
return errors.Wrap(err, "failed to match includepatterns")
}
if fi.IsDir() {
if isDir {
dir.includeMatchInfo = matchInfo
}
if !m {
if fi.IsDir() && onlyPrefixIncludes {
if isDir && onlyPrefixIncludes {
// Optimization: we can skip walking this dir if no include
// patterns could match anything inside it.
dirSlash := path + string(filepath.Separator)
@ -191,12 +197,12 @@ func Walk(ctx context.Context, p string, opt *WalkOpt, fn filepath.WalkFunc) err
return errors.Wrap(err, "failed to match excludepatterns")
}
if fi.IsDir() {
if isDir {
dir.excludeMatchInfo = matchInfo
}
if m {
if fi.IsDir() && onlyPrefixExcludeExceptions {
if isDir && onlyPrefixExcludeExceptions {
// Optimization: we can skip walking this dir if no
// exceptions to exclude patterns could match anything
// inside it.
@ -230,7 +236,7 @@ func Walk(ctx context.Context, p string, opt *WalkOpt, fn filepath.WalkFunc) err
if includeMatcher != nil || excludeMatcher != nil {
defer func() {
if fi.IsDir() {
if isDir {
parentDirs = append(parentDirs, dir)
}
}()

13
vendor/modules.txt vendored

@ -32,16 +32,16 @@ github.com/cenkalti/backoff/v4
github.com/cespare/xxhash/v2
# github.com/cloudflare/cfssl v0.0.0-20181213083726-b94e044bb51e
## explicit
# github.com/compose-spec/compose-go v1.0.8
# github.com/compose-spec/compose-go v1.2.1
## explicit
github.com/compose-spec/compose-go/consts
github.com/compose-spec/compose-go/dotenv
github.com/compose-spec/compose-go/errdefs
github.com/compose-spec/compose-go/interpolation
github.com/compose-spec/compose-go/loader
github.com/compose-spec/compose-go/schema
github.com/compose-spec/compose-go/template
github.com/compose-spec/compose-go/types
# github.com/compose-spec/godotenv v1.1.1
github.com/compose-spec/godotenv
# github.com/containerd/console v1.0.3
## explicit
github.com/containerd/console
@ -292,9 +292,9 @@ github.com/matttproud/golang_protobuf_extensions/pbutil
github.com/miekg/pkcs11
# github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7
github.com/mitchellh/go-wordwrap
# github.com/mitchellh/mapstructure v1.4.2
# github.com/mitchellh/mapstructure v1.4.3
github.com/mitchellh/mapstructure
# github.com/moby/buildkit v0.10.0-rc2.0.20220308185020-fdecd0ae108b
# github.com/moby/buildkit v0.10.1-0.20220403220257-10e6f94bf90d
## explicit
github.com/moby/buildkit/api/services/control
github.com/moby/buildkit/api/types
@ -436,7 +436,8 @@ github.com/theupdateframework/notary/tuf/data
github.com/theupdateframework/notary/tuf/signed
github.com/theupdateframework/notary/tuf/utils
github.com/theupdateframework/notary/tuf/validation
# github.com/tonistiigi/fsutil v0.0.0-20220115021204-b19f7f9cb274
# github.com/tonistiigi/fsutil v0.0.0-20220315205639-9ed612626da3
## explicit
github.com/tonistiigi/fsutil
github.com/tonistiigi/fsutil/types
# github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea

Loading…
Cancel
Save