123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124 |
- 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
- }
|