add support for excludes in input dir

This adds support for `--include` and `--exclude` for directory inputs.

For instance, this is what you would want to use when passing dagger
repository as an input:

```
inputs:
    repository:
        dir:
            path: .
            exclude:
                - '**/node_modules'
                - cmd/dagger/dagger
                - cmd/dagger/dagger-debug
```

Signed-off-by: Andrea Luzzardi <aluzzardi@gmail.com>
This commit is contained in:
Andrea Luzzardi 2021-05-28 16:04:16 -07:00
parent 2f9a5df397
commit b627b4bc88
16 changed files with 103 additions and 93 deletions

View File

@ -57,7 +57,7 @@ var computeCmd = &cobra.Command{
for _, input := range viper.GetStringSlice("input-dir") {
parts := strings.SplitN(input, "=", 2)
k, v := parts[0], parts[1]
err := st.SetInput(k, state.DirInput(v, []string{}))
err := st.SetInput(k, state.DirInput(v, []string{}, []string{}))
if err != nil {
lg.
Fatal().

View File

@ -43,11 +43,20 @@ var dirCmd = &cobra.Command{
p = "./" + p
}
updateEnvironmentInput(ctx, args[0], state.DirInput(p, []string{}))
updateEnvironmentInput(ctx, args[0],
state.DirInput(
p,
viper.GetStringSlice("include"),
viper.GetStringSlice("exclude"),
),
)
},
}
func init() {
dirCmd.Flags().StringSlice("include", []string{}, "Include pattern")
dirCmd.Flags().StringSlice("exclude", []string{}, "Exclude pattern")
if err := viper.BindPFlags(dirCmd.Flags()); err != nil {
panic(err)
}

View File

@ -12,7 +12,7 @@ func TestLocalDirs(t *testing.T) {
Path: "/tmp/source",
Plan: "/tmp/source/plan",
}
require.NoError(t, st.SetInput("www.source", state.DirInput("/", []string{})))
require.NoError(t, st.SetInput("www.source", state.DirInput("/", []string{}, []string{})))
environment, err := New(st)
require.NoError(t, err)

View File

@ -1,7 +1,6 @@
package environment
import (
"bytes"
"context"
"encoding/json"
"errors"
@ -9,7 +8,6 @@ import (
"io/fs"
"net"
"net/url"
"path"
"strings"
"cuelang.org/go/cue"
@ -19,7 +17,6 @@ import (
"github.com/moby/buildkit/exporter/containerimage/exptypes"
dockerfilebuilder "github.com/moby/buildkit/frontend/dockerfile/builder"
"github.com/moby/buildkit/frontend/dockerfile/dockerfile2llb"
"github.com/moby/buildkit/frontend/dockerfile/dockerignore"
bkgw "github.com/moby/buildkit/frontend/gateway/client"
bkpb "github.com/moby/buildkit/solver/pb"
"github.com/rs/zerolog/log"
@ -29,10 +26,6 @@ import (
"go.dagger.io/dagger/solver"
)
const (
daggerignoreFilename = ".daggerignore"
)
// An execution pipeline
type Pipeline struct {
code *compiler.Value
@ -298,71 +291,50 @@ func (p *Pipeline) Local(ctx context.Context, op *compiler.Value, st llb.State)
return st, err
}
// daggerignore processing
// buildkit related setup
daggerignoreState := llb.Local(
dir,
llb.SessionID(p.s.SessionID()),
llb.FollowPaths([]string{daggerignoreFilename}),
llb.SharedKeyHint(dir+"-"+daggerignoreFilename),
llb.WithCustomName(p.vertexNamef("Try loading %s", path.Join(dir, daggerignoreFilename))),
)
ref, err := p.s.Solve(ctx, daggerignoreState)
if err != nil {
return st, err
}
// try to read file
var daggerignore []byte
// bool in case file is empty
ignorefound := true
daggerignore, err = ref.ReadFile(ctx, bkgw.ReadRequest{
Filename: daggerignoreFilename,
})
// hack for string introspection because !errors.Is(err, os.ErrNotExist) does not work, same for fs
if err != nil {
if !strings.Contains(err.Error(), ".daggerignore: no such file or directory") {
return st, err
}
ignorefound = false
}
// parse out excludes, works even if file does not exist
var excludes []string
excludes, err = dockerignore.ReadAll(bytes.NewBuffer(daggerignore))
if err != nil {
return st, fmt.Errorf("%w failed to parse daggerignore", err)
}
// log out patterns if file exists
if ignorefound {
log.
Ctx(ctx).
Debug().
Str("patterns", fmt.Sprint(excludes)).
Msg("daggerignore exclude patterns")
}
// FIXME: Remove the `Copy` and use `Local` directly.
//
// Copy'ing is a costly operation which should be unnecessary.
// However, using llb.Local directly breaks caching sometimes for unknown reasons.
return st.File(
llb.Copy(
llb.Local(
dir,
// llb.FollowPaths(include),
llb.ExcludePatterns(excludes),
llb.WithCustomName(p.vertexNamef("Local %s [transfer]", dir)),
opts := []llb.LocalOption{
llb.WithCustomName(p.vertexNamef("Local %s", dir)),
// Without hint, multiple `llb.Local` operations on the
// same path get a different digest.
llb.SessionID(p.s.SessionID()),
llb.SharedKeyHint(dir),
),
"/",
"/",
),
llb.WithCustomName(p.vertexNamef("Local %s [copy]", dir)),
}
includes, err := op.Lookup("include").List()
if err != nil {
return st, err
}
if len(includes) > 0 {
includePatterns := []string{}
for _, i := range includes {
pattern, err := i.String()
if err != nil {
return st, err
}
includePatterns = append(includePatterns, pattern)
}
opts = append(opts, llb.IncludePatterns(includePatterns))
}
excludes, err := op.Lookup("exclude").List()
if err != nil {
return st, err
}
if len(excludes) > 0 {
excludePatterns := []string{}
for _, i := range excludes {
pattern, err := i.String()
if err != nil {
return st, err
}
excludePatterns = append(excludePatterns, pattern)
}
opts = append(opts, llb.ExcludePatterns(excludePatterns))
}
return llb.Local(
dir,
opts...,
), nil
}
@ -811,21 +783,10 @@ func (p *Pipeline) FetchGit(ctx context.Context, op *compiler.Value, st llb.Stat
gitOpts = append(gitOpts, llb.WithCustomName(p.vertexNamef("FetchGit %s@%s", remoteRedacted, ref)))
// FIXME: Remove the `Copy` and use `Git` directly.
//
// Copy'ing is a costly operation which should be unnecessary.
// However, using llb.Git directly breaks caching sometimes for unknown reasons.
return st.File(
llb.Copy(
llb.Git(
return llb.Git(
remote,
ref,
gitOpts...,
),
"/",
"/",
),
llb.WithCustomName(p.vertexNamef("FetchGit %s@%s [copy]", remoteRedacted, ref)),
), nil
}

View File

@ -61,18 +61,20 @@ func (i Input) Compile(key string, state *State) (*compiler.Value, error) {
}
// An input artifact loaded from a local directory
func DirInput(path string, include []string) Input {
func DirInput(path string, include []string, exclude []string) Input {
return Input{
Dir: &dirInput{
Path: path,
Include: include,
Exclude: exclude,
},
}
}
type dirInput struct {
Path string `json:"path,omitempty"`
Include []string `json:"include,omitempty"`
Path string `yaml:"path,omitempty"`
Include []string `yaml:"include,omitempty"`
Exclude []string `yaml:"exclude,omitempty"`
}
func (dir dirInput) Compile(_ string, state *State) (*compiler.Value, error) {
@ -88,6 +90,14 @@ func (dir dirInput) Compile(_ string, state *State) (*compiler.Value, error) {
return nil, err
}
}
excludeLLB := []byte("[]")
if len(dir.Exclude) > 0 {
var err error
excludeLLB, err = json.Marshal(dir.Exclude)
if err != nil {
return nil, err
}
}
p := dir.Path
if !filepath.IsAbs(p) {
@ -98,18 +108,19 @@ func (dir dirInput) Compile(_ string, state *State) (*compiler.Value, error) {
}
llb := fmt.Sprintf(
`#up: [{do:"local",dir:"%s", include:%s}]`,
`#up: [{do:"local",dir:"%s", include:%s, exclude:%s}]`,
p,
includeLLB,
excludeLLB,
)
return compiler.Compile("", llb)
}
// An input artifact loaded from a git repository
type gitInput struct {
Remote string `json:"remote,omitempty"`
Ref string `json:"ref,omitempty"`
Dir string `json:"dir,omitempty"`
Remote string `yaml:"remote,omitempty"`
Ref string `yaml:"ref,omitempty"`
Dir string `yaml:"dir,omitempty"`
}
func GitInput(remote, ref, dir string) Input {
@ -145,7 +156,7 @@ func DockerInput(ref string) Input {
}
type dockerInput struct {
Ref string `json:"ref,omitempty"`
Ref string `yaml:"ref,omitempty"`
}
func (i dockerInput) Compile(_ string, _ *State) (*compiler.Value, error) {

View File

@ -26,7 +26,7 @@ type State struct {
// Cue module containing the environment plan
// The input's top-level artifact is used as a module directory.
func (s *State) PlanSource() Input {
return DirInput(s.Plan, []string{"*.cue", "cue.mod"})
return DirInput(s.Plan, []string{"*.cue", "cue.mod"}, []string{})
}
func (s *State) SetInput(key string, value Input) error {

View File

@ -36,6 +36,7 @@ package op
do: "local"
dir: string
include: [...string]
exclude: [...string]
}
// FIXME: bring back load (more efficient than copy)

View File

@ -94,6 +94,6 @@ setup() {
assert_output "secret=mySecret"
}
@test ".daggerignore" {
"$DAGGER" compute --input-dir TestData="$TESTDIR"/compute/ignore/testdata "$TESTDIR"/compute/ignore
@test "compute: exclude" {
"$DAGGER" up -w "$TESTDIR"/compute/exclude
}

View File

@ -0,0 +1,2 @@
# dagger state
state/**

View File

@ -0,0 +1,28 @@
name: default
inputs:
TestData:
dir:
path: ./testdata
exclude:
- a.txt
- '*/*.json'
sops:
kms: []
gcp_kms: []
azure_kv: []
hc_vault: []
age:
- recipient: age1gxwmtwahzwdmrskhf90ppwlnze30lgpm056kuesrxzeuyclrwvpsupwtpk
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSArcmRHMFByQkZSdGhteHhm
T2pSWW1UcU5LaFJabFJORllydFk3UkVsSHhVCndQZWNUKzVOeUovTTdCR3FmUXpO
c29GQXhpZkxwSmoweWxqZG1CMkcrRGcKLS0tIDhRMTVSc3NXSWIxSm55TGkwT1E1
L0NzY0RIMWNkc3k2WStOUGg4SndNRm8Kk7QSP/8spn1Set08VejVW9k4ZwBFqR0T
Ff/N73yNvo633hrfEJtTkhA/aZYyG9bPJy9s9vRDoNFkdTLSFcYX5g==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2021-05-28T23:20:28Z"
mac: ENC[AES256_GCM,data:nuRBGMu6MkiZ5DyuZy3drz4NXoUod8bYzOkJJiIj/fq2shN7oJNShF7UWDpW4FcknD5uldXpKqO3PmGpoxra95TTkoIHsbsQxSIrXUDhXl9CD5WOCwelUstv8f5r4nl+m3tSsW+4rIXdj/9ZB0DulMO0AqPp9I3XHG7glBMWYro=,iv:XGYGZpmC1dOIaTxcEJKtUmv1Fax+8ESPeWnjIGeOVPI=,tag:U/AKlJub9HsMrIpsjDxrYA==,type:str]
pgp: []
encrypted_suffix: secret
version: 3.7.1

View File

@ -1,2 +0,0 @@
a.txt
*/*.json