1
0
mirror of https://github.com/go-task/task.git synced 2024-12-04 10:24:45 +02:00
task/taskfile/node_http.go

125 lines
2.8 KiB
Go

package taskfile
import (
"context"
"io"
"net/http"
"net/url"
"path/filepath"
"time"
"github.com/go-task/task/v3/errors"
"github.com/go-task/task/v3/internal/execext"
"github.com/go-task/task/v3/internal/filepathext"
"github.com/go-task/task/v3/internal/logger"
)
// An HTTPNode is a node that reads a Taskfile from a remote location via HTTP.
type HTTPNode struct {
*BaseNode
URL *url.URL
logger *logger.Logger
timeout time.Duration
}
func NewHTTPNode(
l *logger.Logger,
entrypoint string,
dir string,
insecure bool,
timeout time.Duration,
opts ...NodeOption,
) (*HTTPNode, error) {
base := NewBaseNode(dir, opts...)
url, err := url.Parse(entrypoint)
if err != nil {
return nil, err
}
if url.Scheme == "http" && !insecure {
return nil, &errors.TaskfileNotSecureError{URI: entrypoint}
}
return &HTTPNode{
BaseNode: base,
URL: url,
timeout: timeout,
logger: l,
}, nil
}
func (node *HTTPNode) Location() string {
return node.URL.String()
}
func (node *HTTPNode) Remote() bool {
return true
}
func (node *HTTPNode) Read(ctx context.Context) ([]byte, error) {
url, err := RemoteExists(ctx, node.logger, node.URL, node.timeout)
if err != nil {
return nil, err
}
node.URL = url
req, err := http.NewRequest("GET", node.URL.String(), nil)
if err != nil {
return nil, errors.TaskfileFetchFailedError{URI: node.URL.String()}
}
resp, err := http.DefaultClient.Do(req.WithContext(ctx))
if err != nil {
if errors.Is(err, context.DeadlineExceeded) {
return nil, &errors.TaskfileNetworkTimeoutError{URI: node.URL.String(), Timeout: node.timeout}
}
return nil, errors.TaskfileFetchFailedError{URI: node.URL.String()}
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, errors.TaskfileFetchFailedError{
URI: node.URL.String(),
HTTPStatusCode: resp.StatusCode,
}
}
// Read the entire response body
b, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
return b, nil
}
func (node *HTTPNode) ResolveEntrypoint(entrypoint string) (string, error) {
ref, err := url.Parse(entrypoint)
if err != nil {
return "", err
}
return node.URL.ResolveReference(ref).String(), nil
}
func (node *HTTPNode) 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
parent := node.Dir()
if node.Parent() != nil {
parent = node.Parent().Dir()
}
return filepathext.SmartJoin(parent, path), nil
}
func (node *HTTPNode) FilenameAndLastDir() (string, string) {
dir, filename := filepath.Split(node.URL.Path)
return filepath.Base(dir), filename
}