Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: enable root remote taskfiles #1347

Merged
merged 6 commits into from
Mar 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions cmd/task/task.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"fmt"
"log"
"os"
"path/filepath"
"strings"

"github.com/spf13/pflag"
Expand Down Expand Up @@ -94,11 +93,6 @@ func run() error {
dir = home
}

if entrypoint != "" {
dir = filepath.Dir(entrypoint)
entrypoint = filepath.Base(entrypoint)
}

var taskSorter sort.TaskSorter
switch flags.TaskSort {
case "none":
Expand Down
4 changes: 0 additions & 4 deletions internal/flags/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,10 +131,6 @@ func Validate() error {
return nil
}

if Dir != "" && Entrypoint != "" {
return errors.New("task: You can't set both --dir and --taskfile")
}

if Output.Name != "group" {
if Output.Group.Begin != "" {
return errors.New("task: You can't set --output-group-begin without --output=group")
Expand Down
4 changes: 2 additions & 2 deletions setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,11 @@ func (e *Executor) Setup() error {
}

func (e *Executor) getRootNode() (taskfile.Node, error) {
node, err := taskfile.NewRootNode(e.Dir, e.Entrypoint, e.Insecure)
node, err := taskfile.NewRootNode(e.Logger, e.Entrypoint, e.Dir, e.Insecure, e.Timeout)
if err != nil {
return nil, err
}
e.Dir = node.BaseDir()
e.Dir = node.Dir()
return node, err
}

Expand Down
16 changes: 6 additions & 10 deletions task_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ func (fct fileContentTest) Run(t *testing.T) {

for name, expectContent := range fct.Files {
t.Run(fct.name(name), func(t *testing.T) {
path := filepathext.SmartJoin(fct.Dir, name)
path := filepathext.SmartJoin(e.Dir, name)
b, err := os.ReadFile(path)
require.NoError(t, err, "Error reading file")
s := string(b)
Expand Down Expand Up @@ -1123,8 +1123,8 @@ func TestIncludesOptionalExplicitFalse(t *testing.T) {

func TestIncludesFromCustomTaskfile(t *testing.T) {
tt := fileContentTest{
Entrypoint: "testdata/includes_yaml/Custom.ext",
Dir: "testdata/includes_yaml",
Entrypoint: "Custom.ext",
Target: "default",
TrimSpace: true,
Files: map[string]string{
Expand Down Expand Up @@ -1486,16 +1486,12 @@ func TestDotenvShouldIncludeAllEnvFiles(t *testing.T) {
}

func TestDotenvShouldErrorWhenIncludingDependantDotenvs(t *testing.T) {
const dir = "testdata/dotenv/error_included_envs"
const entry = "Taskfile.yml"

var buff bytes.Buffer
e := task.Executor{
Dir: dir,
Entrypoint: entry,
Summary: true,
Stdout: &buff,
Stderr: &buff,
Dir: "testdata/dotenv/error_included_envs",
Summary: true,
Stdout: &buff,
Stderr: &buff,
}

err := e.Setup()
Expand Down
39 changes: 0 additions & 39 deletions taskfile/ast/include.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,9 @@ package ast

import (
"fmt"
"path/filepath"
"strings"

"gopkg.in/yaml.v3"

"github.com/go-task/task/v3/internal/execext"
"github.com/go-task/task/v3/internal/filepathext"
omap "github.com/go-task/task/v3/internal/omap"
)

Expand All @@ -22,7 +18,6 @@ type Include struct {
Aliases []string
AdvancedImport bool
Vars *Vars
BaseDir string // The directory from which the including taskfile was loaded; used to resolve relative paths
}

// Includes represents information about included tasksfiles
Expand Down Expand Up @@ -120,39 +115,5 @@ func (include *Include) DeepCopy() *Include {
Internal: include.Internal,
AdvancedImport: include.AdvancedImport,
Vars: include.Vars.DeepCopy(),
BaseDir: include.BaseDir,
}
}

// FullTaskfilePath returns the fully qualified path to the included taskfile
func (include *Include) FullTaskfilePath() (string, error) {
return include.resolvePath(include.Taskfile)
}

// FullDirPath returns the fully qualified path to the included taskfile's working directory
func (include *Include) FullDirPath() (string, error) {
return include.resolvePath(include.Dir)
}

func (include *Include) resolvePath(path string) (string, error) {
// If the file is remote, we don't need to resolve the path
if strings.Contains(include.Taskfile, "://") {
return path, nil
}

path, err := execext.Expand(path)
if err != nil {
return "", err
}

if filepathext.IsAbs(path) {
return path, nil
}

result, err := filepath.Abs(filepathext.SmartJoin(include.BaseDir, path))
if err != nil {
return "", fmt.Errorf("task: error resolving path %s relative to %s: %w", path, include.BaseDir, err)
}

return result, nil
}
33 changes: 16 additions & 17 deletions taskfile/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,57 +5,56 @@ import (
"os"
"path/filepath"
"strings"
"time"

"github.com/go-task/task/v3/errors"
"github.com/go-task/task/v3/internal/experiments"
"github.com/go-task/task/v3/internal/logger"
)

type Node interface {
Read(ctx context.Context) ([]byte, error)
Parent() Node
Location() string
Dir() string
Optional() bool
Remote() bool
BaseDir() string
ResolveEntrypoint(entrypoint string) (string, error)
ResolveDir(dir string) (string, error)
}

func NewRootNode(
dir string,
l *logger.Logger,
entrypoint string,
dir string,
insecure bool,
timeout time.Duration,
) (Node, error) {
dir = getDefaultDir(entrypoint, dir)
// Check if there is something to read on STDIN
stat, _ := os.Stdin.Stat()
if (stat.Mode()&os.ModeCharDevice) == 0 && stat.Size() > 0 {
return NewStdinNode(dir)
}
// If no entrypoint is specified, search for a taskfile
if entrypoint == "" {
root, err := ExistsWalk(dir)
if err != nil {
return nil, err
}
return NewNode(root, insecure)
}
// Use the specified entrypoint
uri := filepath.Join(dir, entrypoint)
return NewNode(uri, insecure)
return NewNode(l, entrypoint, dir, insecure, timeout)
}

func NewNode(
uri string,
l *logger.Logger,
entrypoint string,
dir string,
insecure bool,
timeout time.Duration,
opts ...NodeOption,
) (Node, error) {
var node Node
var err error
switch getScheme(uri) {
switch getScheme(entrypoint) {
case "http", "https":
node, err = NewHTTPNode(uri, insecure, opts...)
node, err = NewHTTPNode(l, entrypoint, dir, insecure, timeout, opts...)
default:
// If no other scheme matches, we assume it's a file
node, err = NewFileNode(uri, opts...)
node, err = NewFileNode(l, entrypoint, dir, opts...)
}
if node.Remote() && !experiments.RemoteTaskfiles.Enabled {
return nil, errors.New("task: Remote taskfiles are not enabled. You can read more about this experiment and how to enable it at https://taskfile.dev/experiments/remote-taskfiles")
Expand Down
8 changes: 7 additions & 1 deletion taskfile/node_base.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ type (
BaseNode struct {
parent Node
optional bool
dir string
}
)

func NewBaseNode(opts ...NodeOption) *BaseNode {
func NewBaseNode(dir string, opts ...NodeOption) *BaseNode {
node := &BaseNode{
parent: nil,
optional: false,
dir: dir,
}

// Apply options
Expand Down Expand Up @@ -45,3 +47,7 @@ func WithOptional(optional bool) NodeOption {
func (node *BaseNode) Optional() bool {
return node.optional
}

func (node *BaseNode) Dir() string {
return node.dir
}
88 changes: 72 additions & 16 deletions taskfile/node_file.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,39 +5,34 @@ import (
"io"
"os"
"path/filepath"
"strings"

"github.com/go-task/task/v3/internal/execext"
"github.com/go-task/task/v3/internal/filepathext"
"github.com/go-task/task/v3/internal/logger"
)

// A FileNode is a node that reads a taskfile from the local filesystem.
type FileNode struct {
*BaseNode
Dir string
Entrypoint string
}

func NewFileNode(uri string, opts ...NodeOption) (*FileNode, error) {
base := NewBaseNode(opts...)
if uri == "" {
d, err := os.Getwd()
if err != nil {
return nil, err
}
uri = d
}
path, err := Exists(uri)
func NewFileNode(l *logger.Logger, entrypoint, dir string, opts ...NodeOption) (*FileNode, error) {
var err error
base := NewBaseNode(dir, opts...)
entrypoint, base.dir, err = resolveFileNodeEntrypointAndDir(l, entrypoint, base.dir)
if err != nil {
return nil, err
}
return &FileNode{
BaseNode: base,
Dir: filepath.Dir(path),
Entrypoint: filepath.Base(path),
Entrypoint: entrypoint,
}, nil
}

func (node *FileNode) Location() string {
return filepathext.SmartJoin(node.Dir, node.Entrypoint)
return node.Entrypoint
}

func (node *FileNode) Remote() bool {
Expand All @@ -53,6 +48,67 @@ func (node *FileNode) Read(ctx context.Context) ([]byte, error) {
return io.ReadAll(f)
}

func (node *FileNode) BaseDir() string {
return node.Dir
// resolveFileNodeEntrypointAndDir resolves checks the values of entrypoint and dir and
// populates them with default values if necessary.
func resolveFileNodeEntrypointAndDir(l *logger.Logger, entrypoint, dir string) (string, string, error) {
var err error
if entrypoint != "" {
entrypoint, err = Exists(l, entrypoint)
if err != nil {
return "", "", err
}
if dir == "" {
dir = filepath.Dir(entrypoint)
}
return entrypoint, dir, nil
}
if dir == "" {
dir, err = os.Getwd()
if err != nil {
return "", "", err
}
}
entrypoint, err = ExistsWalk(l, dir)
if err != nil {
return "", "", err
}
dir = filepath.Dir(entrypoint)
return entrypoint, dir, nil
}

func (node *FileNode) ResolveEntrypoint(entrypoint string) (string, error) {
// If the file is remote, we don't need to resolve the path
if strings.Contains(entrypoint, "://") {
return entrypoint, nil
}

path, err := execext.Expand(entrypoint)
if err != nil {
return "", err
}

if filepathext.IsAbs(path) {
return path, nil
}

// NOTE: Uses the directory of the entrypoint (Taskfile), not the current working directory
// This means that files are included relative to one another
entrypointDir := filepath.Dir(node.Entrypoint)
return filepathext.SmartJoin(entrypointDir, path), nil
}

func (node *FileNode) ResolveDir(dir string) (string, error) {
path, err := execext.Expand(dir)
if err != nil {
return "", err
}

if filepathext.IsAbs(path) {
return path, nil
}

// NOTE: Uses the directory of the entrypoint (Taskfile), not the current working directory
// This means that files are included relative to one another
entrypointDir := filepath.Dir(node.Entrypoint)
return filepathext.SmartJoin(entrypointDir, path), nil
}
Loading
Loading