This repository has been archived on 2024-04-08. You can view files and clone it, but cannot push or open issues or pull requests.
dagger/dagger/script.go
Solomon Hykes f933278d43 Fix spec validation & merge so that default values are correctly applied
Signed-off-by: Solomon Hykes <sh.github.6811@hykes.org>
2021-01-26 16:52:14 -08:00

119 lines
2.6 KiB
Go

package dagger
import (
"context"
"cuelang.org/go/cue"
"github.com/pkg/errors"
"github.com/rs/zerolog/log"
)
var (
ErrAbortExecution = errors.New("execution stopped")
)
type Script struct {
v *Value
}
func NewScript(v *Value) (*Script, error) {
// Validate & merge with spec
final, err := v.Finalize(v.cc.Spec().Get("#Script"))
if err != nil {
return nil, errors.Wrap(err, "invalid script")
}
return newScript(final)
}
// Same as newScript, but without spec merge + validation.
func newScript(v *Value) (*Script, error) {
if !v.Exists() {
return nil, ErrNotExist
}
return &Script{
v: v,
}, nil
}
func (s *Script) Value() *Value {
return s.v
}
// Return the operation at index idx
func (s *Script) Op(idx int) (*Op, error) {
v := s.v.LookupPath(cue.MakePath(cue.Index(idx)))
if !v.Exists() {
return nil, ErrNotExist
}
return newOp(v)
}
// Return the number of operations in the script
func (s *Script) Len() uint64 {
l, _ := s.v.Len().Uint64()
return l
}
// Run a dagger script
func (s *Script) Execute(ctx context.Context, fs FS, out *Fillable) (FS, error) {
err := s.v.RangeList(func(idx int, v *Value) error {
// If op not concrete, interrupt without error.
// This allows gradual resolution:
// compute what you can compute.. leave the rest incomplete.
if err := v.IsConcreteR(); err != nil {
log.
Ctx(ctx).
Warn().
Int("op", idx).
// FIXME: tell user which inputs are missing (by inspecting references)
Msg("script is missing inputs and has not been fully executed")
return ErrAbortExecution
}
op, err := newOp(v)
if err != nil {
return errors.Wrapf(err, "validate op %d/%d", idx+1, s.v.Len())
}
fs, err = op.Execute(ctx, fs, out)
if err != nil {
return errors.Wrapf(err, "execute op %d/%d", idx+1, s.v.Len())
}
return nil
})
// If the execution was gracefully stopped, do not return an error
if err == ErrAbortExecution {
return fs, nil
}
return fs, err
}
func (s *Script) Walk(ctx context.Context, fn func(op *Op) error) error {
return s.v.RangeList(func(idx int, v *Value) error {
op, err := newOp(v)
if err != nil {
return errors.Wrapf(err, "validate op %d/%d", idx+1, s.v.Len())
}
if err := op.Walk(ctx, fn); err != nil {
return err
}
return nil
})
}
func (s *Script) LocalDirs(ctx context.Context) ([]string, error) {
var dirs []string
err := s.Walk(ctx, func(op *Op) error {
if err := op.Validate("#Local"); err != nil {
// Ignore all operations except 'do:"local"'
return nil
}
dir, err := op.Get("dir").String()
if err != nil {
return errors.Wrap(err, "invalid 'local' operation")
}
dirs = append(dirs, dir)
return nil
})
return dirs, err
}