Compare commits

...

37 Commits

Author SHA1 Message Date
Andrey Nering
3cad318b70 v3.15.2
I pushed v3.15.1 tagged in a commit in a detached branch by mistake.

Repushing as v3.15.2
2022-09-08 21:26:18 -03:00
Andrey Nering
8c6002cae6 v3.15.1 2022-09-08 21:22:19 -03:00
Andrey Nering
0355bbaf3b Merge pull request #861 from cdaguerre/feat/make-zsh-completion-configurable
Make zsh completion list configurable
2022-09-08 21:01:04 -03:00
Andrey Nering
2ba083a650 Merge pull request #863 from MarioSchwalbe/bash-completion
Fix handling of CLI_ARGS
2022-09-08 20:59:16 -03:00
Andrey Nering
c79ea5a257 Merge pull request #866 from pd93/fix-read-dotenv
fix: ignore empty dotfile file names
2022-09-08 20:58:25 -03:00
Pete Davison
44706f4957 fix: ignore empty dotfile file names 2022-09-08 18:51:39 +00:00
Mario Schwalbe
a1b3bb03ed * Fix handling of CLI_ARGS 2022-09-08 19:03:29 +02:00
Christian Daguerre
76caa16909 Make zsh completion list configurable 2022-09-07 10:33:55 +02:00
Andrey Nering
160b788198 Merge pull request #859 from jfhovinne/docs-reprobuilds
Document how to reproduce release binaries
2022-09-05 14:29:42 -03:00
jfhovinne
eada62f62c Document how to reproduce executables 2022-09-05 19:08:37 +02:00
Andrey Nering
bd9419e6db Website: Use "Roboto" as the main font
This is to add consistency between different operating systems, specially
Windows which used "Segoe UI" before this commit.
2022-09-04 17:38:29 -03:00
Andrey Nering
bdd9de3001 CHANGELOG: Add extra link 2022-09-03 18:28:24 -03:00
Andrey Nering
200ba4ed04 v3.15.0 2022-09-03 18:21:40 -03:00
Andrey Nering
1e8939dd58 Merge pull request #857 from go-task/feature/add-dir-special-variables
Add ROOT_DIR and TASKFILE_DIR special variables
2022-09-03 18:18:43 -03:00
Andrey Nering
f45dd11e53 Add ROOT_DIR and TASKFILE_DIR special variables
Closes #215
2022-09-03 18:14:54 -03:00
Andrey Nering
1a0cc1d64d Update favicon.ico 2022-08-31 13:21:43 -03:00
Wes McNamee
421cb522d9 Merge pull request #846 from rootulp/patch-1
fix: grammar in docs
2022-08-24 22:07:23 -07:00
Rootul Patel
1b18b041d6 fix: grammar in docs 2022-08-24 20:30:21 -04:00
Andrey Nering
8788703ac6 CHANGELOG for #831
Closes #826
2022-08-23 18:43:04 -03:00
Andrey Nering
b6c25e3ad9 Mention #844 on CHANGELOG 2022-08-23 18:40:34 -03:00
Andrey Nering
73eaa68cd1 Merge pull request #844 from MarioSchwalbe/bash-completion
Use --silent to get the list of tasks (bash completion)
2022-08-23 18:39:47 -03:00
Andrey Nering
beb927f7b4 Merge pull request #831 from ilewin/check_path_for_symlinks_issue_826
Attempt to fix Task not following symlinks
2022-08-23 18:38:17 -03:00
ilewin
cdc969cd4e Added test to check if symlinks are evaluated for task source files 2022-08-23 18:36:19 +02:00
ilewin
2a67499f12 Issue #826. Replaced zglob.Glob func with GlobFollowSymlinks to evaluate symlinks 2022-08-23 18:25:11 +02:00
Mario Schwalbe
6a3cc79daa * Use --silent to get list of tasks 2022-08-23 18:03:15 +02:00
Andrey Nering
97d4a947ee Merge pull request #838 from pcgeek86/patch-1
Update releasing.md
2022-08-17 20:50:18 -03:00
Wes McNamee
e0e47ad9a0 Merge pull request #841 from cristaloleg/fix-linter
Fix go-critic suggestions
2022-08-17 12:38:12 -07:00
Oleg Kovalov
b08eac58e9 Fix go-critic suggestions 2022-08-17 19:37:58 +02:00
Trevor Sullivan
c2148a359d Update releasing.md 2022-08-16 04:33:54 -06:00
Andrey Nering
c172185a24 Update CHANGELOG 2022-08-15 09:55:11 -03:00
Andrey Nering
1140a5c4ae Merge pull request #835 from MarioSchwalbe/bash-completion
Improved bash completion
2022-08-15 09:52:07 -03:00
Mario Schwalbe
3cc378c960 * Convert indentation to 2 spaces 2022-08-13 21:55:35 +02:00
Andrey Nering
9b3a961303 Merge pull request #832 from jfhovinne/reprobuilds
Modify goreleaser defaults for reproducible builds
2022-08-13 12:04:27 -03:00
Mario Schwalbe
d048555149 Improved bash completion for task 2022-08-11 20:48:41 +02:00
jfhovinne
7533858a52 Modify goreleaser defaults for reproducible builds 2022-08-10 15:33:17 +02:00
Andrey Nering
c4e10ef0aa Refactor: Add SmartJoin to handle IsAbs automatically 2022-08-06 18:19:07 -03:00
Andrey Nering
c20842e7cd v3.14.1: Add CHANGELOG to website 2022-08-03 22:26:39 -03:00
37 changed files with 397 additions and 120 deletions

View File

@@ -17,6 +17,9 @@ build:
goarch: 386 goarch: 386
env: env:
- CGO_ENABLED=0 - CGO_ENABLED=0
mod_timestamp: '{{ .CommitTimestamp }}'
flags:
- -trimpath
ldflags: ldflags:
- -s -w # Don't set main.version. - -s -w # Don't set main.version.

View File

@@ -1,5 +1,25 @@
# Changelog # Changelog
## v3.15.2 - 2022-09-08
- Fix error when using variable in `env:` introduced in the previous release
([#858](https://github.com/go-task/task/issues/858), [#866](https://github.com/go-task/task/pull/866)).
- Fix handling of `CLI_ARGS` (`--`) in Bash completion
([#863](https://github.com/go-task/task/pull/863)).
- On zsh completion, add ability to replace `--list-all` with `--list` as
already possible on the Bash completion
([#861](https://github.com/go-task/task/pull/861)).
## v3.15.0 - 2022-09-03
- Add new special variables `ROOT_DIR` and `TASKFILE_DIR`. This was a highly
requested feature
([#215](https://github.com/go-task/task/issues/215), [#857](https://github.com/go-task/task/pull/857), [Documentation](https://taskfile.dev/api/#special-variables)).
- Follow symlinks on `sources`
([#826](https://github.com/go-task/task/issues/826), [#831](https://github.com/go-task/task/pull/831)).
- Improvements and fixes to Bash completion
([#835](https://github.com/go-task/task/pull/835), [#844](https://github.com/go-task/task/pull/844)).
## v3.14.1 - 2022-08-03 ## v3.14.1 - 2022-08-03
- Always resolve relative include paths relative to the including Taskfile - Always resolve relative include paths relative to the including Taskfile

View File

@@ -80,6 +80,19 @@ tasks:
cmds: cmds:
- goreleaser --snapshot --rm-dist - goreleaser --snapshot --rm-dist
docs:changelog:
desc: Copy CHANGELOG.md to the documentation website
vars:
FILE: docs/docs/changelog.md
cmds:
- rm {{.FILE}}
- 'echo "---" >> {{.FILE}}'
- 'echo "slug: /changelog/" >> {{.FILE}}'
- 'echo "sidebar_position: 6" >> {{.FILE}}'
- 'echo "---" >> {{.FILE}}'
- 'echo "" >> {{.FILE}}'
- 'cat CHANGELOG.md >> {{.FILE}}'
packages: packages:
cmds: cmds:
- echo '{{.GO_PACKAGES}}' - echo '{{.GO_PACKAGES}}'

View File

@@ -1,26 +1,55 @@
#!/bin/bash # vim: set tabstop=2 shiftwidth=2 expandtab:
GO_TASK_PROGNAME=task _GO_TASK_COMPLETION_LIST_OPTION='--list-all'
_go_task_completion() function _task()
{ {
local cur local cur prev words cword
_get_comp_words_by_ref -n : cur _init_completion -n : || return
case "$cur" in # Check for `--` within command-line and quit or strip suffix.
--*) local i
local options for i in "${!words[@]}"; do
options="$(_parse_help task)" if [ "${words[$i]}" == "--" ]; then
mapfile -t COMPREPLY < <(compgen -W "$options" -- "$cur") # Do not complete words following `--` passed to CLI_ARGS.
[ $cword -gt $i ] && return
# Remove the words following `--` to not put --list in CLI_ARGS.
words=( "${words[@]:0:$i}" )
break
fi
done
# Handle special arguments of options.
case "$prev" in
-d|--dir)
_filedir -d
return $?
;; ;;
*) -t|--taskfile)
local tasks _filedir yaml || return $?
tasks="$($GO_TASK_PROGNAME --list-all 2> /dev/null | awk 'NR>1 { sub(/:$/,"",$2); print $2 }')" _filedir yml
mapfile -t COMPREPLY < <(compgen -W "$tasks" -- "$cur") return $?
;;
-o|--output)
COMPREPLY=( $( compgen -W "interleaved group prefixed" -- $cur ) )
return 0
;; ;;
esac esac
# Handle normal options.
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "$(_parse_help $1)" -- $cur ) )
return 0
;;
esac
# Prepare task name completions.
local tasks=( $( "${words[@]}" --silent $_GO_TASK_COMPLETION_LIST_OPTION 2> /dev/null ) )
COMPREPLY=( $( compgen -W "${tasks[*]}" -- "$cur" ) )
# Post-process because task names might contain colons.
__ltrim_colon_completions "$cur" __ltrim_colon_completions "$cur"
} }
complete -F _go_task_completion $GO_TASK_PROGNAME complete -F _task task

View File

@@ -3,6 +3,8 @@
local context state state_descr line local context state state_descr line
typeset -A opt_args typeset -A opt_args
_GO_TASK_COMPLETION_LIST_OPTION="${GO_TASK_COMPLETION_LIST_OPTION:---list-all}"
# Listing commands from Taskfile.yml # Listing commands from Taskfile.yml
function __task_list() { function __task_list() {
local -a scripts cmd local -a scripts cmd
@@ -27,7 +29,7 @@ function __task_list() {
(( enabled )) || return 0 (( enabled )) || return 0
scripts=() scripts=()
for item in "${(@)${(f)$("${cmd[@]}" --list-all)}[2,-1]#\* }"; do for item in "${(@)${(f)$("${cmd[@]}" $_GO_TASK_COMPLETION_LIST_OPTION)}[2,-1]#\* }"; do
task="${item%%:[[:space:]]*}" task="${item%%:[[:space:]]*}"
desc="${item##[^[:space:]]##[[:space:]]##}" desc="${item##[^[:space:]]##[[:space:]]##}"
scripts+=( "${task//:/\\:}:$desc" ) scripts+=( "${task//:/\\:}:$desc" )

View File

@@ -9,9 +9,10 @@ tasks:
start: start:
desc: Start website desc: Start website
vars: vars:
HOST: '{{default "localhost" .HOST}}'
PORT: '{{default "3001" .PORT}}' PORT: '{{default "3001" .PORT}}'
cmds: cmds:
- npx docusaurus start --no-open --port={{.PORT}} - npx docusaurus start --no-open --host={{.HOST}} --port={{.PORT}}
build: build:
desc: Build website desc: Build website

View File

@@ -44,6 +44,19 @@ variable
| | `--version` | `bool` | `false` | Show Task version. | | | `--version` | `bool` | `false` | Show Task version. |
| `-w` | `--watch` | `bool` | `false` | Enables watch of the given task. | | `-w` | `--watch` | `bool` | `false` | Enables watch of the given task. |
## Special Variables
There are some special variables that is available on the templating system:
| Var | Description |
| - | - |
| `CLI_ARGS` | Contain all extra arguments passed after `--` when calling Task through the CLI. |
| `TASK` | The name of the current task. |
| `ROOT_DIR` | The absolute path of the root Taskfile. |
| `TASKFILE_DIR` | The absolute path of the included Taskfile. |
| `CHECKSUM` | The checksum of the files listed in `sources`. Only available within the `status` prop and if method is set to `checksum`. |
| `TIMESTAMP` | The date object of the greatest timestamp of the files listes in `sources`. Only available within the `status` prop and if method is set to `timestamp`. |
## ENV ## ENV
Some environment variables can be overriden to adjust Task behavior. Some environment variables can be overriden to adjust Task behavior.

View File

@@ -5,6 +5,34 @@ sidebar_position: 6
# Changelog # Changelog
## v3.15.2 - 2022-09-08
- Fix error when using variable in `env:` introduced in the previous release
([#858](https://github.com/go-task/task/issues/858), [#866](https://github.com/go-task/task/pull/866)).
- Fix handling of `CLI_ARGS` (`--`) in Bash completion
([#863](https://github.com/go-task/task/pull/863)).
- On zsh completion, add ability to replace `--list-all` with `--list` as
already possible on the Bash completion
([#861](https://github.com/go-task/task/pull/861)).
## v3.15.0 - 2022-09-03
- Add new special variables `ROOT_DIR` and `TASKFILE_DIR`. This was a highly
requested feature
([#215](https://github.com/go-task/task/issues/215), [#857](https://github.com/go-task/task/pull/857), [Documentation](https://taskfile.dev/api/#special-variables)).
- Follow symlinks on `sources`
([#826](https://github.com/go-task/task/issues/826), [#831](https://github.com/go-task/task/pull/831)).
- Improvements and fixes to Bash completion
([#835](https://github.com/go-task/task/pull/835), [#844](https://github.com/go-task/task/pull/844)).
## v3.14.1 - 2022-08-03
- Always resolve relative include paths relative to the including Taskfile
([#822](https://github.com/go-task/task/issues/822), [#823](https://github.com/go-task/task/pull/823)).
- Fix ZSH and PowerShell completions to consider all tasks instead of just the
public ones (those with descriptions)
([#803](https://github.com/go-task/task/pull/803)).
## v3.14.0 - 2022-07-08 ## v3.14.0 - 2022-07-08
- Add ability to override the `.task` directory location with the - Add ability to override the `.task` directory location with the

View File

@@ -41,7 +41,7 @@ guide to check the full schema documentation and Task features.
`$PATH` and you're done! Or you can also install using [Homebrew][homebrew], `$PATH` and you're done! Or you can also install using [Homebrew][homebrew],
[Snapcraft][snapcraft], or [Scoop][scoop] if you want. [Snapcraft][snapcraft], or [Scoop][scoop] if you want.
- Available on CIs: by adding [this simple command](installation.md#install-script) - Available on CIs: by adding [this simple command](installation.md#install-script)
to install on your CI script and you're done to use Task as part of your CI pipeline; to install on your CI script and you're ready to use Task as part of your CI pipeline;
- Truly cross-platform: while most build tools only work well on Linux or macOS, - Truly cross-platform: while most build tools only work well on Linux or macOS,
Task also supports Windows thanks to [this shell interpreter for Go][sh]. Task also supports Windows thanks to [this shell interpreter for Go][sh].
- Great for code generation: you can easily [prevent a task from running](/usage#prevent-unnecessary-work) - Great for code generation: you can easily [prevent a task from running](/usage#prevent-unnecessary-work)

View File

@@ -13,6 +13,10 @@ the `test-release` task of the Taskfile.
artifacts automatically when a new Git tag is pushed to master artifacts automatically when a new Git tag is pushed to master
(raw executables and DEB and RPM packages). (raw executables and DEB and RPM packages).
Since v3.15.0, raw executables can also be reproduced and verified locally by
checking out a specific tag and calling `goreleaser build`, using the Go version
defined in the above GitHub Actions.
# Homebrew # Homebrew
To release a new version on the [Homebrew tap][homebrewtap] edit the To release a new version on the [Homebrew tap][homebrewtap] edit the
@@ -31,9 +35,9 @@ the [Snapcraft dashboard][snapcraftdashboard].
# Scoop # Scoop
Scoop is a community owned installation method. Scoop owners usually take care Scoop is a command-line package manager for the Windows operating system.
of updating versions there by editing Scoop package manifests are maintained by the community.
[this file](https://github.com/lukesampson/scoop-extras/blob/master/bucket/task.json). Scoop owners usually take care of updating versions there by editing [this file](https://github.com/lukesampson/scoop-extras/blob/master/bucket/task.json).
If you think its Task version is outdated, open an issue to let us know. If you think its Task version is outdated, open an issue to let us know.
# Nix # Nix

View File

@@ -1,4 +1,9 @@
@import url('https://fonts.googleapis.com/css2?family=Roboto+Mono:ital,wght@0,400;0,700;1,400;1,700&family=Roboto:ital,wght@0,400;0,700;1,400;1,700&display=swap');
:root { :root {
--ifm-font-family-base: Roboto, system-ui, -apple-system, Segoe UI, Ubuntu, Cantarell, Noto Sans, sans-serif, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
--ifm-font-family-monospace: 'Roboto Mono', SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;
--ifm-color-primary: #43ABA2 ; --ifm-color-primary: #43ABA2 ;
--ifm-color-primary-dark: #3AB2A6; --ifm-color-primary-dark: #3AB2A6;
--ifm-color-primary-darker: #32B8AB; --ifm-color-primary-darker: #32B8AB;
@@ -11,13 +16,6 @@
} }
[data-theme='dark'] { [data-theme='dark'] {
--ifm-color-primary: #43ABA2 ;
--ifm-color-primary-dark: #3AB2A6;
--ifm-color-primary-darker: #32B8AB;
--ifm-color-primary-darkest: #29BEB0;
--ifm-color-primary-light: #4CA59D;
--ifm-color-primary-lighter: #559F98;
--ifm-color-primary-lightest: #5D9993;
--docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3); --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3);
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 170 KiB

View File

@@ -17,7 +17,7 @@ type taskNotFoundError struct {
} }
func (err *taskNotFoundError) Error() string { func (err *taskNotFoundError) Error() string {
return fmt.Sprintf(`task: Task "%s" not found`, err.taskName) return fmt.Sprintf(`task: Task %q not found`, err.taskName)
} }
type TaskRunError struct { type TaskRunError struct {
@@ -26,7 +26,7 @@ type TaskRunError struct {
} }
func (err *TaskRunError) Error() string { func (err *TaskRunError) Error() string {
return fmt.Sprintf(`task: Failed to run task "%s": %v`, err.taskName, err.err) return fmt.Sprintf(`task: Failed to run task %q: %v`, err.taskName, err.err)
} }
func (err *TaskRunError) ExitCode() int { func (err *TaskRunError) ExitCode() int {
@@ -46,7 +46,7 @@ type MaximumTaskCallExceededError struct {
func (e *MaximumTaskCallExceededError) Error() string { func (e *MaximumTaskCallExceededError) Error() string {
return fmt.Sprintf( return fmt.Sprintf(
`task: maximum task call exceeded (%d) for task "%s": probably an cyclic dep or infinite loop`, `task: maximum task call exceeded (%d) for task %q: probably an cyclic dep or infinite loop`,
MaximumTaskCall, MaximumTaskCall,
e.task, e.task,
) )

View File

@@ -4,7 +4,8 @@ import (
"fmt" "fmt"
"io" "io"
"os" "os"
"path/filepath"
"github.com/go-task/task/v3/internal/filepathext"
) )
const defaultTaskfile = `# https://taskfile.dev const defaultTaskfile = `# https://taskfile.dev
@@ -23,13 +24,13 @@ tasks:
// InitTaskfile Taskfile creates a new Taskfile // InitTaskfile Taskfile creates a new Taskfile
func InitTaskfile(w io.Writer, dir string) error { func InitTaskfile(w io.Writer, dir string) error {
f := filepath.Join(dir, "Taskfile.yaml") f := filepathext.SmartJoin(dir, "Taskfile.yaml")
if _, err := os.Stat(f); err == nil { if _, err := os.Stat(f); err == nil {
return ErrTaskfileAlreadyExists return ErrTaskfileAlreadyExists
} }
if err := os.WriteFile(f, []byte(defaultTaskfile), 0644); err != nil { if err := os.WriteFile(f, []byte(defaultTaskfile), 0o644); err != nil {
return err return err
} }
fmt.Fprintf(w, "Taskfile.yaml created in the current directory\n") fmt.Fprintf(w, "Taskfile.yaml created in the current directory\n")

View File

@@ -4,12 +4,12 @@ import (
"bytes" "bytes"
"context" "context"
"fmt" "fmt"
"path/filepath"
"strings" "strings"
"sync" "sync"
"github.com/go-task/task/v3/internal/compiler" "github.com/go-task/task/v3/internal/compiler"
"github.com/go-task/task/v3/internal/execext" "github.com/go-task/task/v3/internal/execext"
"github.com/go-task/task/v3/internal/filepathext"
"github.com/go-task/task/v3/internal/logger" "github.com/go-task/task/v3/internal/logger"
"github.com/go-task/task/v3/internal/templater" "github.com/go-task/task/v3/internal/templater"
"github.com/go-task/task/v3/taskfile" "github.com/go-task/task/v3/taskfile"
@@ -44,7 +44,13 @@ func (c *CompilerV3) FastGetVariables(t *taskfile.Task, call taskfile.Call) (*ta
func (c *CompilerV3) getVariables(t *taskfile.Task, call *taskfile.Call, evaluateShVars bool) (*taskfile.Vars, error) { func (c *CompilerV3) getVariables(t *taskfile.Task, call *taskfile.Call, evaluateShVars bool) (*taskfile.Vars, error) {
result := compiler.GetEnviron() result := compiler.GetEnviron()
if t != nil { if t != nil {
result.Set("TASK", taskfile.Var{Static: t.Task}) specialVars, err := c.getSpecialVars(t)
if err != nil {
return nil, err
}
for k, v := range specialVars {
result.Set(k, taskfile.Var{Static: v})
}
} }
getRangeFunc := func(dir string) func(k string, v taskfile.Var) error { getRangeFunc := func(dir string) func(k string, v taskfile.Var) error {
@@ -83,9 +89,7 @@ func (c *CompilerV3) getVariables(t *taskfile.Task, call *taskfile.Call, evaluat
if err := tr.Err(); err != nil { if err := tr.Err(); err != nil {
return nil, err return nil, err
} }
if !filepath.IsAbs(dir) { dir = filepathext.SmartJoin(c.Dir, dir)
dir = filepath.Join(c.Dir, dir)
}
taskRangeFunc = getRangeFunc(dir) taskRangeFunc = getRangeFunc(dir)
} }
@@ -167,3 +171,23 @@ func (c *CompilerV3) ResetCache() {
c.dynamicCache = nil c.dynamicCache = nil
} }
func (c *CompilerV3) getSpecialVars(t *taskfile.Task) (map[string]string, error) {
taskfileDir, err := c.getTaskfileDir(t)
if err != nil {
return nil, err
}
return map[string]string{
"TASK": t.Task,
"ROOT_DIR": c.Dir,
"TASKFILE_DIR": taskfileDir,
}, nil
}
func (c *CompilerV3) getTaskfileDir(t *taskfile.Task) (string, error) {
if t.IncludedTaskfile != nil {
return t.IncludedTaskfile.FullDirPath()
}
return c.Dir, nil
}

View File

@@ -73,7 +73,7 @@ func IsExitError(err error) bool {
// if available. // if available.
func Expand(s string) (string, error) { func Expand(s string) (string, error) {
s = filepath.ToSlash(s) s = filepath.ToSlash(s)
s = strings.Replace(s, " ", `\ `, -1) s = strings.ReplaceAll(s, " ", `\ `)
fields, err := shell.Fields(s, nil) fields, err := shell.Fields(s, nil)
if err != nil { if err != nil {
return "", err return "", err

View File

@@ -0,0 +1,14 @@
package filepathext
import (
"path/filepath"
)
// SmartJoin joins two paths, but only if the second is not already an
// absolute path.
func SmartJoin(a, b string) string {
if filepath.IsAbs(b) {
return b
}
return filepath.Join(a, b)
}

View File

@@ -8,6 +8,8 @@ import (
"path/filepath" "path/filepath"
"regexp" "regexp"
"strings" "strings"
"github.com/go-task/task/v3/internal/filepathext"
) )
// Checksum validades if a task is up to date by calculating its source // Checksum validades if a task is up to date by calculating its source
@@ -43,8 +45,8 @@ func (c *Checksum) IsUpToDate() (bool, error) {
} }
if !c.Dry { if !c.Dry {
_ = os.MkdirAll(filepath.Join(c.TempDir, "checksum"), 0755) _ = os.MkdirAll(filepathext.SmartJoin(c.TempDir, "checksum"), 0o755)
if err = os.WriteFile(checksumFile, []byte(newMd5+"\n"), 0644); err != nil { if err = os.WriteFile(checksumFile, []byte(newMd5+"\n"), 0o644); err != nil {
return false, err return false, err
} }
} }

View File

@@ -2,12 +2,12 @@ package status
import ( import (
"os" "os"
"path/filepath"
"sort" "sort"
"github.com/mattn/go-zglob" "github.com/mattn/go-zglob"
"github.com/go-task/task/v3/internal/execext" "github.com/go-task/task/v3/internal/execext"
"github.com/go-task/task/v3/internal/filepathext"
) )
func globs(dir string, globs []string) ([]string, error) { func globs(dir string, globs []string) ([]string, error) {
@@ -25,17 +25,18 @@ func globs(dir string, globs []string) ([]string, error) {
func Glob(dir string, g string) ([]string, error) { func Glob(dir string, g string) ([]string, error) {
files := make([]string, 0) files := make([]string, 0)
if !filepath.IsAbs(g) { g = filepathext.SmartJoin(dir, g)
g = filepath.Join(dir, g)
}
g, err := execext.Expand(g) g, err := execext.Expand(g)
if err != nil { if err != nil {
return nil, err return nil, err
} }
fs, err := zglob.Glob(g)
fs, err := zglob.GlobFollowSymlinks(g)
if err != nil { if err != nil {
return nil, err return nil, err
} }
for _, f := range fs { for _, f := range fs {
info, err := os.Stat(f) info, err := os.Stat(f)
if err != nil { if err != nil {

View File

@@ -19,11 +19,11 @@ func init() {
"OS": func() string { return runtime.GOOS }, "OS": func() string { return runtime.GOOS },
"ARCH": func() string { return runtime.GOARCH }, "ARCH": func() string { return runtime.GOARCH },
"catLines": func(s string) string { "catLines": func(s string) string {
s = strings.Replace(s, "\r\n", " ", -1) s = strings.ReplaceAll(s, "\r\n", " ")
return strings.Replace(s, "\n", " ", -1) return strings.ReplaceAll(s, "\n", " ")
}, },
"splitLines": func(s string) []string { "splitLines": func(s string) []string {
s = strings.Replace(s, "\r\n", "\n", -1) s = strings.ReplaceAll(s, "\r\n", "\n")
return strings.Split(s, "\n") return strings.Split(s, "\n")
}, },
"fromSlash": func(path string) string { "fromSlash": func(path string) string {

View File

@@ -12,6 +12,7 @@ import (
compilerv2 "github.com/go-task/task/v3/internal/compiler/v2" compilerv2 "github.com/go-task/task/v3/internal/compiler/v2"
compilerv3 "github.com/go-task/task/v3/internal/compiler/v3" compilerv3 "github.com/go-task/task/v3/internal/compiler/v3"
"github.com/go-task/task/v3/internal/execext" "github.com/go-task/task/v3/internal/execext"
"github.com/go-task/task/v3/internal/filepathext"
"github.com/go-task/task/v3/internal/logger" "github.com/go-task/task/v3/internal/logger"
"github.com/go-task/task/v3/internal/output" "github.com/go-task/task/v3/internal/output"
"github.com/go-task/task/v3/taskfile" "github.com/go-task/task/v3/taskfile"
@@ -19,6 +20,10 @@ import (
) )
func (e *Executor) Setup() error { func (e *Executor) Setup() error {
if err := e.setCurrentDir(); err != nil {
return err
}
if err := e.readTaskfile(); err != nil { if err := e.readTaskfile(); err != nil {
return err return err
} }
@@ -52,6 +57,23 @@ func (e *Executor) Setup() error {
return nil return nil
} }
func (e *Executor) setCurrentDir() error {
if e.Dir == "" {
wd, err := os.Getwd()
if err != nil {
return err
}
e.Dir = wd
} else if !filepath.IsAbs(e.Dir) {
abs, err := filepath.Abs(e.Dir)
if err != nil {
return err
}
e.Dir = abs
}
return nil
}
func (e *Executor) readTaskfile() error { func (e *Executor) readTaskfile() error {
var err error var err error
e.Taskfile, err = read.Taskfile(&read.ReaderNode{ e.Taskfile, err = read.Taskfile(&read.ReaderNode{
@@ -69,7 +91,7 @@ func (e *Executor) setupTempDir() error {
} }
if os.Getenv("TASK_TEMP_DIR") == "" { if os.Getenv("TASK_TEMP_DIR") == "" {
e.TempDir = filepath.Join(e.Dir, ".task") e.TempDir = filepathext.SmartJoin(e.Dir, ".task")
} else if filepath.IsAbs(os.Getenv("TASK_TEMP_DIR")) || strings.HasPrefix(os.Getenv("TASK_TEMP_DIR"), "~") { } else if filepath.IsAbs(os.Getenv("TASK_TEMP_DIR")) || strings.HasPrefix(os.Getenv("TASK_TEMP_DIR"), "~") {
tempDir, err := execext.Expand(os.Getenv("TASK_TEMP_DIR")) tempDir, err := execext.Expand(os.Getenv("TASK_TEMP_DIR"))
if err != nil { if err != nil {
@@ -77,9 +99,9 @@ func (e *Executor) setupTempDir() error {
} }
projectDir, _ := filepath.Abs(e.Dir) projectDir, _ := filepath.Abs(e.Dir)
projectName := filepath.Base(projectDir) projectName := filepath.Base(projectDir)
e.TempDir = filepath.Join(tempDir, projectName) e.TempDir = filepathext.SmartJoin(tempDir, projectName)
} else { } else {
e.TempDir = filepath.Join(e.Dir, os.Getenv("TASK_TEMP_DIR")) e.TempDir = filepathext.SmartJoin(e.Dir, os.Getenv("TASK_TEMP_DIR"))
} }
return nil return nil

View File

@@ -9,7 +9,7 @@ import (
) )
// NOTE(@andreynering): This function intercepts SIGINT and SIGTERM signals // NOTE(@andreynering): This function intercepts SIGINT and SIGTERM signals
// so the Task process is not killed immediatelly and processes running have // so the Task process is not killed immediately and processes running have
// time to do cleanup work. // time to do cleanup work.
func (e *Executor) InterceptInterruptSignals() { func (e *Executor) InterceptInterruptSignals() {
ch := make(chan os.Signal, 3) ch := make(chan os.Signal, 3)

View File

@@ -181,7 +181,7 @@ func (e *Executor) mkdir(t *taskfile.Task) error {
defer mutex.Unlock() defer mutex.Unlock()
if _, err := os.Stat(t.Dir); os.IsNotExist(err) { if _, err := os.Stat(t.Dir); os.IsNotExist(err) {
if err := os.MkdirAll(t.Dir, 0755); err != nil { if err := os.MkdirAll(t.Dir, 0o755); err != nil {
return err return err
} }
} }

View File

@@ -14,6 +14,7 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/go-task/task/v3" "github.com/go-task/task/v3"
"github.com/go-task/task/v3/internal/filepathext"
"github.com/go-task/task/v3/taskfile" "github.com/go-task/task/v3/taskfile"
) )
@@ -37,12 +38,12 @@ func (fct fileContentTest) name(file string) string {
func (fct fileContentTest) Run(t *testing.T) { func (fct fileContentTest) Run(t *testing.T) {
for f := range fct.Files { for f := range fct.Files {
_ = os.Remove(filepath.Join(fct.Dir, f)) _ = os.Remove(filepathext.SmartJoin(fct.Dir, f))
} }
e := &task.Executor{ e := &task.Executor{
Dir: fct.Dir, Dir: fct.Dir,
TempDir: filepath.Join(fct.Dir, ".task"), TempDir: filepathext.SmartJoin(fct.Dir, ".task"),
Entrypoint: fct.Entrypoint, Entrypoint: fct.Entrypoint,
Stdout: io.Discard, Stdout: io.Discard,
Stderr: io.Discard, Stderr: io.Discard,
@@ -52,7 +53,7 @@ func (fct fileContentTest) Run(t *testing.T) {
for name, expectContent := range fct.Files { for name, expectContent := range fct.Files {
t.Run(fct.name(name), func(t *testing.T) { t.Run(fct.name(name), func(t *testing.T) {
path := filepath.Join(fct.Dir, name) path := filepathext.SmartJoin(fct.Dir, name)
b, err := os.ReadFile(path) b, err := os.ReadFile(path)
assert.NoError(t, err, "Error reading file") assert.NoError(t, err, "Error reading file")
s := string(b) s := string(b)
@@ -163,6 +164,39 @@ func TestMultilineVars(t *testing.T) {
} }
} }
func TestSpecialVars(t *testing.T) {
const dir = "testdata/special_vars"
const target = "default"
var buff bytes.Buffer
e := &task.Executor{
Dir: dir,
Stdout: &buff,
Stderr: &buff,
Silent: true,
}
assert.NoError(t, e.Setup())
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: target}))
toAbs := func(rel string) string {
abs, err := filepath.Abs(rel)
assert.NoError(t, err)
return abs
}
output := buff.String()
// Root Taskfile
assert.Contains(t, output, "root/TASK=print")
assert.Contains(t, output, "root/ROOT_DIR="+toAbs("testdata/special_vars"))
assert.Contains(t, output, "root/TASKFILE_DIR="+toAbs("testdata/special_vars"))
// Included Taskfile
assert.Contains(t, output, "included/TASK=included:print")
assert.Contains(t, output, "included/ROOT_DIR="+toAbs("testdata/special_vars"))
assert.Contains(t, output, "included/TASKFILE_DIR="+toAbs("testdata/special_vars/included"))
}
func TestVarsInvalidTmpl(t *testing.T) { func TestVarsInvalidTmpl(t *testing.T) {
const ( const (
dir = "testdata/vars/v2" dir = "testdata/vars/v2"
@@ -235,7 +269,7 @@ func TestDeps(t *testing.T) {
} }
for _, f := range files { for _, f := range files {
_ = os.Remove(filepath.Join(dir, f)) _ = os.Remove(filepathext.SmartJoin(dir, f))
} }
e := &task.Executor{ e := &task.Executor{
@@ -247,7 +281,7 @@ func TestDeps(t *testing.T) {
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "default"})) assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "default"}))
for _, f := range files { for _, f := range files {
f = filepath.Join(dir, f) f = filepathext.SmartJoin(dir, f)
if _, err := os.Stat(f); err != nil { if _, err := os.Stat(f); err != nil {
t.Errorf("File %s should exist", f) t.Errorf("File %s should exist", f)
} }
@@ -263,7 +297,7 @@ func TestStatus(t *testing.T) {
} }
for _, f := range files { for _, f := range files {
path := filepath.Join(dir, f) path := filepathext.SmartJoin(dir, f)
_ = os.Remove(path) _ = os.Remove(path)
if _, err := os.Stat(path); err == nil { if _, err := os.Stat(path); err == nil {
t.Errorf("File should not exist: %v", err) t.Errorf("File should not exist: %v", err)
@@ -273,7 +307,7 @@ func TestStatus(t *testing.T) {
var buff bytes.Buffer var buff bytes.Buffer
e := &task.Executor{ e := &task.Executor{
Dir: dir, Dir: dir,
TempDir: filepath.Join(dir, ".task"), TempDir: filepathext.SmartJoin(dir, ".task"),
Stdout: &buff, Stdout: &buff,
Stderr: &buff, Stderr: &buff,
Silent: true, Silent: true,
@@ -283,7 +317,7 @@ func TestStatus(t *testing.T) {
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "gen-bar"})) assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "gen-bar"}))
for _, f := range files { for _, f := range files {
if _, err := os.Stat(filepath.Join(dir, f)); err != nil { if _, err := os.Stat(filepathext.SmartJoin(dir, f)); err != nil {
t.Errorf("File should exist: %v", err) t.Errorf("File should exist: %v", err)
} }
} }
@@ -360,10 +394,10 @@ func TestGenerates(t *testing.T) {
fileWithSpaces = "my text file.txt" fileWithSpaces = "my text file.txt"
) )
var srcFile = filepath.Join(dir, srcTask) var srcFile = filepathext.SmartJoin(dir, srcTask)
for _, task := range []string{srcTask, relTask, absTask, fileWithSpaces} { for _, task := range []string{srcTask, relTask, absTask, fileWithSpaces} {
path := filepath.Join(dir, task) path := filepathext.SmartJoin(dir, task)
_ = os.Remove(path) _ = os.Remove(path)
if _, err := os.Stat(path); err == nil { if _, err := os.Stat(path); err == nil {
t.Errorf("File should not exist: %v", err) t.Errorf("File should not exist: %v", err)
@@ -379,7 +413,7 @@ func TestGenerates(t *testing.T) {
assert.NoError(t, e.Setup()) assert.NoError(t, e.Setup())
for _, theTask := range []string{relTask, absTask, fileWithSpaces} { for _, theTask := range []string{relTask, absTask, fileWithSpaces} {
var destFile = filepath.Join(dir, theTask) var destFile = filepathext.SmartJoin(dir, theTask)
var upToDate = fmt.Sprintf("task: Task \"%s\" is up to date\n", srcTask) + var upToDate = fmt.Sprintf("task: Task \"%s\" is up to date\n", srcTask) +
fmt.Sprintf("task: Task \"%s\" is up to date\n", theTask) fmt.Sprintf("task: Task \"%s\" is up to date\n", theTask)
@@ -416,16 +450,16 @@ func TestStatusChecksum(t *testing.T) {
} }
for _, f := range files { for _, f := range files {
_ = os.Remove(filepath.Join(dir, f)) _ = os.Remove(filepathext.SmartJoin(dir, f))
_, err := os.Stat(filepath.Join(dir, f)) _, err := os.Stat(filepathext.SmartJoin(dir, f))
assert.Error(t, err) assert.Error(t, err)
} }
var buff bytes.Buffer var buff bytes.Buffer
e := task.Executor{ e := task.Executor{
Dir: dir, Dir: dir,
TempDir: filepath.Join(dir, ".task"), TempDir: filepathext.SmartJoin(dir, ".task"),
Stdout: &buff, Stdout: &buff,
Stderr: &buff, Stderr: &buff,
} }
@@ -433,7 +467,7 @@ func TestStatusChecksum(t *testing.T) {
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "build"})) assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "build"}))
for _, f := range files { for _, f := range files {
_, err := os.Stat(filepath.Join(dir, f)) _, err := os.Stat(filepathext.SmartJoin(dir, f))
assert.NoError(t, err) assert.NoError(t, err)
} }
@@ -577,13 +611,13 @@ func TestListCanListDescOnly(t *testing.T) {
func TestStatusVariables(t *testing.T) { func TestStatusVariables(t *testing.T) {
const dir = "testdata/status_vars" const dir = "testdata/status_vars"
_ = os.RemoveAll(filepath.Join(dir, ".task")) _ = os.RemoveAll(filepathext.SmartJoin(dir, ".task"))
_ = os.Remove(filepath.Join(dir, "generated.txt")) _ = os.Remove(filepathext.SmartJoin(dir, "generated.txt"))
var buff bytes.Buffer var buff bytes.Buffer
e := task.Executor{ e := task.Executor{
Dir: dir, Dir: dir,
TempDir: filepath.Join(dir, ".task"), TempDir: filepathext.SmartJoin(dir, ".task"),
Stdout: &buff, Stdout: &buff,
Stderr: &buff, Stderr: &buff,
Silent: false, Silent: false,
@@ -594,7 +628,7 @@ func TestStatusVariables(t *testing.T) {
assert.Contains(t, buff.String(), "d41d8cd98f00b204e9800998ecf8427e") assert.Contains(t, buff.String(), "d41d8cd98f00b204e9800998ecf8427e")
inf, err := os.Stat(filepath.Join(dir, "source.txt")) inf, err := os.Stat(filepathext.SmartJoin(dir, "source.txt"))
assert.NoError(t, err) assert.NoError(t, err)
ts := fmt.Sprintf("%d", inf.ModTime().Unix()) ts := fmt.Sprintf("%d", inf.ModTime().Unix())
tf := inf.ModTime().String() tf := inf.ModTime().String()
@@ -605,7 +639,7 @@ func TestStatusVariables(t *testing.T) {
func TestInit(t *testing.T) { func TestInit(t *testing.T) {
const dir = "testdata/init" const dir = "testdata/init"
var file = filepath.Join(dir, "Taskfile.yaml") var file = filepathext.SmartJoin(dir, "Taskfile.yaml")
_ = os.Remove(file) _ = os.Remove(file)
if _, err := os.Stat(file); err == nil { if _, err := os.Stat(file); err == nil {
@@ -694,7 +728,7 @@ func TestExpand(t *testing.T) {
func TestDry(t *testing.T) { func TestDry(t *testing.T) {
const dir = "testdata/dry" const dir = "testdata/dry"
file := filepath.Join(dir, "file.txt") file := filepathext.SmartJoin(dir, "file.txt")
_ = os.Remove(file) _ = os.Remove(file)
var buff bytes.Buffer var buff bytes.Buffer
@@ -719,12 +753,12 @@ func TestDry(t *testing.T) {
func TestDryChecksum(t *testing.T) { func TestDryChecksum(t *testing.T) {
const dir = "testdata/dry_checksum" const dir = "testdata/dry_checksum"
checksumFile := filepath.Join(dir, ".task/checksum/default") checksumFile := filepathext.SmartJoin(dir, ".task/checksum/default")
_ = os.Remove(checksumFile) _ = os.Remove(checksumFile)
e := task.Executor{ e := task.Executor{
Dir: dir, Dir: dir,
TempDir: filepath.Join(dir, ".task"), TempDir: filepathext.SmartJoin(dir, ".task"),
Stdout: io.Discard, Stdout: io.Discard,
Stderr: io.Discard, Stderr: io.Discard,
Dry: true, Dry: true,
@@ -776,12 +810,6 @@ func TestIncludesMultiLevel(t *testing.T) {
func TestIncludeCycle(t *testing.T) { func TestIncludeCycle(t *testing.T) {
const dir = "testdata/includes_cycle" const dir = "testdata/includes_cycle"
wd, err := os.Getwd()
assert.Nil(t, err)
message := "task: include cycle detected between %s/%s/one/Taskfile.yml <--> %s/%s/Taskfile.yml"
expectedError := fmt.Sprintf(message, wd, dir, wd, dir)
var buff bytes.Buffer var buff bytes.Buffer
e := task.Executor{ e := task.Executor{
Dir: dir, Dir: dir,
@@ -790,7 +818,9 @@ func TestIncludeCycle(t *testing.T) {
Silent: true, Silent: true,
} }
assert.EqualError(t, e.Setup(), expectedError) err := e.Setup()
assert.Error(t, err)
assert.Contains(t, err.Error(), "task: include cycle detected between")
} }
func TestIncorrectVersionIncludes(t *testing.T) { func TestIncorrectVersionIncludes(t *testing.T) {
@@ -964,12 +994,12 @@ func TestSummary(t *testing.T) {
assert.NoError(t, e.Setup()) assert.NoError(t, e.Setup())
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "task-with-summary"}, taskfile.Call{Task: "other-task-with-summary"})) assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "task-with-summary"}, taskfile.Call{Task: "other-task-with-summary"}))
data, err := os.ReadFile(filepath.Join(dir, "task-with-summary.txt")) data, err := os.ReadFile(filepathext.SmartJoin(dir, "task-with-summary.txt"))
assert.NoError(t, err) assert.NoError(t, err)
expectedOutput := string(data) expectedOutput := string(data)
if runtime.GOOS == "windows" { if runtime.GOOS == "windows" {
expectedOutput = strings.Replace(expectedOutput, "\r\n", "\n", -1) expectedOutput = strings.ReplaceAll(expectedOutput, "\r\n", "\n")
} }
assert.Equal(t, expectedOutput, buff.String()) assert.Equal(t, expectedOutput, buff.String())
@@ -1337,3 +1367,36 @@ func TestErrorCode(t *testing.T) {
assert.True(t, ok, "cannot cast returned error to *task.TaskRunError") assert.True(t, ok, "cannot cast returned error to *task.TaskRunError")
assert.Equal(t, 42, casted.ExitCode(), "unexpected exit code from task") assert.Equal(t, 42, casted.ExitCode(), "unexpected exit code from task")
} }
func TestEvaluateSymlinksInPaths(t *testing.T) {
const dir = "testdata/evaluate_symlinks_in_paths"
var buff bytes.Buffer
e := &task.Executor{
Dir: dir,
Stdout: &buff,
Stderr: &buff,
Silent: false,
}
assert.NoError(t, e.Setup())
err := e.Run(context.Background(), taskfile.Call{Task: "default"})
assert.NoError(t, err)
assert.NotEqual(t, `task: Task "default" is up to date`, strings.TrimSpace(buff.String()))
buff.Reset()
err = e.Run(context.Background(), taskfile.Call{Task: "test-sym"})
assert.NoError(t, err)
assert.NotEqual(t, `task: Task "test-sym" is up to date`, strings.TrimSpace(buff.String()))
buff.Reset()
err = e.Run(context.Background(), taskfile.Call{Task: "default"})
assert.NoError(t, err)
assert.NotEqual(t, `task: Task "default" is up to date`, strings.TrimSpace(buff.String()))
buff.Reset()
err = e.Run(context.Background(), taskfile.Call{Task: "default"})
assert.NoError(t, err)
assert.Equal(t, `task: Task "default" is up to date`, strings.TrimSpace(buff.String()))
buff.Reset()
err = e.Run(context.Background(), taskfile.Call{Task: "reset"})
assert.NoError(t, err)
buff.Reset()
err = os.RemoveAll(dir + "/.task")
assert.NoError(t, err)
}

View File

@@ -6,6 +6,7 @@ import (
"path/filepath" "path/filepath"
"github.com/go-task/task/v3/internal/execext" "github.com/go-task/task/v3/internal/execext"
"github.com/go-task/task/v3/internal/filepathext"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
@@ -133,7 +134,7 @@ func (it *IncludedTaskfile) resolvePath(path string) (string, error) {
return path, nil return path, nil
} }
result, err := filepath.Abs(filepath.Join(it.BaseDir, path)) result, err := filepath.Abs(filepathext.SmartJoin(it.BaseDir, path))
if err != nil { if err != nil {
return "", fmt.Errorf("task: error resolving path %s relative to %s: %w", path, it.BaseDir, err) return "", fmt.Errorf("task: error resolving path %s relative to %s: %w", path, it.BaseDir, err)
} }

View File

@@ -2,11 +2,11 @@ package read
import ( import (
"os" "os"
"path/filepath"
"github.com/joho/godotenv" "github.com/joho/godotenv"
"github.com/go-task/task/v3/internal/compiler" "github.com/go-task/task/v3/internal/compiler"
"github.com/go-task/task/v3/internal/filepathext"
"github.com/go-task/task/v3/internal/templater" "github.com/go-task/task/v3/internal/templater"
"github.com/go-task/task/v3/taskfile" "github.com/go-task/task/v3/taskfile"
) )
@@ -27,10 +27,11 @@ func Dotenv(c compiler.Compiler, tf *taskfile.Taskfile, dir string) (*taskfile.V
for _, dotEnvPath := range tf.Dotenv { for _, dotEnvPath := range tf.Dotenv {
dotEnvPath = tr.Replace(dotEnvPath) dotEnvPath = tr.Replace(dotEnvPath)
if dotEnvPath == "" {
if !filepath.IsAbs(dotEnvPath) { continue
dotEnvPath = filepath.Join(dir, dotEnvPath)
} }
dotEnvPath = filepathext.SmartJoin(dir, dotEnvPath)
if _, err := os.Stat(dotEnvPath); os.IsNotExist(err) { if _, err := os.Stat(dotEnvPath); os.IsNotExist(err) {
continue continue
} }

View File

@@ -9,6 +9,7 @@ import (
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
"github.com/go-task/task/v3/internal/filepathext"
"github.com/go-task/task/v3/internal/templater" "github.com/go-task/task/v3/internal/templater"
"github.com/go-task/task/v3/taskfile" "github.com/go-task/task/v3/taskfile"
) )
@@ -44,7 +45,7 @@ func Taskfile(readerNode *ReaderNode) (*taskfile.Taskfile, error) {
readerNode.Dir = d readerNode.Dir = d
} }
path, err := exists(filepath.Join(readerNode.Dir, readerNode.Entrypoint)) path, err := exists(filepathext.SmartJoin(readerNode.Dir, readerNode.Entrypoint))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -140,12 +141,10 @@ func Taskfile(readerNode *ReaderNode) (*taskfile.Taskfile, error) {
} }
for _, task := range includedTaskfile.Tasks { for _, task := range includedTaskfile.Tasks {
if !filepath.IsAbs(task.Dir) { task.Dir = filepathext.SmartJoin(dir, task.Dir)
task.Dir = filepath.Join(dir, task.Dir)
}
task.IncludeVars = includedTask.Vars task.IncludeVars = includedTask.Vars
task.IncludedTaskfileVars = includedTaskfile.Vars task.IncludedTaskfileVars = includedTaskfile.Vars
task.IncludedTaskfile = &includedTask
} }
} }
@@ -159,7 +158,7 @@ func Taskfile(readerNode *ReaderNode) (*taskfile.Taskfile, error) {
} }
if v < 3.0 { if v < 3.0 {
path = filepath.Join(readerNode.Dir, fmt.Sprintf("Taskfile_%s.yml", runtime.GOOS)) path = filepathext.SmartJoin(readerNode.Dir, fmt.Sprintf("Taskfile_%s.yml", runtime.GOOS))
if _, err = os.Stat(path); err == nil { if _, err = os.Stat(path); err == nil {
osTaskfile, err := readTaskfile(path) osTaskfile, err := readTaskfile(path)
if err != nil { if err != nil {
@@ -191,29 +190,19 @@ func readTaskfile(file string) (*taskfile.Taskfile, error) {
return &t, yaml.NewDecoder(f).Decode(&t) return &t, yaml.NewDecoder(f).Decode(&t)
} }
// exists finds a Taskfile at the stated location, returning a fully qualified path to the file
func exists(path string) (string, error) { func exists(path string) (string, error) {
fi, err := os.Stat(path) fi, err := os.Stat(path)
if err != nil { if err != nil {
return "", err return "", err
} }
if fi.Mode().IsRegular() { if fi.Mode().IsRegular() {
// File exists, return a fully qualified path return path, nil
result, err := filepath.Abs(path)
if err != nil {
return "", err
}
return result, nil
} }
for _, n := range defaultTaskfiles { for _, n := range defaultTaskfiles {
fpath := filepath.Join(path, n) fpath := filepathext.SmartJoin(path, n)
if _, err := os.Stat(fpath); err == nil { if _, err := os.Stat(fpath); err == nil {
result, err := filepath.Abs(fpath) return fpath, nil
if err != nil {
return "", err
}
return result, nil
} }
} }
@@ -228,14 +217,14 @@ func checkCircularIncludes(node *ReaderNode) error {
return errors.New("task: failed to check for include cycle: node.Parent was nil") return errors.New("task: failed to check for include cycle: node.Parent was nil")
} }
var curNode = node var curNode = node
var basePath = filepath.Join(node.Dir, node.Entrypoint) var basePath = filepathext.SmartJoin(node.Dir, node.Entrypoint)
for curNode.Parent != nil { for curNode.Parent != nil {
curNode = curNode.Parent curNode = curNode.Parent
curPath := filepath.Join(curNode.Dir, curNode.Entrypoint) curPath := filepathext.SmartJoin(curNode.Dir, curNode.Entrypoint)
if curPath == basePath { if curPath == basePath {
return fmt.Errorf("task: include cycle detected between %s <--> %s", return fmt.Errorf("task: include cycle detected between %s <--> %s",
curPath, curPath,
filepath.Join(node.Parent.Dir, node.Parent.Entrypoint), filepathext.SmartJoin(node.Parent.Dir, node.Parent.Entrypoint),
) )
} }
} }

View File

@@ -3,11 +3,11 @@ package read
import ( import (
"fmt" "fmt"
"os" "os"
"path/filepath"
"runtime" "runtime"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
"github.com/go-task/task/v3/internal/filepathext"
"github.com/go-task/task/v3/taskfile" "github.com/go-task/task/v3/taskfile"
) )
@@ -15,7 +15,7 @@ import (
func Taskvars(dir string) (*taskfile.Vars, error) { func Taskvars(dir string) (*taskfile.Vars, error) {
vars := &taskfile.Vars{} vars := &taskfile.Vars{}
path := filepath.Join(dir, "Taskvars.yml") path := filepathext.SmartJoin(dir, "Taskvars.yml")
if _, err := os.Stat(path); err == nil { if _, err := os.Stat(path); err == nil {
vars, err = readTaskvars(path) vars, err = readTaskvars(path)
if err != nil { if err != nil {
@@ -23,7 +23,7 @@ func Taskvars(dir string) (*taskfile.Vars, error) {
} }
} }
path = filepath.Join(dir, fmt.Sprintf("Taskvars_%s.yml", runtime.GOOS)) path = filepathext.SmartJoin(dir, fmt.Sprintf("Taskvars_%s.yml", runtime.GOOS))
if _, err := os.Stat(path); err == nil { if _, err := os.Stat(path); err == nil {
osVars, err := readTaskvars(path) osVars, err := readTaskvars(path)
if err != nil { if err != nil {

View File

@@ -26,6 +26,7 @@ type Task struct {
Run string Run string
IncludeVars *Vars IncludeVars *Vars
IncludedTaskfileVars *Vars IncludedTaskfileVars *Vars
IncludedTaskfile *IncludedTaskfile
} }
func (t *Task) Name() string { func (t *Task) Name() string {

View File

@@ -0,0 +1,17 @@
version: '3'
tasks:
default:
sources:
- src/**/*
cmds:
- echo "some job"
test-sym:
cmds:
- echo "shared file source changed" > src/shared/b
reset:
cmds:
- echo "shared file source" > src/shared/b
- echo "file source" > src/a

View File

@@ -0,0 +1 @@
shared file source

View File

@@ -0,0 +1 @@
inner shared file source

View File

@@ -0,0 +1 @@
file source

View File

@@ -0,0 +1 @@
../shared

18
testdata/special_vars/Taskfile.yml vendored Normal file
View File

@@ -0,0 +1,18 @@
version: '3'
includes:
included:
taskfile: ./included
dir: ./included
tasks:
default:
cmds:
- task: print
- task: included:print
print:
cmds:
- echo root/TASK={{.TASK}}
- echo root/ROOT_DIR={{.ROOT_DIR}}
- echo root/TASKFILE_DIR={{.TASKFILE_DIR}}

View File

@@ -0,0 +1,8 @@
version: '3'
tasks:
print:
cmds:
- echo included/TASK={{.TASK}}
- echo included/ROOT_DIR={{.ROOT_DIR}}
- echo included/TASKFILE_DIR={{.TASKFILE_DIR}}

View File

@@ -1,10 +1,10 @@
package task package task
import ( import (
"path/filepath"
"strings" "strings"
"github.com/go-task/task/v3/internal/execext" "github.com/go-task/task/v3/internal/execext"
"github.com/go-task/task/v3/internal/filepathext"
"github.com/go-task/task/v3/internal/status" "github.com/go-task/task/v3/internal/status"
"github.com/go-task/task/v3/internal/templater" "github.com/go-task/task/v3/internal/templater"
"github.com/go-task/task/v3/taskfile" "github.com/go-task/task/v3/taskfile"
@@ -68,8 +68,8 @@ func (e *Executor) compiledTask(call taskfile.Call, evaluateShVars bool) (*taskf
if err != nil { if err != nil {
return nil, err return nil, err
} }
if e.Dir != "" && !filepath.IsAbs(new.Dir) { if e.Dir != "" {
new.Dir = filepath.Join(e.Dir, new.Dir) new.Dir = filepathext.SmartJoin(e.Dir, new.Dir)
} }
if new.Prefix == "" { if new.Prefix == "" {
new.Prefix = new.Task new.Prefix = new.Task