mirror of
https://github.com/go-task/task.git
synced 2025-05-31 23:19:42 +02:00
feat: prefer remote taskfiles over cached ones (#1345)
* feat: prefer remote taskfiles over cached ones * feat: implemented cache on network timeout * feat: --download always downloads, but never executes tasks * feat: --timeout flag * fix: bug with timeout error handling * chore: changelog
This commit is contained in:
parent
834babe0ef
commit
546a4d7e46
15
CHANGELOG.md
15
CHANGELOG.md
@ -2,9 +2,16 @@
|
|||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
|
- The
|
||||||
|
[Remote Taskfiles experiment](https://taskfile.dev/experiments/remote-taskfiles)
|
||||||
|
now prefers remote files over cached ones by default (#1317, #1345 by @pd93).
|
||||||
|
- Added `--timeout` flag to the
|
||||||
|
[Remote Taskfiles experiment](https://taskfile.dev/experiments/remote-taskfiles)
|
||||||
|
(#1317, #1345 by @pd93).
|
||||||
- Fix bug where dynamic `vars:` and `env:` were being executed when they should
|
- Fix bug where dynamic `vars:` and `env:` were being executed when they should
|
||||||
actually be skipped by `platforms:` (#1273, #1377 by @andreynering).
|
actually be skipped by `platforms:` (#1273, #1377 by @andreynering).
|
||||||
- Fix `schema.json` to make `silent` valid in `cmds` that use `for` (#1385, #1386 by @iainvm).
|
- Fix `schema.json` to make `silent` valid in `cmds` that use `for` (#1385,
|
||||||
|
#1386 by @iainvm).
|
||||||
- Add new `--no-status` flag to skip expensive status checks when running
|
- Add new `--no-status` flag to skip expensive status checks when running
|
||||||
`task --list --json` (#1348, #1368 by @amancevice).
|
`task --list --json` (#1348, #1368 by @amancevice).
|
||||||
|
|
||||||
@ -12,7 +19,7 @@
|
|||||||
|
|
||||||
- Enabled the `--yes` flag for the
|
- Enabled the `--yes` flag for the
|
||||||
[Remote Taskfiles experiment](https://taskfile.dev/experiments/remote-taskfiles)
|
[Remote Taskfiles experiment](https://taskfile.dev/experiments/remote-taskfiles)
|
||||||
(#1344 by @pd93).
|
(#1317, #1344 by @pd93).
|
||||||
- Add ability to set `watch: true` in a task to automatically run it in watch
|
- Add ability to set `watch: true` in a task to automatically run it in watch
|
||||||
mode (#231, #1361 by @andreynering).
|
mode (#231, #1361 by @andreynering).
|
||||||
- Fixed a bug on the watch mode where paths that contained `.git` (like
|
- Fixed a bug on the watch mode where paths that contained `.git` (like
|
||||||
@ -26,8 +33,8 @@
|
|||||||
exists to detect recursive calls, but will be removed in favor of a better
|
exists to detect recursive calls, but will be removed in favor of a better
|
||||||
algorithm soon (#1321, #1332).
|
algorithm soon (#1321, #1332).
|
||||||
- Fixed templating on descriptions on `task --list` (#1343 by @blackjid).
|
- Fixed templating on descriptions on `task --list` (#1343 by @blackjid).
|
||||||
- Fixed a bug where precondition errors were incorrectly being printed when
|
- Fixed a bug where precondition errors were incorrectly being printed when task
|
||||||
task execution was aborted (#1337, #1338 by @sylv-io).
|
execution was aborted (#1337, #1338 by @sylv-io).
|
||||||
|
|
||||||
## v3.30.1 - 2023-09-14
|
## v3.30.1 - 2023-09-14
|
||||||
|
|
||||||
|
@ -75,6 +75,7 @@ var flags struct {
|
|||||||
experiments bool
|
experiments bool
|
||||||
download bool
|
download bool
|
||||||
offline bool
|
offline bool
|
||||||
|
timeout time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@ -150,6 +151,7 @@ func run() error {
|
|||||||
if experiments.RemoteTaskfiles {
|
if experiments.RemoteTaskfiles {
|
||||||
pflag.BoolVar(&flags.download, "download", false, "Downloads a cached version of a remote Taskfile.")
|
pflag.BoolVar(&flags.download, "download", false, "Downloads a cached version of a remote Taskfile.")
|
||||||
pflag.BoolVar(&flags.offline, "offline", false, "Forces Task to only use local or cached Taskfiles.")
|
pflag.BoolVar(&flags.offline, "offline", false, "Forces Task to only use local or cached Taskfiles.")
|
||||||
|
pflag.DurationVar(&flags.timeout, "timeout", time.Second*10, "Timeout for downloading remote Taskfiles.")
|
||||||
}
|
}
|
||||||
|
|
||||||
pflag.Parse()
|
pflag.Parse()
|
||||||
@ -235,6 +237,7 @@ func run() error {
|
|||||||
Insecure: flags.insecure,
|
Insecure: flags.insecure,
|
||||||
Download: flags.download,
|
Download: flags.download,
|
||||||
Offline: flags.offline,
|
Offline: flags.offline,
|
||||||
|
Timeout: flags.timeout,
|
||||||
Watch: flags.watch,
|
Watch: flags.watch,
|
||||||
Verbose: flags.verbose,
|
Verbose: flags.verbose,
|
||||||
Silent: flags.silent,
|
Silent: flags.silent,
|
||||||
@ -270,6 +273,12 @@ func run() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the download flag is specified, we should stop execution as soon as
|
||||||
|
// taskfile is downloaded
|
||||||
|
if flags.download {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
if listOptions.ShouldListTasks() {
|
if listOptions.ShouldListTasks() {
|
||||||
foundTasks, err := e.ListTasks(listOptions)
|
foundTasks, err := e.ListTasks(listOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -298,9 +307,7 @@ func run() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If there are no calls, run the default task instead
|
// If there are no calls, run the default task instead
|
||||||
// Unless the download flag is specified, in which case we want to download
|
if len(calls) == 0 {
|
||||||
// the Taskfile and do nothing else
|
|
||||||
if len(calls) == 0 && !flags.download {
|
|
||||||
calls = append(calls, taskfile.Call{Task: "default", Direct: true})
|
calls = append(calls, taskfile.Call{Task: "default", Direct: true})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,16 +74,17 @@ you are doing.
|
|||||||
|
|
||||||
## Caching & Running Offline
|
## Caching & Running Offline
|
||||||
|
|
||||||
If for whatever reason, you don't have access to the internet, but you still
|
Whenever you run a remote Taskfile, the latest copy will be downloaded from the
|
||||||
need to be able to run your tasks, you are able to use the `--download` flag to
|
internet and cached locally. If for whatever reason, you lose access to the
|
||||||
store a cached copy of the remote Taskfile.
|
internet, you will still be able to run your tasks by specifying the `--offline`
|
||||||
|
flag. This will tell Task to use the latest cached version of the file instead
|
||||||
|
of trying to download it. You are able to use the `--download` flag to update
|
||||||
|
the cached version of the remote files without running any tasks.
|
||||||
|
|
||||||
<!-- TODO: The following behavior may change -->
|
By default, Task will timeout requests to download remote files after 10 seconds
|
||||||
|
and look for a cached copy instead. This timeout can be configured by setting
|
||||||
If Task detects that you have a local copy of the remote Taskfile, it will use
|
the `--timeout` flag and specifying a duration. For example, `--timeout 5s` will
|
||||||
your local copy instead of downloading the remote file. You can force Task to
|
set the timeout to 5 seconds.
|
||||||
work offline by using the `--offline` flag. This will prevent Task from making
|
|
||||||
any calls to remote sources.
|
|
||||||
|
|
||||||
<!-- prettier-ignore-start -->
|
<!-- prettier-ignore-start -->
|
||||||
[remote-taskfiles-experiment]: https://github.com/go-task/task/issues/1317
|
[remote-taskfiles-experiment]: https://github.com/go-task/task/issues/1317
|
||||||
|
@ -18,6 +18,7 @@ const (
|
|||||||
CodeTaskfileNotSecure
|
CodeTaskfileNotSecure
|
||||||
CodeTaskfileCacheNotFound
|
CodeTaskfileCacheNotFound
|
||||||
CodeTaskfileVersionNotDefined
|
CodeTaskfileVersionNotDefined
|
||||||
|
CodeTaskfileNetworkTimeout
|
||||||
)
|
)
|
||||||
|
|
||||||
// Task related exit codes
|
// Task related exit codes
|
||||||
|
@ -3,6 +3,7 @@ package errors
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TaskfileNotFoundError is returned when no appropriate Taskfile is found when
|
// TaskfileNotFoundError is returned when no appropriate Taskfile is found when
|
||||||
@ -137,3 +138,26 @@ func (err *TaskfileVersionNotDefined) Error() string {
|
|||||||
func (err *TaskfileVersionNotDefined) Code() int {
|
func (err *TaskfileVersionNotDefined) Code() int {
|
||||||
return CodeTaskfileVersionNotDefined
|
return CodeTaskfileVersionNotDefined
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TaskfileNetworkTimeout is returned when the user attempts to use a remote
|
||||||
|
// Taskfile but a network connection could not be established within the timeout.
|
||||||
|
type TaskfileNetworkTimeout struct {
|
||||||
|
URI string
|
||||||
|
Timeout time.Duration
|
||||||
|
CheckedCache bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err *TaskfileNetworkTimeout) Error() string {
|
||||||
|
var cacheText string
|
||||||
|
if err.CheckedCache {
|
||||||
|
cacheText = " and no offline copy was found in the cache"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf(
|
||||||
|
`task: Network connection timed out after %s while attempting to download Taskfile %q%s`,
|
||||||
|
err.Timeout, err.URI, cacheText,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err *TaskfileNetworkTimeout) Code() int {
|
||||||
|
return CodeTaskfileNetworkTimeout
|
||||||
|
}
|
||||||
|
1
setup.go
1
setup.go
@ -91,6 +91,7 @@ func (e *Executor) readTaskfile() error {
|
|||||||
e.Insecure,
|
e.Insecure,
|
||||||
e.Download,
|
e.Download,
|
||||||
e.Offline,
|
e.Offline,
|
||||||
|
e.Timeout,
|
||||||
e.TempDir,
|
e.TempDir,
|
||||||
e.Logger,
|
e.Logger,
|
||||||
)
|
)
|
||||||
|
1
task.go
1
task.go
@ -46,6 +46,7 @@ type Executor struct {
|
|||||||
Insecure bool
|
Insecure bool
|
||||||
Download bool
|
Download bool
|
||||||
Offline bool
|
Offline bool
|
||||||
|
Timeout time.Duration
|
||||||
Watch bool
|
Watch bool
|
||||||
Verbose bool
|
Verbose bool
|
||||||
Silent bool
|
Silent bool
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"time"
|
||||||
|
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
|
|
||||||
@ -37,6 +38,7 @@ func readTaskfile(
|
|||||||
node Node,
|
node Node,
|
||||||
download,
|
download,
|
||||||
offline bool,
|
offline bool,
|
||||||
|
timeout time.Duration,
|
||||||
tempDir string,
|
tempDir string,
|
||||||
l *logger.Logger,
|
l *logger.Logger,
|
||||||
) (*taskfile.Taskfile, error) {
|
) (*taskfile.Taskfile, error) {
|
||||||
@ -51,35 +53,44 @@ func readTaskfile(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the file is remote, check if we have a cached copy
|
// If the file is remote and we're in offline mode, check if we have a cached copy
|
||||||
// If we're told to download, skip the cache
|
if node.Remote() && offline {
|
||||||
if node.Remote() && !download {
|
if b, err = cache.read(node); errors.Is(err, os.ErrNotExist) {
|
||||||
if b, err = cache.read(node); !errors.Is(err, os.ErrNotExist) && err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if b != nil {
|
|
||||||
l.VerboseOutf(logger.Magenta, "task: [%s] Fetched cached copy\n", node.Location())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the file is remote, we found nothing in the cache and we're not
|
|
||||||
// allowed to download it then we can't do anything.
|
|
||||||
if node.Remote() && b == nil && offline {
|
|
||||||
if b == nil && offline {
|
|
||||||
return nil, &errors.TaskfileCacheNotFound{URI: node.Location()}
|
return nil, &errors.TaskfileCacheNotFound{URI: node.Location()}
|
||||||
}
|
} else if err != nil {
|
||||||
}
|
|
||||||
|
|
||||||
// If we still don't have a copy, get the file in the usual way
|
|
||||||
if b == nil {
|
|
||||||
b, err = node.Read(context.Background())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
l.VerboseOutf(logger.Magenta, "task: [%s] Fetched cached copy\n", node.Location())
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
downloaded := false
|
||||||
|
ctx, cf := context.WithTimeout(context.Background(), timeout)
|
||||||
|
defer cf()
|
||||||
|
|
||||||
|
// Read the file
|
||||||
|
b, err = node.Read(ctx)
|
||||||
|
// If we timed out then we likely have a network issue
|
||||||
|
if node.Remote() && errors.Is(ctx.Err(), context.DeadlineExceeded) {
|
||||||
|
// If a download was requested, then we can't use a cached copy
|
||||||
|
if download {
|
||||||
|
return nil, &errors.TaskfileNetworkTimeout{URI: node.Location(), Timeout: timeout}
|
||||||
|
}
|
||||||
|
// Search for any cached copies
|
||||||
|
if b, err = cache.read(node); errors.Is(err, os.ErrNotExist) {
|
||||||
|
return nil, &errors.TaskfileNetworkTimeout{URI: node.Location(), Timeout: timeout, CheckedCache: true}
|
||||||
|
} else if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
l.VerboseOutf(logger.Magenta, "task: [%s] Network timeout. Fetched cached copy\n", node.Location())
|
||||||
|
} else if err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
downloaded = true
|
||||||
|
}
|
||||||
|
|
||||||
// If the node was remote, we need to check the checksum
|
// If the node was remote, we need to check the checksum
|
||||||
if node.Remote() {
|
if node.Remote() && downloaded {
|
||||||
l.VerboseOutf(logger.Magenta, "task: [%s] Fetched remote copy\n", node.Location())
|
l.VerboseOutf(logger.Magenta, "task: [%s] Fetched remote copy\n", node.Location())
|
||||||
|
|
||||||
// Get the checksums
|
// Get the checksums
|
||||||
@ -102,24 +113,21 @@ func readTaskfile(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the hash has changed (or is new), store it in the cache
|
// If the hash has changed (or is new)
|
||||||
if checksum != cachedChecksum {
|
if checksum != cachedChecksum {
|
||||||
|
// Store the checksum
|
||||||
if err := cache.writeChecksum(node, checksum); err != nil {
|
if err := cache.writeChecksum(node, checksum); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
// Cache the file
|
||||||
|
l.VerboseOutf(logger.Magenta, "task: [%s] Caching downloaded file\n", node.Location())
|
||||||
|
if err = cache.write(node, b); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the file is remote and we need to cache it
|
|
||||||
if node.Remote() && download {
|
|
||||||
l.VerboseOutf(logger.Magenta, "task: [%s] Caching downloaded file\n", node.Location())
|
|
||||||
// Cache the file for later
|
|
||||||
if err = cache.write(node, b); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var t taskfile.Taskfile
|
var t taskfile.Taskfile
|
||||||
if err := yaml.Unmarshal(b, &t); err != nil {
|
if err := yaml.Unmarshal(b, &t); err != nil {
|
||||||
return nil, &errors.TaskfileInvalidError{URI: filepathext.TryAbsToRel(node.Location()), Err: err}
|
return nil, &errors.TaskfileInvalidError{URI: filepathext.TryAbsToRel(node.Location()), Err: err}
|
||||||
@ -137,12 +145,13 @@ func Taskfile(
|
|||||||
insecure bool,
|
insecure bool,
|
||||||
download bool,
|
download bool,
|
||||||
offline bool,
|
offline bool,
|
||||||
|
timeout time.Duration,
|
||||||
tempDir string,
|
tempDir string,
|
||||||
l *logger.Logger,
|
l *logger.Logger,
|
||||||
) (*taskfile.Taskfile, error) {
|
) (*taskfile.Taskfile, error) {
|
||||||
var _taskfile func(Node) (*taskfile.Taskfile, error)
|
var _taskfile func(Node) (*taskfile.Taskfile, error)
|
||||||
_taskfile = func(node Node) (*taskfile.Taskfile, error) {
|
_taskfile = func(node Node) (*taskfile.Taskfile, error) {
|
||||||
t, err := readTaskfile(node, download, offline, tempDir, l)
|
t, err := readTaskfile(node, download, offline, timeout, tempDir, l)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user