From c77c8a419b0c4b6905882c61bc663df5aaef6289 Mon Sep 17 00:00:00 2001 From: Valentin Maerten Date: Sat, 7 Sep 2024 21:54:05 +0200 Subject: [PATCH] refactor: check if the remote exists just before reading it (#1713) * refactor: check if the remote exists in the read to avoid doing it in offline mode * fix: timeout error was not working * fix: use cached copy if available --- taskfile/node_http.go | 24 ++++++++++++++---------- taskfile/reader.go | 3 ++- taskfile/taskfile.go | 6 +++++- 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/taskfile/node_http.go b/taskfile/node_http.go index e21c2c7130..fa415d3c1f 100644 --- a/taskfile/node_http.go +++ b/taskfile/node_http.go @@ -17,7 +17,9 @@ import ( // An HTTPNode is a node that reads a Taskfile from a remote location via HTTP. type HTTPNode struct { *BaseNode - URL *url.URL + URL *url.URL + logger *logger.Logger + timeout time.Duration } func NewHTTPNode( @@ -36,18 +38,12 @@ func NewHTTPNode( if url.Scheme == "http" && !insecure { return nil, &errors.TaskfileNotSecureError{URI: entrypoint} } - ctx, cf := context.WithTimeout(context.Background(), timeout) - defer cf() - url, err = RemoteExists(ctx, l, url) - if err != nil { - return nil, err - } - if errors.Is(ctx.Err(), context.DeadlineExceeded) { - return nil, &errors.TaskfileNetworkTimeoutError{URI: url.String(), Timeout: timeout} - } + return &HTTPNode{ BaseNode: base, URL: url, + timeout: timeout, + logger: l, }, nil } @@ -60,6 +56,11 @@ func (node *HTTPNode) Remote() bool { } 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()} @@ -67,6 +68,9 @@ func (node *HTTPNode) Read(ctx context.Context) ([]byte, error) { 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() diff --git a/taskfile/reader.go b/taskfile/reader.go index 32783dea66..1f1fefe2a1 100644 --- a/taskfile/reader.go +++ b/taskfile/reader.go @@ -208,8 +208,9 @@ func (r *Reader) readNode(node Node) (*ast.Taskfile, error) { // Read the file b, err = node.Read(ctx) + var taskfileNetworkTimeoutError *errors.TaskfileNetworkTimeoutError // If we timed out then we likely have a network issue - if node.Remote() && errors.Is(ctx.Err(), context.DeadlineExceeded) { + if node.Remote() && errors.As(err, &taskfileNetworkTimeoutError) { // If a download was requested, then we can't use a cached copy if r.download { return nil, &errors.TaskfileNetworkTimeoutError{URI: node.Location(), Timeout: r.timeout} diff --git a/taskfile/taskfile.go b/taskfile/taskfile.go index ca915eae75..9e62a0f486 100644 --- a/taskfile/taskfile.go +++ b/taskfile/taskfile.go @@ -8,6 +8,7 @@ import ( "path/filepath" "slices" "strings" + "time" "github.com/go-task/task/v3/errors" "github.com/go-task/task/v3/internal/filepathext" @@ -40,7 +41,7 @@ var ( // at the given URL with any of the default Taskfile files names. If any of // these match a file, the first matching path will be returned. If no files are // found, an error will be returned. -func RemoteExists(ctx context.Context, l *logger.Logger, u *url.URL) (*url.URL, error) { +func RemoteExists(ctx context.Context, l *logger.Logger, u *url.URL, timeout time.Duration) (*url.URL, error) { // Create a new HEAD request for the given URL to check if the resource exists req, err := http.NewRequest("HEAD", u.String(), nil) if err != nil { @@ -50,6 +51,9 @@ func RemoteExists(ctx context.Context, l *logger.Logger, u *url.URL) (*url.URL, // Request the given URL resp, err := http.DefaultClient.Do(req.WithContext(ctx)) if err != nil { + if errors.Is(ctx.Err(), context.DeadlineExceeded) { + return nil, &errors.TaskfileNetworkTimeoutError{URI: u.String(), Timeout: timeout} + } return nil, errors.TaskfileFetchFailedError{URI: u.String()} } defer resp.Body.Close()