commit
b2895d3105
@ -75,7 +75,7 @@ func New(ctx context.Context, host string, cfg Config) (*Client, error) {
|
||||
type DoFunc func(context.Context, solver.Solver) error
|
||||
|
||||
// FIXME: return completed *Route, instead of *compiler.Value
|
||||
func (c *Client) Do(ctx context.Context, pctx *plancontext.Context, localdirs map[string]string, fn DoFunc) error {
|
||||
func (c *Client) Do(ctx context.Context, pctx *plancontext.Context, fn DoFunc) error {
|
||||
lg := log.Ctx(ctx)
|
||||
eg, gctx := errgroup.WithContext(ctx)
|
||||
|
||||
@ -90,13 +90,13 @@ func (c *Client) Do(ctx context.Context, pctx *plancontext.Context, localdirs ma
|
||||
|
||||
// Spawn build function
|
||||
eg.Go(func() error {
|
||||
return c.buildfn(gctx, pctx, localdirs, fn, events)
|
||||
return c.buildfn(gctx, pctx, fn, events)
|
||||
})
|
||||
|
||||
return eg.Wait()
|
||||
}
|
||||
|
||||
func (c *Client) buildfn(ctx context.Context, pctx *plancontext.Context, localdirs map[string]string, fn DoFunc, ch chan *bk.SolveStatus) error {
|
||||
func (c *Client) buildfn(ctx context.Context, pctx *plancontext.Context, fn DoFunc, ch chan *bk.SolveStatus) error {
|
||||
wg := sync.WaitGroup{}
|
||||
|
||||
// Close output channel
|
||||
@ -111,6 +111,11 @@ func (c *Client) buildfn(ctx context.Context, pctx *plancontext.Context, localdi
|
||||
// buildkit auth provider (registry)
|
||||
auth := solver.NewRegistryAuthProvider()
|
||||
|
||||
localdirs, err := pctx.LocalDirs.Paths()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Setup solve options
|
||||
opts := bk.SolveOpt{
|
||||
LocalDirs: localdirs,
|
||||
|
@ -198,7 +198,7 @@ var computeCmd = &cobra.Command{
|
||||
lg.Fatal().Err(err).Msg("unable to create environment")
|
||||
}
|
||||
|
||||
err = cl.Do(ctx, env.Context(), env.Context().Directories.Paths(), func(ctx context.Context, s solver.Solver) error {
|
||||
err = cl.Do(ctx, env.Context(), func(ctx context.Context, s solver.Solver) error {
|
||||
// check that all inputs are set
|
||||
checkInputs(ctx, env)
|
||||
|
||||
|
@ -83,7 +83,7 @@ var editCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
cl := common.NewClient(ctx)
|
||||
err = cl.Do(ctx, env.Context(), env.Context().Directories.Paths(), func(ctx context.Context, s solver.Solver) error {
|
||||
err = cl.Do(ctx, env.Context(), func(ctx context.Context, s solver.Solver) error {
|
||||
// check for cue errors by scanning all the inputs
|
||||
_, err := env.ScanInputs(ctx, true)
|
||||
if err != nil {
|
||||
|
@ -47,7 +47,7 @@ var listCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
cl := common.NewClient(ctx)
|
||||
err = cl.Do(ctx, env.Context(), env.Context().Directories.Paths(), func(ctx context.Context, s solver.Solver) error {
|
||||
err = cl.Do(ctx, env.Context(), func(ctx context.Context, s solver.Solver) error {
|
||||
inputs, err := env.ScanInputs(ctx, false)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -59,7 +59,7 @@ func updateEnvironmentInput(ctx context.Context, cmd *cobra.Command, target stri
|
||||
}
|
||||
|
||||
cl := common.NewClient(ctx)
|
||||
err = cl.Do(ctx, env.Context(), env.Context().Directories.Paths(), func(ctx context.Context, s solver.Solver) error {
|
||||
err = cl.Do(ctx, env.Context(), func(ctx context.Context, s solver.Solver) error {
|
||||
// the inputs are set, check for cue errors by scanning all the inputs
|
||||
_, err := env.ScanInputs(ctx, true)
|
||||
if err != nil {
|
||||
|
@ -46,7 +46,7 @@ var listCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
cl := common.NewClient(ctx)
|
||||
err = cl.Do(ctx, env.Context(), env.Context().Directories.Paths(), func(ctx context.Context, s solver.Solver) error {
|
||||
err = cl.Do(ctx, env.Context(), func(ctx context.Context, s solver.Solver) error {
|
||||
return ListOutputs(ctx, env, true)
|
||||
})
|
||||
|
||||
|
@ -80,7 +80,7 @@ var upCmd = &cobra.Command{
|
||||
lg.Fatal().Err(err).Msg("unable to create environment")
|
||||
}
|
||||
|
||||
err = cl.Do(ctx, env.Context(), env.Context().Directories.Paths(), func(ctx context.Context, s solver.Solver) error {
|
||||
err = cl.Do(ctx, env.Context(), func(ctx context.Context, s solver.Solver) error {
|
||||
// check that all inputs are set
|
||||
if err := checkInputs(ctx, env); err != nil {
|
||||
return err
|
||||
@ -119,11 +119,7 @@ func europaUp(ctx context.Context, cl *client.Client, path string) error {
|
||||
lg.Fatal().Err(err).Msg("failed to load plan")
|
||||
}
|
||||
|
||||
localdirs, err := p.LocalDirectories()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return cl.Do(ctx, p.Context(), localdirs, func(ctx context.Context, s solver.Solver) error {
|
||||
return cl.Do(ctx, p.Context(), func(ctx context.Context, s solver.Solver) error {
|
||||
if err := p.Up(ctx, s); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -24,6 +24,10 @@ func NewValue() *Value {
|
||||
return DefaultCompiler.NewValue()
|
||||
}
|
||||
|
||||
func NewValueWithContent(x interface{}, selectors ...cue.Selector) (*Value, error) {
|
||||
return DefaultCompiler.NewValueWithContent(x, selectors...)
|
||||
}
|
||||
|
||||
// FIXME can be refactored away now?
|
||||
func Wrap(v cue.Value) *Value {
|
||||
return DefaultCompiler.Wrap(v)
|
||||
@ -80,6 +84,14 @@ func (c *Compiler) NewValue() *Value {
|
||||
return empty
|
||||
}
|
||||
|
||||
func (c *Compiler) NewValueWithContent(x interface{}, selectors ...cue.Selector) (*Value, error) {
|
||||
v := c.NewValue()
|
||||
if err := v.FillPath(cue.MakePath(selectors...), x); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func (c *Compiler) Compile(name string, src string) (*Value, error) {
|
||||
c.lock()
|
||||
defer c.unlock()
|
||||
|
@ -20,6 +20,18 @@ _No input._
|
||||
|
||||
_No output._
|
||||
|
||||
## dagger.#FS
|
||||
|
||||
A reference to a filesystem tree. For example: - The root filesystem of a container - A source code repository - A directory containing binary artifacts Rule of thumb: if it fits in a tar archive, it fits in a #FS.
|
||||
|
||||
### dagger.#FS Inputs
|
||||
|
||||
_No input._
|
||||
|
||||
### dagger.#FS Outputs
|
||||
|
||||
_No output._
|
||||
|
||||
## dagger.#Plan
|
||||
|
||||
A deployment plan executed by `dagger up`
|
||||
@ -34,7 +46,7 @@ _No output._
|
||||
|
||||
## dagger.#Secret
|
||||
|
||||
Secret value
|
||||
A reference to an external secret, for example: - A password - A SSH private key - An API token Secrets are never merged in the Cue tree. They can only be used by a special filesystem mount designed to minimize leak risk.
|
||||
|
||||
### dagger.#Secret Inputs
|
||||
|
||||
|
@ -40,6 +40,13 @@ const (
|
||||
StateCompleted = State("completed")
|
||||
)
|
||||
|
||||
var (
|
||||
fsIDPath = cue.MakePath(
|
||||
cue.Hid("_fs", "alpha.dagger.io/dagger"),
|
||||
cue.Str("id"),
|
||||
)
|
||||
)
|
||||
|
||||
// An execution pipeline
|
||||
type Pipeline struct {
|
||||
code *compiler.Value
|
||||
@ -95,8 +102,19 @@ func IsComponent(v *compiler.Value) bool {
|
||||
return v.Lookup("#up").Exists()
|
||||
}
|
||||
|
||||
func isFS(v *compiler.Value) bool {
|
||||
return v.LookupPath(fsIDPath).Exists()
|
||||
}
|
||||
|
||||
func ops(code *compiler.Value) ([]*compiler.Value, error) {
|
||||
ops := []*compiler.Value{}
|
||||
|
||||
// dagger.#FS forward compat
|
||||
// FIXME: remove this
|
||||
if isFS(code) {
|
||||
ops = append(ops, code)
|
||||
}
|
||||
|
||||
// 1. attachment array
|
||||
if IsComponent(code) {
|
||||
xops, err := code.Lookup("#up").List()
|
||||
@ -138,6 +156,12 @@ func Analyze(fn func(*compiler.Value) error, code *compiler.Value) error {
|
||||
}
|
||||
|
||||
func analyzeOp(fn func(*compiler.Value) error, op *compiler.Value) error {
|
||||
// dagger.#FS forward compat
|
||||
// FIXME: remove this
|
||||
if isFS(op) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := fn(op); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -243,6 +267,21 @@ func (p *Pipeline) run(ctx context.Context) error {
|
||||
}
|
||||
|
||||
func (p *Pipeline) doOp(ctx context.Context, op *compiler.Value, st llb.State) (llb.State, error) {
|
||||
// dagger.#FS forward compat
|
||||
// FIXME: remove this
|
||||
if isFS(op) {
|
||||
id, err := op.LookupPath(fsIDPath).String()
|
||||
if err != nil {
|
||||
return st, err
|
||||
}
|
||||
|
||||
fs := p.pctx.FS.Get(plancontext.ContextKey(id))
|
||||
if fs == nil {
|
||||
return st, fmt.Errorf("fs %q not found", id)
|
||||
}
|
||||
return fs.Result.ToState()
|
||||
}
|
||||
|
||||
do, err := op.Lookup("do").String()
|
||||
if err != nil {
|
||||
return st, err
|
||||
@ -361,31 +400,49 @@ func (p *Pipeline) Copy(ctx context.Context, op *compiler.Value, st llb.State) (
|
||||
}
|
||||
|
||||
func (p *Pipeline) Local(ctx context.Context, op *compiler.Value, st llb.State) (llb.State, error) {
|
||||
id, err := op.Lookup("id").String()
|
||||
dir, err := op.Lookup("dir").String()
|
||||
if err != nil {
|
||||
return st, err
|
||||
}
|
||||
dir := p.pctx.Directories.Get(plancontext.ContextKey(id))
|
||||
if dir == nil {
|
||||
return st, fmt.Errorf("directory %q not found", id)
|
||||
}
|
||||
|
||||
opts := []llb.LocalOption{
|
||||
llb.WithCustomName(p.vertexNamef("Local %s", dir.Path)),
|
||||
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.Path),
|
||||
llb.SharedKeyHint(dir),
|
||||
}
|
||||
|
||||
if len(dir.Include) > 0 {
|
||||
opts = append(opts, llb.IncludePatterns(dir.Include))
|
||||
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
|
||||
}
|
||||
// Excludes .dagger directory by default
|
||||
excludePatterns := []string{"**/.dagger/"}
|
||||
if len(dir.Exclude) > 0 {
|
||||
excludePatterns = dir.Exclude
|
||||
if len(excludes) > 0 {
|
||||
for _, i := range excludes {
|
||||
pattern, err := i.String()
|
||||
if err != nil {
|
||||
return st, err
|
||||
}
|
||||
excludePatterns = append(excludePatterns, pattern)
|
||||
}
|
||||
}
|
||||
opts = append(opts, llb.ExcludePatterns(excludePatterns))
|
||||
|
||||
@ -396,13 +453,13 @@ func (p *Pipeline) Local(ctx context.Context, op *compiler.Value, st llb.State)
|
||||
return st.File(
|
||||
llb.Copy(
|
||||
llb.Local(
|
||||
dir.Path,
|
||||
dir,
|
||||
opts...,
|
||||
),
|
||||
"/",
|
||||
"/",
|
||||
),
|
||||
llb.WithCustomName(p.vertexNamef("Local %s [copy]", dir.Path)),
|
||||
llb.WithCustomName(p.vertexNamef("Local %s [copy]", dir)),
|
||||
), nil
|
||||
}
|
||||
|
||||
|
31
plan/plan.go
31
plan/plan.go
@ -3,7 +3,6 @@ package plan
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@ -35,10 +34,16 @@ func Load(ctx context.Context, path, pkg string) (*Plan, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Plan{
|
||||
p := &Plan{
|
||||
context: plancontext.New(),
|
||||
source: v,
|
||||
}, nil
|
||||
}
|
||||
|
||||
if err := p.registerLocalDirs(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func (p *Plan) Context() *plancontext.Context {
|
||||
@ -49,30 +54,26 @@ func (p *Plan) Source() *compiler.Value {
|
||||
return p.source
|
||||
}
|
||||
|
||||
// LocalDirectories scans the context for local imports.
|
||||
// registerLocalDirectories scans the context for local imports.
|
||||
// BuildKit requires to known the list of directories ahead of time.
|
||||
func (p *Plan) LocalDirectories() (map[string]string, error) {
|
||||
dirs := map[string]string{}
|
||||
|
||||
func (p *Plan) registerLocalDirs() error {
|
||||
imports, err := p.source.Lookup("context.imports").Fields()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
for _, v := range imports {
|
||||
dir, err := v.Value.Lookup("path").String()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
abs, err := filepath.Abs(dir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
dirs[dir] = abs
|
||||
p.context.LocalDirs.Register(&plancontext.LocalDir{
|
||||
Path: dir,
|
||||
})
|
||||
}
|
||||
|
||||
return dirs, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// Up executes the plan
|
||||
|
@ -2,9 +2,9 @@ package task
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"cuelang.org/go/cue"
|
||||
"github.com/moby/buildkit/client/llb"
|
||||
"go.dagger.io/dagger/compiler"
|
||||
"go.dagger.io/dagger/plancontext"
|
||||
"go.dagger.io/dagger/solver"
|
||||
@ -17,21 +17,64 @@ func init() {
|
||||
type importTask struct {
|
||||
}
|
||||
|
||||
func (c importTask) Run(ctx context.Context, pctx *plancontext.Context, _ solver.Solver, v *compiler.Value) (*compiler.Value, error) {
|
||||
var dir *plancontext.Directory
|
||||
func (c importTask) Run(ctx context.Context, pctx *plancontext.Context, s solver.Solver, v *compiler.Value) (*compiler.Value, error) {
|
||||
var dir struct {
|
||||
Path string
|
||||
Include []string
|
||||
Exclude []string
|
||||
}
|
||||
|
||||
if err := v.Decode(&dir); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Check that directory exists
|
||||
if _, err := os.Stat(dir.Path); os.IsNotExist(err) {
|
||||
return nil, fmt.Errorf("%q dir doesn't exist", dir.Path)
|
||||
opts := []llb.LocalOption{
|
||||
withCustomName(v, "Local %s", dir.Path),
|
||||
// Without hint, multiple `llb.Local` operations on the
|
||||
// same path get a different digest.
|
||||
llb.SessionID(s.SessionID()),
|
||||
llb.SharedKeyHint(dir.Path),
|
||||
}
|
||||
|
||||
id := pctx.Directories.Register(dir)
|
||||
return compiler.Compile("", fmt.Sprintf(
|
||||
`fs: #up: [{do: "local", id: %q}]`,
|
||||
id,
|
||||
))
|
||||
if len(dir.Include) > 0 {
|
||||
opts = append(opts, llb.IncludePatterns(dir.Include))
|
||||
}
|
||||
|
||||
// Excludes .dagger directory by default
|
||||
excludePatterns := []string{"**/.dagger/"}
|
||||
if len(dir.Exclude) > 0 {
|
||||
excludePatterns = dir.Exclude
|
||||
}
|
||||
opts = append(opts, llb.ExcludePatterns(excludePatterns))
|
||||
|
||||
// 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.
|
||||
st := llb.Scratch().File(
|
||||
llb.Copy(
|
||||
llb.Local(
|
||||
dir.Path,
|
||||
opts...,
|
||||
),
|
||||
"/",
|
||||
"/",
|
||||
),
|
||||
withCustomName(v, "Local %s [copy]", dir.Path),
|
||||
)
|
||||
|
||||
result, err := s.Solve(ctx, st, pctx.Platform.Get())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
id := pctx.FS.Register(&plancontext.FS{
|
||||
Result: result,
|
||||
})
|
||||
|
||||
return compiler.NewValueWithContent(id,
|
||||
cue.Str("fs"),
|
||||
cue.Hid("_fs", "alpha.dagger.io/dagger"),
|
||||
cue.Str("id"),
|
||||
)
|
||||
}
|
||||
|
@ -40,9 +40,8 @@ func (c secretEnvTask) Run(ctx context.Context, pctx *plancontext.Context, _ sol
|
||||
PlainText: env,
|
||||
})
|
||||
|
||||
out := compiler.NewValue()
|
||||
if err := out.FillPath(cue.ParsePath("contents.id"), id); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
return compiler.NewValueWithContent(id,
|
||||
cue.Str("contents"),
|
||||
cue.Str("id"),
|
||||
)
|
||||
}
|
||||
|
@ -39,9 +39,8 @@ func (c secretFileTask) Run(ctx context.Context, pctx *plancontext.Context, _ so
|
||||
PlainText: string(data),
|
||||
})
|
||||
|
||||
out := compiler.NewValue()
|
||||
if err := out.FillPath(cue.ParsePath("contents.id"), id); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
return compiler.NewValueWithContent(id,
|
||||
cue.Str("contents"),
|
||||
cue.Str("id"),
|
||||
)
|
||||
}
|
||||
|
14
plan/task/util.go
Normal file
14
plan/task/util.go
Normal file
@ -0,0 +1,14 @@
|
||||
package task
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/moby/buildkit/client/llb"
|
||||
"go.dagger.io/dagger/compiler"
|
||||
)
|
||||
|
||||
func withCustomName(v *compiler.Value, format string, a ...interface{}) llb.ConstraintsOpt {
|
||||
prefix := fmt.Sprintf("@%s@", v.Path().String())
|
||||
name := fmt.Sprintf(format, a...)
|
||||
return llb.WithCustomName(prefix + " " + name)
|
||||
}
|
@ -16,7 +16,8 @@ type ContextKey string
|
||||
// secret := ctx.Secrets.Get(id)
|
||||
type Context struct {
|
||||
Platform *platformContext
|
||||
Directories *directoryContext
|
||||
FS *fsContext
|
||||
LocalDirs *localDirContext
|
||||
Secrets *secretContext
|
||||
Services *serviceContext
|
||||
}
|
||||
@ -26,8 +27,11 @@ func New() *Context {
|
||||
Platform: &platformContext{
|
||||
platform: defaultPlatform,
|
||||
},
|
||||
Directories: &directoryContext{
|
||||
store: make(map[ContextKey]*Directory),
|
||||
FS: &fsContext{
|
||||
store: make(map[ContextKey]*FS),
|
||||
},
|
||||
LocalDirs: &localDirContext{
|
||||
store: make(map[ContextKey]*LocalDir),
|
||||
},
|
||||
Secrets: &secretContext{
|
||||
store: make(map[ContextKey]*Secret),
|
||||
|
@ -1,54 +0,0 @@
|
||||
package plancontext
|
||||
|
||||
import "sync"
|
||||
|
||||
type Directory struct {
|
||||
Path string
|
||||
Include []string
|
||||
Exclude []string
|
||||
}
|
||||
|
||||
type directoryContext struct {
|
||||
l sync.RWMutex
|
||||
store map[ContextKey]*Directory
|
||||
}
|
||||
|
||||
func (c *directoryContext) Register(directory *Directory) ContextKey {
|
||||
c.l.Lock()
|
||||
defer c.l.Unlock()
|
||||
|
||||
id := hashID(directory)
|
||||
c.store[id] = directory
|
||||
return id
|
||||
}
|
||||
|
||||
func (c *directoryContext) Get(id ContextKey) *Directory {
|
||||
c.l.RLock()
|
||||
defer c.l.RUnlock()
|
||||
|
||||
return c.store[id]
|
||||
}
|
||||
|
||||
func (c *directoryContext) List() []*Directory {
|
||||
c.l.RLock()
|
||||
defer c.l.RUnlock()
|
||||
|
||||
directories := make([]*Directory, 0, len(c.store))
|
||||
for _, d := range c.store {
|
||||
directories = append(directories, d)
|
||||
}
|
||||
|
||||
return directories
|
||||
}
|
||||
|
||||
func (c *directoryContext) Paths() map[string]string {
|
||||
c.l.RLock()
|
||||
defer c.l.RUnlock()
|
||||
|
||||
directories := make(map[string]string)
|
||||
for _, d := range c.store {
|
||||
directories[d.Path] = d.Path
|
||||
}
|
||||
|
||||
return directories
|
||||
}
|
32
plancontext/fs.go
Normal file
32
plancontext/fs.go
Normal file
@ -0,0 +1,32 @@
|
||||
package plancontext
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
bkgw "github.com/moby/buildkit/frontend/gateway/client"
|
||||
)
|
||||
|
||||
type FS struct {
|
||||
Result bkgw.Reference
|
||||
}
|
||||
|
||||
type fsContext struct {
|
||||
l sync.RWMutex
|
||||
store map[ContextKey]*FS
|
||||
}
|
||||
|
||||
func (c *fsContext) Register(fs *FS) ContextKey {
|
||||
c.l.Lock()
|
||||
defer c.l.Unlock()
|
||||
|
||||
id := hashID(fs)
|
||||
c.store[id] = fs
|
||||
return id
|
||||
}
|
||||
|
||||
func (c *fsContext) Get(id ContextKey) *FS {
|
||||
c.l.RLock()
|
||||
defer c.l.RUnlock()
|
||||
|
||||
return c.store[id]
|
||||
}
|
60
plancontext/localdir.go
Normal file
60
plancontext/localdir.go
Normal file
@ -0,0 +1,60 @@
|
||||
package plancontext
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type LocalDir struct {
|
||||
Path string
|
||||
}
|
||||
|
||||
type localDirContext struct {
|
||||
l sync.RWMutex
|
||||
store map[ContextKey]*LocalDir
|
||||
}
|
||||
|
||||
func (c *localDirContext) Register(directory *LocalDir) ContextKey {
|
||||
c.l.Lock()
|
||||
defer c.l.Unlock()
|
||||
|
||||
id := hashID(directory)
|
||||
c.store[id] = directory
|
||||
return id
|
||||
}
|
||||
|
||||
func (c *localDirContext) Get(id ContextKey) *LocalDir {
|
||||
c.l.RLock()
|
||||
defer c.l.RUnlock()
|
||||
|
||||
return c.store[id]
|
||||
}
|
||||
|
||||
func (c *localDirContext) List() []*LocalDir {
|
||||
c.l.RLock()
|
||||
defer c.l.RUnlock()
|
||||
|
||||
directories := make([]*LocalDir, 0, len(c.store))
|
||||
for _, d := range c.store {
|
||||
directories = append(directories, d)
|
||||
}
|
||||
|
||||
return directories
|
||||
}
|
||||
|
||||
func (c *localDirContext) Paths() (map[string]string, error) {
|
||||
c.l.RLock()
|
||||
defer c.l.RUnlock()
|
||||
|
||||
directories := make(map[string]string)
|
||||
for _, d := range c.store {
|
||||
abs, err := filepath.Abs(d.Path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
directories[d.Path] = abs
|
||||
}
|
||||
|
||||
return directories, nil
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
package state
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
@ -82,6 +83,27 @@ type dirInput struct {
|
||||
}
|
||||
|
||||
func (dir dirInput) Compile(state *State) (*compiler.Value, error) {
|
||||
// FIXME: serialize an intermediate struct, instead of generating cue source
|
||||
|
||||
// json.Marshal([]string{}) returns []byte("null"), which wreaks havoc
|
||||
// in Cue because `null` is not a `[...string]`
|
||||
includeLLB := []byte("[]")
|
||||
if len(dir.Include) > 0 {
|
||||
var err error
|
||||
includeLLB, err = json.Marshal(dir.Include)
|
||||
if err != nil {
|
||||
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) {
|
||||
p = filepath.Clean(path.Join(state.Project, dir.Path))
|
||||
@ -94,15 +116,20 @@ func (dir dirInput) Compile(state *State) (*compiler.Value, error) {
|
||||
return nil, fmt.Errorf("%q dir doesn't exist", dir.Path)
|
||||
}
|
||||
|
||||
id := state.Context.Directories.Register(&plancontext.Directory{
|
||||
dirPath, err := json.Marshal(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
state.Context.LocalDirs.Register(&plancontext.LocalDir{
|
||||
Path: p,
|
||||
Include: dir.Include,
|
||||
Exclude: dir.Exclude,
|
||||
})
|
||||
|
||||
llb := fmt.Sprintf(
|
||||
`#up: [{do:"local", id: "%s"}]`,
|
||||
id,
|
||||
`#up: [{do: "local", dir: %s, include: %s, exclude: %s}]`,
|
||||
dirPath,
|
||||
includeLLB,
|
||||
excludeLLB,
|
||||
)
|
||||
return compiler.Compile("", llb)
|
||||
}
|
||||
|
@ -5,6 +5,16 @@ import (
|
||||
"alpha.dagger.io/dagger/op"
|
||||
)
|
||||
|
||||
// A reference to a filesystem tree.
|
||||
// For example:
|
||||
// - The root filesystem of a container
|
||||
// - A source code repository
|
||||
// - A directory containing binary artifacts
|
||||
// Rule of thumb: if it fits in a tar archive, it fits in a #FS.
|
||||
#FS: {
|
||||
_fs: id: string
|
||||
}
|
||||
|
||||
// An artifact such as source code checkout, container image, binary archive...
|
||||
// May be passed as user input, or computed by a buildkit pipeline
|
||||
#Artifact: {
|
||||
@ -21,7 +31,12 @@ import (
|
||||
id: string
|
||||
}
|
||||
|
||||
// Secret value
|
||||
// A reference to an external secret, for example:
|
||||
// - A password
|
||||
// - A SSH private key
|
||||
// - An API token
|
||||
// Secrets are never merged in the Cue tree. They can only be used
|
||||
// by a special filesystem mount designed to minimize leak risk.
|
||||
#Secret: {
|
||||
@dagger(secret)
|
||||
|
||||
|
@ -21,7 +21,9 @@ package op
|
||||
|
||||
#Local: {
|
||||
do: "local"
|
||||
id: string
|
||||
dir: string
|
||||
include: [...string]
|
||||
exclude: [...string]
|
||||
}
|
||||
|
||||
// FIXME: bring back load (more efficient than copy)
|
||||
|
@ -20,7 +20,7 @@ package dagger
|
||||
path: string
|
||||
include?: [...string]
|
||||
exclude?: [...string]
|
||||
fs: #Artifact
|
||||
fs: #FS
|
||||
}
|
||||
|
||||
// Securely load external secrets
|
||||
|
Reference in New Issue
Block a user