mirror of
https://github.com/go-task/task.git
synced 2025-02-09 13:47:06 +02:00
feat: implement gentle force experiment draft (#1216)
* feat: implement gentle force experiment draft * docs: changelog
This commit is contained in:
parent
d8a12fe56d
commit
5fdaa9aa36
@ -6,8 +6,10 @@
|
||||
- e.g. `taskfile.yml`, `taskfile.yaml`, `taskfile.dist.yml` &
|
||||
`taskfile.dist.yaml`
|
||||
- Bug fixed were made to the
|
||||
[npm installation method](https://taskfile.dev/installation/#npm).
|
||||
(#1190, by @sounisi5011).
|
||||
[npm installation method](https://taskfile.dev/installation/#npm). (#1190, by
|
||||
@sounisi5011).
|
||||
- Added the [gentle force experiment](https://taskfile.dev/experiments) as a
|
||||
draft (#1200, #1216 by @pd93).
|
||||
|
||||
## v3.26.0 - 2023-06-10
|
||||
|
||||
|
@ -13,7 +13,7 @@ func ParseV3(args ...string) ([]taskfile.Call, *taskfile.Vars) {
|
||||
|
||||
for _, arg := range args {
|
||||
if !strings.Contains(arg, "=") {
|
||||
calls = append(calls, taskfile.Call{Task: arg})
|
||||
calls = append(calls, taskfile.Call{Task: arg, Direct: true})
|
||||
continue
|
||||
}
|
||||
|
||||
@ -22,7 +22,7 @@ func ParseV3(args ...string) ([]taskfile.Call, *taskfile.Vars) {
|
||||
}
|
||||
|
||||
if len(calls) == 0 {
|
||||
calls = append(calls, taskfile.Call{Task: "default"})
|
||||
calls = append(calls, taskfile.Call{Task: "default", Direct: true})
|
||||
}
|
||||
|
||||
return calls, globals
|
||||
@ -35,7 +35,7 @@ func ParseV2(args ...string) ([]taskfile.Call, *taskfile.Vars) {
|
||||
|
||||
for _, arg := range args {
|
||||
if !strings.Contains(arg, "=") {
|
||||
calls = append(calls, taskfile.Call{Task: arg})
|
||||
calls = append(calls, taskfile.Call{Task: arg, Direct: true})
|
||||
continue
|
||||
}
|
||||
|
||||
@ -52,7 +52,7 @@ func ParseV2(args ...string) ([]taskfile.Call, *taskfile.Vars) {
|
||||
}
|
||||
|
||||
if len(calls) == 0 {
|
||||
calls = append(calls, taskfile.Call{Task: "default"})
|
||||
calls = append(calls, taskfile.Call{Task: "default", Direct: true})
|
||||
}
|
||||
|
||||
return calls, globals
|
||||
|
@ -20,17 +20,17 @@ func TestArgsV3(t *testing.T) {
|
||||
{
|
||||
Args: []string{"task-a", "task-b", "task-c"},
|
||||
ExpectedCalls: []taskfile.Call{
|
||||
{Task: "task-a"},
|
||||
{Task: "task-b"},
|
||||
{Task: "task-c"},
|
||||
{Task: "task-a", Direct: true},
|
||||
{Task: "task-b", Direct: true},
|
||||
{Task: "task-c", Direct: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
Args: []string{"task-a", "FOO=bar", "task-b", "task-c", "BAR=baz", "BAZ=foo"},
|
||||
ExpectedCalls: []taskfile.Call{
|
||||
{Task: "task-a"},
|
||||
{Task: "task-b"},
|
||||
{Task: "task-c"},
|
||||
{Task: "task-a", Direct: true},
|
||||
{Task: "task-b", Direct: true},
|
||||
{Task: "task-c", Direct: true},
|
||||
},
|
||||
ExpectedGlobals: &taskfile.Vars{
|
||||
OrderedMap: orderedmap.FromMapWithOrder(
|
||||
@ -46,7 +46,7 @@ func TestArgsV3(t *testing.T) {
|
||||
{
|
||||
Args: []string{"task-a", "CONTENT=with some spaces"},
|
||||
ExpectedCalls: []taskfile.Call{
|
||||
{Task: "task-a"},
|
||||
{Task: "task-a", Direct: true},
|
||||
},
|
||||
ExpectedGlobals: &taskfile.Vars{
|
||||
OrderedMap: orderedmap.FromMapWithOrder(
|
||||
@ -60,8 +60,8 @@ func TestArgsV3(t *testing.T) {
|
||||
{
|
||||
Args: []string{"FOO=bar", "task-a", "task-b"},
|
||||
ExpectedCalls: []taskfile.Call{
|
||||
{Task: "task-a"},
|
||||
{Task: "task-b"},
|
||||
{Task: "task-a", Direct: true},
|
||||
{Task: "task-b", Direct: true},
|
||||
},
|
||||
ExpectedGlobals: &taskfile.Vars{
|
||||
OrderedMap: orderedmap.FromMapWithOrder(
|
||||
@ -75,19 +75,19 @@ func TestArgsV3(t *testing.T) {
|
||||
{
|
||||
Args: nil,
|
||||
ExpectedCalls: []taskfile.Call{
|
||||
{Task: "default"},
|
||||
{Task: "default", Direct: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
Args: []string{},
|
||||
ExpectedCalls: []taskfile.Call{
|
||||
{Task: "default"},
|
||||
{Task: "default", Direct: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
Args: []string{"FOO=bar", "BAR=baz"},
|
||||
ExpectedCalls: []taskfile.Call{
|
||||
{Task: "default"},
|
||||
{Task: "default", Direct: true},
|
||||
},
|
||||
ExpectedGlobals: &taskfile.Vars{
|
||||
OrderedMap: orderedmap.FromMapWithOrder(
|
||||
@ -122,16 +122,17 @@ func TestArgsV2(t *testing.T) {
|
||||
{
|
||||
Args: []string{"task-a", "task-b", "task-c"},
|
||||
ExpectedCalls: []taskfile.Call{
|
||||
{Task: "task-a"},
|
||||
{Task: "task-b"},
|
||||
{Task: "task-c"},
|
||||
{Task: "task-a", Direct: true},
|
||||
{Task: "task-b", Direct: true},
|
||||
{Task: "task-c", Direct: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
Args: []string{"task-a", "FOO=bar", "task-b", "task-c", "BAR=baz", "BAZ=foo"},
|
||||
ExpectedCalls: []taskfile.Call{
|
||||
{
|
||||
Task: "task-a",
|
||||
Task: "task-a",
|
||||
Direct: true,
|
||||
Vars: &taskfile.Vars{
|
||||
OrderedMap: orderedmap.FromMapWithOrder(
|
||||
map[string]taskfile.Var{
|
||||
@ -141,9 +142,10 @@ func TestArgsV2(t *testing.T) {
|
||||
),
|
||||
},
|
||||
},
|
||||
{Task: "task-b"},
|
||||
{Task: "task-b", Direct: true},
|
||||
{
|
||||
Task: "task-c",
|
||||
Task: "task-c",
|
||||
Direct: true,
|
||||
Vars: &taskfile.Vars{
|
||||
OrderedMap: orderedmap.FromMapWithOrder(
|
||||
map[string]taskfile.Var{
|
||||
@ -160,7 +162,8 @@ func TestArgsV2(t *testing.T) {
|
||||
Args: []string{"task-a", "CONTENT=with some spaces"},
|
||||
ExpectedCalls: []taskfile.Call{
|
||||
{
|
||||
Task: "task-a",
|
||||
Task: "task-a",
|
||||
Direct: true,
|
||||
Vars: &taskfile.Vars{
|
||||
OrderedMap: orderedmap.FromMapWithOrder(
|
||||
map[string]taskfile.Var{
|
||||
@ -175,8 +178,8 @@ func TestArgsV2(t *testing.T) {
|
||||
{
|
||||
Args: []string{"FOO=bar", "task-a", "task-b"},
|
||||
ExpectedCalls: []taskfile.Call{
|
||||
{Task: "task-a"},
|
||||
{Task: "task-b"},
|
||||
{Task: "task-a", Direct: true},
|
||||
{Task: "task-b", Direct: true},
|
||||
},
|
||||
ExpectedGlobals: &taskfile.Vars{
|
||||
OrderedMap: orderedmap.FromMapWithOrder(
|
||||
@ -190,19 +193,19 @@ func TestArgsV2(t *testing.T) {
|
||||
{
|
||||
Args: nil,
|
||||
ExpectedCalls: []taskfile.Call{
|
||||
{Task: "default"},
|
||||
{Task: "default", Direct: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
Args: []string{},
|
||||
ExpectedCalls: []taskfile.Call{
|
||||
{Task: "default"},
|
||||
{Task: "default", Direct: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
Args: []string{"FOO=bar", "BAR=baz"},
|
||||
ExpectedCalls: []taskfile.Call{
|
||||
{Task: "default"},
|
||||
{Task: "default", Direct: true},
|
||||
},
|
||||
ExpectedGlobals: &taskfile.Vars{
|
||||
OrderedMap: orderedmap.FromMapWithOrder(
|
||||
|
@ -15,6 +15,7 @@ import (
|
||||
"github.com/go-task/task/v3"
|
||||
"github.com/go-task/task/v3/args"
|
||||
"github.com/go-task/task/v3/errors"
|
||||
"github.com/go-task/task/v3/internal/experiments"
|
||||
"github.com/go-task/task/v3/internal/logger"
|
||||
"github.com/go-task/task/v3/internal/sort"
|
||||
ver "github.com/go-task/task/v3/internal/version"
|
||||
@ -53,6 +54,7 @@ var flags struct {
|
||||
taskSort string
|
||||
status bool
|
||||
force bool
|
||||
forceAll bool
|
||||
watch bool
|
||||
verbose bool
|
||||
silent bool
|
||||
@ -78,7 +80,6 @@ func main() {
|
||||
Verbose: flags.verbose,
|
||||
Color: flags.color,
|
||||
}
|
||||
|
||||
if err, ok := err.(*errors.TaskRunError); ok && flags.exitCode {
|
||||
l.Errf(logger.Red, "%v\n", err)
|
||||
os.Exit(err.TaskExitCode())
|
||||
@ -110,7 +111,6 @@ func run() error {
|
||||
pflag.BoolVarP(&flags.listJson, "json", "j", false, "Formats task list as JSON.")
|
||||
pflag.StringVar(&flags.taskSort, "sort", "", "Changes the order of the tasks when listed. [default|alphanumeric|none].")
|
||||
pflag.BoolVar(&flags.status, "status", false, "Exits with non-zero exit code if any of the given tasks is not up-to-date.")
|
||||
pflag.BoolVarP(&flags.force, "force", "f", false, "Forces execution even when the task is up-to-date.")
|
||||
pflag.BoolVarP(&flags.watch, "watch", "w", false, "Enables watch of the given task.")
|
||||
pflag.BoolVarP(&flags.verbose, "verbose", "v", false, "Enables verbose mode.")
|
||||
pflag.BoolVarP(&flags.silent, "silent", "s", false, "Disables echoing.")
|
||||
@ -129,6 +129,15 @@ func run() error {
|
||||
pflag.IntVarP(&flags.concurrency, "concurrency", "C", 0, "Limit number tasks to run concurrently.")
|
||||
pflag.DurationVarP(&flags.interval, "interval", "I", 0, "Interval to watch for changes.")
|
||||
pflag.BoolVarP(&flags.global, "global", "g", false, "Runs global Taskfile, from $HOME/{T,t}askfile.{yml,yaml}.")
|
||||
|
||||
// Gentle force experiment will override the force flag and add a new force-all flag
|
||||
if experiments.GentleForce {
|
||||
pflag.BoolVarP(&flags.force, "force", "f", false, "Forces execution of the directly called task.")
|
||||
pflag.BoolVar(&flags.forceAll, "force-all", false, "Forces execution of the called task and all its dependant tasks.")
|
||||
} else {
|
||||
pflag.BoolVarP(&flags.forceAll, "force", "f", false, "Forces execution even when the task is up-to-date.")
|
||||
}
|
||||
|
||||
pflag.Parse()
|
||||
|
||||
if flags.version {
|
||||
@ -194,6 +203,7 @@ func run() error {
|
||||
|
||||
e := task.Executor{
|
||||
Force: flags.force,
|
||||
ForceAll: flags.forceAll,
|
||||
Watch: flags.watch,
|
||||
Verbose: flags.verbose,
|
||||
Silent: flags.silent,
|
||||
|
@ -70,6 +70,27 @@ version 3 as soon as possible.
|
||||
A list of changes between version 2 and version 3 are available in the [Task v3
|
||||
Release Notes][version-3-release-notes].
|
||||
|
||||
### ![experiment] Gentle Force ([#1200](https://github.com/go-task/task/issues/1200))
|
||||
|
||||
- Environment variable: `TASK_X_FORCE=1`
|
||||
- Breaks: `--force` flag
|
||||
|
||||
The `--force` flag currently forces _all_ tasks to run regardless of the status
|
||||
checks. This can be useful, but we have found that most of the time users only
|
||||
expect the direct task they are calling to be forced and _not_ all of its
|
||||
dependant tasks.
|
||||
|
||||
This experiment changes the `--force` flag to only force the directly called
|
||||
task. All dependant tasks will have their statuses checked as normal and will
|
||||
only run if Task considers them to be out of date. A new `--force-all` flag will
|
||||
also be added to maintain the current behavior for users that need this
|
||||
functionality.
|
||||
|
||||
If you want to migrate, but continue to force all dependant tasks to run, you
|
||||
should replace all uses of the `--force` flag with `--force-all`. Alternatively,
|
||||
if you want to adopt the new behavior, you can continue to use the `--force`
|
||||
flag as you do now!
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
[breaking-change-proposal]: https://github.com/go-task/task/discussions/1191
|
||||
[deprecate-version-2-schema]: https://github.com/go-task/task/issues/1197
|
||||
|
@ -11,13 +11,13 @@ import (
|
||||
|
||||
const envPrefix = "TASK_X_"
|
||||
|
||||
var TestExperiment bool
|
||||
var GentleForce bool
|
||||
|
||||
func init() {
|
||||
if err := readDotEnv(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
TestExperiment = parseEnv("TestExperiment")
|
||||
GentleForce = parseEnv("GENTLE_FORCE")
|
||||
}
|
||||
|
||||
func parseEnv(xName string) bool {
|
||||
|
4
task.go
4
task.go
@ -50,6 +50,7 @@ type Executor struct {
|
||||
TempDir string
|
||||
Entrypoint string
|
||||
Force bool
|
||||
ForceAll bool
|
||||
Watch bool
|
||||
Verbose bool
|
||||
Silent bool
|
||||
@ -179,7 +180,8 @@ func (e *Executor) RunTask(ctx context.Context, call taskfile.Call) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if !e.Force {
|
||||
skipFingerprinting := e.ForceAll || (call.Direct && e.Force)
|
||||
if !skipFingerprinting {
|
||||
if err := ctx.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
46
task_test.go
46
task_test.go
@ -2120,3 +2120,49 @@ func TestSilence(t *testing.T) {
|
||||
|
||||
buff.Reset()
|
||||
}
|
||||
|
||||
func TestForce(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
env map[string]string
|
||||
force bool
|
||||
forceAll bool
|
||||
}{
|
||||
{
|
||||
name: "force",
|
||||
force: true,
|
||||
},
|
||||
{
|
||||
name: "force-all",
|
||||
forceAll: true,
|
||||
},
|
||||
{
|
||||
name: "force with gentle force experiment",
|
||||
force: true,
|
||||
env: map[string]string{
|
||||
"TASK_X_GENTLE_FORCE": "1",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "force-all with gentle force experiment",
|
||||
forceAll: true,
|
||||
env: map[string]string{
|
||||
"TASK_X_GENTLE_FORCE": "1",
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var buff bytes.Buffer
|
||||
e := task.Executor{
|
||||
Dir: "testdata/force",
|
||||
Stdout: &buff,
|
||||
Stderr: &buff,
|
||||
Force: tt.force,
|
||||
ForceAll: tt.forceAll,
|
||||
}
|
||||
require.NoError(t, e.Setup())
|
||||
require.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "task-with-dep", Direct: true}))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -5,4 +5,5 @@ type Call struct {
|
||||
Task string
|
||||
Vars *Vars
|
||||
Silent bool
|
||||
Direct bool // Was the task called directly or via another task?
|
||||
}
|
||||
|
19
testdata/force/Taskfile.yml
vendored
Normal file
19
testdata/force/Taskfile.yml
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
version: "3"
|
||||
|
||||
tasks:
|
||||
task-with-dep:
|
||||
status: [ test true ]
|
||||
deps: [ indirect ]
|
||||
cmds:
|
||||
- echo "direct"
|
||||
|
||||
task-with-subtask:
|
||||
status: [ test true ]
|
||||
cmds:
|
||||
- task: indirect
|
||||
- echo "direct"
|
||||
|
||||
indirect:
|
||||
status: [ test true ]
|
||||
cmds:
|
||||
- echo "indirect"
|
Loading…
x
Reference in New Issue
Block a user