mirror of
https://github.com/go-task/task.git
synced 2026-07-02 17:08:45 +00:00
Compare commits
101 Commits
v3.0.0-pre
...
v3.0.0-pre
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ee7f2a541f | ||
|
|
59a00eae98 | ||
|
|
df8293bee6 | ||
|
|
935216f179 | ||
|
|
f56bbd46fd | ||
|
|
9f0f18c5c4 | ||
|
|
191c34c9c4 | ||
|
|
6a604b3002 | ||
|
|
5a435b533e | ||
|
|
4b027722b1 | ||
|
|
68ce8642b1 | ||
|
|
4913b6a0f1 | ||
|
|
aee0ab05f4 | ||
|
|
b44432f24a | ||
|
|
86be13ff1f | ||
|
|
ee95df0e57 | ||
|
|
442c29f020 | ||
|
|
739037fc37 | ||
|
|
f8252020aa | ||
|
|
38b87439fe | ||
|
|
116879f7ea | ||
|
|
2eafb2f067 | ||
|
|
c36f0f6f7f | ||
|
|
bfc033959b | ||
|
|
814f350b6d | ||
|
|
05db8ce582 | ||
|
|
3fd36a0c72 | ||
|
|
cbb12b29bd | ||
|
|
6ed30f1add | ||
|
|
a044c41c66 | ||
|
|
fb78e53a14 | ||
|
|
acfbbaa549 | ||
|
|
d52d74c64c | ||
|
|
d36f73de01 | ||
|
|
628c4a7b5f | ||
|
|
ca07a663e1 | ||
|
|
66d008391e | ||
|
|
eef84bda26 | ||
|
|
e0defe71aa | ||
|
|
069257151e | ||
|
|
3f80a3b39e | ||
|
|
b2a56161bb | ||
|
|
5e75639244 | ||
|
|
cb2cd3e10f | ||
|
|
0acb911d6a | ||
|
|
17ad7060b3 | ||
|
|
f38ba7fcd3 | ||
|
|
a3464068bd | ||
|
|
347ecc028f | ||
|
|
94ac60fa09 | ||
|
|
d567e23e50 | ||
|
|
8ff81562d2 | ||
|
|
7a8142ed92 | ||
|
|
eaba1b9cc8 | ||
|
|
b08b3546d2 | ||
|
|
7453e688fd | ||
|
|
32b097b3f2 | ||
|
|
22394def78 | ||
|
|
68ecb7fbdd | ||
|
|
de98a53b43 | ||
|
|
1c9fbf92c6 | ||
|
|
c068b05232 | ||
|
|
15338ecb18 | ||
|
|
01e9a8f720 | ||
|
|
4bdfe64afb | ||
|
|
b7b752b92f | ||
|
|
b7bcd204b4 | ||
|
|
ec934ba3c0 | ||
|
|
7373639f57 | ||
|
|
d718527a1f | ||
|
|
48add0f293 | ||
|
|
a4685229c9 | ||
|
|
f0bc4d26a0 | ||
|
|
1d3b93d88d | ||
|
|
62752ba7e1 | ||
|
|
6a4f420187 | ||
|
|
6640632683 | ||
|
|
09d5d802d0 | ||
|
|
fea23ed6d4 | ||
|
|
10a6c4dc7a | ||
|
|
4cdaa72224 | ||
|
|
27bc1ca5d1 | ||
|
|
1ea49188c9 | ||
|
|
3084ef129c | ||
|
|
c0d112f858 | ||
|
|
2265dda84c | ||
|
|
263b094cab | ||
|
|
fbd13614a5 | ||
|
|
9eab74b595 | ||
|
|
5acdb041a9 | ||
|
|
0494d7ebe3 | ||
|
|
9a8442c946 | ||
|
|
e1dcd0b441 | ||
|
|
a152db7054 | ||
|
|
b9e092674e | ||
|
|
4162b5f41d | ||
|
|
67ae6f210f | ||
|
|
f6c5a46626 | ||
|
|
d6f7e01c53 | ||
|
|
46463e4e24 | ||
|
|
393712ead2 |
@@ -7,7 +7,6 @@ insert_final_newline = true
|
|||||||
charset = utf-8
|
charset = utf-8
|
||||||
trim_trailing_whitespace = true
|
trim_trailing_whitespace = true
|
||||||
indent_style = tab
|
indent_style = tab
|
||||||
indent_size = 8
|
|
||||||
|
|
||||||
[*.{md,yml,yaml,json,toml,htm,html}]
|
[*.{md,yml,yaml,json,toml,htm,html}]
|
||||||
indent_style = space
|
indent_style = space
|
||||||
|
|||||||
1
.github/FUNDING.yml
vendored
1
.github/FUNDING.yml
vendored
@@ -1 +1,2 @@
|
|||||||
open_collective: task
|
open_collective: task
|
||||||
|
patreon: andreynering
|
||||||
|
|||||||
26
.github/workflows/release.yml
vendored
Normal file
26
.github/workflows/release.yml
vendored
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
name: goreleaser
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- '*'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
goreleaser:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v1
|
||||||
|
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v1
|
||||||
|
with:
|
||||||
|
go-version: 1.14.x
|
||||||
|
|
||||||
|
- name: Run GoReleaser
|
||||||
|
uses: goreleaser/goreleaser-action@v1
|
||||||
|
with:
|
||||||
|
version: latest
|
||||||
|
args: release --rm-dist
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
||||||
61
.github/workflows/test.yml
vendored
61
.github/workflows/test.yml
vendored
@@ -1,59 +1,30 @@
|
|||||||
name: Test
|
name: Test
|
||||||
on: [push]
|
on: [push, pull_request]
|
||||||
jobs:
|
jobs:
|
||||||
linux:
|
test:
|
||||||
name: Linux
|
name: Test
|
||||||
runs-on: ubuntu-latest
|
strategy:
|
||||||
|
matrix:
|
||||||
|
go-version: [1.13.x, 1.14.x]
|
||||||
|
platform: [ubuntu-latest, macos-latest, windows-latest]
|
||||||
|
runs-on: ${{matrix.platform}}
|
||||||
steps:
|
steps:
|
||||||
- name: Set up Go 1.13
|
- name: Set up Go ${{matrix.go-version}}
|
||||||
uses: actions/setup-go@v1
|
uses: actions/setup-go@v1
|
||||||
with:
|
with:
|
||||||
go-version: 1.13
|
go-version: ${{matrix.go-version}}
|
||||||
id: go
|
id: go
|
||||||
|
|
||||||
- name: Check out code into the Go module directory
|
- name: Check out code into the Go module directory
|
||||||
uses: actions/checkout@v1
|
uses: actions/checkout@v1
|
||||||
|
|
||||||
- name: Build
|
- name: Download Go modules
|
||||||
run: go build -o ./task -v ./cmd/task
|
run: go mod download
|
||||||
|
env:
|
||||||
- name: Test
|
GOPROXY: https://proxy.golang.org
|
||||||
run: ./task test
|
|
||||||
|
|
||||||
windows:
|
|
||||||
name: Windows
|
|
||||||
runs-on: windows-latest
|
|
||||||
steps:
|
|
||||||
- name: Set up Go 1.13
|
|
||||||
uses: actions/setup-go@v1
|
|
||||||
with:
|
|
||||||
go-version: 1.13
|
|
||||||
id: go
|
|
||||||
|
|
||||||
- name: Check out code into the Go module directory
|
|
||||||
uses: actions/checkout@v1
|
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: go install -v ./cmd/task
|
run: go build -o ./bin/task -v ./cmd/task
|
||||||
|
|
||||||
- name: Test
|
- name: Test
|
||||||
run: go test -v ./...
|
run: ./bin/task test
|
||||||
|
|
||||||
macos:
|
|
||||||
name: MacOS
|
|
||||||
runs-on: macOS-latest
|
|
||||||
steps:
|
|
||||||
- name: Set up Go 1.13
|
|
||||||
uses: actions/setup-go@v1
|
|
||||||
with:
|
|
||||||
go-version: 1.13
|
|
||||||
id: go
|
|
||||||
|
|
||||||
- name: Check out code into the Go module directory
|
|
||||||
uses: actions/checkout@v1
|
|
||||||
|
|
||||||
- name: Build
|
|
||||||
run: go build -o ./task -v ./cmd/task
|
|
||||||
|
|
||||||
- name: Test
|
|
||||||
run: ./task test
|
|
||||||
|
|||||||
@@ -40,4 +40,4 @@ nfpms:
|
|||||||
formats:
|
formats:
|
||||||
- deb
|
- deb
|
||||||
- rpm
|
- rpm
|
||||||
name_template: "{{.ProjectName}}_{{.Os}}_{{.Arch}}"
|
file_name_template: "{{.ProjectName}}_{{.Os}}_{{.Arch}}"
|
||||||
|
|||||||
22
.travis.yml
22
.travis.yml
@@ -1,22 +0,0 @@
|
|||||||
language: go
|
|
||||||
|
|
||||||
go:
|
|
||||||
- 1.12.x
|
|
||||||
- 1.13.x
|
|
||||||
|
|
||||||
addons:
|
|
||||||
apt:
|
|
||||||
packages:
|
|
||||||
- rpm
|
|
||||||
|
|
||||||
script:
|
|
||||||
- go install -v ./cmd/task
|
|
||||||
- task ci
|
|
||||||
|
|
||||||
deploy:
|
|
||||||
- provider: script
|
|
||||||
skip_cleanup: true
|
|
||||||
script: curl -sL http://git.io/goreleaser | bash
|
|
||||||
on:
|
|
||||||
tags: true
|
|
||||||
condition: $TRAVIS_OS_NAME = linux
|
|
||||||
45
CHANGELOG.md
45
CHANGELOG.md
@@ -1,5 +1,24 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
# v3.0.0 - Preview 4
|
||||||
|
|
||||||
|
- Refactor how variables work on version 3
|
||||||
|
([#311](https://github.com/go-task/task/pull/311)).
|
||||||
|
- Disallow `expansions` on v3 since it has no effect.
|
||||||
|
- `Taskvars.yml` is not automatically included anymore.
|
||||||
|
- `Taskfile_{{OS}}.yml` is not automatically included anymore.
|
||||||
|
- Allow interpolation on `includes`, so you can manually include a Taskfile
|
||||||
|
based on operation system, for example.
|
||||||
|
|
||||||
|
# v3.0.0 - Preview 3
|
||||||
|
|
||||||
|
- Expose `.TASK` variable in templates with the task name
|
||||||
|
([#252](https://github.com/go-task/task/issues/252)).
|
||||||
|
- Implement short task syntax
|
||||||
|
([#194](https://github.com/go-task/task/issues/194), [#240](https://github.com/go-task/task/pull/240)).
|
||||||
|
- Added option to make included Taskfile run commands on its own directory
|
||||||
|
([#260](https://github.com/go-task/task/issues/260), [#144](https://github.com/go-task/task/issues/144))
|
||||||
|
|
||||||
# v3.0.0 - Preview 2
|
# v3.0.0 - Preview 2
|
||||||
|
|
||||||
- Taskfiles in version 1 are not supported anymore
|
- Taskfiles in version 1 are not supported anymore
|
||||||
@@ -27,6 +46,32 @@
|
|||||||
commands are green, errors are red, etc
|
commands are green, errors are red, etc
|
||||||
([#207](https://github.com/go-task/task/pull/207)).
|
([#207](https://github.com/go-task/task/pull/207)).
|
||||||
|
|
||||||
|
## v2.8.1 - 2019-05-20
|
||||||
|
|
||||||
|
- Fix error code for the `--help` flag
|
||||||
|
([#300](https://github.com/go-task/task/issues/300), [#330](https://github.com/go-task/task/pull/330)).
|
||||||
|
- Print version to stdout instead of stderr
|
||||||
|
([#299](https://github.com/go-task/task/issues/299), [#329](https://github.com/go-task/task/pull/329)).
|
||||||
|
- Supress `context` errors when using the `--watch` flag
|
||||||
|
([#313](https://github.com/go-task/task/issues/313), [#317](https://github.com/go-task/task/pull/317)).
|
||||||
|
- Support templating on description
|
||||||
|
([#276](https://github.com/go-task/task/issues/276), [#283](https://github.com/go-task/task/pull/283)).
|
||||||
|
|
||||||
|
## v2.8.0 - 2019-12-07
|
||||||
|
|
||||||
|
- Add `--parallel` flag (alias `-p`) to run tasks given by the command line in
|
||||||
|
parallel
|
||||||
|
([#266](https://github.com/go-task/task/pull/266)).
|
||||||
|
- Fixed bug where calling the `task` CLI only informing global vars would not
|
||||||
|
execute the `default` task.
|
||||||
|
- Add hability to silent all tasks by adding `silent: true` a the root of the
|
||||||
|
Taskfile.
|
||||||
|
|
||||||
|
## v2.7.1 - 2019-11-10
|
||||||
|
|
||||||
|
- Fix error being raised when `exit 0` was called
|
||||||
|
([#251](https://github.com/go-task/task/issues/251)).
|
||||||
|
|
||||||
## v2.7.0 - 2019-09-22
|
## v2.7.0 - 2019-09-22
|
||||||
|
|
||||||
- Fixed panic bug when assigning a global variable
|
- Fixed panic bug when assigning a global variable
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
[](https://travis-ci.org/go-task/task)
|

|
||||||
|

|
||||||
|
|
||||||
# Task
|
# Task
|
||||||
|
|
||||||
|
|||||||
17
Taskfile.yml
17
Taskfile.yml
@@ -1,7 +1,9 @@
|
|||||||
version: '3'
|
version: '3'
|
||||||
|
|
||||||
includes:
|
includes:
|
||||||
docs: ./docs
|
docs:
|
||||||
|
taskfile: ./docs
|
||||||
|
dir: ./docs
|
||||||
|
|
||||||
vars:
|
vars:
|
||||||
GIT_COMMIT:
|
GIT_COMMIT:
|
||||||
@@ -72,15 +74,12 @@ tasks:
|
|||||||
- cp ./install-task.sh ./docs/install.sh
|
- cp ./install-task.sh ./docs/install.sh
|
||||||
|
|
||||||
ci:
|
ci:
|
||||||
cmds:
|
- task: go-get
|
||||||
- task: go-get
|
vars: {REPO: golang.org/x/lint/golint}
|
||||||
vars: {REPO: golang.org/x/lint/golint}
|
- task: lint
|
||||||
- task: lint
|
- task: test
|
||||||
- task: test
|
|
||||||
|
|
||||||
go-get:
|
go-get: go get -u {{.REPO}}
|
||||||
cmds:
|
|
||||||
- go get -u {{.REPO}}
|
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
cmds:
|
cmds:
|
||||||
|
|||||||
@@ -1,421 +0,0 @@
|
|||||||
// This small web app is used to redirect from the old taskfile.org domain
|
|
||||||
// to the new taskfile.dev without breaking CIs that uses cURL to download
|
|
||||||
// "/install.sh" without the -L flag (which follow redirects).
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
if r.URL.Path == "/install.sh" {
|
|
||||||
println("Dumping install.sh")
|
|
||||||
|
|
||||||
w.Write(installShContent)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
println("Redirecting to https://taskfile.dev" + r.URL.Path)
|
|
||||||
|
|
||||||
w.Header().Set("Location", "https://taskfile.dev"+r.URL.Path)
|
|
||||||
w.WriteHeader(301)
|
|
||||||
})
|
|
||||||
|
|
||||||
println("Listening :8080")
|
|
||||||
|
|
||||||
panic(http.ListenAndServe(":8080", nil))
|
|
||||||
}
|
|
||||||
|
|
||||||
var installShContent = []byte(`#!/bin/sh
|
|
||||||
set -e
|
|
||||||
# Code generated by godownloader on 2018-04-07T17:47:38Z. DO NOT EDIT.
|
|
||||||
#
|
|
||||||
|
|
||||||
usage() {
|
|
||||||
this=$1
|
|
||||||
cat <<EOF
|
|
||||||
$this: download go binaries for go-task/task
|
|
||||||
|
|
||||||
Usage: $this [-b] bindir [-d] [tag]
|
|
||||||
-b sets bindir or installation directory, Defaults to ./bin
|
|
||||||
-d turns on debug logging
|
|
||||||
[tag] is a tag from
|
|
||||||
https://github.com/go-task/task/releases
|
|
||||||
If tag is missing, then the latest will be used.
|
|
||||||
|
|
||||||
Generated by godownloader
|
|
||||||
https://github.com/goreleaser/godownloader
|
|
||||||
|
|
||||||
EOF
|
|
||||||
exit 2
|
|
||||||
}
|
|
||||||
|
|
||||||
parse_args() {
|
|
||||||
#BINDIR is ./bin unless set be ENV
|
|
||||||
# over-ridden by flag below
|
|
||||||
|
|
||||||
BINDIR=${BINDIR:-./bin}
|
|
||||||
while getopts "b:dh?" arg; do
|
|
||||||
case "$arg" in
|
|
||||||
b) BINDIR="$OPTARG" ;;
|
|
||||||
d) log_set_priority 10 ;;
|
|
||||||
h | \?) usage "$0" ;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
shift $((OPTIND - 1))
|
|
||||||
TAG=$1
|
|
||||||
}
|
|
||||||
# this function wraps all the destructive operations
|
|
||||||
# if a curl|bash cuts off the end of the script due to
|
|
||||||
# network, either nothing will happen or will syntax error
|
|
||||||
# out preventing half-done work
|
|
||||||
execute() {
|
|
||||||
tmpdir=$(mktmpdir)
|
|
||||||
log_debug "downloading files into ${tmpdir}"
|
|
||||||
http_download "${tmpdir}/${TARBALL}" "${TARBALL_URL}"
|
|
||||||
http_download "${tmpdir}/${CHECKSUM}" "${CHECKSUM_URL}"
|
|
||||||
hash_sha256_verify "${tmpdir}/${TARBALL}" "${tmpdir}/${CHECKSUM}"
|
|
||||||
srcdir="${tmpdir}"
|
|
||||||
(cd "${tmpdir}" && untar "${TARBALL}")
|
|
||||||
install -d "${BINDIR}"
|
|
||||||
for binexe in "task" ; do
|
|
||||||
if [ "$OS" = "windows" ]; then
|
|
||||||
binexe="${binexe}.exe"
|
|
||||||
fi
|
|
||||||
install "${srcdir}/${binexe}" "${BINDIR}/"
|
|
||||||
log_info "installed ${BINDIR}/${binexe}"
|
|
||||||
done
|
|
||||||
}
|
|
||||||
is_supported_platform() {
|
|
||||||
platform=$1
|
|
||||||
found=1
|
|
||||||
case "$platform" in
|
|
||||||
windows/386) found=0 ;;
|
|
||||||
windows/amd64) found=0 ;;
|
|
||||||
darwin/386) found=0 ;;
|
|
||||||
darwin/amd64) found=0 ;;
|
|
||||||
linux/386) found=0 ;;
|
|
||||||
linux/amd64) found=0 ;;
|
|
||||||
esac
|
|
||||||
case "$platform" in
|
|
||||||
darwin/386) found=1 ;;
|
|
||||||
esac
|
|
||||||
return $found
|
|
||||||
}
|
|
||||||
check_platform() {
|
|
||||||
if is_supported_platform "$PLATFORM"; then
|
|
||||||
# optional logging goes here
|
|
||||||
true
|
|
||||||
else
|
|
||||||
log_crit "platform $PLATFORM is not supported. Make sure this script is up-to-date and file request at https://github.com/${PREFIX}/issues/new"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
tag_to_version() {
|
|
||||||
if [ -z "${TAG}" ]; then
|
|
||||||
log_info "checking GitHub for latest tag"
|
|
||||||
else
|
|
||||||
log_info "checking GitHub for tag '${TAG}'"
|
|
||||||
fi
|
|
||||||
REALTAG=$(github_release "$OWNER/$REPO" "${TAG}") && true
|
|
||||||
if test -z "$REALTAG"; then
|
|
||||||
log_crit "unable to find '${TAG}' - use 'latest' or see https://github.com/${PREFIX}/releases for details"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
# if version starts with 'v', remove it
|
|
||||||
TAG="$REALTAG"
|
|
||||||
VERSION=${TAG#v}
|
|
||||||
}
|
|
||||||
adjust_format() {
|
|
||||||
# change format (tar.gz or zip) based on ARCH
|
|
||||||
case ${ARCH} in
|
|
||||||
windows) FORMAT=zip ;;
|
|
||||||
esac
|
|
||||||
true
|
|
||||||
}
|
|
||||||
adjust_os() {
|
|
||||||
# adjust archive name based on OS
|
|
||||||
true
|
|
||||||
}
|
|
||||||
adjust_arch() {
|
|
||||||
# adjust archive name based on ARCH
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
cat /dev/null <<EOF
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
https://github.com/client9/shlib - portable posix shell functions
|
|
||||||
Public domain - http://unlicense.org
|
|
||||||
https://github.com/client9/shlib/blob/master/LICENSE.md
|
|
||||||
but credit (and pull requests) appreciated.
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
EOF
|
|
||||||
is_command() {
|
|
||||||
command -v "$1" >/dev/null
|
|
||||||
}
|
|
||||||
echoerr() {
|
|
||||||
echo "$@" 1>&2
|
|
||||||
}
|
|
||||||
log_prefix() {
|
|
||||||
echo "$0"
|
|
||||||
}
|
|
||||||
_logp=6
|
|
||||||
log_set_priority() {
|
|
||||||
_logp="$1"
|
|
||||||
}
|
|
||||||
log_priority() {
|
|
||||||
if test -z "$1"; then
|
|
||||||
echo "$_logp"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
[ "$1" -le "$_logp" ]
|
|
||||||
}
|
|
||||||
log_tag() {
|
|
||||||
case $1 in
|
|
||||||
0) echo "emerg" ;;
|
|
||||||
1) echo "alert" ;;
|
|
||||||
2) echo "crit" ;;
|
|
||||||
3) echo "err" ;;
|
|
||||||
4) echo "warning" ;;
|
|
||||||
5) echo "notice" ;;
|
|
||||||
6) echo "info" ;;
|
|
||||||
7) echo "debug" ;;
|
|
||||||
*) echo "$1" ;;
|
|
||||||
esac
|
|
||||||
}
|
|
||||||
log_debug() {
|
|
||||||
log_priority 7 || return 0
|
|
||||||
echoerr "$(log_prefix)" "$(log_tag 7)" "$@"
|
|
||||||
}
|
|
||||||
log_info() {
|
|
||||||
log_priority 6 || return 0
|
|
||||||
echoerr "$(log_prefix)" "$(log_tag 6)" "$@"
|
|
||||||
}
|
|
||||||
log_err() {
|
|
||||||
log_priority 3 || return 0
|
|
||||||
echoerr "$(log_prefix)" "$(log_tag 3)" "$@"
|
|
||||||
}
|
|
||||||
log_crit() {
|
|
||||||
log_priority 2 || return 0
|
|
||||||
echoerr "$(log_prefix)" "$(log_tag 2)" "$@"
|
|
||||||
}
|
|
||||||
uname_os() {
|
|
||||||
os=$(uname -s | tr '[:upper:]' '[:lower:]')
|
|
||||||
case "$os" in
|
|
||||||
msys_nt) os="windows" ;;
|
|
||||||
esac
|
|
||||||
echo "$os"
|
|
||||||
}
|
|
||||||
uname_arch() {
|
|
||||||
arch=$(uname -m)
|
|
||||||
case $arch in
|
|
||||||
x86_64) arch="amd64" ;;
|
|
||||||
x86) arch="386" ;;
|
|
||||||
i686) arch="386" ;;
|
|
||||||
i386) arch="386" ;;
|
|
||||||
aarch64) arch="arm64" ;;
|
|
||||||
armv5*) arch="arm5" ;;
|
|
||||||
armv6*) arch="arm6" ;;
|
|
||||||
armv7*) arch="arm7" ;;
|
|
||||||
esac
|
|
||||||
echo ${arch}
|
|
||||||
}
|
|
||||||
uname_os_check() {
|
|
||||||
os=$(uname_os)
|
|
||||||
case "$os" in
|
|
||||||
darwin) return 0 ;;
|
|
||||||
dragonfly) return 0 ;;
|
|
||||||
freebsd) return 0 ;;
|
|
||||||
linux) return 0 ;;
|
|
||||||
android) return 0 ;;
|
|
||||||
nacl) return 0 ;;
|
|
||||||
netbsd) return 0 ;;
|
|
||||||
openbsd) return 0 ;;
|
|
||||||
plan9) return 0 ;;
|
|
||||||
solaris) return 0 ;;
|
|
||||||
windows) return 0 ;;
|
|
||||||
esac
|
|
||||||
log_crit "uname_os_check '$(uname -s)' got converted to '$os' which is not a GOOS value. Please file bug at https://github.com/client9/shlib"
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
uname_arch_check() {
|
|
||||||
arch=$(uname_arch)
|
|
||||||
case "$arch" in
|
|
||||||
386) return 0 ;;
|
|
||||||
amd64) return 0 ;;
|
|
||||||
arm64) return 0 ;;
|
|
||||||
armv5) return 0 ;;
|
|
||||||
armv6) return 0 ;;
|
|
||||||
armv7) return 0 ;;
|
|
||||||
ppc64) return 0 ;;
|
|
||||||
ppc64le) return 0 ;;
|
|
||||||
mips) return 0 ;;
|
|
||||||
mipsle) return 0 ;;
|
|
||||||
mips64) return 0 ;;
|
|
||||||
mips64le) return 0 ;;
|
|
||||||
s390x) return 0 ;;
|
|
||||||
amd64p32) return 0 ;;
|
|
||||||
esac
|
|
||||||
log_crit "uname_arch_check '$(uname -m)' got converted to '$arch' which is not a GOARCH value. Please file bug report at https://github.com/client9/shlib"
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
untar() {
|
|
||||||
tarball=$1
|
|
||||||
case "${tarball}" in
|
|
||||||
*.tar.gz | *.tgz) tar -xzf "${tarball}" ;;
|
|
||||||
*.tar) tar -xf "${tarball}" ;;
|
|
||||||
*.zip) unzip "${tarball}" ;;
|
|
||||||
*)
|
|
||||||
log_err "untar unknown archive format for ${tarball}"
|
|
||||||
return 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
}
|
|
||||||
mktmpdir() {
|
|
||||||
test -z "$TMPDIR" && TMPDIR="$(mktemp -d)"
|
|
||||||
mkdir -p "${TMPDIR}"
|
|
||||||
echo "${TMPDIR}"
|
|
||||||
}
|
|
||||||
http_download_curl() {
|
|
||||||
local_file=$1
|
|
||||||
source_url=$2
|
|
||||||
header=$3
|
|
||||||
if [ -z "$header" ]; then
|
|
||||||
code=$(curl -w '%{http_code}' -sL -o "$local_file" "$source_url")
|
|
||||||
else
|
|
||||||
code=$(curl -w '%{http_code}' -sL -H "$header" -o "$local_file" "$source_url")
|
|
||||||
fi
|
|
||||||
if [ "$code" != "200" ]; then
|
|
||||||
log_debug "http_download_curl received HTTP status $code"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
http_download_wget() {
|
|
||||||
local_file=$1
|
|
||||||
source_url=$2
|
|
||||||
header=$3
|
|
||||||
if [ -z "$header" ]; then
|
|
||||||
wget -q -O "$local_file" "$source_url"
|
|
||||||
else
|
|
||||||
wget -q --header "$header" -O "$local_file" "$source_url"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
http_download() {
|
|
||||||
log_debug "http_download $2"
|
|
||||||
if is_command curl; then
|
|
||||||
http_download_curl "$@"
|
|
||||||
return
|
|
||||||
elif is_command wget; then
|
|
||||||
http_download_wget "$@"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
log_crit "http_download unable to find wget or curl"
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
http_copy() {
|
|
||||||
tmp=$(mktemp)
|
|
||||||
http_download "${tmp}" "$1" "$2" || return 1
|
|
||||||
body=$(cat "$tmp")
|
|
||||||
rm -f "${tmp}"
|
|
||||||
echo "$body"
|
|
||||||
}
|
|
||||||
github_release() {
|
|
||||||
owner_repo=$1
|
|
||||||
version=$2
|
|
||||||
test -z "$version" && version="latest"
|
|
||||||
giturl="https://github.com/${owner_repo}/releases/${version}"
|
|
||||||
json=$(http_copy "$giturl" "Accept:application/json")
|
|
||||||
test -z "$json" && return 1
|
|
||||||
version=$(echo "$json" | tr -s '\n' ' ' | sed 's/.*"tag_name":"//' | sed 's/".*//')
|
|
||||||
test -z "$version" && return 1
|
|
||||||
echo "$version"
|
|
||||||
}
|
|
||||||
hash_sha256() {
|
|
||||||
TARGET=${1:-/dev/stdin}
|
|
||||||
if is_command gsha256sum; then
|
|
||||||
hash=$(gsha256sum "$TARGET") || return 1
|
|
||||||
echo "$hash" | cut -d ' ' -f 1
|
|
||||||
elif is_command sha256sum; then
|
|
||||||
hash=$(sha256sum "$TARGET") || return 1
|
|
||||||
echo "$hash" | cut -d ' ' -f 1
|
|
||||||
elif is_command shasum; then
|
|
||||||
hash=$(shasum -a 256 "$TARGET" 2>/dev/null) || return 1
|
|
||||||
echo "$hash" | cut -d ' ' -f 1
|
|
||||||
elif is_command openssl; then
|
|
||||||
hash=$(openssl -dst openssl dgst -sha256 "$TARGET") || return 1
|
|
||||||
echo "$hash" | cut -d ' ' -f a
|
|
||||||
else
|
|
||||||
log_crit "hash_sha256 unable to find command to compute sha-256 hash"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
hash_sha256_verify() {
|
|
||||||
TARGET=$1
|
|
||||||
checksums=$2
|
|
||||||
if [ -z "$checksums" ]; then
|
|
||||||
log_err "hash_sha256_verify checksum file not specified in arg2"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
BASENAME=${TARGET##*/}
|
|
||||||
want=$(grep "${BASENAME}" "${checksums}" 2>/dev/null | tr '\t' ' ' | cut -d ' ' -f 1)
|
|
||||||
if [ -z "$want" ]; then
|
|
||||||
log_err "hash_sha256_verify unable to find checksum for '${TARGET}' in '${checksums}'"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
got=$(hash_sha256 "$TARGET")
|
|
||||||
if [ "$want" != "$got" ]; then
|
|
||||||
log_err "hash_sha256_verify checksum for '$TARGET' did not verify ${want} vs $got"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
cat /dev/null <<EOF
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
End of functions from https://github.com/client9/shlib
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
EOF
|
|
||||||
|
|
||||||
PROJECT_NAME="task"
|
|
||||||
OWNER=go-task
|
|
||||||
REPO="task"
|
|
||||||
BINARY=task
|
|
||||||
FORMAT=tar.gz
|
|
||||||
OS=$(uname_os)
|
|
||||||
ARCH=$(uname_arch)
|
|
||||||
PREFIX="$OWNER/$REPO"
|
|
||||||
|
|
||||||
# use in logging routines
|
|
||||||
log_prefix() {
|
|
||||||
echo "$PREFIX"
|
|
||||||
}
|
|
||||||
PLATFORM="${OS}/${ARCH}"
|
|
||||||
GITHUB_DOWNLOAD=https://github.com/${OWNER}/${REPO}/releases/download
|
|
||||||
|
|
||||||
uname_os_check "$OS"
|
|
||||||
uname_arch_check "$ARCH"
|
|
||||||
|
|
||||||
parse_args "$@"
|
|
||||||
|
|
||||||
check_platform
|
|
||||||
|
|
||||||
tag_to_version
|
|
||||||
|
|
||||||
adjust_format
|
|
||||||
|
|
||||||
adjust_os
|
|
||||||
|
|
||||||
adjust_arch
|
|
||||||
|
|
||||||
log_info "found version: ${VERSION} for ${TAG}/${OS}/${ARCH}"
|
|
||||||
|
|
||||||
NAME=${BINARY}_${OS}_${ARCH}
|
|
||||||
TARBALL=${NAME}.${FORMAT}
|
|
||||||
TARBALL_URL=${GITHUB_DOWNLOAD}/${TAG}/${TARBALL}
|
|
||||||
CHECKSUM=task_checksums.txt
|
|
||||||
CHECKSUM_URL=${GITHUB_DOWNLOAD}/${TAG}/${CHECKSUM}
|
|
||||||
|
|
||||||
|
|
||||||
execute
|
|
||||||
`)
|
|
||||||
@@ -2,6 +2,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
@@ -52,6 +53,7 @@ func main() {
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
versionFlag bool
|
versionFlag bool
|
||||||
|
helpFlag bool
|
||||||
init bool
|
init bool
|
||||||
list bool
|
list bool
|
||||||
status bool
|
status bool
|
||||||
@@ -61,6 +63,7 @@ func main() {
|
|||||||
silent bool
|
silent bool
|
||||||
dry bool
|
dry bool
|
||||||
summary bool
|
summary bool
|
||||||
|
parallel bool
|
||||||
dir string
|
dir string
|
||||||
entrypoint string
|
entrypoint string
|
||||||
output string
|
output string
|
||||||
@@ -68,6 +71,7 @@ func main() {
|
|||||||
)
|
)
|
||||||
|
|
||||||
pflag.BoolVar(&versionFlag, "version", false, "show Task version")
|
pflag.BoolVar(&versionFlag, "version", false, "show Task version")
|
||||||
|
pflag.BoolVarP(&helpFlag, "help", "h", false, "shows Task usage")
|
||||||
pflag.BoolVarP(&init, "init", "i", false, "creates a new Taskfile.yml in the current folder")
|
pflag.BoolVarP(&init, "init", "i", false, "creates a new Taskfile.yml in the current folder")
|
||||||
pflag.BoolVarP(&list, "list", "l", false, "lists tasks with description of current Taskfile")
|
pflag.BoolVarP(&list, "list", "l", false, "lists tasks with description of current Taskfile")
|
||||||
pflag.BoolVar(&status, "status", false, "exits with non-zero exit code if any of the given tasks is not up-to-date")
|
pflag.BoolVar(&status, "status", false, "exits with non-zero exit code if any of the given tasks is not up-to-date")
|
||||||
@@ -75,6 +79,7 @@ func main() {
|
|||||||
pflag.BoolVarP(&watch, "watch", "w", false, "enables watch of the given task")
|
pflag.BoolVarP(&watch, "watch", "w", false, "enables watch of the given task")
|
||||||
pflag.BoolVarP(&verbose, "verbose", "v", false, "enables verbose mode")
|
pflag.BoolVarP(&verbose, "verbose", "v", false, "enables verbose mode")
|
||||||
pflag.BoolVarP(&silent, "silent", "s", false, "disables echoing")
|
pflag.BoolVarP(&silent, "silent", "s", false, "disables echoing")
|
||||||
|
pflag.BoolVarP(¶llel, "parallel", "p", false, "executes tasks provided on command line in parallel")
|
||||||
pflag.BoolVar(&dry, "dry", false, "compiles and prints tasks in the order that they would be run, without executing them")
|
pflag.BoolVar(&dry, "dry", false, "compiles and prints tasks in the order that they would be run, without executing them")
|
||||||
pflag.BoolVar(&summary, "summary", false, "show summary about a task")
|
pflag.BoolVar(&summary, "summary", false, "show summary about a task")
|
||||||
pflag.StringVarP(&dir, "dir", "d", "", "sets directory of execution")
|
pflag.StringVarP(&dir, "dir", "d", "", "sets directory of execution")
|
||||||
@@ -84,7 +89,12 @@ func main() {
|
|||||||
pflag.Parse()
|
pflag.Parse()
|
||||||
|
|
||||||
if versionFlag {
|
if versionFlag {
|
||||||
log.Printf("Task version: %s\n", version)
|
fmt.Printf("Task version: %s\n", version)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if helpFlag {
|
||||||
|
pflag.Usage()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,6 +129,7 @@ func main() {
|
|||||||
Dry: dry,
|
Dry: dry,
|
||||||
Entrypoint: entrypoint,
|
Entrypoint: entrypoint,
|
||||||
Summary: summary,
|
Summary: summary,
|
||||||
|
Parallel: parallel,
|
||||||
Color: color,
|
Color: color,
|
||||||
|
|
||||||
Stdin: os.Stdin,
|
Stdin: os.Stdin,
|
||||||
@@ -136,16 +147,8 @@ func main() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
arguments := pflag.Args()
|
calls, globals := args.Parse(pflag.Args()...)
|
||||||
if len(arguments) == 0 {
|
e.Taskfile.Vars.Merge(globals)
|
||||||
e.Logger.Errf(logger.Yellow, "task: No argument given, trying default task")
|
|
||||||
arguments = []string{"default"}
|
|
||||||
}
|
|
||||||
|
|
||||||
calls, globals := args.Parse(arguments...)
|
|
||||||
for name, value := range globals {
|
|
||||||
e.Taskfile.Vars[name] = value
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
if !watch {
|
if !watch {
|
||||||
|
|||||||
21
completion/bash/task.bash
Normal file
21
completion/bash/task.bash
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
_task_completion()
|
||||||
|
{
|
||||||
|
local scripts;
|
||||||
|
local curr_arg;
|
||||||
|
|
||||||
|
# Remove colon from word breaks
|
||||||
|
COMP_WORDBREAKS=${COMP_WORDBREAKS//:}
|
||||||
|
|
||||||
|
scripts=$(task -l | sed '1d' | awk '{ print $2 }' | sed 's/:$//');
|
||||||
|
|
||||||
|
curr_arg="${COMP_WORDS[COMP_CWORD]:-"."}"
|
||||||
|
|
||||||
|
# Do not accept more than 1 argument
|
||||||
|
if [ "${#COMP_WORDS[@]}" != "2" ]; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
COMPREPLY=($(compgen -c | echo "$scripts" | grep $curr_arg));
|
||||||
|
}
|
||||||
|
|
||||||
|
complete -F _task_completion task
|
||||||
10
completion/ps/task.ps1
Normal file
10
completion/ps/task.ps1
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
$scriptBlock = {
|
||||||
|
param($commandName, $wordToComplete, $cursorPosition)
|
||||||
|
$curReg = "task{.exe}? (.*?)$"
|
||||||
|
$startsWith = $wordToComplete | Select-String $curReg -AllMatches | ForEach-Object { $_.Matches.Groups[1].Value }
|
||||||
|
$reg = "\* ($startsWith.+?):"
|
||||||
|
$listOutput = $(task -l)
|
||||||
|
$listOutput | Select-String $reg -AllMatches | ForEach-Object { $_.Matches.Groups[1].Value + " " }
|
||||||
|
}
|
||||||
|
|
||||||
|
Register-ArgumentCompleter -Native -CommandName task -ScriptBlock $scriptBlock
|
||||||
@@ -9,4 +9,4 @@ tasks:
|
|||||||
serve:
|
serve:
|
||||||
desc: Serves the documentation site locally
|
desc: Serves the documentation site locally
|
||||||
cmds:
|
cmds:
|
||||||
- docsify serve docs
|
- docsify serve .
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
- [Installation](installation.md)
|
- [Installation](installation.md)
|
||||||
- [Usage](usage.md)
|
- [Usage](usage.md)
|
||||||
|
- [Styleguide](styleguide.md)
|
||||||
- [Taskfile Versions](taskfile_versions.md)
|
- [Taskfile Versions](taskfile_versions.md)
|
||||||
- [Examples](examples.md)
|
- [Examples](examples.md)
|
||||||
- [Releasing Task](releasing_task.md)
|
- [Releasing Task](releasing_task.md)
|
||||||
- [Alternative Task Runners](alternative_task_runners.md)
|
- [Alternative Task Runners](alternative_task_runners.md)
|
||||||
- [Github](https://github.com/go-task/task)
|
- [GitHub](https://github.com/go-task/task)
|
||||||
|
|||||||
@@ -40,21 +40,29 @@ scoop install task
|
|||||||
This installation method is community owned. After a new release of Task, it
|
This installation method is community owned. After a new release of Task, it
|
||||||
may take some time until it's available on Scoop.
|
may take some time until it's available on Scoop.
|
||||||
|
|
||||||
|
## Arch Linux
|
||||||
|
|
||||||
|
If you're on Arch Linux you can install Task from
|
||||||
|
[AUR](https://aur.archlinux.org/packages/taskfile-git) using your favorite
|
||||||
|
package manager such as `yay`, `pacaur` or `yaourt`:
|
||||||
|
|
||||||
|
```cmd
|
||||||
|
yay -S taskfile-git
|
||||||
|
```
|
||||||
|
|
||||||
|
This installation method is community owned, but since it's `-git` version of
|
||||||
|
the package, it's always latest available version based on the Git repository.
|
||||||
|
|
||||||
## Go
|
## Go
|
||||||
|
|
||||||
Task now uses [Go Modules](https://github.com/golang/go/wiki/Modules), which
|
Task now uses [Go Modules](https://github.com/golang/go/wiki/Modules), which
|
||||||
means you may have trouble compiling it on older Go versions.
|
means you may have trouble compiling it on older Go versions or using
|
||||||
|
`$GOPATH`.
|
||||||
|
|
||||||
For CI environments we recommend using the [Install Script](#install-script)
|
For CI environments we recommend using the [Install Script](#install-script)
|
||||||
instead, which is faster and more stable, since it'll just download the latest
|
instead, which is faster and more stable, since it'll just download the latest
|
||||||
released binary, instead of compiling the edge (master branch) version.
|
released binary, instead of compiling the edge (master branch) version.
|
||||||
|
|
||||||
Installing in your `$GOPATH`:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go get -u -v github.com/go-task/task/cmd/task
|
|
||||||
```
|
|
||||||
|
|
||||||
Installing in another directory:
|
Installing in another directory:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -62,7 +70,7 @@ git clone https://github.com/go-task/task
|
|||||||
cd task
|
cd task
|
||||||
|
|
||||||
# compiling binary to $GOPATH/bin
|
# compiling binary to $GOPATH/bin
|
||||||
go install -v
|
go install -v ./cmd/task
|
||||||
|
|
||||||
# compiling it to another location
|
# compiling it to another location
|
||||||
# use -o ./task.exe on Windows
|
# use -o ./task.exe on Windows
|
||||||
@@ -83,6 +91,19 @@ curl -sL https://taskfile.dev/install.sh | sh
|
|||||||
|
|
||||||
> This method will download the binary on the local `./bin` directory by default.
|
> This method will download the binary on the local `./bin` directory by default.
|
||||||
|
|
||||||
|
## GitHub Actions
|
||||||
|
|
||||||
|
If you want to install Task in GitHub Actions you can try using
|
||||||
|
[this action](https://github.com/arduino/actions/tree/master/setup-taskfile)
|
||||||
|
by the Arduino team:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- name: Install Task
|
||||||
|
uses: Arduino/actions/setup-taskfile@master
|
||||||
|
```
|
||||||
|
|
||||||
|
This installation method is community owned.
|
||||||
|
|
||||||
[go]: https://golang.org/
|
[go]: https://golang.org/
|
||||||
[snapcraft]: https://snapcraft.io/
|
[snapcraft]: https://snapcraft.io/
|
||||||
[homebrew]: https://brew.sh/
|
[homebrew]: https://brew.sh/
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ The release process of Task is done with the help of
|
|||||||
[GoReleaser][goreleaser]. You can test the release process locally by calling
|
[GoReleaser][goreleaser]. You can test the release process locally by calling
|
||||||
the `test-release` task of the Taskfile.
|
the `test-release` task of the Taskfile.
|
||||||
|
|
||||||
The Travis CI should release automatically when a new
|
[GitHub Actions](https://github.com/go-task/task/actions) should release
|
||||||
Git tag is pushed to master, either for the artifact uploading (raw executables
|
artifacts automatically when a new Git tag is pushed to master
|
||||||
and DEB and RPM packages)
|
(raw executables and DEB and RPM packages).
|
||||||
|
|
||||||
# Homebrew
|
# Homebrew
|
||||||
|
|
||||||
|
|||||||
213
docs/styleguide.md
Normal file
213
docs/styleguide.md
Normal file
@@ -0,0 +1,213 @@
|
|||||||
|
# Styleguide
|
||||||
|
|
||||||
|
This is the official Task styleguide for `Taskfile.yml` files. This guide
|
||||||
|
contains some basic instructions to keep your Taskfile clean and familiar to
|
||||||
|
other users.
|
||||||
|
|
||||||
|
This contains general guidelines, but don't necessarely need to be strictly
|
||||||
|
followed. Feel free to disagree and proceed differently in some point if you
|
||||||
|
need or want to. Also, feel free to open issues or pull requests with
|
||||||
|
improvements to this guide.
|
||||||
|
|
||||||
|
## Use `Taskfile.yml` and not `taskfile.yml`
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# bad
|
||||||
|
taskfile.yml
|
||||||
|
|
||||||
|
|
||||||
|
# good
|
||||||
|
Taskfile.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
This is important especially for Linux users. Windows and macOS have case
|
||||||
|
insensitive filesystems, so `taskfile.yml` will end up working, even that not
|
||||||
|
officially supported. On Linux, only `Taskfile.yml` will work, though.
|
||||||
|
|
||||||
|
## Use the correct order of keywords
|
||||||
|
|
||||||
|
- `version:`
|
||||||
|
- `includes:`
|
||||||
|
- Configuration ones, like `output:`, `expansions:` or `silent:`
|
||||||
|
- `vars:`
|
||||||
|
- `env:`
|
||||||
|
- `tasks:`
|
||||||
|
|
||||||
|
## Use 2 spaces for indentation
|
||||||
|
|
||||||
|
This is the most common convention for YAML files, and Task follows it.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# bad
|
||||||
|
tasks:
|
||||||
|
foo:
|
||||||
|
cmds:
|
||||||
|
- echo 'foo'
|
||||||
|
|
||||||
|
|
||||||
|
# good
|
||||||
|
tasks:
|
||||||
|
foo:
|
||||||
|
cmds:
|
||||||
|
- echo 'foo'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Separate with spaces the mains sections
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# bad
|
||||||
|
version: 2
|
||||||
|
includes:
|
||||||
|
docker: ./docker/Taskfile.yml
|
||||||
|
output: prefixed
|
||||||
|
expansions: 3
|
||||||
|
vars:
|
||||||
|
FOO: bar
|
||||||
|
env:
|
||||||
|
BAR: baz
|
||||||
|
tasks:
|
||||||
|
# ...
|
||||||
|
|
||||||
|
|
||||||
|
# good
|
||||||
|
version: 2
|
||||||
|
|
||||||
|
includes:
|
||||||
|
docker: ./docker/Taskfile.yml
|
||||||
|
|
||||||
|
output: prefixed
|
||||||
|
expansions: 3
|
||||||
|
|
||||||
|
vars:
|
||||||
|
FOO: bar
|
||||||
|
|
||||||
|
env:
|
||||||
|
BAR: baz
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
# ...
|
||||||
|
```
|
||||||
|
|
||||||
|
## Add spaces between tasks
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# bad
|
||||||
|
version: 2
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
foo:
|
||||||
|
cmds:
|
||||||
|
- echo 'foo'
|
||||||
|
bar:
|
||||||
|
cmds:
|
||||||
|
- echo 'bar'
|
||||||
|
baz:
|
||||||
|
cmds:
|
||||||
|
- echo 'baz'
|
||||||
|
|
||||||
|
|
||||||
|
# good
|
||||||
|
version: 2
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
foo:
|
||||||
|
cmds:
|
||||||
|
- echo 'foo'
|
||||||
|
|
||||||
|
bar:
|
||||||
|
cmds:
|
||||||
|
- echo 'bar'
|
||||||
|
|
||||||
|
baz:
|
||||||
|
cmds:
|
||||||
|
- echo 'baz'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Use upper-case variable names
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# bad
|
||||||
|
version: 2
|
||||||
|
|
||||||
|
vars:
|
||||||
|
binary_name: myapp
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
build:
|
||||||
|
cmds:
|
||||||
|
- go build -o {{.binary_name}} .
|
||||||
|
|
||||||
|
|
||||||
|
# good
|
||||||
|
version: 2
|
||||||
|
|
||||||
|
vars:
|
||||||
|
BINARY_NAME: myapp
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
build:
|
||||||
|
cmds:
|
||||||
|
- go build -o {{.BINARY_NAME}} .
|
||||||
|
```
|
||||||
|
|
||||||
|
## Don't wrap vars in spaces when templating
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# bad
|
||||||
|
version: 2
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
greet:
|
||||||
|
cmds:
|
||||||
|
- echo '{{ .MESSAGE }}'
|
||||||
|
|
||||||
|
|
||||||
|
# good
|
||||||
|
version: 2
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
greet:
|
||||||
|
cmds:
|
||||||
|
- echo '{{.MESSAGE}}'
|
||||||
|
```
|
||||||
|
|
||||||
|
This convention is also used by most people for any Go templating.
|
||||||
|
|
||||||
|
## Separate task name words with a dash
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# bad
|
||||||
|
version: 2
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
do_something_fancy:
|
||||||
|
cmds:
|
||||||
|
- echo 'Do something'
|
||||||
|
|
||||||
|
|
||||||
|
# good
|
||||||
|
version: 2
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
do-something-fancy:
|
||||||
|
cmds:
|
||||||
|
- echo 'Do something'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Use colon for task namespacing
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# good
|
||||||
|
version: 2
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
docker:build:
|
||||||
|
cmds:
|
||||||
|
- docker ...
|
||||||
|
|
||||||
|
docker:run:
|
||||||
|
cmds:
|
||||||
|
- docker-compose ...
|
||||||
|
```
|
||||||
|
|
||||||
|
This is also done automatically when using included Taskfiles.
|
||||||
@@ -63,7 +63,7 @@ tasks:
|
|||||||
- echo $GREETING
|
- echo $GREETING
|
||||||
```
|
```
|
||||||
|
|
||||||
> NOTE: `env` supports expansion and and retrieving output from a shell command
|
> NOTE: `env` supports expansion and retrieving output from a shell command
|
||||||
> just like variables, as you can see on the [Variables](#variables) section.
|
> just like variables, as you can see on the [Variables](#variables) section.
|
||||||
|
|
||||||
## Operating System specific tasks
|
## Operating System specific tasks
|
||||||
@@ -124,6 +124,21 @@ namespace. So, you'd call `task docs:serve` to run the `serve` task from
|
|||||||
`documentation/Taskfile.yml` or `task docker:build` to run the `build` task
|
`documentation/Taskfile.yml` or `task docker:build` to run the `build` task
|
||||||
from the `DockerTasks.yml` file.
|
from the `DockerTasks.yml` file.
|
||||||
|
|
||||||
|
### Directory of included Taskfile
|
||||||
|
|
||||||
|
By default, included Taskfile's tasks are ran in the current directory, even
|
||||||
|
if the Taskfile is in another directory, but you can force its tasks to run
|
||||||
|
in another directory by using this alternative syntax:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: '3'
|
||||||
|
|
||||||
|
includes:
|
||||||
|
docs:
|
||||||
|
taskfile: ./docs/Taskfile.yml
|
||||||
|
dir: ./docs
|
||||||
|
```
|
||||||
|
|
||||||
> The included Taskfiles must be using the same schema version the main
|
> The included Taskfiles must be using the same schema version the main
|
||||||
> Taskfile uses.
|
> Taskfile uses.
|
||||||
|
|
||||||
@@ -193,6 +208,9 @@ tasks:
|
|||||||
If there is more than one dependency, they always run in parallel for better
|
If there is more than one dependency, they always run in parallel for better
|
||||||
performance.
|
performance.
|
||||||
|
|
||||||
|
> You can also make the tasks given by the command line run in parallel by
|
||||||
|
> using the `--parallel` flag (alias `-p`). Example: `task --parallel js css`.
|
||||||
|
|
||||||
If you want to pass information to dependencies, you can do that the same
|
If you want to pass information to dependencies, you can do that the same
|
||||||
manner as you would to [call another task](#calling-another-task):
|
manner as you would to [call another task](#calling-another-task):
|
||||||
|
|
||||||
@@ -436,6 +454,8 @@ Example of sending parameters with environment variables:
|
|||||||
$ TASK_VARIABLE=a-value task do-something
|
$ TASK_VARIABLE=a-value task do-something
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> TIP: A special variable `.TASK` is always available containg the task name.
|
||||||
|
|
||||||
Since some shells don't support above syntax to set environment variables
|
Since some shells don't support above syntax to set environment variables
|
||||||
(Windows) tasks also accepts a similar style when not in the beginning of
|
(Windows) tasks also accepts a similar style when not in the beginning of
|
||||||
the command. Variables given in this form are only visible to the task called
|
the command. Variables given in this form are only visible to the task called
|
||||||
@@ -694,7 +714,7 @@ With silent mode on, the below will be print instead:
|
|||||||
Print something
|
Print something
|
||||||
```
|
```
|
||||||
|
|
||||||
There's three ways to enable silent mode:
|
There are four ways to enable silent mode:
|
||||||
|
|
||||||
* At command level:
|
* At command level:
|
||||||
|
|
||||||
@@ -720,6 +740,19 @@ tasks:
|
|||||||
silent: true
|
silent: true
|
||||||
```
|
```
|
||||||
|
|
||||||
|
* Globally at Taskfile level:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: '2'
|
||||||
|
|
||||||
|
silent: true
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
echo:
|
||||||
|
cmds:
|
||||||
|
- echo "Print something"
|
||||||
|
```
|
||||||
|
|
||||||
* Or globally with `--silent` or `-s` flag
|
* Or globally with `--silent` or `-s` flag
|
||||||
|
|
||||||
If you want to suppress STDOUT instead, just redirect a command to `/dev/null`:
|
If you want to suppress STDOUT instead, just redirect a command to `/dev/null`:
|
||||||
@@ -835,6 +868,22 @@ $ task default
|
|||||||
|
|
||||||
> The `output` option can also be specified by the `--output` or `-o` flags.
|
> The `output` option can also be specified by the `--output` or `-o` flags.
|
||||||
|
|
||||||
|
## Short task syntax
|
||||||
|
|
||||||
|
Starting on Task v3, you can now write tasks with a shorter syntax if they
|
||||||
|
have the default settings (e.g. no custom `env:`, `vars:`, `silent:` , etc):
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: '3'
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
build: go build -v -o ./app{{exeExt}} .
|
||||||
|
|
||||||
|
build:
|
||||||
|
- task: build
|
||||||
|
- ./app{{exeExt}} -h localhost -p 8080
|
||||||
|
```
|
||||||
|
|
||||||
## Watch tasks
|
## Watch tasks
|
||||||
|
|
||||||
If you give a `--watch` or `-w` argument, task will watch for file changes
|
If you give a `--watch` or `-w` argument, task will watch for file changes
|
||||||
|
|||||||
10
go.mod
10
go.mod
@@ -2,15 +2,15 @@ module github.com/go-task/task/v2
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/fatih/color v1.7.0
|
github.com/fatih/color v1.7.0
|
||||||
github.com/go-task/slim-sprig v0.0.0-20190623010546-24867827a98b
|
github.com/go-task/slim-sprig v0.0.0-20200516131648-f9bac4e523eb
|
||||||
github.com/mattn/go-colorable v0.1.2 // indirect
|
github.com/mattn/go-colorable v0.1.2 // indirect
|
||||||
github.com/mattn/go-zglob v0.0.1
|
github.com/mattn/go-zglob v0.0.1
|
||||||
github.com/radovskyb/watcher v1.0.5
|
github.com/radovskyb/watcher v1.0.5
|
||||||
github.com/spf13/pflag v1.0.3
|
github.com/spf13/pflag v1.0.3
|
||||||
github.com/stretchr/testify v1.3.0
|
github.com/stretchr/testify v1.5.1
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e
|
||||||
gopkg.in/yaml.v2 v2.2.1
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c
|
||||||
mvdan.cc/sh/v3 v3.0.0-alpha2.0.20190908210725-4a0ebd2f3c1b
|
mvdan.cc/sh/v3 v3.1.1
|
||||||
)
|
)
|
||||||
|
|
||||||
go 1.13
|
go 1.13
|
||||||
|
|||||||
58
go.sum
58
go.sum
@@ -1,50 +1,66 @@
|
|||||||
|
github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w=
|
||||||
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
|
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
|
||||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||||
github.com/go-task/slim-sprig v0.0.0-20190623010546-24867827a98b h1:z6iCP1USASmEZtKTzynd/rP4vOtBLlsD3v24wItbJIs=
|
github.com/go-task/slim-sprig v0.0.0-20200516131648-f9bac4e523eb h1:/qbv1F67s6ehqX9mG23cJOeca3FWpOVKgtPfPUMAi0k=
|
||||||
github.com/go-task/slim-sprig v0.0.0-20190623010546-24867827a98b/go.mod h1:XLIiFDBy2M8pA/fEL5rx9xr2EAzrDEO0S5brm5iekOE=
|
github.com/go-task/slim-sprig v0.0.0-20200516131648-f9bac4e523eb/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||||
|
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
|
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
|
||||||
|
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
|
github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
|
||||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||||
github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE=
|
github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE=
|
||||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||||
github.com/mattn/go-zglob v0.0.1 h1:xsEx/XUoVlI6yXjqBK062zYhRTZltCNmYPx6v+8DNaY=
|
github.com/mattn/go-zglob v0.0.1 h1:xsEx/XUoVlI6yXjqBK062zYhRTZltCNmYPx6v+8DNaY=
|
||||||
github.com/mattn/go-zglob v0.0.1/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo=
|
github.com/mattn/go-zglob v0.0.1/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo=
|
||||||
|
github.com/pkg/diff v0.0.0-20190930165518-531926345625/go.mod h1:kFj35MyHn14a6pIgWhm46KVjJr5CHys3eEYxkuKD1EI=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/radovskyb/watcher v1.0.5 h1:wqt7gb+HjGacvFoLTKeT44C+XVPxu7bvHvKT1IvZ7rw=
|
github.com/radovskyb/watcher v1.0.5 h1:wqt7gb+HjGacvFoLTKeT44C+XVPxu7bvHvKT1IvZ7rw=
|
||||||
github.com/radovskyb/watcher v1.0.5/go.mod h1:78okwvY5wPdzcb1UYnip1pvrZNIVEIh/Cm+ZuvsUYIg=
|
github.com/radovskyb/watcher v1.0.5/go.mod h1:78okwvY5wPdzcb1UYnip1pvrZNIVEIh/Cm+ZuvsUYIg=
|
||||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||||
|
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||||
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
|
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
|
||||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v0.0.0-20180319223459-c679ae2cc0cb/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc=
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
|
||||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
|
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7 h1:LepdCS8Gf/MVejFIt8lsiexZATdoGVyp5bcyS+rYoUI=
|
golang.org/x/sys v0.0.0-20200217220822-9197077df867 h1:JoRuNIf+rpHl+VhScRQQvzbHed86tKkqwPMV34T8myw=
|
||||||
golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/term v0.0.0-20191110171634-ad39bd3f0407 h1:5zh5atpUEdIc478E/ebrIaHLKcfVvG6dL/fGv7BcMoM=
|
||||||
|
golang.org/x/term v0.0.0-20191110171634-ad39bd3f0407/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||||
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
|
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
mvdan.cc/sh/v3 v3.0.0-alpha2.0.20190908210725-4a0ebd2f3c1b h1:kzTXBacNrjp7n8ncNC894X9NUeL8yeKVS5MS0bigJwE=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||||
mvdan.cc/sh/v3 v3.0.0-alpha2.0.20190908210725-4a0ebd2f3c1b/go.mod h1:6Cd5lQRZZIHeKAdYoLvhhse0oDOIrS5gBoGacWnuiUE=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
mvdan.cc/editorconfig v0.1.1-0.20200121172147-e40951bde157/go.mod h1:Ge4atmRUYqueGppvJ7JNrtqpqokoJEFxYbP0Z+WeKS8=
|
||||||
|
mvdan.cc/sh/v3 v3.1.1 h1:niuYC5Ug0KzLuN6CNX3ru37v4MkVD5Wm9T4Mk2eJr9A=
|
||||||
|
mvdan.cc/sh/v3 v3.1.1/go.mod h1:F+Vm4ZxPJxDKExMLhvjuI50oPnedVXpfjNSrusiTOno=
|
||||||
|
|||||||
4
help.go
4
help.go
@@ -30,6 +30,10 @@ func (e *Executor) tasksWithDesc() (tasks []*taskfile.Task) {
|
|||||||
tasks = make([]*taskfile.Task, 0, len(e.Taskfile.Tasks))
|
tasks = make([]*taskfile.Task, 0, len(e.Taskfile.Tasks))
|
||||||
for _, task := range e.Taskfile.Tasks {
|
for _, task := range e.Taskfile.Tasks {
|
||||||
if task.Desc != "" {
|
if task.Desc != "" {
|
||||||
|
compiledTask, err := e.CompiledTask(taskfile.Call{Task: task.Task})
|
||||||
|
if err == nil {
|
||||||
|
task = compiledTask
|
||||||
|
}
|
||||||
tasks = append(tasks, task)
|
tasks = append(tasks, task)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Parse parses command line argument: tasks and vars of each task
|
// Parse parses command line argument: tasks and vars of each task
|
||||||
func Parse(args ...string) ([]taskfile.Call, taskfile.Vars) {
|
func Parse(args ...string) ([]taskfile.Call, *taskfile.Vars) {
|
||||||
var calls []taskfile.Call
|
var calls []taskfile.Call
|
||||||
var globals taskfile.Vars
|
var globals *taskfile.Vars
|
||||||
|
|
||||||
for _, arg := range args {
|
for _, arg := range args {
|
||||||
if !strings.Contains(arg, "=") {
|
if !strings.Contains(arg, "=") {
|
||||||
@@ -19,21 +19,23 @@ func Parse(args ...string) ([]taskfile.Call, taskfile.Vars) {
|
|||||||
|
|
||||||
if len(calls) < 1 {
|
if len(calls) < 1 {
|
||||||
if globals == nil {
|
if globals == nil {
|
||||||
globals = taskfile.Vars{}
|
globals = &taskfile.Vars{}
|
||||||
}
|
}
|
||||||
|
|
||||||
name, value := splitVar(arg)
|
name, value := splitVar(arg)
|
||||||
globals[name] = taskfile.Var{Static: value}
|
globals.Set(name, taskfile.Var{Static: value})
|
||||||
} else {
|
} else {
|
||||||
if calls[len(calls)-1].Vars == nil {
|
if calls[len(calls)-1].Vars == nil {
|
||||||
calls[len(calls)-1].Vars = make(taskfile.Vars)
|
calls[len(calls)-1].Vars = &taskfile.Vars{}
|
||||||
}
|
}
|
||||||
|
name, value := splitVar(arg)
|
||||||
name, value := splitVar((arg))
|
calls[len(calls)-1].Vars.Set(name, taskfile.Var{Static: value})
|
||||||
calls[len(calls)-1].Vars[name] = taskfile.Var{Static: value}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(calls) == 0 {
|
||||||
|
calls = append(calls, taskfile.Call{Task: "default"})
|
||||||
|
}
|
||||||
|
|
||||||
return calls, globals
|
return calls, globals
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ func TestArgs(t *testing.T) {
|
|||||||
tests := []struct {
|
tests := []struct {
|
||||||
Args []string
|
Args []string
|
||||||
ExpectedCalls []taskfile.Call
|
ExpectedCalls []taskfile.Call
|
||||||
ExpectedGlobals taskfile.Vars
|
ExpectedGlobals *taskfile.Vars
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
Args: []string{"task-a", "task-b", "task-c"},
|
Args: []string{"task-a", "task-b", "task-c"},
|
||||||
@@ -29,16 +29,22 @@ func TestArgs(t *testing.T) {
|
|||||||
ExpectedCalls: []taskfile.Call{
|
ExpectedCalls: []taskfile.Call{
|
||||||
{
|
{
|
||||||
Task: "task-a",
|
Task: "task-a",
|
||||||
Vars: taskfile.Vars{
|
Vars: &taskfile.Vars{
|
||||||
"FOO": taskfile.Var{Static: "bar"},
|
Keys: []string{"FOO"},
|
||||||
|
Mapping: map[string]taskfile.Var{
|
||||||
|
"FOO": taskfile.Var{Static: "bar"},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{Task: "task-b"},
|
{Task: "task-b"},
|
||||||
{
|
{
|
||||||
Task: "task-c",
|
Task: "task-c",
|
||||||
Vars: taskfile.Vars{
|
Vars: &taskfile.Vars{
|
||||||
"BAR": taskfile.Var{Static: "baz"},
|
Keys: []string{"BAR", "BAZ"},
|
||||||
"BAZ": taskfile.Var{Static: "foo"},
|
Mapping: map[string]taskfile.Var{
|
||||||
|
"BAR": taskfile.Var{Static: "baz"},
|
||||||
|
"BAZ": taskfile.Var{Static: "foo"},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -48,8 +54,11 @@ func TestArgs(t *testing.T) {
|
|||||||
ExpectedCalls: []taskfile.Call{
|
ExpectedCalls: []taskfile.Call{
|
||||||
{
|
{
|
||||||
Task: "task-a",
|
Task: "task-a",
|
||||||
Vars: taskfile.Vars{
|
Vars: &taskfile.Vars{
|
||||||
"CONTENT": taskfile.Var{Static: "with some spaces"},
|
Keys: []string{"CONTENT"},
|
||||||
|
Mapping: map[string]taskfile.Var{
|
||||||
|
"CONTENT": taskfile.Var{Static: "with some spaces"},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -60,8 +69,36 @@ func TestArgs(t *testing.T) {
|
|||||||
{Task: "task-a"},
|
{Task: "task-a"},
|
||||||
{Task: "task-b"},
|
{Task: "task-b"},
|
||||||
},
|
},
|
||||||
ExpectedGlobals: taskfile.Vars{
|
ExpectedGlobals: &taskfile.Vars{
|
||||||
"FOO": {Static: "bar"},
|
Keys: []string{"FOO"},
|
||||||
|
Mapping: map[string]taskfile.Var{
|
||||||
|
"FOO": {Static: "bar"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Args: nil,
|
||||||
|
ExpectedCalls: []taskfile.Call{
|
||||||
|
{Task: "default"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Args: []string{},
|
||||||
|
ExpectedCalls: []taskfile.Call{
|
||||||
|
{Task: "default"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Args: []string{"FOO=bar", "BAR=baz"},
|
||||||
|
ExpectedCalls: []taskfile.Call{
|
||||||
|
{Task: "default"},
|
||||||
|
},
|
||||||
|
ExpectedGlobals: &taskfile.Vars{
|
||||||
|
Keys: []string{"FOO", "BAR"},
|
||||||
|
Mapping: map[string]taskfile.Var{
|
||||||
|
"FOO": {Static: "bar"},
|
||||||
|
"BAR": {Static: "baz"},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,6 @@ import (
|
|||||||
// Compiler handles compilation of a task before its execution.
|
// Compiler handles compilation of a task before its execution.
|
||||||
// E.g. variable merger, template processing, etc.
|
// E.g. variable merger, template processing, etc.
|
||||||
type Compiler interface {
|
type Compiler interface {
|
||||||
GetVariables(t *taskfile.Task, call taskfile.Call) (taskfile.Vars, error)
|
GetVariables(t *taskfile.Task, call taskfile.Call) (*taskfile.Vars, error)
|
||||||
HandleDynamicVar(v taskfile.Var) (string, error)
|
HandleDynamicVar(v taskfile.Var) (string, error)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,16 +9,12 @@ import (
|
|||||||
|
|
||||||
// GetEnviron the all return all environment variables encapsulated on a
|
// GetEnviron the all return all environment variables encapsulated on a
|
||||||
// taskfile.Vars
|
// taskfile.Vars
|
||||||
func GetEnviron() taskfile.Vars {
|
func GetEnviron() *taskfile.Vars {
|
||||||
var (
|
m := &taskfile.Vars{}
|
||||||
env = os.Environ()
|
for _, e := range os.Environ() {
|
||||||
m = make(taskfile.Vars, len(env))
|
|
||||||
)
|
|
||||||
|
|
||||||
for _, e := range env {
|
|
||||||
keyVal := strings.SplitN(e, "=", 2)
|
keyVal := strings.SplitN(e, "=", 2)
|
||||||
key, val := keyVal[0], keyVal[1]
|
key, val := keyVal[0], keyVal[1]
|
||||||
m[key] = taskfile.Var{Static: val}
|
m.Set(key, taskfile.Var{Static: val})
|
||||||
}
|
}
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,8 +19,8 @@ var _ compiler.Compiler = &CompilerV2{}
|
|||||||
type CompilerV2 struct {
|
type CompilerV2 struct {
|
||||||
Dir string
|
Dir string
|
||||||
|
|
||||||
Taskvars taskfile.Vars
|
Taskvars *taskfile.Vars
|
||||||
TaskfileVars taskfile.Vars
|
TaskfileVars *taskfile.Vars
|
||||||
|
|
||||||
Expansions int
|
Expansions int
|
||||||
|
|
||||||
@@ -36,9 +36,10 @@ type CompilerV2 struct {
|
|||||||
// 3. Taskfile variables
|
// 3. Taskfile variables
|
||||||
// 4. Taskvars file variables
|
// 4. Taskvars file variables
|
||||||
// 5. Environment variables
|
// 5. Environment variables
|
||||||
func (c *CompilerV2) GetVariables(t *taskfile.Task, call taskfile.Call) (taskfile.Vars, error) {
|
func (c *CompilerV2) GetVariables(t *taskfile.Task, call taskfile.Call) (*taskfile.Vars, error) {
|
||||||
vr := varResolver{c: c, vars: compiler.GetEnviron()}
|
vr := varResolver{c: c, vars: compiler.GetEnviron()}
|
||||||
for _, vars := range []taskfile.Vars{c.Taskvars, c.TaskfileVars, call.Vars, t.Vars} {
|
vr.vars.Set("TASK", taskfile.Var{Static: t.Task})
|
||||||
|
for _, vars := range []*taskfile.Vars{c.Taskvars, c.TaskfileVars, call.Vars, t.Vars} {
|
||||||
for i := 0; i < c.Expansions; i++ {
|
for i := 0; i < c.Expansions; i++ {
|
||||||
vr.merge(vars)
|
vr.merge(vars)
|
||||||
}
|
}
|
||||||
@@ -48,16 +49,16 @@ func (c *CompilerV2) GetVariables(t *taskfile.Task, call taskfile.Call) (taskfil
|
|||||||
|
|
||||||
type varResolver struct {
|
type varResolver struct {
|
||||||
c *CompilerV2
|
c *CompilerV2
|
||||||
vars taskfile.Vars
|
vars *taskfile.Vars
|
||||||
err error
|
err error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vr *varResolver) merge(vars taskfile.Vars) {
|
func (vr *varResolver) merge(vars *taskfile.Vars) {
|
||||||
if vr.err != nil {
|
if vr.err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
tr := templater.Templater{Vars: vr.vars}
|
tr := templater.Templater{Vars: vr.vars}
|
||||||
for k, v := range vars {
|
vars.Range(func(k string, v taskfile.Var) error {
|
||||||
v = taskfile.Var{
|
v = taskfile.Var{
|
||||||
Static: tr.Replace(v.Static),
|
Static: tr.Replace(v.Static),
|
||||||
Sh: tr.Replace(v.Sh),
|
Sh: tr.Replace(v.Sh),
|
||||||
@@ -65,10 +66,11 @@ func (vr *varResolver) merge(vars taskfile.Vars) {
|
|||||||
static, err := vr.c.HandleDynamicVar(v)
|
static, err := vr.c.HandleDynamicVar(v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
vr.err = err
|
vr.err = err
|
||||||
return
|
return err
|
||||||
}
|
}
|
||||||
vr.vars[k] = taskfile.Var{Static: static}
|
vr.vars.Set(k, taskfile.Var{Static: static})
|
||||||
}
|
return nil
|
||||||
|
})
|
||||||
vr.err = tr.Err()
|
vr.err = tr.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
98
internal/compiler/v3/compiler_v3.go
Normal file
98
internal/compiler/v3/compiler_v3.go
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
package v3
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/go-task/task/v2/internal/compiler"
|
||||||
|
"github.com/go-task/task/v2/internal/execext"
|
||||||
|
"github.com/go-task/task/v2/internal/logger"
|
||||||
|
"github.com/go-task/task/v2/internal/taskfile"
|
||||||
|
"github.com/go-task/task/v2/internal/templater"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ compiler.Compiler = &CompilerV3{}
|
||||||
|
|
||||||
|
type CompilerV3 struct {
|
||||||
|
Dir string
|
||||||
|
|
||||||
|
TaskfileVars *taskfile.Vars
|
||||||
|
|
||||||
|
Logger *logger.Logger
|
||||||
|
|
||||||
|
dynamicCache map[string]string
|
||||||
|
muDynamicCache sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CompilerV3) GetVariables(t *taskfile.Task, call taskfile.Call) (*taskfile.Vars, error) {
|
||||||
|
result := compiler.GetEnviron()
|
||||||
|
result.Set("TASK", taskfile.Var{Static: t.Task})
|
||||||
|
|
||||||
|
rangeFunc := func(k string, v taskfile.Var) error {
|
||||||
|
tr := templater.Templater{Vars: result, RemoveNoValue: true}
|
||||||
|
v = taskfile.Var{
|
||||||
|
Static: tr.Replace(v.Static),
|
||||||
|
Sh: tr.Replace(v.Sh),
|
||||||
|
}
|
||||||
|
if err := tr.Err(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
static, err := c.HandleDynamicVar(v)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
result.Set(k, taskfile.Var{Static: static})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.TaskfileVars.Range(rangeFunc); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := call.Vars.Range(rangeFunc); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := t.Vars.Range(rangeFunc); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CompilerV3) HandleDynamicVar(v taskfile.Var) (string, error) {
|
||||||
|
if v.Static != "" || v.Sh == "" {
|
||||||
|
return v.Static, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
c.muDynamicCache.Lock()
|
||||||
|
defer c.muDynamicCache.Unlock()
|
||||||
|
|
||||||
|
if c.dynamicCache == nil {
|
||||||
|
c.dynamicCache = make(map[string]string, 30)
|
||||||
|
}
|
||||||
|
if result, ok := c.dynamicCache[v.Sh]; ok {
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var stdout bytes.Buffer
|
||||||
|
opts := &execext.RunCommandOptions{
|
||||||
|
Command: v.Sh,
|
||||||
|
Dir: c.Dir,
|
||||||
|
Stdout: &stdout,
|
||||||
|
Stderr: c.Logger.Stderr,
|
||||||
|
}
|
||||||
|
if err := execext.RunCommand(context.Background(), opts); err != nil {
|
||||||
|
return "", fmt.Errorf(`task: Command "%s" in taskvars file failed: %s`, opts.Command, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trim a single trailing newline from the result to make most command
|
||||||
|
// output easier to use in shell commands.
|
||||||
|
result := strings.TrimSuffix(stdout.String(), "\n")
|
||||||
|
|
||||||
|
c.dynamicCache[v.Sh] = result
|
||||||
|
c.Logger.VerboseErrf(logger.Magenta, `task: dynamic variable: '%s' result: '%s'`, v.Sh, result)
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
13
internal/execext/devnull.go
Normal file
13
internal/execext/devnull.go
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package execext
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ io.ReadWriteCloser = devNull{}
|
||||||
|
|
||||||
|
type devNull struct{}
|
||||||
|
|
||||||
|
func (devNull) Read(p []byte) (int, error) { return 0, io.EOF }
|
||||||
|
func (devNull) Write(p []byte) (int, error) { return len(p), nil }
|
||||||
|
func (devNull) Close() error { return nil }
|
||||||
@@ -49,7 +49,12 @@ func RunCommand(ctx context.Context, opts *RunCommandOptions) error {
|
|||||||
interp.Dir(opts.Dir),
|
interp.Dir(opts.Dir),
|
||||||
interp.Env(expand.ListEnviron(environ...)),
|
interp.Env(expand.ListEnviron(environ...)),
|
||||||
|
|
||||||
interp.WithOpenModules(interp.OpenDevImpls),
|
interp.OpenHandler(func(ctx context.Context, path string, flag int, perm os.FileMode) (io.ReadWriteCloser, error) {
|
||||||
|
if path == "/dev/null" {
|
||||||
|
return devNull{}, nil
|
||||||
|
}
|
||||||
|
return interp.DefaultOpenHandler()(ctx, path, flag, perm)
|
||||||
|
}),
|
||||||
|
|
||||||
interp.StdIO(opts.Stdin, opts.Stdout, opts.Stderr),
|
interp.StdIO(opts.Stdin, opts.Stdout, opts.Stderr),
|
||||||
)
|
)
|
||||||
@@ -61,12 +66,10 @@ func RunCommand(ctx context.Context, opts *RunCommandOptions) error {
|
|||||||
|
|
||||||
// IsExitError returns true the given error is an exis status error
|
// IsExitError returns true the given error is an exis status error
|
||||||
func IsExitError(err error) bool {
|
func IsExitError(err error) bool {
|
||||||
switch err.(type) {
|
if _, ok := interp.IsExitStatus(err); ok {
|
||||||
case interp.ExitStatus, interp.ShellExitStatus:
|
|
||||||
return true
|
return true
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Expand is a helper to mvdan.cc/shell.Fields that returns the first field
|
// Expand is a helper to mvdan.cc/shell.Fields that returns the first field
|
||||||
|
|||||||
@@ -10,13 +10,12 @@ type PrintFunc func(io.Writer, string, ...interface{})
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
Default PrintFunc = color.New(color.Reset).FprintfFunc()
|
Default PrintFunc = color.New(color.Reset).FprintfFunc()
|
||||||
Bold PrintFunc = color.New(color.Bold).FprintfFunc()
|
Blue PrintFunc = color.New(color.FgBlue).FprintfFunc()
|
||||||
Blue PrintFunc = color.New(color.FgBlue, color.Bold).FprintfFunc()
|
Green PrintFunc = color.New(color.FgGreen).FprintfFunc()
|
||||||
Green PrintFunc = color.New(color.FgGreen, color.Bold).FprintfFunc()
|
Cyan PrintFunc = color.New(color.FgCyan).FprintfFunc()
|
||||||
Cyan PrintFunc = color.New(color.FgCyan, color.Bold).FprintfFunc()
|
Yellow PrintFunc = color.New(color.FgYellow).FprintfFunc()
|
||||||
Yellow PrintFunc = color.New(color.FgYellow, color.Bold).FprintfFunc()
|
Magenta PrintFunc = color.New(color.FgMagenta).FprintfFunc()
|
||||||
Magenta PrintFunc = color.New(color.FgMagenta, color.Bold).FprintfFunc()
|
Red PrintFunc = color.New(color.FgRed).FprintfFunc()
|
||||||
Red PrintFunc = color.New(color.FgRed, color.Bold).FprintfFunc()
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Logger is just a wrapper that prints stuff to STDOUT or STDERR,
|
// Logger is just a wrapper that prints stuff to STDOUT or STDERR,
|
||||||
|
|||||||
@@ -9,12 +9,12 @@ import (
|
|||||||
|
|
||||||
func PrintTasks(l *logger.Logger, t *taskfile.Taskfile, c []taskfile.Call) {
|
func PrintTasks(l *logger.Logger, t *taskfile.Taskfile, c []taskfile.Call) {
|
||||||
for i, call := range c {
|
for i, call := range c {
|
||||||
printSpaceBetweenSummaries(l, i)
|
PrintSpaceBetweenSummaries(l, i)
|
||||||
PrintTask(l, t.Tasks[call.Task])
|
PrintTask(l, t.Tasks[call.Task])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func printSpaceBetweenSummaries(l *logger.Logger, i int) {
|
func PrintSpaceBetweenSummaries(l *logger.Logger, i int) {
|
||||||
spaceRequired := i > 0
|
spaceRequired := i > 0
|
||||||
if !spaceRequired {
|
if !spaceRequired {
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -3,5 +3,5 @@ package taskfile
|
|||||||
// Call is the parameters to a task call
|
// Call is the parameters to a task call
|
||||||
type Call struct {
|
type Call struct {
|
||||||
Task string
|
Task string
|
||||||
Vars Vars
|
Vars *Vars
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,14 +10,14 @@ type Cmd struct {
|
|||||||
Cmd string
|
Cmd string
|
||||||
Silent bool
|
Silent bool
|
||||||
Task string
|
Task string
|
||||||
Vars Vars
|
Vars *Vars
|
||||||
IgnoreError bool
|
IgnoreError bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dep is a task dependency
|
// Dep is a task dependency
|
||||||
type Dep struct {
|
type Dep struct {
|
||||||
Task string
|
Task string
|
||||||
Vars Vars
|
Vars *Vars
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -51,7 +51,7 @@ func (c *Cmd) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
|||||||
}
|
}
|
||||||
var taskCall struct {
|
var taskCall struct {
|
||||||
Task string
|
Task string
|
||||||
Vars Vars
|
Vars *Vars
|
||||||
}
|
}
|
||||||
if err := unmarshal(&taskCall); err == nil {
|
if err := unmarshal(&taskCall); err == nil {
|
||||||
c.Task = taskCall.Task
|
c.Task = taskCall.Task
|
||||||
@@ -70,7 +70,7 @@ func (d *Dep) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
|||||||
}
|
}
|
||||||
var taskCall struct {
|
var taskCall struct {
|
||||||
Task string
|
Task string
|
||||||
Vars Vars
|
Vars *Vars
|
||||||
}
|
}
|
||||||
if err := unmarshal(&taskCall); err == nil {
|
if err := unmarshal(&taskCall); err == nil {
|
||||||
d.Task = taskCall.Task
|
d.Task = taskCall.Task
|
||||||
|
|||||||
40
internal/taskfile/included_taskfile.go
Normal file
40
internal/taskfile/included_taskfile.go
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
package taskfile
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrCantUnmarshalIncludedTaskfile is returned for invalid var YAML.
|
||||||
|
ErrCantUnmarshalIncludedTaskfile = errors.New("task: can't unmarshal included value")
|
||||||
|
)
|
||||||
|
|
||||||
|
// IncludedTaskfile represents information about included tasksfile
|
||||||
|
type IncludedTaskfile struct {
|
||||||
|
Taskfile string
|
||||||
|
Dir string
|
||||||
|
AdvancedImport bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// IncludedTaskfiles represents information about included tasksfiles
|
||||||
|
type IncludedTaskfiles = map[string]IncludedTaskfile
|
||||||
|
|
||||||
|
// UnmarshalYAML implements yaml.Unmarshaler interface
|
||||||
|
func (it *IncludedTaskfile) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||||
|
var str string
|
||||||
|
if err := unmarshal(&str); err == nil {
|
||||||
|
it.Taskfile = str
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var includedTaskfile struct {
|
||||||
|
Taskfile string
|
||||||
|
Dir string
|
||||||
|
}
|
||||||
|
if err := unmarshal(&includedTaskfile); err == nil {
|
||||||
|
it.Dir = includedTaskfile.Dir
|
||||||
|
it.Taskfile = includedTaskfile.Taskfile
|
||||||
|
it.AdvancedImport = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return ErrCantUnmarshalIncludedTaskfile
|
||||||
|
}
|
||||||
@@ -22,25 +22,20 @@ func Merge(t1, t2 *Taskfile, namespaces ...string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if t1.Includes == nil {
|
if t1.Includes == nil {
|
||||||
t1.Includes = make(map[string]string)
|
t1.Includes = make(IncludedTaskfiles)
|
||||||
}
|
}
|
||||||
for k, v := range t2.Includes {
|
for k, v := range t2.Includes {
|
||||||
t1.Includes[k] = v
|
t1.Includes[k] = v
|
||||||
}
|
}
|
||||||
|
|
||||||
if t1.Vars == nil {
|
if t1.Vars == nil {
|
||||||
t1.Vars = make(Vars)
|
t1.Vars = &Vars{}
|
||||||
}
|
}
|
||||||
for k, v := range t2.Vars {
|
|
||||||
t1.Vars[k] = v
|
|
||||||
}
|
|
||||||
|
|
||||||
if t1.Env == nil {
|
if t1.Env == nil {
|
||||||
t1.Env = make(Vars)
|
t1.Env = &Vars{}
|
||||||
}
|
|
||||||
for k, v := range t2.Env {
|
|
||||||
t1.Env[k] = v
|
|
||||||
}
|
}
|
||||||
|
t1.Vars.Merge(t2.Vars)
|
||||||
|
t1.Env.Merge(t2.Env)
|
||||||
|
|
||||||
if t1.Tasks == nil {
|
if t1.Tasks == nil {
|
||||||
t1.Tasks = make(Tasks)
|
t1.Tasks = make(Tasks)
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
"github.com/go-task/task/v2/internal/taskfile"
|
"github.com/go-task/task/v2/internal/taskfile"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestPreconditionParse(t *testing.T) {
|
func TestPreconditionParse(t *testing.T) {
|
||||||
|
|||||||
@@ -8,8 +8,9 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
"github.com/go-task/task/v2/internal/taskfile"
|
"github.com/go-task/task/v2/internal/taskfile"
|
||||||
|
"github.com/go-task/task/v2/internal/templater"
|
||||||
|
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -28,8 +29,30 @@ func Taskfile(dir string, entrypoint string) (*taskfile.Taskfile, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
for namespace, path := range t.Includes {
|
v, err := t.ParsedVersion()
|
||||||
path = filepath.Join(dir, path)
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for namespace, includedTask := range t.Includes {
|
||||||
|
if v >= 3.0 {
|
||||||
|
tr := templater.Templater{Vars: &taskfile.Vars{}, RemoveNoValue: true}
|
||||||
|
includedTask = taskfile.IncludedTaskfile{
|
||||||
|
Taskfile: tr.Replace(includedTask.Taskfile),
|
||||||
|
Dir: tr.Replace(includedTask.Dir),
|
||||||
|
AdvancedImport: includedTask.AdvancedImport,
|
||||||
|
}
|
||||||
|
if err := tr.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if filepath.IsAbs(includedTask.Taskfile) {
|
||||||
|
path = includedTask.Taskfile
|
||||||
|
} else {
|
||||||
|
path = filepath.Join(dir, includedTask.Taskfile)
|
||||||
|
}
|
||||||
|
|
||||||
info, err := os.Stat(path)
|
info, err := os.Stat(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -44,19 +67,30 @@ func Taskfile(dir string, entrypoint string) (*taskfile.Taskfile, error) {
|
|||||||
if len(includedTaskfile.Includes) > 0 {
|
if len(includedTaskfile.Includes) > 0 {
|
||||||
return nil, ErrIncludedTaskfilesCantHaveIncludes
|
return nil, ErrIncludedTaskfilesCantHaveIncludes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if includedTask.AdvancedImport {
|
||||||
|
for _, task := range includedTaskfile.Tasks {
|
||||||
|
if !filepath.IsAbs(task.Dir) {
|
||||||
|
task.Dir = filepath.Join(includedTask.Dir, task.Dir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if err = taskfile.Merge(t, includedTaskfile, namespace); err != nil {
|
if err = taskfile.Merge(t, includedTaskfile, namespace); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
path = filepath.Join(dir, fmt.Sprintf("Taskfile_%s.yml", runtime.GOOS))
|
if v < 3.0 {
|
||||||
if _, err = os.Stat(path); err == nil {
|
path = filepath.Join(dir, fmt.Sprintf("Taskfile_%s.yml", runtime.GOOS))
|
||||||
osTaskfile, err := readTaskfile(path)
|
if _, err = os.Stat(path); err == nil {
|
||||||
if err != nil {
|
osTaskfile, err := readTaskfile(path)
|
||||||
return nil, err
|
if err != nil {
|
||||||
}
|
return nil, err
|
||||||
if err = taskfile.Merge(t, osTaskfile); err != nil {
|
}
|
||||||
return nil, err
|
if err = taskfile.Merge(t, osTaskfile); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,12 +8,12 @@ import (
|
|||||||
|
|
||||||
"github.com/go-task/task/v2/internal/taskfile"
|
"github.com/go-task/task/v2/internal/taskfile"
|
||||||
|
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Taskvars reads a Taskvars for a given directory
|
// Taskvars reads a Taskvars for a given directory
|
||||||
func Taskvars(dir string) (taskfile.Vars, error) {
|
func Taskvars(dir string) (*taskfile.Vars, error) {
|
||||||
vars := make(taskfile.Vars)
|
vars := &taskfile.Vars{}
|
||||||
|
|
||||||
path := filepath.Join(dir, "Taskvars.yml")
|
path := filepath.Join(dir, "Taskvars.yml")
|
||||||
if _, err := os.Stat(path); err == nil {
|
if _, err := os.Stat(path); err == nil {
|
||||||
@@ -29,24 +29,17 @@ func Taskvars(dir string) (taskfile.Vars, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
vars.Merge(osVars)
|
||||||
if vars == nil {
|
|
||||||
vars = osVars
|
|
||||||
} else {
|
|
||||||
for k, v := range osVars {
|
|
||||||
vars[k] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return vars, nil
|
return vars, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func readTaskvars(file string) (taskfile.Vars, error) {
|
func readTaskvars(file string) (*taskfile.Vars, error) {
|
||||||
f, err := os.Open(file)
|
f, err := os.Open(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var vars taskfile.Vars
|
var vars taskfile.Vars
|
||||||
return vars, yaml.NewDecoder(f).Decode(&vars)
|
return &vars, yaml.NewDecoder(f).Decode(&vars)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +1,86 @@
|
|||||||
package taskfile
|
package taskfile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
// Tasks represents a group of tasks
|
// Tasks represents a group of tasks
|
||||||
type Tasks map[string]*Task
|
type Tasks map[string]*Task
|
||||||
|
|
||||||
// Task represents a task
|
// Task represents a task
|
||||||
type Task struct {
|
type Task struct {
|
||||||
Task string
|
Task string
|
||||||
Cmds []*Cmd
|
Cmds []*Cmd
|
||||||
Deps []*Dep
|
Deps []*Dep
|
||||||
Desc string
|
Desc string
|
||||||
Summary string
|
Summary string
|
||||||
Sources []string
|
Sources []string
|
||||||
Generates []string
|
Generates []string
|
||||||
Status []string
|
Status []string
|
||||||
Preconditions []*Precondition
|
Preconditions []*Precondition
|
||||||
Dir string
|
Dir string
|
||||||
Vars Vars
|
Vars *Vars
|
||||||
Env Vars
|
Env *Vars
|
||||||
Silent bool
|
Silent bool
|
||||||
Method string
|
Method string
|
||||||
Prefix string
|
Prefix string
|
||||||
IgnoreError bool `yaml:"ignore_error"`
|
IgnoreError bool
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrCantUnmarshalTask is returned for invalid task YAML
|
||||||
|
ErrCantUnmarshalTask = errors.New("task: can't unmarshal task value")
|
||||||
|
)
|
||||||
|
|
||||||
|
func (t *Task) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||||
|
var cmd Cmd
|
||||||
|
if err := unmarshal(&cmd); err == nil && cmd.Cmd != "" {
|
||||||
|
t.Cmds = append(t.Cmds, &cmd)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var cmds []*Cmd
|
||||||
|
if err := unmarshal(&cmds); err == nil && len(cmds) > 0 {
|
||||||
|
t.Cmds = cmds
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var task struct {
|
||||||
|
Cmds []*Cmd
|
||||||
|
Deps []*Dep
|
||||||
|
Desc string
|
||||||
|
Summary string
|
||||||
|
Sources []string
|
||||||
|
Generates []string
|
||||||
|
Status []string
|
||||||
|
Preconditions []*Precondition
|
||||||
|
Dir string
|
||||||
|
Vars *Vars
|
||||||
|
Env *Vars
|
||||||
|
Silent bool
|
||||||
|
Method string
|
||||||
|
Prefix string
|
||||||
|
IgnoreError bool `yaml:"ignore_error"`
|
||||||
|
}
|
||||||
|
if err := unmarshal(&task); err == nil {
|
||||||
|
t.Cmds = task.Cmds
|
||||||
|
t.Deps = task.Deps
|
||||||
|
t.Desc = task.Desc
|
||||||
|
t.Summary = task.Summary
|
||||||
|
t.Sources = task.Sources
|
||||||
|
t.Generates = task.Generates
|
||||||
|
t.Status = task.Status
|
||||||
|
t.Preconditions = task.Preconditions
|
||||||
|
t.Dir = task.Dir
|
||||||
|
t.Vars = task.Vars
|
||||||
|
t.Env = task.Env
|
||||||
|
t.Silent = task.Silent
|
||||||
|
t.Method = task.Method
|
||||||
|
t.Prefix = task.Prefix
|
||||||
|
t.IgnoreError = task.IgnoreError
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return ErrCantUnmarshalTask
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,21 @@
|
|||||||
package taskfile
|
package taskfile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
// Taskfile represents a Taskfile.yml
|
// Taskfile represents a Taskfile.yml
|
||||||
type Taskfile struct {
|
type Taskfile struct {
|
||||||
Version string
|
Version string
|
||||||
Expansions int
|
Expansions int
|
||||||
Output string
|
Output string
|
||||||
Method string
|
Method string
|
||||||
Includes map[string]string
|
Includes IncludedTaskfiles
|
||||||
Vars Vars
|
Vars *Vars
|
||||||
Env Vars
|
Env *Vars
|
||||||
Tasks Tasks
|
Tasks Tasks
|
||||||
|
Silent bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalYAML implements yaml.Unmarshaler interface
|
// UnmarshalYAML implements yaml.Unmarshaler interface
|
||||||
@@ -19,10 +25,11 @@ func (tf *Taskfile) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
|||||||
Expansions int
|
Expansions int
|
||||||
Output string
|
Output string
|
||||||
Method string
|
Method string
|
||||||
Includes map[string]string
|
Includes IncludedTaskfiles
|
||||||
Vars Vars
|
Vars *Vars
|
||||||
Env Vars
|
Env *Vars
|
||||||
Tasks Tasks
|
Tasks Tasks
|
||||||
|
Silent bool
|
||||||
}
|
}
|
||||||
if err := unmarshal(&taskfile); err != nil {
|
if err := unmarshal(&taskfile); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -35,11 +42,24 @@ func (tf *Taskfile) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
|||||||
tf.Vars = taskfile.Vars
|
tf.Vars = taskfile.Vars
|
||||||
tf.Env = taskfile.Env
|
tf.Env = taskfile.Env
|
||||||
tf.Tasks = taskfile.Tasks
|
tf.Tasks = taskfile.Tasks
|
||||||
|
tf.Silent = taskfile.Silent
|
||||||
if tf.Expansions <= 0 {
|
if tf.Expansions <= 0 {
|
||||||
tf.Expansions = 2
|
tf.Expansions = 2
|
||||||
}
|
}
|
||||||
if tf.Vars == nil {
|
if tf.Vars == nil {
|
||||||
tf.Vars = make(Vars)
|
tf.Vars = &Vars{}
|
||||||
|
}
|
||||||
|
if tf.Env == nil {
|
||||||
|
tf.Env = &Vars{}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ParsedVersion returns the version as a float64
|
||||||
|
func (tf *Taskfile) ParsedVersion() (float64, error) {
|
||||||
|
v, err := strconv.ParseFloat(tf.Version, 64)
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf(`task: Could not parse taskfile version "%s": %v`, tf.Version, err)
|
||||||
|
}
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
"github.com/go-task/task/v2/internal/taskfile"
|
"github.com/go-task/task/v2/internal/taskfile"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCmdParse(t *testing.T) {
|
func TestCmdParse(t *testing.T) {
|
||||||
@@ -33,9 +33,12 @@ vars:
|
|||||||
{
|
{
|
||||||
yamlTaskCall,
|
yamlTaskCall,
|
||||||
&taskfile.Cmd{},
|
&taskfile.Cmd{},
|
||||||
&taskfile.Cmd{Task: "another-task", Vars: taskfile.Vars{
|
&taskfile.Cmd{Task: "another-task", Vars: &taskfile.Vars{
|
||||||
"PARAM1": taskfile.Var{Static: "VALUE1"},
|
Keys: []string{"PARAM1", "PARAM2"},
|
||||||
"PARAM2": taskfile.Var{Static: "VALUE2"},
|
Mapping: map[string]taskfile.Var{
|
||||||
|
"PARAM1": taskfile.Var{Static: "VALUE1"},
|
||||||
|
"PARAM2": taskfile.Var{Static: "VALUE2"},
|
||||||
|
},
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -46,9 +49,12 @@ vars:
|
|||||||
{
|
{
|
||||||
yamlTaskCall,
|
yamlTaskCall,
|
||||||
&taskfile.Dep{},
|
&taskfile.Dep{},
|
||||||
&taskfile.Dep{Task: "another-task", Vars: taskfile.Vars{
|
&taskfile.Dep{Task: "another-task", Vars: &taskfile.Vars{
|
||||||
"PARAM1": taskfile.Var{Static: "VALUE1"},
|
Keys: []string{"PARAM1", "PARAM2"},
|
||||||
"PARAM2": taskfile.Var{Static: "VALUE2"},
|
Mapping: map[string]taskfile.Var{
|
||||||
|
"PARAM1": taskfile.Var{Static: "VALUE1"},
|
||||||
|
"PARAM2": taskfile.Var{Static: "VALUE2"},
|
||||||
|
},
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package taskfile
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -11,17 +13,83 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Vars is a string[string] variables map.
|
// Vars is a string[string] variables map.
|
||||||
type Vars map[string]Var
|
type Vars struct {
|
||||||
|
Keys []string
|
||||||
|
Mapping map[string]Var
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalYAML implements the yaml.Unmarshaler interface.
|
||||||
|
func (vs *Vars) UnmarshalYAML(node *yaml.Node) error {
|
||||||
|
if node.Kind != yaml.MappingNode {
|
||||||
|
return errors.New("task: vars is not a map")
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE(@andreynering): on this style of custom unmarsheling,
|
||||||
|
// even number contains the keys, while odd numbers contains
|
||||||
|
// the values.
|
||||||
|
for i := 0; i < len(node.Content); i += 2 {
|
||||||
|
keyNode := node.Content[i]
|
||||||
|
valueNode := node.Content[i+1]
|
||||||
|
|
||||||
|
var v Var
|
||||||
|
if err := valueNode.Decode(&v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
vs.Set(keyNode.Value, v)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge merges the given Vars into the caller one
|
||||||
|
func (vs *Vars) Merge(other *Vars) {
|
||||||
|
other.Range(func(key string, value Var) error {
|
||||||
|
vs.Set(key, value)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set sets a value to a given key
|
||||||
|
func (vs *Vars) Set(key string, value Var) {
|
||||||
|
if vs.Mapping == nil {
|
||||||
|
vs.Mapping = make(map[string]Var, 1)
|
||||||
|
}
|
||||||
|
if !strSliceContains(vs.Keys, key) {
|
||||||
|
vs.Keys = append(vs.Keys, key)
|
||||||
|
}
|
||||||
|
vs.Mapping[key] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
func strSliceContains(s []string, str string) bool {
|
||||||
|
for _, v := range s {
|
||||||
|
if v == str {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Range allows you to loop into the vars in its right order
|
||||||
|
func (vs *Vars) Range(yield func(key string, value Var) error) error {
|
||||||
|
if vs == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
for _, k := range vs.Keys {
|
||||||
|
if err := yield(k, vs.Mapping[k]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// ToCacheMap converts Vars to a map containing only the static
|
// ToCacheMap converts Vars to a map containing only the static
|
||||||
// variables
|
// variables
|
||||||
func (vs Vars) ToCacheMap() (m map[string]interface{}) {
|
func (vs *Vars) ToCacheMap() (m map[string]interface{}) {
|
||||||
m = make(map[string]interface{}, len(vs))
|
m = make(map[string]interface{}, len(vs.Keys))
|
||||||
for k, v := range vs {
|
vs.Range(func(k string, v Var) error {
|
||||||
if v.Sh != "" {
|
if v.Sh != "" {
|
||||||
// Dynamic variable is not yet resolved; trigger
|
// Dynamic variable is not yet resolved; trigger
|
||||||
// <no value> to be used in templates.
|
// <no value> to be used in templates.
|
||||||
continue
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if v.Live != nil {
|
if v.Live != nil {
|
||||||
@@ -29,7 +97,8 @@ func (vs Vars) ToCacheMap() (m map[string]interface{}) {
|
|||||||
} else {
|
} else {
|
||||||
m[k] = v.Static
|
m[k] = v.Static
|
||||||
}
|
}
|
||||||
}
|
return nil
|
||||||
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package templater
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"strings"
|
||||||
"text/template"
|
"text/template"
|
||||||
|
|
||||||
"github.com/go-task/task/v2/internal/taskfile"
|
"github.com/go-task/task/v2/internal/taskfile"
|
||||||
@@ -12,7 +13,8 @@ import (
|
|||||||
// happen will be assigned to r.err, and consecutive calls to funcs will just
|
// happen will be assigned to r.err, and consecutive calls to funcs will just
|
||||||
// return the zero value.
|
// return the zero value.
|
||||||
type Templater struct {
|
type Templater struct {
|
||||||
Vars taskfile.Vars
|
Vars *taskfile.Vars
|
||||||
|
RemoveNoValue bool
|
||||||
|
|
||||||
cacheMap map[string]interface{}
|
cacheMap map[string]interface{}
|
||||||
err error
|
err error
|
||||||
@@ -42,6 +44,9 @@ func (r *Templater) Replace(str string) string {
|
|||||||
r.err = err
|
r.err = err
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
if r.RemoveNoValue {
|
||||||
|
return strings.ReplaceAll(b.String(), "<no value>", "")
|
||||||
|
}
|
||||||
return b.String()
|
return b.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,20 +62,22 @@ func (r *Templater) ReplaceSlice(strs []string) []string {
|
|||||||
return new
|
return new
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Templater) ReplaceVars(vars taskfile.Vars) taskfile.Vars {
|
func (r *Templater) ReplaceVars(vars *taskfile.Vars) *taskfile.Vars {
|
||||||
if r.err != nil || len(vars) == 0 {
|
if r.err != nil || vars == nil || len(vars.Keys) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
new := make(taskfile.Vars, len(vars))
|
var new taskfile.Vars
|
||||||
for k, v := range vars {
|
vars.Range(func(k string, v taskfile.Var) error {
|
||||||
new[k] = taskfile.Var{
|
new.Set(k, taskfile.Var{
|
||||||
Static: r.Replace(v.Static),
|
Static: r.Replace(v.Static),
|
||||||
Live: v.Live,
|
Live: v.Live,
|
||||||
Sh: r.Replace(v.Sh),
|
Sh: r.Replace(v.Sh),
|
||||||
}
|
})
|
||||||
}
|
return nil
|
||||||
return new
|
})
|
||||||
|
|
||||||
|
return &new
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Templater) Err() error {
|
func (r *Templater) Err() error {
|
||||||
|
|||||||
74
task.go
74
task.go
@@ -6,12 +6,12 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
"github.com/go-task/task/v2/internal/compiler"
|
"github.com/go-task/task/v2/internal/compiler"
|
||||||
compilerv2 "github.com/go-task/task/v2/internal/compiler/v2"
|
compilerv2 "github.com/go-task/task/v2/internal/compiler/v2"
|
||||||
|
compilerv3 "github.com/go-task/task/v2/internal/compiler/v3"
|
||||||
"github.com/go-task/task/v2/internal/execext"
|
"github.com/go-task/task/v2/internal/execext"
|
||||||
"github.com/go-task/task/v2/internal/logger"
|
"github.com/go-task/task/v2/internal/logger"
|
||||||
"github.com/go-task/task/v2/internal/output"
|
"github.com/go-task/task/v2/internal/output"
|
||||||
@@ -40,6 +40,7 @@ type Executor struct {
|
|||||||
Silent bool
|
Silent bool
|
||||||
Dry bool
|
Dry bool
|
||||||
Summary bool
|
Summary bool
|
||||||
|
Parallel bool
|
||||||
Color bool
|
Color bool
|
||||||
|
|
||||||
Stdin io.Reader
|
Stdin io.Reader
|
||||||
@@ -51,7 +52,7 @@ type Executor struct {
|
|||||||
Output output.Output
|
Output output.Output
|
||||||
OutputStyle string
|
OutputStyle string
|
||||||
|
|
||||||
taskvars taskfile.Vars
|
taskvars *taskfile.Vars
|
||||||
|
|
||||||
taskCallCount map[string]*int32
|
taskCallCount map[string]*int32
|
||||||
mkdirMutexMap map[string]*sync.Mutex
|
mkdirMutexMap map[string]*sync.Mutex
|
||||||
@@ -69,7 +70,14 @@ func (e *Executor) Run(ctx context.Context, calls ...taskfile.Call) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if e.Summary {
|
if e.Summary {
|
||||||
summary.PrintTasks(e.Logger, e.Taskfile, calls)
|
for i, c := range calls {
|
||||||
|
compiledTask, err := e.CompiledTask(c)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
summary.PrintSpaceBetweenSummaries(e.Logger, i)
|
||||||
|
summary.PrintTask(e.Logger, compiledTask)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,12 +85,18 @@ func (e *Executor) Run(ctx context.Context, calls ...taskfile.Call) error {
|
|||||||
return e.watchTasks(calls...)
|
return e.watchTasks(calls...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
g, ctx := errgroup.WithContext(ctx)
|
||||||
for _, c := range calls {
|
for _, c := range calls {
|
||||||
if err := e.RunTask(ctx, c); err != nil {
|
c := c
|
||||||
return err
|
if e.Parallel {
|
||||||
|
g.Go(func() error { return e.RunTask(ctx, c) })
|
||||||
|
} else {
|
||||||
|
if err := e.RunTask(ctx, c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return g.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup setups Executor's internal state
|
// Setup setups Executor's internal state
|
||||||
@@ -96,11 +110,19 @@ func (e *Executor) Setup() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
e.taskvars, err = read.Taskvars(e.Dir)
|
|
||||||
|
v, err := e.Taskfile.ParsedVersion()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if v < 3.0 {
|
||||||
|
e.taskvars, err = read.Taskvars(e.Dir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if e.Stdin == nil {
|
if e.Stdin == nil {
|
||||||
e.Stdin = os.Stdin
|
e.Stdin = os.Stdin
|
||||||
}
|
}
|
||||||
@@ -117,11 +139,6 @@ func (e *Executor) Setup() error {
|
|||||||
Color: e.Color,
|
Color: e.Color,
|
||||||
}
|
}
|
||||||
|
|
||||||
v, err := strconv.ParseFloat(e.Taskfile.Version, 64)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf(`task: Could not parse taskfile version "%s": %v`, e.Taskfile.Version, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if v < 2 {
|
if v < 2 {
|
||||||
return fmt.Errorf(`task: Taskfile versions prior to v2 are not supported anymore`)
|
return fmt.Errorf(`task: Taskfile versions prior to v2 are not supported anymore`)
|
||||||
}
|
}
|
||||||
@@ -140,12 +157,20 @@ func (e *Executor) Setup() error {
|
|||||||
e.Logger.Color = false
|
e.Logger.Color = false
|
||||||
}
|
}
|
||||||
|
|
||||||
e.Compiler = &compilerv2.CompilerV2{
|
if v < 3 {
|
||||||
Dir: e.Dir,
|
e.Compiler = &compilerv2.CompilerV2{
|
||||||
Taskvars: e.taskvars,
|
Dir: e.Dir,
|
||||||
TaskfileVars: e.Taskfile.Vars,
|
Taskvars: e.taskvars,
|
||||||
Expansions: e.Taskfile.Expansions,
|
TaskfileVars: e.Taskfile.Vars,
|
||||||
Logger: e.Logger,
|
Expansions: e.Taskfile.Expansions,
|
||||||
|
Logger: e.Logger,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
e.Compiler = &compilerv3.CompilerV3{
|
||||||
|
Dir: e.Dir,
|
||||||
|
TaskfileVars: e.Taskfile.Vars,
|
||||||
|
Logger: e.Logger,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if v < 2.1 && e.Taskfile.Output != "" {
|
if v < 2.1 && e.Taskfile.Output != "" {
|
||||||
@@ -154,6 +179,9 @@ func (e *Executor) Setup() error {
|
|||||||
if v < 2.2 && len(e.Taskfile.Includes) > 0 {
|
if v < 2.2 && len(e.Taskfile.Includes) > 0 {
|
||||||
return fmt.Errorf(`task: Including Taskfiles is only available starting on Taskfile version v2.2`)
|
return fmt.Errorf(`task: Including Taskfiles is only available starting on Taskfile version v2.2`)
|
||||||
}
|
}
|
||||||
|
if v >= 3.0 && e.Taskfile.Expansions > 2 {
|
||||||
|
return fmt.Errorf(`task: The "expansions" setting is not available anymore on v3.0`)
|
||||||
|
}
|
||||||
|
|
||||||
if e.OutputStyle != "" {
|
if e.OutputStyle != "" {
|
||||||
e.Taskfile.Output = e.OutputStyle
|
e.Taskfile.Output = e.OutputStyle
|
||||||
@@ -200,6 +228,14 @@ func (e *Executor) Setup() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if v < 3 {
|
||||||
|
for _, taskfile := range e.Taskfile.Includes {
|
||||||
|
if taskfile.AdvancedImport {
|
||||||
|
return errors.New(`task: Import with additional parameters is only available starting on Taskfile version v3`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
e.taskCallCount = make(map[string]*int32, len(e.Taskfile.Tasks))
|
e.taskCallCount = make(map[string]*int32, len(e.Taskfile.Tasks))
|
||||||
e.mkdirMutexMap = make(map[string]*sync.Mutex, len(e.Taskfile.Tasks))
|
e.mkdirMutexMap = make(map[string]*sync.Mutex, len(e.Taskfile.Tasks))
|
||||||
for k := range e.Taskfile.Tasks {
|
for k := range e.Taskfile.Tasks {
|
||||||
@@ -309,7 +345,7 @@ func (e *Executor) runCommand(ctx context.Context, t *taskfile.Task, call taskfi
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
case cmd.Cmd != "":
|
case cmd.Cmd != "":
|
||||||
if e.Verbose || (!cmd.Silent && !t.Silent && !e.Silent) {
|
if e.Verbose || (!cmd.Silent && !t.Silent && !e.Taskfile.Silent && !e.Silent) {
|
||||||
e.Logger.Errf(logger.Green, "task: %s", cmd.Cmd)
|
e.Logger.Errf(logger.Green, "task: %s", cmd.Cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
55
task_test.go
55
task_test.go
@@ -98,6 +98,7 @@ func TestVarsV2(t *testing.T) {
|
|||||||
"nestedtmpl2_foo2.txt": "<no value>",
|
"nestedtmpl2_foo2.txt": "<no value>",
|
||||||
"override.txt": "bar",
|
"override.txt": "bar",
|
||||||
"nested.txt": "Taskvars-TaskfileVars-TaskVars",
|
"nested.txt": "Taskvars-TaskfileVars-TaskVars",
|
||||||
|
"task_name.txt": "hello",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
tt.Run(t)
|
tt.Run(t)
|
||||||
@@ -106,6 +107,20 @@ func TestVarsV2(t *testing.T) {
|
|||||||
tt.Run(t)
|
tt.Run(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestVarsV3(t *testing.T) {
|
||||||
|
tt := fileContentTest{
|
||||||
|
Dir: "testdata/vars/v3",
|
||||||
|
Target: "default",
|
||||||
|
Files: map[string]string{
|
||||||
|
"missing-var.txt": "\n",
|
||||||
|
"var-order.txt": "ABCDEF\n",
|
||||||
|
"dependent-sh.txt": "123456\n",
|
||||||
|
"with-call.txt": "Hi, ABC123!\n",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
tt.Run(t)
|
||||||
|
}
|
||||||
|
|
||||||
func TestMultilineVars(t *testing.T) {
|
func TestMultilineVars(t *testing.T) {
|
||||||
for _, dir := range []string{"testdata/vars/v2/multiline"} {
|
for _, dir := range []string{"testdata/vars/v2/multiline"} {
|
||||||
tt := fileContentTest{
|
tt := fileContentTest{
|
||||||
@@ -539,14 +554,33 @@ func TestIncludes(t *testing.T) {
|
|||||||
Target: "default",
|
Target: "default",
|
||||||
TrimSpace: true,
|
TrimSpace: true,
|
||||||
Files: map[string]string{
|
Files: map[string]string{
|
||||||
"main.txt": "main",
|
"main.txt": "main",
|
||||||
"included_directory.txt": "included_directory",
|
"included_directory.txt": "included_directory",
|
||||||
"included_taskfile.txt": "included_taskfile",
|
"included_directory_without_dir.txt": "included_directory_without_dir",
|
||||||
|
"included_taskfile_without_dir.txt": "included_taskfile_without_dir",
|
||||||
|
"./module2/included_directory_with_dir.txt": "included_directory_with_dir",
|
||||||
|
"./module2/included_taskfile_with_dir.txt": "included_taskfile_with_dir",
|
||||||
|
"os_include.txt": "os",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
tt.Run(t)
|
tt.Run(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIncorrectVersionIncludes(t *testing.T) {
|
||||||
|
const dir = "testdata/incorrect_includes"
|
||||||
|
expectedError := "task: Import with additional parameters is only available starting on Taskfile version v3"
|
||||||
|
|
||||||
|
var buff bytes.Buffer
|
||||||
|
e := task.Executor{
|
||||||
|
Dir: dir,
|
||||||
|
Stdout: &buff,
|
||||||
|
Stderr: &buff,
|
||||||
|
Silent: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.EqualError(t, e.Setup(), expectedError)
|
||||||
|
}
|
||||||
|
|
||||||
func TestIncludesEmptyMain(t *testing.T) {
|
func TestIncludesEmptyMain(t *testing.T) {
|
||||||
tt := fileContentTest{
|
tt := fileContentTest{
|
||||||
Dir: "testdata/includes_empty",
|
Dir: "testdata/includes_empty",
|
||||||
@@ -683,3 +717,18 @@ func TestDisplaysErrorOnUnsupportedVersion(t *testing.T) {
|
|||||||
assert.Equal(t, "task: Taskfile versions prior to v2 are not supported anymore", err.Error())
|
assert.Equal(t, "task: Taskfile versions prior to v2 are not supported anymore", err.Error())
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestShortTaskNotation(t *testing.T) {
|
||||||
|
const dir = "testdata/short_task_notation"
|
||||||
|
|
||||||
|
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: "default"}))
|
||||||
|
assert.Equal(t, "string-slice-1\nstring-slice-2\nstring\n", buff.String())
|
||||||
|
}
|
||||||
|
|||||||
2
testdata/checksum/Taskfile.yml
vendored
2
testdata/checksum/Taskfile.yml
vendored
@@ -1,4 +1,4 @@
|
|||||||
version: '2'
|
version: '3'
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
build:
|
build:
|
||||||
|
|||||||
2
testdata/cyclic/Taskfile.yml
vendored
2
testdata/cyclic/Taskfile.yml
vendored
@@ -1,4 +1,4 @@
|
|||||||
version: '2'
|
version: '3'
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
task-1:
|
task-1:
|
||||||
|
|||||||
2
testdata/deps/Taskfile.yml
vendored
2
testdata/deps/Taskfile.yml
vendored
@@ -1,4 +1,4 @@
|
|||||||
version: '2'
|
version: '3'
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
default:
|
default:
|
||||||
|
|||||||
2
testdata/dir/Taskfile.yml
vendored
2
testdata/dir/Taskfile.yml
vendored
@@ -1,4 +1,4 @@
|
|||||||
version: '2'
|
version: '3'
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
whereami:
|
whereami:
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
version: '2'
|
version: '3'
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
whereami:
|
whereami:
|
||||||
|
|||||||
2
testdata/dir/explicit_exists/Taskfile.yml
vendored
2
testdata/dir/explicit_exists/Taskfile.yml
vendored
@@ -1,4 +1,4 @@
|
|||||||
version: '2'
|
version: '3'
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
whereami:
|
whereami:
|
||||||
|
|||||||
2
testdata/dry/Taskfile.yml
vendored
2
testdata/dry/Taskfile.yml
vendored
@@ -1,4 +1,4 @@
|
|||||||
version: '2'
|
version: '3'
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
build:
|
build:
|
||||||
|
|||||||
2
testdata/dry_checksum/Taskfile.yml
vendored
2
testdata/dry_checksum/Taskfile.yml
vendored
@@ -1,4 +1,4 @@
|
|||||||
version: '2'
|
version: '3'
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
default:
|
default:
|
||||||
|
|||||||
2
testdata/env/Taskfile.yml
vendored
2
testdata/env/Taskfile.yml
vendored
@@ -1,4 +1,4 @@
|
|||||||
version: '2'
|
version: '3'
|
||||||
|
|
||||||
vars:
|
vars:
|
||||||
BAZ:
|
BAZ:
|
||||||
|
|||||||
2
testdata/expand/Taskfile.yml
vendored
2
testdata/expand/Taskfile.yml
vendored
@@ -1,4 +1,4 @@
|
|||||||
version: '2'
|
version: '3'
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
pwd:
|
pwd:
|
||||||
|
|||||||
5
testdata/generates/Taskfile.yml
vendored
5
testdata/generates/Taskfile.yml
vendored
@@ -1,4 +1,7 @@
|
|||||||
version: '2'
|
version: '3'
|
||||||
|
|
||||||
|
vars:
|
||||||
|
BUILD_DIR: $pwd
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
abs.txt:
|
abs.txt:
|
||||||
|
|||||||
1
testdata/generates/Taskvars.yml
vendored
1
testdata/generates/Taskvars.yml
vendored
@@ -1 +0,0 @@
|
|||||||
BUILD_DIR: $pwd
|
|
||||||
2
testdata/ignore_errors/Taskfile.yml
vendored
2
testdata/ignore_errors/Taskfile.yml
vendored
@@ -1,4 +1,4 @@
|
|||||||
version: '2'
|
version: '3'
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
task-should-pass:
|
task-should-pass:
|
||||||
|
|||||||
18
testdata/includes/Taskfile.yml
vendored
18
testdata/includes/Taskfile.yml
vendored
@@ -1,8 +1,19 @@
|
|||||||
version: '2'
|
version: '3'
|
||||||
|
|
||||||
includes:
|
includes:
|
||||||
included: ./included
|
included: ./included
|
||||||
included_taskfile: ./Taskfile2.yml
|
included_taskfile: ./Taskfile2.yml
|
||||||
|
included_without_dir:
|
||||||
|
taskfile: ./module1
|
||||||
|
included_taskfile_without_dir:
|
||||||
|
taskfile: ./module1/Taskfile.yml
|
||||||
|
included_with_dir:
|
||||||
|
taskfile: ./module2
|
||||||
|
dir: ./module2
|
||||||
|
included_taskfile_with_dir:
|
||||||
|
taskfile: ./module2/Taskfile.yml
|
||||||
|
dir: ./module2
|
||||||
|
included_os: ./Taskfile_{{OS}}.yml
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
default:
|
default:
|
||||||
@@ -10,6 +21,11 @@ tasks:
|
|||||||
- task: gen
|
- task: gen
|
||||||
- task: included:gen
|
- task: included:gen
|
||||||
- task: included_taskfile:gen
|
- task: included_taskfile:gen
|
||||||
|
- task: included_without_dir:gen_file
|
||||||
|
- task: included_taskfile_without_dir:gen_dir
|
||||||
|
- task: included_with_dir:gen_file
|
||||||
|
- task: included_taskfile_with_dir:gen_dir
|
||||||
|
- task: included_os:gen
|
||||||
|
|
||||||
gen:
|
gen:
|
||||||
cmds:
|
cmds:
|
||||||
|
|||||||
2
testdata/includes/Taskfile2.yml
vendored
2
testdata/includes/Taskfile2.yml
vendored
@@ -1,4 +1,4 @@
|
|||||||
version: '2'
|
version: '3'
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
gen:
|
gen:
|
||||||
|
|||||||
4
testdata/includes/Taskfile_darwin.yml
vendored
Normal file
4
testdata/includes/Taskfile_darwin.yml
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
version: '3'
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
gen: echo 'os' > os_include.txt
|
||||||
4
testdata/includes/Taskfile_linux.yml
vendored
Normal file
4
testdata/includes/Taskfile_linux.yml
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
version: '3'
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
gen: echo 'os' > os_include.txt
|
||||||
4
testdata/includes/Taskfile_windows.yml
vendored
Normal file
4
testdata/includes/Taskfile_windows.yml
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
version: '3'
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
gen: echo 'os' > os_include.txt
|
||||||
2
testdata/includes/included/Taskfile.yml
vendored
2
testdata/includes/included/Taskfile.yml
vendored
@@ -1,4 +1,4 @@
|
|||||||
version: '2'
|
version: '3'
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
gen:
|
gen:
|
||||||
|
|||||||
10
testdata/includes/module1/Taskfile.yml
vendored
Normal file
10
testdata/includes/module1/Taskfile.yml
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
version: '3'
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
gen_dir:
|
||||||
|
cmds:
|
||||||
|
- echo included_directory_without_dir > included_directory_without_dir.txt
|
||||||
|
|
||||||
|
gen_file:
|
||||||
|
cmds:
|
||||||
|
- echo included_taskfile_without_dir > included_taskfile_without_dir.txt
|
||||||
10
testdata/includes/module2/Taskfile.yml
vendored
Normal file
10
testdata/includes/module2/Taskfile.yml
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
version: '3'
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
gen_dir:
|
||||||
|
cmds:
|
||||||
|
- echo included_directory_with_dir > included_directory_with_dir.txt
|
||||||
|
|
||||||
|
gen_file:
|
||||||
|
cmds:
|
||||||
|
- echo included_taskfile_with_dir > included_taskfile_with_dir.txt
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
version: '2'
|
version: '3'
|
||||||
|
|
||||||
includes:
|
includes:
|
||||||
included: Taskfile2.yml
|
included: Taskfile2.yml
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
version: '2'
|
version: '3'
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
call-root:
|
call-root:
|
||||||
|
|||||||
2
testdata/includes_deps/Taskfile.yml
vendored
2
testdata/includes_deps/Taskfile.yml
vendored
@@ -1,4 +1,4 @@
|
|||||||
version: '2'
|
version: '3'
|
||||||
|
|
||||||
includes:
|
includes:
|
||||||
included: Taskfile2.yml
|
included: Taskfile2.yml
|
||||||
|
|||||||
2
testdata/includes_deps/Taskfile2.yml
vendored
2
testdata/includes_deps/Taskfile2.yml
vendored
@@ -1,4 +1,4 @@
|
|||||||
version: '2'
|
version: '3'
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
default:
|
default:
|
||||||
|
|||||||
2
testdata/includes_empty/Taskfile.yml
vendored
2
testdata/includes_empty/Taskfile.yml
vendored
@@ -1,4 +1,4 @@
|
|||||||
version: '2'
|
version: '3'
|
||||||
|
|
||||||
includes:
|
includes:
|
||||||
included: Taskfile2.yml
|
included: Taskfile2.yml
|
||||||
|
|||||||
2
testdata/includes_empty/Taskfile2.yml
vendored
2
testdata/includes_empty/Taskfile2.yml
vendored
@@ -1,4 +1,4 @@
|
|||||||
version: '2'
|
version: '3'
|
||||||
|
|
||||||
vars:
|
vars:
|
||||||
FILE: file.txt
|
FILE: file.txt
|
||||||
|
|||||||
10
testdata/incorrect_includes/Taskfile.yml
vendored
Normal file
10
testdata/incorrect_includes/Taskfile.yml
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
version: '2.6'
|
||||||
|
|
||||||
|
includes:
|
||||||
|
included:
|
||||||
|
taskfile: ./included
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
default:
|
||||||
|
cmds:
|
||||||
|
- task: gen
|
||||||
6
testdata/incorrect_includes/included/Taskfile.yml
vendored
Normal file
6
testdata/incorrect_includes/included/Taskfile.yml
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
version: '2.6'
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
gen:
|
||||||
|
cmds:
|
||||||
|
- echo incorrect includes test
|
||||||
6
testdata/params/Taskfile.yml
vendored
6
testdata/params/Taskfile.yml
vendored
@@ -1,4 +1,8 @@
|
|||||||
version: '2'
|
version: '3'
|
||||||
|
|
||||||
|
vars:
|
||||||
|
PORTUGUESE_HELLO_WORLD: Olá, mundo!
|
||||||
|
GERMAN: Hello
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
default:
|
default:
|
||||||
|
|||||||
2
testdata/params/Taskvars.yml
vendored
2
testdata/params/Taskvars.yml
vendored
@@ -1,2 +0,0 @@
|
|||||||
PORTUGUESE_HELLO_WORLD: Olá, mundo!
|
|
||||||
GERMAN: "Hello"
|
|
||||||
2
testdata/precondition/Taskfile.yml
vendored
2
testdata/precondition/Taskfile.yml
vendored
@@ -1,4 +1,4 @@
|
|||||||
version: '2'
|
version: '3'
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
foo:
|
foo:
|
||||||
|
|||||||
12
testdata/short_task_notation/Taskfile.yml
vendored
Normal file
12
testdata/short_task_notation/Taskfile.yml
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
version: '3'
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
default:
|
||||||
|
- task: string-slice
|
||||||
|
- task: string
|
||||||
|
|
||||||
|
string-slice:
|
||||||
|
- echo "string-slice-1"
|
||||||
|
- echo "string-slice-2"
|
||||||
|
|
||||||
|
string: echo "string"
|
||||||
2
testdata/status/Taskfile.yml
vendored
2
testdata/status/Taskfile.yml
vendored
@@ -1,4 +1,4 @@
|
|||||||
version: '2'
|
version: '3'
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
gen-foo:
|
gen-foo:
|
||||||
|
|||||||
2
testdata/summary/Taskfile.yml
vendored
2
testdata/summary/Taskfile.yml
vendored
@@ -1,4 +1,4 @@
|
|||||||
version: '2'
|
version: '3'
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
task-with-summary:
|
task-with-summary:
|
||||||
|
|||||||
1
testdata/vars/v2/Taskfile.yml
vendored
1
testdata/vars/v2/Taskfile.yml
vendored
@@ -32,6 +32,7 @@ tasks:
|
|||||||
- echo '{{.NESTEDTMPL2_FOO2}}' > nestedtmpl2_foo2.txt
|
- echo '{{.NESTEDTMPL2_FOO2}}' > nestedtmpl2_foo2.txt
|
||||||
- echo {{.OVERRIDE}} > override.txt
|
- echo {{.OVERRIDE}} > override.txt
|
||||||
- echo '{{.NESTED3}}' > nested.txt
|
- echo '{{.NESTED3}}' > nested.txt
|
||||||
|
- echo '{{.TASK}}' > task_name.txt
|
||||||
vars:
|
vars:
|
||||||
FOO: foo
|
FOO: foo
|
||||||
BAR: $echo bar
|
BAR: $echo bar
|
||||||
|
|||||||
1
testdata/vars/v3/.gitignore
vendored
Normal file
1
testdata/vars/v3/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
*.txt
|
||||||
46
testdata/vars/v3/Taskfile.yml
vendored
Normal file
46
testdata/vars/v3/Taskfile.yml
vendored
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
version: '3'
|
||||||
|
|
||||||
|
vars:
|
||||||
|
VAR_A: A
|
||||||
|
VAR_B: '{{.VAR_A}}B'
|
||||||
|
VAR_C: '{{.VAR_B}}C'
|
||||||
|
|
||||||
|
VAR_1: {sh: echo 1}
|
||||||
|
VAR_2: {sh: 'echo "{{.VAR_1}}2"'}
|
||||||
|
VAR_3: {sh: 'echo "{{.VAR_2}}3"'}
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
default:
|
||||||
|
- task: missing-var
|
||||||
|
- task: var-order
|
||||||
|
- task: dependent-sh
|
||||||
|
- task: with-call
|
||||||
|
|
||||||
|
missing-var: echo '{{.NON_EXISTING_VAR}}' > missing-var.txt
|
||||||
|
|
||||||
|
var-order:
|
||||||
|
vars:
|
||||||
|
VAR_D: '{{.VAR_C}}D'
|
||||||
|
VAR_E: '{{.VAR_D}}E'
|
||||||
|
VAR_F: '{{.VAR_E}}F'
|
||||||
|
cmds:
|
||||||
|
- echo '{{.VAR_F}}' > var-order.txt
|
||||||
|
|
||||||
|
dependent-sh:
|
||||||
|
vars:
|
||||||
|
VAR_4: {sh: 'echo "{{.VAR_3}}4"'}
|
||||||
|
VAR_5: {sh: 'echo "{{.VAR_4}}5"'}
|
||||||
|
VAR_6: {sh: 'echo "{{.VAR_5}}6"'}
|
||||||
|
cmds:
|
||||||
|
- echo '{{.VAR_6}}' > dependent-sh.txt
|
||||||
|
|
||||||
|
with-call:
|
||||||
|
- task: called-task
|
||||||
|
vars:
|
||||||
|
ABC123: '{{.VAR_C}}{{.VAR_3}}'
|
||||||
|
|
||||||
|
called-task:
|
||||||
|
vars:
|
||||||
|
MESSAGE: Hi, {{.ABC123}}!
|
||||||
|
cmds:
|
||||||
|
- echo "{{.MESSAGE}}" > with-call.txt
|
||||||
30
variables.go
30
variables.go
@@ -23,11 +23,17 @@ func (e *Executor) CompiledTask(call taskfile.Call) (*taskfile.Task, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
r := templater.Templater{Vars: vars}
|
v, err := e.Taskfile.ParsedVersion()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
r := templater.Templater{Vars: vars, RemoveNoValue: v >= 3.0}
|
||||||
|
|
||||||
new := taskfile.Task{
|
new := taskfile.Task{
|
||||||
Task: origTask.Task,
|
Task: origTask.Task,
|
||||||
Desc: r.Replace(origTask.Desc),
|
Desc: r.Replace(origTask.Desc),
|
||||||
|
Summary: r.Replace(origTask.Summary),
|
||||||
Sources: r.ReplaceSlice(origTask.Sources),
|
Sources: r.ReplaceSlice(origTask.Sources),
|
||||||
Generates: r.ReplaceSlice(origTask.Generates),
|
Generates: r.ReplaceSlice(origTask.Generates),
|
||||||
Dir: r.Replace(origTask.Dir),
|
Dir: r.Replace(origTask.Dir),
|
||||||
@@ -49,19 +55,19 @@ func (e *Executor) CompiledTask(call taskfile.Call) (*taskfile.Task, error) {
|
|||||||
new.Prefix = new.Task
|
new.Prefix = new.Task
|
||||||
}
|
}
|
||||||
|
|
||||||
new.Env = make(taskfile.Vars, len(e.Taskfile.Env)+len(origTask.Env))
|
new.Env = &taskfile.Vars{}
|
||||||
for k, v := range r.ReplaceVars(e.Taskfile.Env) {
|
new.Env.Merge(r.ReplaceVars(e.Taskfile.Env))
|
||||||
new.Env[k] = v
|
new.Env.Merge(r.ReplaceVars(origTask.Env))
|
||||||
}
|
err = new.Env.Range(func(k string, v taskfile.Var) error {
|
||||||
for k, v := range r.ReplaceVars(origTask.Env) {
|
|
||||||
new.Env[k] = v
|
|
||||||
}
|
|
||||||
for k, v := range new.Env {
|
|
||||||
static, err := e.Compiler.HandleDynamicVar(v)
|
static, err := e.Compiler.HandleDynamicVar(v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
new.Env[k] = taskfile.Var{Static: static}
|
new.Env.Set(k, taskfile.Var{Static: static})
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(origTask.Cmds) > 0 {
|
if len(origTask.Cmds) > 0 {
|
||||||
@@ -102,7 +108,7 @@ func (e *Executor) CompiledTask(call taskfile.Call) (*taskfile.Task, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
vars[strings.ToUpper(checker.Kind())] = taskfile.Var{Live: value}
|
vars.Set(strings.ToUpper(checker.Kind()), taskfile.Var{Live: value})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adding new variables, requires us to refresh the templaters
|
// Adding new variables, requires us to refresh the templaters
|
||||||
|
|||||||
21
vendor/github.com/mitchellh/go-homedir/LICENSE
generated
vendored
21
vendor/github.com/mitchellh/go-homedir/LICENSE
generated
vendored
@@ -1,21 +0,0 @@
|
|||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2013 Mitchell Hashimoto
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
||||||
14
vendor/github.com/mitchellh/go-homedir/README.md
generated
vendored
14
vendor/github.com/mitchellh/go-homedir/README.md
generated
vendored
@@ -1,14 +0,0 @@
|
|||||||
# go-homedir
|
|
||||||
|
|
||||||
This is a Go library for detecting the user's home directory without
|
|
||||||
the use of cgo, so the library can be used in cross-compilation environments.
|
|
||||||
|
|
||||||
Usage is incredibly simple, just call `homedir.Dir()` to get the home directory
|
|
||||||
for a user, and `homedir.Expand()` to expand the `~` in a path to the home
|
|
||||||
directory.
|
|
||||||
|
|
||||||
**Why not just use `os/user`?** The built-in `os/user` package requires
|
|
||||||
cgo on Darwin systems. This means that any Go code that uses that package
|
|
||||||
cannot cross compile. But 99% of the time the use for `os/user` is just to
|
|
||||||
retrieve the home directory, which we can do for the current user without
|
|
||||||
cgo. This library does that, enabling cross-compilation.
|
|
||||||
1
vendor/github.com/mitchellh/go-homedir/go.mod
generated
vendored
1
vendor/github.com/mitchellh/go-homedir/go.mod
generated
vendored
@@ -1 +0,0 @@
|
|||||||
module github.com/mitchellh/go-homedir
|
|
||||||
157
vendor/github.com/mitchellh/go-homedir/homedir.go
generated
vendored
157
vendor/github.com/mitchellh/go-homedir/homedir.go
generated
vendored
@@ -1,157 +0,0 @@
|
|||||||
package homedir
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"errors"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"path/filepath"
|
|
||||||
"runtime"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
// DisableCache will disable caching of the home directory. Caching is enabled
|
|
||||||
// by default.
|
|
||||||
var DisableCache bool
|
|
||||||
|
|
||||||
var homedirCache string
|
|
||||||
var cacheLock sync.RWMutex
|
|
||||||
|
|
||||||
// Dir returns the home directory for the executing user.
|
|
||||||
//
|
|
||||||
// This uses an OS-specific method for discovering the home directory.
|
|
||||||
// An error is returned if a home directory cannot be detected.
|
|
||||||
func Dir() (string, error) {
|
|
||||||
if !DisableCache {
|
|
||||||
cacheLock.RLock()
|
|
||||||
cached := homedirCache
|
|
||||||
cacheLock.RUnlock()
|
|
||||||
if cached != "" {
|
|
||||||
return cached, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cacheLock.Lock()
|
|
||||||
defer cacheLock.Unlock()
|
|
||||||
|
|
||||||
var result string
|
|
||||||
var err error
|
|
||||||
if runtime.GOOS == "windows" {
|
|
||||||
result, err = dirWindows()
|
|
||||||
} else {
|
|
||||||
// Unix-like system, so just assume Unix
|
|
||||||
result, err = dirUnix()
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
homedirCache = result
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Expand expands the path to include the home directory if the path
|
|
||||||
// is prefixed with `~`. If it isn't prefixed with `~`, the path is
|
|
||||||
// returned as-is.
|
|
||||||
func Expand(path string) (string, error) {
|
|
||||||
if len(path) == 0 {
|
|
||||||
return path, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if path[0] != '~' {
|
|
||||||
return path, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(path) > 1 && path[1] != '/' && path[1] != '\\' {
|
|
||||||
return "", errors.New("cannot expand user-specific home dir")
|
|
||||||
}
|
|
||||||
|
|
||||||
dir, err := Dir()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return filepath.Join(dir, path[1:]), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func dirUnix() (string, error) {
|
|
||||||
homeEnv := "HOME"
|
|
||||||
if runtime.GOOS == "plan9" {
|
|
||||||
// On plan9, env vars are lowercase.
|
|
||||||
homeEnv = "home"
|
|
||||||
}
|
|
||||||
|
|
||||||
// First prefer the HOME environmental variable
|
|
||||||
if home := os.Getenv(homeEnv); home != "" {
|
|
||||||
return home, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var stdout bytes.Buffer
|
|
||||||
|
|
||||||
// If that fails, try OS specific commands
|
|
||||||
if runtime.GOOS == "darwin" {
|
|
||||||
cmd := exec.Command("sh", "-c", `dscl -q . -read /Users/"$(whoami)" NFSHomeDirectory | sed 's/^[^ ]*: //'`)
|
|
||||||
cmd.Stdout = &stdout
|
|
||||||
if err := cmd.Run(); err == nil {
|
|
||||||
result := strings.TrimSpace(stdout.String())
|
|
||||||
if result != "" {
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
cmd := exec.Command("getent", "passwd", strconv.Itoa(os.Getuid()))
|
|
||||||
cmd.Stdout = &stdout
|
|
||||||
if err := cmd.Run(); err != nil {
|
|
||||||
// If the error is ErrNotFound, we ignore it. Otherwise, return it.
|
|
||||||
if err != exec.ErrNotFound {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if passwd := strings.TrimSpace(stdout.String()); passwd != "" {
|
|
||||||
// username:password:uid:gid:gecos:home:shell
|
|
||||||
passwdParts := strings.SplitN(passwd, ":", 7)
|
|
||||||
if len(passwdParts) > 5 {
|
|
||||||
return passwdParts[5], nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If all else fails, try the shell
|
|
||||||
stdout.Reset()
|
|
||||||
cmd := exec.Command("sh", "-c", "cd && pwd")
|
|
||||||
cmd.Stdout = &stdout
|
|
||||||
if err := cmd.Run(); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
result := strings.TrimSpace(stdout.String())
|
|
||||||
if result == "" {
|
|
||||||
return "", errors.New("blank output when reading home directory")
|
|
||||||
}
|
|
||||||
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func dirWindows() (string, error) {
|
|
||||||
// First prefer the HOME environmental variable
|
|
||||||
if home := os.Getenv("HOME"); home != "" {
|
|
||||||
return home, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prefer standard environment variable USERPROFILE
|
|
||||||
if home := os.Getenv("USERPROFILE"); home != "" {
|
|
||||||
return home, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
drive := os.Getenv("HOMEDRIVE")
|
|
||||||
path := os.Getenv("HOMEPATH")
|
|
||||||
home := drive + path
|
|
||||||
if drive == "" || path == "" {
|
|
||||||
return "", errors.New("HOMEDRIVE, HOMEPATH, or USERPROFILE are blank")
|
|
||||||
}
|
|
||||||
|
|
||||||
return home, nil
|
|
||||||
}
|
|
||||||
82
vendor/github.com/stretchr/testify/assert/assertion_format.go
generated
vendored
82
vendor/github.com/stretchr/testify/assert/assertion_format.go
generated
vendored
@@ -113,6 +113,17 @@ func Errorf(t TestingT, err error, msg string, args ...interface{}) bool {
|
|||||||
return Error(t, err, append([]interface{}{msg}, args...)...)
|
return Error(t, err, append([]interface{}{msg}, args...)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Eventuallyf asserts that given condition will be met in waitFor time,
|
||||||
|
// periodically checking target function each tick.
|
||||||
|
//
|
||||||
|
// assert.Eventuallyf(t, func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted")
|
||||||
|
func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool {
|
||||||
|
if h, ok := t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
return Eventually(t, condition, waitFor, tick, append([]interface{}{msg}, args...)...)
|
||||||
|
}
|
||||||
|
|
||||||
// Exactlyf asserts that two objects are equal in value and type.
|
// Exactlyf asserts that two objects are equal in value and type.
|
||||||
//
|
//
|
||||||
// assert.Exactlyf(t, int32(123, "error message %s", "formatted"), int64(123))
|
// assert.Exactlyf(t, int32(123, "error message %s", "formatted"), int64(123))
|
||||||
@@ -157,6 +168,31 @@ func FileExistsf(t TestingT, path string, msg string, args ...interface{}) bool
|
|||||||
return FileExists(t, path, append([]interface{}{msg}, args...)...)
|
return FileExists(t, path, append([]interface{}{msg}, args...)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Greaterf asserts that the first element is greater than the second
|
||||||
|
//
|
||||||
|
// assert.Greaterf(t, 2, 1, "error message %s", "formatted")
|
||||||
|
// assert.Greaterf(t, float64(2, "error message %s", "formatted"), float64(1))
|
||||||
|
// assert.Greaterf(t, "b", "a", "error message %s", "formatted")
|
||||||
|
func Greaterf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool {
|
||||||
|
if h, ok := t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
return Greater(t, e1, e2, append([]interface{}{msg}, args...)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GreaterOrEqualf asserts that the first element is greater than or equal to the second
|
||||||
|
//
|
||||||
|
// assert.GreaterOrEqualf(t, 2, 1, "error message %s", "formatted")
|
||||||
|
// assert.GreaterOrEqualf(t, 2, 2, "error message %s", "formatted")
|
||||||
|
// assert.GreaterOrEqualf(t, "b", "a", "error message %s", "formatted")
|
||||||
|
// assert.GreaterOrEqualf(t, "b", "b", "error message %s", "formatted")
|
||||||
|
func GreaterOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool {
|
||||||
|
if h, ok := t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
return GreaterOrEqual(t, e1, e2, append([]interface{}{msg}, args...)...)
|
||||||
|
}
|
||||||
|
|
||||||
// HTTPBodyContainsf asserts that a specified handler returns a
|
// HTTPBodyContainsf asserts that a specified handler returns a
|
||||||
// body that contains a string.
|
// body that contains a string.
|
||||||
//
|
//
|
||||||
@@ -289,6 +325,14 @@ func JSONEqf(t TestingT, expected string, actual string, msg string, args ...int
|
|||||||
return JSONEq(t, expected, actual, append([]interface{}{msg}, args...)...)
|
return JSONEq(t, expected, actual, append([]interface{}{msg}, args...)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// YAMLEqf asserts that two YAML strings are equivalent.
|
||||||
|
func YAMLEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) bool {
|
||||||
|
if h, ok := t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
return YAMLEq(t, expected, actual, append([]interface{}{msg}, args...)...)
|
||||||
|
}
|
||||||
|
|
||||||
// Lenf asserts that the specified object has specific length.
|
// Lenf asserts that the specified object has specific length.
|
||||||
// Lenf also fails if the object has a type that len() not accept.
|
// Lenf also fails if the object has a type that len() not accept.
|
||||||
//
|
//
|
||||||
@@ -300,6 +344,31 @@ func Lenf(t TestingT, object interface{}, length int, msg string, args ...interf
|
|||||||
return Len(t, object, length, append([]interface{}{msg}, args...)...)
|
return Len(t, object, length, append([]interface{}{msg}, args...)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Lessf asserts that the first element is less than the second
|
||||||
|
//
|
||||||
|
// assert.Lessf(t, 1, 2, "error message %s", "formatted")
|
||||||
|
// assert.Lessf(t, float64(1, "error message %s", "formatted"), float64(2))
|
||||||
|
// assert.Lessf(t, "a", "b", "error message %s", "formatted")
|
||||||
|
func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool {
|
||||||
|
if h, ok := t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
return Less(t, e1, e2, append([]interface{}{msg}, args...)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LessOrEqualf asserts that the first element is less than or equal to the second
|
||||||
|
//
|
||||||
|
// assert.LessOrEqualf(t, 1, 2, "error message %s", "formatted")
|
||||||
|
// assert.LessOrEqualf(t, 2, 2, "error message %s", "formatted")
|
||||||
|
// assert.LessOrEqualf(t, "a", "b", "error message %s", "formatted")
|
||||||
|
// assert.LessOrEqualf(t, "b", "b", "error message %s", "formatted")
|
||||||
|
func LessOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool {
|
||||||
|
if h, ok := t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
return LessOrEqual(t, e1, e2, append([]interface{}{msg}, args...)...)
|
||||||
|
}
|
||||||
|
|
||||||
// Nilf asserts that the specified object is nil.
|
// Nilf asserts that the specified object is nil.
|
||||||
//
|
//
|
||||||
// assert.Nilf(t, err, "error message %s", "formatted")
|
// assert.Nilf(t, err, "error message %s", "formatted")
|
||||||
@@ -444,6 +513,19 @@ func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...in
|
|||||||
return Regexp(t, rx, str, append([]interface{}{msg}, args...)...)
|
return Regexp(t, rx, str, append([]interface{}{msg}, args...)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Samef asserts that two pointers reference the same object.
|
||||||
|
//
|
||||||
|
// assert.Samef(t, ptr1, ptr2, "error message %s", "formatted")
|
||||||
|
//
|
||||||
|
// Both arguments must be pointer variables. Pointer variable sameness is
|
||||||
|
// determined based on the equality of both type and value.
|
||||||
|
func Samef(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
|
||||||
|
if h, ok := t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
return Same(t, expected, actual, append([]interface{}{msg}, args...)...)
|
||||||
|
}
|
||||||
|
|
||||||
// Subsetf asserts that the specified list(array, slice...) contains all
|
// Subsetf asserts that the specified list(array, slice...) contains all
|
||||||
// elements given in the specified subset(array, slice...).
|
// elements given in the specified subset(array, slice...).
|
||||||
//
|
//
|
||||||
|
|||||||
164
vendor/github.com/stretchr/testify/assert/assertion_forward.go
generated
vendored
164
vendor/github.com/stretchr/testify/assert/assertion_forward.go
generated
vendored
@@ -215,6 +215,28 @@ func (a *Assertions) Errorf(err error, msg string, args ...interface{}) bool {
|
|||||||
return Errorf(a.t, err, msg, args...)
|
return Errorf(a.t, err, msg, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Eventually asserts that given condition will be met in waitFor time,
|
||||||
|
// periodically checking target function each tick.
|
||||||
|
//
|
||||||
|
// a.Eventually(func() bool { return true; }, time.Second, 10*time.Millisecond)
|
||||||
|
func (a *Assertions) Eventually(condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool {
|
||||||
|
if h, ok := a.t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
return Eventually(a.t, condition, waitFor, tick, msgAndArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Eventuallyf asserts that given condition will be met in waitFor time,
|
||||||
|
// periodically checking target function each tick.
|
||||||
|
//
|
||||||
|
// a.Eventuallyf(func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted")
|
||||||
|
func (a *Assertions) Eventuallyf(condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool {
|
||||||
|
if h, ok := a.t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
return Eventuallyf(a.t, condition, waitFor, tick, msg, args...)
|
||||||
|
}
|
||||||
|
|
||||||
// Exactly asserts that two objects are equal in value and type.
|
// Exactly asserts that two objects are equal in value and type.
|
||||||
//
|
//
|
||||||
// a.Exactly(int32(123), int64(123))
|
// a.Exactly(int32(123), int64(123))
|
||||||
@@ -303,6 +325,56 @@ func (a *Assertions) FileExistsf(path string, msg string, args ...interface{}) b
|
|||||||
return FileExistsf(a.t, path, msg, args...)
|
return FileExistsf(a.t, path, msg, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Greater asserts that the first element is greater than the second
|
||||||
|
//
|
||||||
|
// a.Greater(2, 1)
|
||||||
|
// a.Greater(float64(2), float64(1))
|
||||||
|
// a.Greater("b", "a")
|
||||||
|
func (a *Assertions) Greater(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool {
|
||||||
|
if h, ok := a.t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
return Greater(a.t, e1, e2, msgAndArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GreaterOrEqual asserts that the first element is greater than or equal to the second
|
||||||
|
//
|
||||||
|
// a.GreaterOrEqual(2, 1)
|
||||||
|
// a.GreaterOrEqual(2, 2)
|
||||||
|
// a.GreaterOrEqual("b", "a")
|
||||||
|
// a.GreaterOrEqual("b", "b")
|
||||||
|
func (a *Assertions) GreaterOrEqual(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool {
|
||||||
|
if h, ok := a.t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
return GreaterOrEqual(a.t, e1, e2, msgAndArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GreaterOrEqualf asserts that the first element is greater than or equal to the second
|
||||||
|
//
|
||||||
|
// a.GreaterOrEqualf(2, 1, "error message %s", "formatted")
|
||||||
|
// a.GreaterOrEqualf(2, 2, "error message %s", "formatted")
|
||||||
|
// a.GreaterOrEqualf("b", "a", "error message %s", "formatted")
|
||||||
|
// a.GreaterOrEqualf("b", "b", "error message %s", "formatted")
|
||||||
|
func (a *Assertions) GreaterOrEqualf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool {
|
||||||
|
if h, ok := a.t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
return GreaterOrEqualf(a.t, e1, e2, msg, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Greaterf asserts that the first element is greater than the second
|
||||||
|
//
|
||||||
|
// a.Greaterf(2, 1, "error message %s", "formatted")
|
||||||
|
// a.Greaterf(float64(2, "error message %s", "formatted"), float64(1))
|
||||||
|
// a.Greaterf("b", "a", "error message %s", "formatted")
|
||||||
|
func (a *Assertions) Greaterf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool {
|
||||||
|
if h, ok := a.t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
return Greaterf(a.t, e1, e2, msg, args...)
|
||||||
|
}
|
||||||
|
|
||||||
// HTTPBodyContains asserts that a specified handler returns a
|
// HTTPBodyContains asserts that a specified handler returns a
|
||||||
// body that contains a string.
|
// body that contains a string.
|
||||||
//
|
//
|
||||||
@@ -567,6 +639,22 @@ func (a *Assertions) JSONEqf(expected string, actual string, msg string, args ..
|
|||||||
return JSONEqf(a.t, expected, actual, msg, args...)
|
return JSONEqf(a.t, expected, actual, msg, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// YAMLEq asserts that two YAML strings are equivalent.
|
||||||
|
func (a *Assertions) YAMLEq(expected string, actual string, msgAndArgs ...interface{}) bool {
|
||||||
|
if h, ok := a.t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
return YAMLEq(a.t, expected, actual, msgAndArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// YAMLEqf asserts that two YAML strings are equivalent.
|
||||||
|
func (a *Assertions) YAMLEqf(expected string, actual string, msg string, args ...interface{}) bool {
|
||||||
|
if h, ok := a.t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
return YAMLEqf(a.t, expected, actual, msg, args...)
|
||||||
|
}
|
||||||
|
|
||||||
// Len asserts that the specified object has specific length.
|
// Len asserts that the specified object has specific length.
|
||||||
// Len also fails if the object has a type that len() not accept.
|
// Len also fails if the object has a type that len() not accept.
|
||||||
//
|
//
|
||||||
@@ -589,6 +677,56 @@ func (a *Assertions) Lenf(object interface{}, length int, msg string, args ...in
|
|||||||
return Lenf(a.t, object, length, msg, args...)
|
return Lenf(a.t, object, length, msg, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Less asserts that the first element is less than the second
|
||||||
|
//
|
||||||
|
// a.Less(1, 2)
|
||||||
|
// a.Less(float64(1), float64(2))
|
||||||
|
// a.Less("a", "b")
|
||||||
|
func (a *Assertions) Less(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool {
|
||||||
|
if h, ok := a.t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
return Less(a.t, e1, e2, msgAndArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LessOrEqual asserts that the first element is less than or equal to the second
|
||||||
|
//
|
||||||
|
// a.LessOrEqual(1, 2)
|
||||||
|
// a.LessOrEqual(2, 2)
|
||||||
|
// a.LessOrEqual("a", "b")
|
||||||
|
// a.LessOrEqual("b", "b")
|
||||||
|
func (a *Assertions) LessOrEqual(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool {
|
||||||
|
if h, ok := a.t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
return LessOrEqual(a.t, e1, e2, msgAndArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LessOrEqualf asserts that the first element is less than or equal to the second
|
||||||
|
//
|
||||||
|
// a.LessOrEqualf(1, 2, "error message %s", "formatted")
|
||||||
|
// a.LessOrEqualf(2, 2, "error message %s", "formatted")
|
||||||
|
// a.LessOrEqualf("a", "b", "error message %s", "formatted")
|
||||||
|
// a.LessOrEqualf("b", "b", "error message %s", "formatted")
|
||||||
|
func (a *Assertions) LessOrEqualf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool {
|
||||||
|
if h, ok := a.t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
return LessOrEqualf(a.t, e1, e2, msg, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lessf asserts that the first element is less than the second
|
||||||
|
//
|
||||||
|
// a.Lessf(1, 2, "error message %s", "formatted")
|
||||||
|
// a.Lessf(float64(1, "error message %s", "formatted"), float64(2))
|
||||||
|
// a.Lessf("a", "b", "error message %s", "formatted")
|
||||||
|
func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool {
|
||||||
|
if h, ok := a.t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
return Lessf(a.t, e1, e2, msg, args...)
|
||||||
|
}
|
||||||
|
|
||||||
// Nil asserts that the specified object is nil.
|
// Nil asserts that the specified object is nil.
|
||||||
//
|
//
|
||||||
// a.Nil(err)
|
// a.Nil(err)
|
||||||
@@ -877,6 +1015,32 @@ func (a *Assertions) Regexpf(rx interface{}, str interface{}, msg string, args .
|
|||||||
return Regexpf(a.t, rx, str, msg, args...)
|
return Regexpf(a.t, rx, str, msg, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Same asserts that two pointers reference the same object.
|
||||||
|
//
|
||||||
|
// a.Same(ptr1, ptr2)
|
||||||
|
//
|
||||||
|
// Both arguments must be pointer variables. Pointer variable sameness is
|
||||||
|
// determined based on the equality of both type and value.
|
||||||
|
func (a *Assertions) Same(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool {
|
||||||
|
if h, ok := a.t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
return Same(a.t, expected, actual, msgAndArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Samef asserts that two pointers reference the same object.
|
||||||
|
//
|
||||||
|
// a.Samef(ptr1, ptr2, "error message %s", "formatted")
|
||||||
|
//
|
||||||
|
// Both arguments must be pointer variables. Pointer variable sameness is
|
||||||
|
// determined based on the equality of both type and value.
|
||||||
|
func (a *Assertions) Samef(expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
|
||||||
|
if h, ok := a.t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
return Samef(a.t, expected, actual, msg, args...)
|
||||||
|
}
|
||||||
|
|
||||||
// Subset asserts that the specified list(array, slice...) contains all
|
// Subset asserts that the specified list(array, slice...) contains all
|
||||||
// elements given in the specified subset(array, slice...).
|
// elements given in the specified subset(array, slice...).
|
||||||
//
|
//
|
||||||
|
|||||||
309
vendor/github.com/stretchr/testify/assert/assertion_order.go
generated
vendored
Normal file
309
vendor/github.com/stretchr/testify/assert/assertion_order.go
generated
vendored
Normal file
@@ -0,0 +1,309 @@
|
|||||||
|
package assert
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
func compare(obj1, obj2 interface{}, kind reflect.Kind) (int, bool) {
|
||||||
|
switch kind {
|
||||||
|
case reflect.Int:
|
||||||
|
{
|
||||||
|
intobj1 := obj1.(int)
|
||||||
|
intobj2 := obj2.(int)
|
||||||
|
if intobj1 > intobj2 {
|
||||||
|
return -1, true
|
||||||
|
}
|
||||||
|
if intobj1 == intobj2 {
|
||||||
|
return 0, true
|
||||||
|
}
|
||||||
|
if intobj1 < intobj2 {
|
||||||
|
return 1, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Int8:
|
||||||
|
{
|
||||||
|
int8obj1 := obj1.(int8)
|
||||||
|
int8obj2 := obj2.(int8)
|
||||||
|
if int8obj1 > int8obj2 {
|
||||||
|
return -1, true
|
||||||
|
}
|
||||||
|
if int8obj1 == int8obj2 {
|
||||||
|
return 0, true
|
||||||
|
}
|
||||||
|
if int8obj1 < int8obj2 {
|
||||||
|
return 1, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Int16:
|
||||||
|
{
|
||||||
|
int16obj1 := obj1.(int16)
|
||||||
|
int16obj2 := obj2.(int16)
|
||||||
|
if int16obj1 > int16obj2 {
|
||||||
|
return -1, true
|
||||||
|
}
|
||||||
|
if int16obj1 == int16obj2 {
|
||||||
|
return 0, true
|
||||||
|
}
|
||||||
|
if int16obj1 < int16obj2 {
|
||||||
|
return 1, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Int32:
|
||||||
|
{
|
||||||
|
int32obj1 := obj1.(int32)
|
||||||
|
int32obj2 := obj2.(int32)
|
||||||
|
if int32obj1 > int32obj2 {
|
||||||
|
return -1, true
|
||||||
|
}
|
||||||
|
if int32obj1 == int32obj2 {
|
||||||
|
return 0, true
|
||||||
|
}
|
||||||
|
if int32obj1 < int32obj2 {
|
||||||
|
return 1, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Int64:
|
||||||
|
{
|
||||||
|
int64obj1 := obj1.(int64)
|
||||||
|
int64obj2 := obj2.(int64)
|
||||||
|
if int64obj1 > int64obj2 {
|
||||||
|
return -1, true
|
||||||
|
}
|
||||||
|
if int64obj1 == int64obj2 {
|
||||||
|
return 0, true
|
||||||
|
}
|
||||||
|
if int64obj1 < int64obj2 {
|
||||||
|
return 1, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Uint:
|
||||||
|
{
|
||||||
|
uintobj1 := obj1.(uint)
|
||||||
|
uintobj2 := obj2.(uint)
|
||||||
|
if uintobj1 > uintobj2 {
|
||||||
|
return -1, true
|
||||||
|
}
|
||||||
|
if uintobj1 == uintobj2 {
|
||||||
|
return 0, true
|
||||||
|
}
|
||||||
|
if uintobj1 < uintobj2 {
|
||||||
|
return 1, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Uint8:
|
||||||
|
{
|
||||||
|
uint8obj1 := obj1.(uint8)
|
||||||
|
uint8obj2 := obj2.(uint8)
|
||||||
|
if uint8obj1 > uint8obj2 {
|
||||||
|
return -1, true
|
||||||
|
}
|
||||||
|
if uint8obj1 == uint8obj2 {
|
||||||
|
return 0, true
|
||||||
|
}
|
||||||
|
if uint8obj1 < uint8obj2 {
|
||||||
|
return 1, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Uint16:
|
||||||
|
{
|
||||||
|
uint16obj1 := obj1.(uint16)
|
||||||
|
uint16obj2 := obj2.(uint16)
|
||||||
|
if uint16obj1 > uint16obj2 {
|
||||||
|
return -1, true
|
||||||
|
}
|
||||||
|
if uint16obj1 == uint16obj2 {
|
||||||
|
return 0, true
|
||||||
|
}
|
||||||
|
if uint16obj1 < uint16obj2 {
|
||||||
|
return 1, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Uint32:
|
||||||
|
{
|
||||||
|
uint32obj1 := obj1.(uint32)
|
||||||
|
uint32obj2 := obj2.(uint32)
|
||||||
|
if uint32obj1 > uint32obj2 {
|
||||||
|
return -1, true
|
||||||
|
}
|
||||||
|
if uint32obj1 == uint32obj2 {
|
||||||
|
return 0, true
|
||||||
|
}
|
||||||
|
if uint32obj1 < uint32obj2 {
|
||||||
|
return 1, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Uint64:
|
||||||
|
{
|
||||||
|
uint64obj1 := obj1.(uint64)
|
||||||
|
uint64obj2 := obj2.(uint64)
|
||||||
|
if uint64obj1 > uint64obj2 {
|
||||||
|
return -1, true
|
||||||
|
}
|
||||||
|
if uint64obj1 == uint64obj2 {
|
||||||
|
return 0, true
|
||||||
|
}
|
||||||
|
if uint64obj1 < uint64obj2 {
|
||||||
|
return 1, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Float32:
|
||||||
|
{
|
||||||
|
float32obj1 := obj1.(float32)
|
||||||
|
float32obj2 := obj2.(float32)
|
||||||
|
if float32obj1 > float32obj2 {
|
||||||
|
return -1, true
|
||||||
|
}
|
||||||
|
if float32obj1 == float32obj2 {
|
||||||
|
return 0, true
|
||||||
|
}
|
||||||
|
if float32obj1 < float32obj2 {
|
||||||
|
return 1, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Float64:
|
||||||
|
{
|
||||||
|
float64obj1 := obj1.(float64)
|
||||||
|
float64obj2 := obj2.(float64)
|
||||||
|
if float64obj1 > float64obj2 {
|
||||||
|
return -1, true
|
||||||
|
}
|
||||||
|
if float64obj1 == float64obj2 {
|
||||||
|
return 0, true
|
||||||
|
}
|
||||||
|
if float64obj1 < float64obj2 {
|
||||||
|
return 1, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.String:
|
||||||
|
{
|
||||||
|
stringobj1 := obj1.(string)
|
||||||
|
stringobj2 := obj2.(string)
|
||||||
|
if stringobj1 > stringobj2 {
|
||||||
|
return -1, true
|
||||||
|
}
|
||||||
|
if stringobj1 == stringobj2 {
|
||||||
|
return 0, true
|
||||||
|
}
|
||||||
|
if stringobj1 < stringobj2 {
|
||||||
|
return 1, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Greater asserts that the first element is greater than the second
|
||||||
|
//
|
||||||
|
// assert.Greater(t, 2, 1)
|
||||||
|
// assert.Greater(t, float64(2), float64(1))
|
||||||
|
// assert.Greater(t, "b", "a")
|
||||||
|
func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool {
|
||||||
|
if h, ok := t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
|
||||||
|
e1Kind := reflect.ValueOf(e1).Kind()
|
||||||
|
e2Kind := reflect.ValueOf(e2).Kind()
|
||||||
|
if e1Kind != e2Kind {
|
||||||
|
return Fail(t, "Elements should be the same type", msgAndArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
res, isComparable := compare(e1, e2, e1Kind)
|
||||||
|
if !isComparable {
|
||||||
|
return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
if res != -1 {
|
||||||
|
return Fail(t, fmt.Sprintf("\"%v\" is not greater than \"%v\"", e1, e2), msgAndArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// GreaterOrEqual asserts that the first element is greater than or equal to the second
|
||||||
|
//
|
||||||
|
// assert.GreaterOrEqual(t, 2, 1)
|
||||||
|
// assert.GreaterOrEqual(t, 2, 2)
|
||||||
|
// assert.GreaterOrEqual(t, "b", "a")
|
||||||
|
// assert.GreaterOrEqual(t, "b", "b")
|
||||||
|
func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool {
|
||||||
|
if h, ok := t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
|
||||||
|
e1Kind := reflect.ValueOf(e1).Kind()
|
||||||
|
e2Kind := reflect.ValueOf(e2).Kind()
|
||||||
|
if e1Kind != e2Kind {
|
||||||
|
return Fail(t, "Elements should be the same type", msgAndArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
res, isComparable := compare(e1, e2, e1Kind)
|
||||||
|
if !isComparable {
|
||||||
|
return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
if res != -1 && res != 0 {
|
||||||
|
return Fail(t, fmt.Sprintf("\"%v\" is not greater than or equal to \"%v\"", e1, e2), msgAndArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Less asserts that the first element is less than the second
|
||||||
|
//
|
||||||
|
// assert.Less(t, 1, 2)
|
||||||
|
// assert.Less(t, float64(1), float64(2))
|
||||||
|
// assert.Less(t, "a", "b")
|
||||||
|
func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool {
|
||||||
|
if h, ok := t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
|
||||||
|
e1Kind := reflect.ValueOf(e1).Kind()
|
||||||
|
e2Kind := reflect.ValueOf(e2).Kind()
|
||||||
|
if e1Kind != e2Kind {
|
||||||
|
return Fail(t, "Elements should be the same type", msgAndArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
res, isComparable := compare(e1, e2, e1Kind)
|
||||||
|
if !isComparable {
|
||||||
|
return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
if res != 1 {
|
||||||
|
return Fail(t, fmt.Sprintf("\"%v\" is not less than \"%v\"", e1, e2), msgAndArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// LessOrEqual asserts that the first element is less than or equal to the second
|
||||||
|
//
|
||||||
|
// assert.LessOrEqual(t, 1, 2)
|
||||||
|
// assert.LessOrEqual(t, 2, 2)
|
||||||
|
// assert.LessOrEqual(t, "a", "b")
|
||||||
|
// assert.LessOrEqual(t, "b", "b")
|
||||||
|
func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool {
|
||||||
|
if h, ok := t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
|
||||||
|
e1Kind := reflect.ValueOf(e1).Kind()
|
||||||
|
e2Kind := reflect.ValueOf(e2).Kind()
|
||||||
|
if e1Kind != e2Kind {
|
||||||
|
return Fail(t, "Elements should be the same type", msgAndArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
res, isComparable := compare(e1, e2, e1Kind)
|
||||||
|
if !isComparable {
|
||||||
|
return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
if res != 1 && res != 0 {
|
||||||
|
return Fail(t, fmt.Sprintf("\"%v\" is not less than or equal to \"%v\"", e1, e2), msgAndArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
96
vendor/github.com/stretchr/testify/assert/assertions.go
generated
vendored
96
vendor/github.com/stretchr/testify/assert/assertions.go
generated
vendored
@@ -18,6 +18,7 @@ import (
|
|||||||
|
|
||||||
"github.com/davecgh/go-spew/spew"
|
"github.com/davecgh/go-spew/spew"
|
||||||
"github.com/pmezard/go-difflib/difflib"
|
"github.com/pmezard/go-difflib/difflib"
|
||||||
|
yaml "gopkg.in/yaml.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:generate go run ../_codegen/main.go -output-package=assert -template=assertion_format.go.tmpl
|
//go:generate go run ../_codegen/main.go -output-package=assert -template=assertion_format.go.tmpl
|
||||||
@@ -350,6 +351,37 @@ func Equal(t TestingT, expected, actual interface{}, msgAndArgs ...interface{})
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Same asserts that two pointers reference the same object.
|
||||||
|
//
|
||||||
|
// assert.Same(t, ptr1, ptr2)
|
||||||
|
//
|
||||||
|
// Both arguments must be pointer variables. Pointer variable sameness is
|
||||||
|
// determined based on the equality of both type and value.
|
||||||
|
func Same(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
|
||||||
|
if h, ok := t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedPtr, actualPtr := reflect.ValueOf(expected), reflect.ValueOf(actual)
|
||||||
|
if expectedPtr.Kind() != reflect.Ptr || actualPtr.Kind() != reflect.Ptr {
|
||||||
|
return Fail(t, "Invalid operation: both arguments must be pointers", msgAndArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedType, actualType := reflect.TypeOf(expected), reflect.TypeOf(actual)
|
||||||
|
if expectedType != actualType {
|
||||||
|
return Fail(t, fmt.Sprintf("Pointer expected to be of type %v, but was %v",
|
||||||
|
expectedType, actualType), msgAndArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
if expected != actual {
|
||||||
|
return Fail(t, fmt.Sprintf("Not same: \n"+
|
||||||
|
"expected: %p %#v\n"+
|
||||||
|
"actual : %p %#v", expected, expected, actual, actual), msgAndArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// formatUnequalValues takes two values of arbitrary types and returns string
|
// formatUnequalValues takes two values of arbitrary types and returns string
|
||||||
// representations appropriate to be presented to the user.
|
// representations appropriate to be presented to the user.
|
||||||
//
|
//
|
||||||
@@ -479,14 +511,14 @@ func isEmpty(object interface{}) bool {
|
|||||||
// collection types are empty when they have no element
|
// collection types are empty when they have no element
|
||||||
case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice:
|
case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice:
|
||||||
return objValue.Len() == 0
|
return objValue.Len() == 0
|
||||||
// pointers are empty if nil or if the value they point to is empty
|
// pointers are empty if nil or if the value they point to is empty
|
||||||
case reflect.Ptr:
|
case reflect.Ptr:
|
||||||
if objValue.IsNil() {
|
if objValue.IsNil() {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
deref := objValue.Elem().Interface()
|
deref := objValue.Elem().Interface()
|
||||||
return isEmpty(deref)
|
return isEmpty(deref)
|
||||||
// for all other types, compare against the zero value
|
// for all other types, compare against the zero value
|
||||||
default:
|
default:
|
||||||
zero := reflect.Zero(objValue.Type())
|
zero := reflect.Zero(objValue.Type())
|
||||||
return reflect.DeepEqual(object, zero.Interface())
|
return reflect.DeepEqual(object, zero.Interface())
|
||||||
@@ -629,7 +661,7 @@ func NotEqual(t TestingT, expected, actual interface{}, msgAndArgs ...interface{
|
|||||||
func includeElement(list interface{}, element interface{}) (ok, found bool) {
|
func includeElement(list interface{}, element interface{}) (ok, found bool) {
|
||||||
|
|
||||||
listValue := reflect.ValueOf(list)
|
listValue := reflect.ValueOf(list)
|
||||||
elementValue := reflect.ValueOf(element)
|
listKind := reflect.TypeOf(list).Kind()
|
||||||
defer func() {
|
defer func() {
|
||||||
if e := recover(); e != nil {
|
if e := recover(); e != nil {
|
||||||
ok = false
|
ok = false
|
||||||
@@ -637,11 +669,12 @@ func includeElement(list interface{}, element interface{}) (ok, found bool) {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if reflect.TypeOf(list).Kind() == reflect.String {
|
if listKind == reflect.String {
|
||||||
|
elementValue := reflect.ValueOf(element)
|
||||||
return true, strings.Contains(listValue.String(), elementValue.String())
|
return true, strings.Contains(listValue.String(), elementValue.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
if reflect.TypeOf(list).Kind() == reflect.Map {
|
if listKind == reflect.Map {
|
||||||
mapKeys := listValue.MapKeys()
|
mapKeys := listValue.MapKeys()
|
||||||
for i := 0; i < len(mapKeys); i++ {
|
for i := 0; i < len(mapKeys); i++ {
|
||||||
if ObjectsAreEqual(mapKeys[i].Interface(), element) {
|
if ObjectsAreEqual(mapKeys[i].Interface(), element) {
|
||||||
@@ -1337,6 +1370,24 @@ func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{
|
|||||||
return Equal(t, expectedJSONAsInterface, actualJSONAsInterface, msgAndArgs...)
|
return Equal(t, expectedJSONAsInterface, actualJSONAsInterface, msgAndArgs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// YAMLEq asserts that two YAML strings are equivalent.
|
||||||
|
func YAMLEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) bool {
|
||||||
|
if h, ok := t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
var expectedYAMLAsInterface, actualYAMLAsInterface interface{}
|
||||||
|
|
||||||
|
if err := yaml.Unmarshal([]byte(expected), &expectedYAMLAsInterface); err != nil {
|
||||||
|
return Fail(t, fmt.Sprintf("Expected value ('%s') is not valid yaml.\nYAML parsing error: '%s'", expected, err.Error()), msgAndArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := yaml.Unmarshal([]byte(actual), &actualYAMLAsInterface); err != nil {
|
||||||
|
return Fail(t, fmt.Sprintf("Input ('%s') needs to be valid yaml.\nYAML error: '%s'", actual, err.Error()), msgAndArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Equal(t, expectedYAMLAsInterface, actualYAMLAsInterface, msgAndArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
func typeAndKind(v interface{}) (reflect.Type, reflect.Kind) {
|
func typeAndKind(v interface{}) (reflect.Type, reflect.Kind) {
|
||||||
t := reflect.TypeOf(v)
|
t := reflect.TypeOf(v)
|
||||||
k := t.Kind()
|
k := t.Kind()
|
||||||
@@ -1371,8 +1422,8 @@ func diff(expected interface{}, actual interface{}) string {
|
|||||||
e = spewConfig.Sdump(expected)
|
e = spewConfig.Sdump(expected)
|
||||||
a = spewConfig.Sdump(actual)
|
a = spewConfig.Sdump(actual)
|
||||||
} else {
|
} else {
|
||||||
e = expected.(string)
|
e = reflect.ValueOf(expected).String()
|
||||||
a = actual.(string)
|
a = reflect.ValueOf(actual).String()
|
||||||
}
|
}
|
||||||
|
|
||||||
diff, _ := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{
|
diff, _ := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{
|
||||||
@@ -1414,3 +1465,34 @@ var spewConfig = spew.ConfigState{
|
|||||||
type tHelper interface {
|
type tHelper interface {
|
||||||
Helper()
|
Helper()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Eventually asserts that given condition will be met in waitFor time,
|
||||||
|
// periodically checking target function each tick.
|
||||||
|
//
|
||||||
|
// assert.Eventually(t, func() bool { return true; }, time.Second, 10*time.Millisecond)
|
||||||
|
func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool {
|
||||||
|
if h, ok := t.(tHelper); ok {
|
||||||
|
h.Helper()
|
||||||
|
}
|
||||||
|
|
||||||
|
timer := time.NewTimer(waitFor)
|
||||||
|
ticker := time.NewTicker(tick)
|
||||||
|
checkPassed := make(chan bool)
|
||||||
|
defer timer.Stop()
|
||||||
|
defer ticker.Stop()
|
||||||
|
defer close(checkPassed)
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-timer.C:
|
||||||
|
return Fail(t, "Condition never satisfied", msgAndArgs...)
|
||||||
|
case result := <-checkPassed:
|
||||||
|
if result {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
case <-ticker.C:
|
||||||
|
go func() {
|
||||||
|
checkPassed <- condition()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
99
vendor/golang.org/x/crypto/scrypt/scrypt.go
generated
vendored
99
vendor/golang.org/x/crypto/scrypt/scrypt.go
generated
vendored
@@ -10,6 +10,7 @@ package scrypt // import "golang.org/x/crypto/scrypt"
|
|||||||
import (
|
import (
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"errors"
|
"errors"
|
||||||
|
"math/bits"
|
||||||
|
|
||||||
"golang.org/x/crypto/pbkdf2"
|
"golang.org/x/crypto/pbkdf2"
|
||||||
)
|
)
|
||||||
@@ -29,7 +30,7 @@ func blockXOR(dst, src []uint32, n int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// salsaXOR applies Salsa20/8 to the XOR of 16 numbers from tmp and in,
|
// salsaXOR applies Salsa20/8 to the XOR of 16 numbers from tmp and in,
|
||||||
// and puts the result into both both tmp and out.
|
// and puts the result into both tmp and out.
|
||||||
func salsaXOR(tmp *[16]uint32, in, out []uint32) {
|
func salsaXOR(tmp *[16]uint32, in, out []uint32) {
|
||||||
w0 := tmp[0] ^ in[0]
|
w0 := tmp[0] ^ in[0]
|
||||||
w1 := tmp[1] ^ in[1]
|
w1 := tmp[1] ^ in[1]
|
||||||
@@ -52,77 +53,45 @@ func salsaXOR(tmp *[16]uint32, in, out []uint32) {
|
|||||||
x9, x10, x11, x12, x13, x14, x15 := w9, w10, w11, w12, w13, w14, w15
|
x9, x10, x11, x12, x13, x14, x15 := w9, w10, w11, w12, w13, w14, w15
|
||||||
|
|
||||||
for i := 0; i < 8; i += 2 {
|
for i := 0; i < 8; i += 2 {
|
||||||
u := x0 + x12
|
x4 ^= bits.RotateLeft32(x0+x12, 7)
|
||||||
x4 ^= u<<7 | u>>(32-7)
|
x8 ^= bits.RotateLeft32(x4+x0, 9)
|
||||||
u = x4 + x0
|
x12 ^= bits.RotateLeft32(x8+x4, 13)
|
||||||
x8 ^= u<<9 | u>>(32-9)
|
x0 ^= bits.RotateLeft32(x12+x8, 18)
|
||||||
u = x8 + x4
|
|
||||||
x12 ^= u<<13 | u>>(32-13)
|
|
||||||
u = x12 + x8
|
|
||||||
x0 ^= u<<18 | u>>(32-18)
|
|
||||||
|
|
||||||
u = x5 + x1
|
x9 ^= bits.RotateLeft32(x5+x1, 7)
|
||||||
x9 ^= u<<7 | u>>(32-7)
|
x13 ^= bits.RotateLeft32(x9+x5, 9)
|
||||||
u = x9 + x5
|
x1 ^= bits.RotateLeft32(x13+x9, 13)
|
||||||
x13 ^= u<<9 | u>>(32-9)
|
x5 ^= bits.RotateLeft32(x1+x13, 18)
|
||||||
u = x13 + x9
|
|
||||||
x1 ^= u<<13 | u>>(32-13)
|
|
||||||
u = x1 + x13
|
|
||||||
x5 ^= u<<18 | u>>(32-18)
|
|
||||||
|
|
||||||
u = x10 + x6
|
x14 ^= bits.RotateLeft32(x10+x6, 7)
|
||||||
x14 ^= u<<7 | u>>(32-7)
|
x2 ^= bits.RotateLeft32(x14+x10, 9)
|
||||||
u = x14 + x10
|
x6 ^= bits.RotateLeft32(x2+x14, 13)
|
||||||
x2 ^= u<<9 | u>>(32-9)
|
x10 ^= bits.RotateLeft32(x6+x2, 18)
|
||||||
u = x2 + x14
|
|
||||||
x6 ^= u<<13 | u>>(32-13)
|
|
||||||
u = x6 + x2
|
|
||||||
x10 ^= u<<18 | u>>(32-18)
|
|
||||||
|
|
||||||
u = x15 + x11
|
x3 ^= bits.RotateLeft32(x15+x11, 7)
|
||||||
x3 ^= u<<7 | u>>(32-7)
|
x7 ^= bits.RotateLeft32(x3+x15, 9)
|
||||||
u = x3 + x15
|
x11 ^= bits.RotateLeft32(x7+x3, 13)
|
||||||
x7 ^= u<<9 | u>>(32-9)
|
x15 ^= bits.RotateLeft32(x11+x7, 18)
|
||||||
u = x7 + x3
|
|
||||||
x11 ^= u<<13 | u>>(32-13)
|
|
||||||
u = x11 + x7
|
|
||||||
x15 ^= u<<18 | u>>(32-18)
|
|
||||||
|
|
||||||
u = x0 + x3
|
x1 ^= bits.RotateLeft32(x0+x3, 7)
|
||||||
x1 ^= u<<7 | u>>(32-7)
|
x2 ^= bits.RotateLeft32(x1+x0, 9)
|
||||||
u = x1 + x0
|
x3 ^= bits.RotateLeft32(x2+x1, 13)
|
||||||
x2 ^= u<<9 | u>>(32-9)
|
x0 ^= bits.RotateLeft32(x3+x2, 18)
|
||||||
u = x2 + x1
|
|
||||||
x3 ^= u<<13 | u>>(32-13)
|
|
||||||
u = x3 + x2
|
|
||||||
x0 ^= u<<18 | u>>(32-18)
|
|
||||||
|
|
||||||
u = x5 + x4
|
x6 ^= bits.RotateLeft32(x5+x4, 7)
|
||||||
x6 ^= u<<7 | u>>(32-7)
|
x7 ^= bits.RotateLeft32(x6+x5, 9)
|
||||||
u = x6 + x5
|
x4 ^= bits.RotateLeft32(x7+x6, 13)
|
||||||
x7 ^= u<<9 | u>>(32-9)
|
x5 ^= bits.RotateLeft32(x4+x7, 18)
|
||||||
u = x7 + x6
|
|
||||||
x4 ^= u<<13 | u>>(32-13)
|
|
||||||
u = x4 + x7
|
|
||||||
x5 ^= u<<18 | u>>(32-18)
|
|
||||||
|
|
||||||
u = x10 + x9
|
x11 ^= bits.RotateLeft32(x10+x9, 7)
|
||||||
x11 ^= u<<7 | u>>(32-7)
|
x8 ^= bits.RotateLeft32(x11+x10, 9)
|
||||||
u = x11 + x10
|
x9 ^= bits.RotateLeft32(x8+x11, 13)
|
||||||
x8 ^= u<<9 | u>>(32-9)
|
x10 ^= bits.RotateLeft32(x9+x8, 18)
|
||||||
u = x8 + x11
|
|
||||||
x9 ^= u<<13 | u>>(32-13)
|
|
||||||
u = x9 + x8
|
|
||||||
x10 ^= u<<18 | u>>(32-18)
|
|
||||||
|
|
||||||
u = x15 + x14
|
x12 ^= bits.RotateLeft32(x15+x14, 7)
|
||||||
x12 ^= u<<7 | u>>(32-7)
|
x13 ^= bits.RotateLeft32(x12+x15, 9)
|
||||||
u = x12 + x15
|
x14 ^= bits.RotateLeft32(x13+x12, 13)
|
||||||
x13 ^= u<<9 | u>>(32-9)
|
x15 ^= bits.RotateLeft32(x14+x13, 18)
|
||||||
u = x13 + x12
|
|
||||||
x14 ^= u<<13 | u>>(32-13)
|
|
||||||
u = x14 + x13
|
|
||||||
x15 ^= u<<18 | u>>(32-18)
|
|
||||||
}
|
}
|
||||||
x0 += w0
|
x0 += w0
|
||||||
x1 += w1
|
x1 += w1
|
||||||
|
|||||||
951
vendor/golang.org/x/crypto/ssh/terminal/terminal.go
generated
vendored
951
vendor/golang.org/x/crypto/ssh/terminal/terminal.go
generated
vendored
@@ -1,951 +0,0 @@
|
|||||||
// Copyright 2011 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package terminal
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"io"
|
|
||||||
"sync"
|
|
||||||
"unicode/utf8"
|
|
||||||
)
|
|
||||||
|
|
||||||
// EscapeCodes contains escape sequences that can be written to the terminal in
|
|
||||||
// order to achieve different styles of text.
|
|
||||||
type EscapeCodes struct {
|
|
||||||
// Foreground colors
|
|
||||||
Black, Red, Green, Yellow, Blue, Magenta, Cyan, White []byte
|
|
||||||
|
|
||||||
// Reset all attributes
|
|
||||||
Reset []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
var vt100EscapeCodes = EscapeCodes{
|
|
||||||
Black: []byte{keyEscape, '[', '3', '0', 'm'},
|
|
||||||
Red: []byte{keyEscape, '[', '3', '1', 'm'},
|
|
||||||
Green: []byte{keyEscape, '[', '3', '2', 'm'},
|
|
||||||
Yellow: []byte{keyEscape, '[', '3', '3', 'm'},
|
|
||||||
Blue: []byte{keyEscape, '[', '3', '4', 'm'},
|
|
||||||
Magenta: []byte{keyEscape, '[', '3', '5', 'm'},
|
|
||||||
Cyan: []byte{keyEscape, '[', '3', '6', 'm'},
|
|
||||||
White: []byte{keyEscape, '[', '3', '7', 'm'},
|
|
||||||
|
|
||||||
Reset: []byte{keyEscape, '[', '0', 'm'},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Terminal contains the state for running a VT100 terminal that is capable of
|
|
||||||
// reading lines of input.
|
|
||||||
type Terminal struct {
|
|
||||||
// AutoCompleteCallback, if non-null, is called for each keypress with
|
|
||||||
// the full input line and the current position of the cursor (in
|
|
||||||
// bytes, as an index into |line|). If it returns ok=false, the key
|
|
||||||
// press is processed normally. Otherwise it returns a replacement line
|
|
||||||
// and the new cursor position.
|
|
||||||
AutoCompleteCallback func(line string, pos int, key rune) (newLine string, newPos int, ok bool)
|
|
||||||
|
|
||||||
// Escape contains a pointer to the escape codes for this terminal.
|
|
||||||
// It's always a valid pointer, although the escape codes themselves
|
|
||||||
// may be empty if the terminal doesn't support them.
|
|
||||||
Escape *EscapeCodes
|
|
||||||
|
|
||||||
// lock protects the terminal and the state in this object from
|
|
||||||
// concurrent processing of a key press and a Write() call.
|
|
||||||
lock sync.Mutex
|
|
||||||
|
|
||||||
c io.ReadWriter
|
|
||||||
prompt []rune
|
|
||||||
|
|
||||||
// line is the current line being entered.
|
|
||||||
line []rune
|
|
||||||
// pos is the logical position of the cursor in line
|
|
||||||
pos int
|
|
||||||
// echo is true if local echo is enabled
|
|
||||||
echo bool
|
|
||||||
// pasteActive is true iff there is a bracketed paste operation in
|
|
||||||
// progress.
|
|
||||||
pasteActive bool
|
|
||||||
|
|
||||||
// cursorX contains the current X value of the cursor where the left
|
|
||||||
// edge is 0. cursorY contains the row number where the first row of
|
|
||||||
// the current line is 0.
|
|
||||||
cursorX, cursorY int
|
|
||||||
// maxLine is the greatest value of cursorY so far.
|
|
||||||
maxLine int
|
|
||||||
|
|
||||||
termWidth, termHeight int
|
|
||||||
|
|
||||||
// outBuf contains the terminal data to be sent.
|
|
||||||
outBuf []byte
|
|
||||||
// remainder contains the remainder of any partial key sequences after
|
|
||||||
// a read. It aliases into inBuf.
|
|
||||||
remainder []byte
|
|
||||||
inBuf [256]byte
|
|
||||||
|
|
||||||
// history contains previously entered commands so that they can be
|
|
||||||
// accessed with the up and down keys.
|
|
||||||
history stRingBuffer
|
|
||||||
// historyIndex stores the currently accessed history entry, where zero
|
|
||||||
// means the immediately previous entry.
|
|
||||||
historyIndex int
|
|
||||||
// When navigating up and down the history it's possible to return to
|
|
||||||
// the incomplete, initial line. That value is stored in
|
|
||||||
// historyPending.
|
|
||||||
historyPending string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewTerminal runs a VT100 terminal on the given ReadWriter. If the ReadWriter is
|
|
||||||
// a local terminal, that terminal must first have been put into raw mode.
|
|
||||||
// prompt is a string that is written at the start of each input line (i.e.
|
|
||||||
// "> ").
|
|
||||||
func NewTerminal(c io.ReadWriter, prompt string) *Terminal {
|
|
||||||
return &Terminal{
|
|
||||||
Escape: &vt100EscapeCodes,
|
|
||||||
c: c,
|
|
||||||
prompt: []rune(prompt),
|
|
||||||
termWidth: 80,
|
|
||||||
termHeight: 24,
|
|
||||||
echo: true,
|
|
||||||
historyIndex: -1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
keyCtrlD = 4
|
|
||||||
keyCtrlU = 21
|
|
||||||
keyEnter = '\r'
|
|
||||||
keyEscape = 27
|
|
||||||
keyBackspace = 127
|
|
||||||
keyUnknown = 0xd800 /* UTF-16 surrogate area */ + iota
|
|
||||||
keyUp
|
|
||||||
keyDown
|
|
||||||
keyLeft
|
|
||||||
keyRight
|
|
||||||
keyAltLeft
|
|
||||||
keyAltRight
|
|
||||||
keyHome
|
|
||||||
keyEnd
|
|
||||||
keyDeleteWord
|
|
||||||
keyDeleteLine
|
|
||||||
keyClearScreen
|
|
||||||
keyPasteStart
|
|
||||||
keyPasteEnd
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
crlf = []byte{'\r', '\n'}
|
|
||||||
pasteStart = []byte{keyEscape, '[', '2', '0', '0', '~'}
|
|
||||||
pasteEnd = []byte{keyEscape, '[', '2', '0', '1', '~'}
|
|
||||||
)
|
|
||||||
|
|
||||||
// bytesToKey tries to parse a key sequence from b. If successful, it returns
|
|
||||||
// the key and the remainder of the input. Otherwise it returns utf8.RuneError.
|
|
||||||
func bytesToKey(b []byte, pasteActive bool) (rune, []byte) {
|
|
||||||
if len(b) == 0 {
|
|
||||||
return utf8.RuneError, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if !pasteActive {
|
|
||||||
switch b[0] {
|
|
||||||
case 1: // ^A
|
|
||||||
return keyHome, b[1:]
|
|
||||||
case 5: // ^E
|
|
||||||
return keyEnd, b[1:]
|
|
||||||
case 8: // ^H
|
|
||||||
return keyBackspace, b[1:]
|
|
||||||
case 11: // ^K
|
|
||||||
return keyDeleteLine, b[1:]
|
|
||||||
case 12: // ^L
|
|
||||||
return keyClearScreen, b[1:]
|
|
||||||
case 23: // ^W
|
|
||||||
return keyDeleteWord, b[1:]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if b[0] != keyEscape {
|
|
||||||
if !utf8.FullRune(b) {
|
|
||||||
return utf8.RuneError, b
|
|
||||||
}
|
|
||||||
r, l := utf8.DecodeRune(b)
|
|
||||||
return r, b[l:]
|
|
||||||
}
|
|
||||||
|
|
||||||
if !pasteActive && len(b) >= 3 && b[0] == keyEscape && b[1] == '[' {
|
|
||||||
switch b[2] {
|
|
||||||
case 'A':
|
|
||||||
return keyUp, b[3:]
|
|
||||||
case 'B':
|
|
||||||
return keyDown, b[3:]
|
|
||||||
case 'C':
|
|
||||||
return keyRight, b[3:]
|
|
||||||
case 'D':
|
|
||||||
return keyLeft, b[3:]
|
|
||||||
case 'H':
|
|
||||||
return keyHome, b[3:]
|
|
||||||
case 'F':
|
|
||||||
return keyEnd, b[3:]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !pasteActive && len(b) >= 6 && b[0] == keyEscape && b[1] == '[' && b[2] == '1' && b[3] == ';' && b[4] == '3' {
|
|
||||||
switch b[5] {
|
|
||||||
case 'C':
|
|
||||||
return keyAltRight, b[6:]
|
|
||||||
case 'D':
|
|
||||||
return keyAltLeft, b[6:]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !pasteActive && len(b) >= 6 && bytes.Equal(b[:6], pasteStart) {
|
|
||||||
return keyPasteStart, b[6:]
|
|
||||||
}
|
|
||||||
|
|
||||||
if pasteActive && len(b) >= 6 && bytes.Equal(b[:6], pasteEnd) {
|
|
||||||
return keyPasteEnd, b[6:]
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we get here then we have a key that we don't recognise, or a
|
|
||||||
// partial sequence. It's not clear how one should find the end of a
|
|
||||||
// sequence without knowing them all, but it seems that [a-zA-Z~] only
|
|
||||||
// appears at the end of a sequence.
|
|
||||||
for i, c := range b[0:] {
|
|
||||||
if c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c == '~' {
|
|
||||||
return keyUnknown, b[i+1:]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return utf8.RuneError, b
|
|
||||||
}
|
|
||||||
|
|
||||||
// queue appends data to the end of t.outBuf
|
|
||||||
func (t *Terminal) queue(data []rune) {
|
|
||||||
t.outBuf = append(t.outBuf, []byte(string(data))...)
|
|
||||||
}
|
|
||||||
|
|
||||||
var eraseUnderCursor = []rune{' ', keyEscape, '[', 'D'}
|
|
||||||
var space = []rune{' '}
|
|
||||||
|
|
||||||
func isPrintable(key rune) bool {
|
|
||||||
isInSurrogateArea := key >= 0xd800 && key <= 0xdbff
|
|
||||||
return key >= 32 && !isInSurrogateArea
|
|
||||||
}
|
|
||||||
|
|
||||||
// moveCursorToPos appends data to t.outBuf which will move the cursor to the
|
|
||||||
// given, logical position in the text.
|
|
||||||
func (t *Terminal) moveCursorToPos(pos int) {
|
|
||||||
if !t.echo {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
x := visualLength(t.prompt) + pos
|
|
||||||
y := x / t.termWidth
|
|
||||||
x = x % t.termWidth
|
|
||||||
|
|
||||||
up := 0
|
|
||||||
if y < t.cursorY {
|
|
||||||
up = t.cursorY - y
|
|
||||||
}
|
|
||||||
|
|
||||||
down := 0
|
|
||||||
if y > t.cursorY {
|
|
||||||
down = y - t.cursorY
|
|
||||||
}
|
|
||||||
|
|
||||||
left := 0
|
|
||||||
if x < t.cursorX {
|
|
||||||
left = t.cursorX - x
|
|
||||||
}
|
|
||||||
|
|
||||||
right := 0
|
|
||||||
if x > t.cursorX {
|
|
||||||
right = x - t.cursorX
|
|
||||||
}
|
|
||||||
|
|
||||||
t.cursorX = x
|
|
||||||
t.cursorY = y
|
|
||||||
t.move(up, down, left, right)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Terminal) move(up, down, left, right int) {
|
|
||||||
movement := make([]rune, 3*(up+down+left+right))
|
|
||||||
m := movement
|
|
||||||
for i := 0; i < up; i++ {
|
|
||||||
m[0] = keyEscape
|
|
||||||
m[1] = '['
|
|
||||||
m[2] = 'A'
|
|
||||||
m = m[3:]
|
|
||||||
}
|
|
||||||
for i := 0; i < down; i++ {
|
|
||||||
m[0] = keyEscape
|
|
||||||
m[1] = '['
|
|
||||||
m[2] = 'B'
|
|
||||||
m = m[3:]
|
|
||||||
}
|
|
||||||
for i := 0; i < left; i++ {
|
|
||||||
m[0] = keyEscape
|
|
||||||
m[1] = '['
|
|
||||||
m[2] = 'D'
|
|
||||||
m = m[3:]
|
|
||||||
}
|
|
||||||
for i := 0; i < right; i++ {
|
|
||||||
m[0] = keyEscape
|
|
||||||
m[1] = '['
|
|
||||||
m[2] = 'C'
|
|
||||||
m = m[3:]
|
|
||||||
}
|
|
||||||
|
|
||||||
t.queue(movement)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Terminal) clearLineToRight() {
|
|
||||||
op := []rune{keyEscape, '[', 'K'}
|
|
||||||
t.queue(op)
|
|
||||||
}
|
|
||||||
|
|
||||||
const maxLineLength = 4096
|
|
||||||
|
|
||||||
func (t *Terminal) setLine(newLine []rune, newPos int) {
|
|
||||||
if t.echo {
|
|
||||||
t.moveCursorToPos(0)
|
|
||||||
t.writeLine(newLine)
|
|
||||||
for i := len(newLine); i < len(t.line); i++ {
|
|
||||||
t.writeLine(space)
|
|
||||||
}
|
|
||||||
t.moveCursorToPos(newPos)
|
|
||||||
}
|
|
||||||
t.line = newLine
|
|
||||||
t.pos = newPos
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Terminal) advanceCursor(places int) {
|
|
||||||
t.cursorX += places
|
|
||||||
t.cursorY += t.cursorX / t.termWidth
|
|
||||||
if t.cursorY > t.maxLine {
|
|
||||||
t.maxLine = t.cursorY
|
|
||||||
}
|
|
||||||
t.cursorX = t.cursorX % t.termWidth
|
|
||||||
|
|
||||||
if places > 0 && t.cursorX == 0 {
|
|
||||||
// Normally terminals will advance the current position
|
|
||||||
// when writing a character. But that doesn't happen
|
|
||||||
// for the last character in a line. However, when
|
|
||||||
// writing a character (except a new line) that causes
|
|
||||||
// a line wrap, the position will be advanced two
|
|
||||||
// places.
|
|
||||||
//
|
|
||||||
// So, if we are stopping at the end of a line, we
|
|
||||||
// need to write a newline so that our cursor can be
|
|
||||||
// advanced to the next line.
|
|
||||||
t.outBuf = append(t.outBuf, '\r', '\n')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Terminal) eraseNPreviousChars(n int) {
|
|
||||||
if n == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if t.pos < n {
|
|
||||||
n = t.pos
|
|
||||||
}
|
|
||||||
t.pos -= n
|
|
||||||
t.moveCursorToPos(t.pos)
|
|
||||||
|
|
||||||
copy(t.line[t.pos:], t.line[n+t.pos:])
|
|
||||||
t.line = t.line[:len(t.line)-n]
|
|
||||||
if t.echo {
|
|
||||||
t.writeLine(t.line[t.pos:])
|
|
||||||
for i := 0; i < n; i++ {
|
|
||||||
t.queue(space)
|
|
||||||
}
|
|
||||||
t.advanceCursor(n)
|
|
||||||
t.moveCursorToPos(t.pos)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// countToLeftWord returns then number of characters from the cursor to the
|
|
||||||
// start of the previous word.
|
|
||||||
func (t *Terminal) countToLeftWord() int {
|
|
||||||
if t.pos == 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
pos := t.pos - 1
|
|
||||||
for pos > 0 {
|
|
||||||
if t.line[pos] != ' ' {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
pos--
|
|
||||||
}
|
|
||||||
for pos > 0 {
|
|
||||||
if t.line[pos] == ' ' {
|
|
||||||
pos++
|
|
||||||
break
|
|
||||||
}
|
|
||||||
pos--
|
|
||||||
}
|
|
||||||
|
|
||||||
return t.pos - pos
|
|
||||||
}
|
|
||||||
|
|
||||||
// countToRightWord returns then number of characters from the cursor to the
|
|
||||||
// start of the next word.
|
|
||||||
func (t *Terminal) countToRightWord() int {
|
|
||||||
pos := t.pos
|
|
||||||
for pos < len(t.line) {
|
|
||||||
if t.line[pos] == ' ' {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
pos++
|
|
||||||
}
|
|
||||||
for pos < len(t.line) {
|
|
||||||
if t.line[pos] != ' ' {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
pos++
|
|
||||||
}
|
|
||||||
return pos - t.pos
|
|
||||||
}
|
|
||||||
|
|
||||||
// visualLength returns the number of visible glyphs in s.
|
|
||||||
func visualLength(runes []rune) int {
|
|
||||||
inEscapeSeq := false
|
|
||||||
length := 0
|
|
||||||
|
|
||||||
for _, r := range runes {
|
|
||||||
switch {
|
|
||||||
case inEscapeSeq:
|
|
||||||
if (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') {
|
|
||||||
inEscapeSeq = false
|
|
||||||
}
|
|
||||||
case r == '\x1b':
|
|
||||||
inEscapeSeq = true
|
|
||||||
default:
|
|
||||||
length++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return length
|
|
||||||
}
|
|
||||||
|
|
||||||
// handleKey processes the given key and, optionally, returns a line of text
|
|
||||||
// that the user has entered.
|
|
||||||
func (t *Terminal) handleKey(key rune) (line string, ok bool) {
|
|
||||||
if t.pasteActive && key != keyEnter {
|
|
||||||
t.addKeyToLine(key)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch key {
|
|
||||||
case keyBackspace:
|
|
||||||
if t.pos == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
t.eraseNPreviousChars(1)
|
|
||||||
case keyAltLeft:
|
|
||||||
// move left by a word.
|
|
||||||
t.pos -= t.countToLeftWord()
|
|
||||||
t.moveCursorToPos(t.pos)
|
|
||||||
case keyAltRight:
|
|
||||||
// move right by a word.
|
|
||||||
t.pos += t.countToRightWord()
|
|
||||||
t.moveCursorToPos(t.pos)
|
|
||||||
case keyLeft:
|
|
||||||
if t.pos == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
t.pos--
|
|
||||||
t.moveCursorToPos(t.pos)
|
|
||||||
case keyRight:
|
|
||||||
if t.pos == len(t.line) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
t.pos++
|
|
||||||
t.moveCursorToPos(t.pos)
|
|
||||||
case keyHome:
|
|
||||||
if t.pos == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
t.pos = 0
|
|
||||||
t.moveCursorToPos(t.pos)
|
|
||||||
case keyEnd:
|
|
||||||
if t.pos == len(t.line) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
t.pos = len(t.line)
|
|
||||||
t.moveCursorToPos(t.pos)
|
|
||||||
case keyUp:
|
|
||||||
entry, ok := t.history.NthPreviousEntry(t.historyIndex + 1)
|
|
||||||
if !ok {
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
if t.historyIndex == -1 {
|
|
||||||
t.historyPending = string(t.line)
|
|
||||||
}
|
|
||||||
t.historyIndex++
|
|
||||||
runes := []rune(entry)
|
|
||||||
t.setLine(runes, len(runes))
|
|
||||||
case keyDown:
|
|
||||||
switch t.historyIndex {
|
|
||||||
case -1:
|
|
||||||
return
|
|
||||||
case 0:
|
|
||||||
runes := []rune(t.historyPending)
|
|
||||||
t.setLine(runes, len(runes))
|
|
||||||
t.historyIndex--
|
|
||||||
default:
|
|
||||||
entry, ok := t.history.NthPreviousEntry(t.historyIndex - 1)
|
|
||||||
if ok {
|
|
||||||
t.historyIndex--
|
|
||||||
runes := []rune(entry)
|
|
||||||
t.setLine(runes, len(runes))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case keyEnter:
|
|
||||||
t.moveCursorToPos(len(t.line))
|
|
||||||
t.queue([]rune("\r\n"))
|
|
||||||
line = string(t.line)
|
|
||||||
ok = true
|
|
||||||
t.line = t.line[:0]
|
|
||||||
t.pos = 0
|
|
||||||
t.cursorX = 0
|
|
||||||
t.cursorY = 0
|
|
||||||
t.maxLine = 0
|
|
||||||
case keyDeleteWord:
|
|
||||||
// Delete zero or more spaces and then one or more characters.
|
|
||||||
t.eraseNPreviousChars(t.countToLeftWord())
|
|
||||||
case keyDeleteLine:
|
|
||||||
// Delete everything from the current cursor position to the
|
|
||||||
// end of line.
|
|
||||||
for i := t.pos; i < len(t.line); i++ {
|
|
||||||
t.queue(space)
|
|
||||||
t.advanceCursor(1)
|
|
||||||
}
|
|
||||||
t.line = t.line[:t.pos]
|
|
||||||
t.moveCursorToPos(t.pos)
|
|
||||||
case keyCtrlD:
|
|
||||||
// Erase the character under the current position.
|
|
||||||
// The EOF case when the line is empty is handled in
|
|
||||||
// readLine().
|
|
||||||
if t.pos < len(t.line) {
|
|
||||||
t.pos++
|
|
||||||
t.eraseNPreviousChars(1)
|
|
||||||
}
|
|
||||||
case keyCtrlU:
|
|
||||||
t.eraseNPreviousChars(t.pos)
|
|
||||||
case keyClearScreen:
|
|
||||||
// Erases the screen and moves the cursor to the home position.
|
|
||||||
t.queue([]rune("\x1b[2J\x1b[H"))
|
|
||||||
t.queue(t.prompt)
|
|
||||||
t.cursorX, t.cursorY = 0, 0
|
|
||||||
t.advanceCursor(visualLength(t.prompt))
|
|
||||||
t.setLine(t.line, t.pos)
|
|
||||||
default:
|
|
||||||
if t.AutoCompleteCallback != nil {
|
|
||||||
prefix := string(t.line[:t.pos])
|
|
||||||
suffix := string(t.line[t.pos:])
|
|
||||||
|
|
||||||
t.lock.Unlock()
|
|
||||||
newLine, newPos, completeOk := t.AutoCompleteCallback(prefix+suffix, len(prefix), key)
|
|
||||||
t.lock.Lock()
|
|
||||||
|
|
||||||
if completeOk {
|
|
||||||
t.setLine([]rune(newLine), utf8.RuneCount([]byte(newLine)[:newPos]))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !isPrintable(key) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if len(t.line) == maxLineLength {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
t.addKeyToLine(key)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// addKeyToLine inserts the given key at the current position in the current
|
|
||||||
// line.
|
|
||||||
func (t *Terminal) addKeyToLine(key rune) {
|
|
||||||
if len(t.line) == cap(t.line) {
|
|
||||||
newLine := make([]rune, len(t.line), 2*(1+len(t.line)))
|
|
||||||
copy(newLine, t.line)
|
|
||||||
t.line = newLine
|
|
||||||
}
|
|
||||||
t.line = t.line[:len(t.line)+1]
|
|
||||||
copy(t.line[t.pos+1:], t.line[t.pos:])
|
|
||||||
t.line[t.pos] = key
|
|
||||||
if t.echo {
|
|
||||||
t.writeLine(t.line[t.pos:])
|
|
||||||
}
|
|
||||||
t.pos++
|
|
||||||
t.moveCursorToPos(t.pos)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Terminal) writeLine(line []rune) {
|
|
||||||
for len(line) != 0 {
|
|
||||||
remainingOnLine := t.termWidth - t.cursorX
|
|
||||||
todo := len(line)
|
|
||||||
if todo > remainingOnLine {
|
|
||||||
todo = remainingOnLine
|
|
||||||
}
|
|
||||||
t.queue(line[:todo])
|
|
||||||
t.advanceCursor(visualLength(line[:todo]))
|
|
||||||
line = line[todo:]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// writeWithCRLF writes buf to w but replaces all occurrences of \n with \r\n.
|
|
||||||
func writeWithCRLF(w io.Writer, buf []byte) (n int, err error) {
|
|
||||||
for len(buf) > 0 {
|
|
||||||
i := bytes.IndexByte(buf, '\n')
|
|
||||||
todo := len(buf)
|
|
||||||
if i >= 0 {
|
|
||||||
todo = i
|
|
||||||
}
|
|
||||||
|
|
||||||
var nn int
|
|
||||||
nn, err = w.Write(buf[:todo])
|
|
||||||
n += nn
|
|
||||||
if err != nil {
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
buf = buf[todo:]
|
|
||||||
|
|
||||||
if i >= 0 {
|
|
||||||
if _, err = w.Write(crlf); err != nil {
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
n++
|
|
||||||
buf = buf[1:]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return n, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Terminal) Write(buf []byte) (n int, err error) {
|
|
||||||
t.lock.Lock()
|
|
||||||
defer t.lock.Unlock()
|
|
||||||
|
|
||||||
if t.cursorX == 0 && t.cursorY == 0 {
|
|
||||||
// This is the easy case: there's nothing on the screen that we
|
|
||||||
// have to move out of the way.
|
|
||||||
return writeWithCRLF(t.c, buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
// We have a prompt and possibly user input on the screen. We
|
|
||||||
// have to clear it first.
|
|
||||||
t.move(0 /* up */, 0 /* down */, t.cursorX /* left */, 0 /* right */)
|
|
||||||
t.cursorX = 0
|
|
||||||
t.clearLineToRight()
|
|
||||||
|
|
||||||
for t.cursorY > 0 {
|
|
||||||
t.move(1 /* up */, 0, 0, 0)
|
|
||||||
t.cursorY--
|
|
||||||
t.clearLineToRight()
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err = t.c.Write(t.outBuf); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
t.outBuf = t.outBuf[:0]
|
|
||||||
|
|
||||||
if n, err = writeWithCRLF(t.c, buf); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
t.writeLine(t.prompt)
|
|
||||||
if t.echo {
|
|
||||||
t.writeLine(t.line)
|
|
||||||
}
|
|
||||||
|
|
||||||
t.moveCursorToPos(t.pos)
|
|
||||||
|
|
||||||
if _, err = t.c.Write(t.outBuf); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
t.outBuf = t.outBuf[:0]
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadPassword temporarily changes the prompt and reads a password, without
|
|
||||||
// echo, from the terminal.
|
|
||||||
func (t *Terminal) ReadPassword(prompt string) (line string, err error) {
|
|
||||||
t.lock.Lock()
|
|
||||||
defer t.lock.Unlock()
|
|
||||||
|
|
||||||
oldPrompt := t.prompt
|
|
||||||
t.prompt = []rune(prompt)
|
|
||||||
t.echo = false
|
|
||||||
|
|
||||||
line, err = t.readLine()
|
|
||||||
|
|
||||||
t.prompt = oldPrompt
|
|
||||||
t.echo = true
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadLine returns a line of input from the terminal.
|
|
||||||
func (t *Terminal) ReadLine() (line string, err error) {
|
|
||||||
t.lock.Lock()
|
|
||||||
defer t.lock.Unlock()
|
|
||||||
|
|
||||||
return t.readLine()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Terminal) readLine() (line string, err error) {
|
|
||||||
// t.lock must be held at this point
|
|
||||||
|
|
||||||
if t.cursorX == 0 && t.cursorY == 0 {
|
|
||||||
t.writeLine(t.prompt)
|
|
||||||
t.c.Write(t.outBuf)
|
|
||||||
t.outBuf = t.outBuf[:0]
|
|
||||||
}
|
|
||||||
|
|
||||||
lineIsPasted := t.pasteActive
|
|
||||||
|
|
||||||
for {
|
|
||||||
rest := t.remainder
|
|
||||||
lineOk := false
|
|
||||||
for !lineOk {
|
|
||||||
var key rune
|
|
||||||
key, rest = bytesToKey(rest, t.pasteActive)
|
|
||||||
if key == utf8.RuneError {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if !t.pasteActive {
|
|
||||||
if key == keyCtrlD {
|
|
||||||
if len(t.line) == 0 {
|
|
||||||
return "", io.EOF
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if key == keyPasteStart {
|
|
||||||
t.pasteActive = true
|
|
||||||
if len(t.line) == 0 {
|
|
||||||
lineIsPasted = true
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
} else if key == keyPasteEnd {
|
|
||||||
t.pasteActive = false
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if !t.pasteActive {
|
|
||||||
lineIsPasted = false
|
|
||||||
}
|
|
||||||
line, lineOk = t.handleKey(key)
|
|
||||||
}
|
|
||||||
if len(rest) > 0 {
|
|
||||||
n := copy(t.inBuf[:], rest)
|
|
||||||
t.remainder = t.inBuf[:n]
|
|
||||||
} else {
|
|
||||||
t.remainder = nil
|
|
||||||
}
|
|
||||||
t.c.Write(t.outBuf)
|
|
||||||
t.outBuf = t.outBuf[:0]
|
|
||||||
if lineOk {
|
|
||||||
if t.echo {
|
|
||||||
t.historyIndex = -1
|
|
||||||
t.history.Add(line)
|
|
||||||
}
|
|
||||||
if lineIsPasted {
|
|
||||||
err = ErrPasteIndicator
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// t.remainder is a slice at the beginning of t.inBuf
|
|
||||||
// containing a partial key sequence
|
|
||||||
readBuf := t.inBuf[len(t.remainder):]
|
|
||||||
var n int
|
|
||||||
|
|
||||||
t.lock.Unlock()
|
|
||||||
n, err = t.c.Read(readBuf)
|
|
||||||
t.lock.Lock()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
t.remainder = t.inBuf[:n+len(t.remainder)]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetPrompt sets the prompt to be used when reading subsequent lines.
|
|
||||||
func (t *Terminal) SetPrompt(prompt string) {
|
|
||||||
t.lock.Lock()
|
|
||||||
defer t.lock.Unlock()
|
|
||||||
|
|
||||||
t.prompt = []rune(prompt)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Terminal) clearAndRepaintLinePlusNPrevious(numPrevLines int) {
|
|
||||||
// Move cursor to column zero at the start of the line.
|
|
||||||
t.move(t.cursorY, 0, t.cursorX, 0)
|
|
||||||
t.cursorX, t.cursorY = 0, 0
|
|
||||||
t.clearLineToRight()
|
|
||||||
for t.cursorY < numPrevLines {
|
|
||||||
// Move down a line
|
|
||||||
t.move(0, 1, 0, 0)
|
|
||||||
t.cursorY++
|
|
||||||
t.clearLineToRight()
|
|
||||||
}
|
|
||||||
// Move back to beginning.
|
|
||||||
t.move(t.cursorY, 0, 0, 0)
|
|
||||||
t.cursorX, t.cursorY = 0, 0
|
|
||||||
|
|
||||||
t.queue(t.prompt)
|
|
||||||
t.advanceCursor(visualLength(t.prompt))
|
|
||||||
t.writeLine(t.line)
|
|
||||||
t.moveCursorToPos(t.pos)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Terminal) SetSize(width, height int) error {
|
|
||||||
t.lock.Lock()
|
|
||||||
defer t.lock.Unlock()
|
|
||||||
|
|
||||||
if width == 0 {
|
|
||||||
width = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
oldWidth := t.termWidth
|
|
||||||
t.termWidth, t.termHeight = width, height
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case width == oldWidth:
|
|
||||||
// If the width didn't change then nothing else needs to be
|
|
||||||
// done.
|
|
||||||
return nil
|
|
||||||
case len(t.line) == 0 && t.cursorX == 0 && t.cursorY == 0:
|
|
||||||
// If there is nothing on current line and no prompt printed,
|
|
||||||
// just do nothing
|
|
||||||
return nil
|
|
||||||
case width < oldWidth:
|
|
||||||
// Some terminals (e.g. xterm) will truncate lines that were
|
|
||||||
// too long when shinking. Others, (e.g. gnome-terminal) will
|
|
||||||
// attempt to wrap them. For the former, repainting t.maxLine
|
|
||||||
// works great, but that behaviour goes badly wrong in the case
|
|
||||||
// of the latter because they have doubled every full line.
|
|
||||||
|
|
||||||
// We assume that we are working on a terminal that wraps lines
|
|
||||||
// and adjust the cursor position based on every previous line
|
|
||||||
// wrapping and turning into two. This causes the prompt on
|
|
||||||
// xterms to move upwards, which isn't great, but it avoids a
|
|
||||||
// huge mess with gnome-terminal.
|
|
||||||
if t.cursorX >= t.termWidth {
|
|
||||||
t.cursorX = t.termWidth - 1
|
|
||||||
}
|
|
||||||
t.cursorY *= 2
|
|
||||||
t.clearAndRepaintLinePlusNPrevious(t.maxLine * 2)
|
|
||||||
case width > oldWidth:
|
|
||||||
// If the terminal expands then our position calculations will
|
|
||||||
// be wrong in the future because we think the cursor is
|
|
||||||
// |t.pos| chars into the string, but there will be a gap at
|
|
||||||
// the end of any wrapped line.
|
|
||||||
//
|
|
||||||
// But the position will actually be correct until we move, so
|
|
||||||
// we can move back to the beginning and repaint everything.
|
|
||||||
t.clearAndRepaintLinePlusNPrevious(t.maxLine)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := t.c.Write(t.outBuf)
|
|
||||||
t.outBuf = t.outBuf[:0]
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
type pasteIndicatorError struct{}
|
|
||||||
|
|
||||||
func (pasteIndicatorError) Error() string {
|
|
||||||
return "terminal: ErrPasteIndicator not correctly handled"
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrPasteIndicator may be returned from ReadLine as the error, in addition
|
|
||||||
// to valid line data. It indicates that bracketed paste mode is enabled and
|
|
||||||
// that the returned line consists only of pasted data. Programs may wish to
|
|
||||||
// interpret pasted data more literally than typed data.
|
|
||||||
var ErrPasteIndicator = pasteIndicatorError{}
|
|
||||||
|
|
||||||
// SetBracketedPasteMode requests that the terminal bracket paste operations
|
|
||||||
// with markers. Not all terminals support this but, if it is supported, then
|
|
||||||
// enabling this mode will stop any autocomplete callback from running due to
|
|
||||||
// pastes. Additionally, any lines that are completely pasted will be returned
|
|
||||||
// from ReadLine with the error set to ErrPasteIndicator.
|
|
||||||
func (t *Terminal) SetBracketedPasteMode(on bool) {
|
|
||||||
if on {
|
|
||||||
io.WriteString(t.c, "\x1b[?2004h")
|
|
||||||
} else {
|
|
||||||
io.WriteString(t.c, "\x1b[?2004l")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// stRingBuffer is a ring buffer of strings.
|
|
||||||
type stRingBuffer struct {
|
|
||||||
// entries contains max elements.
|
|
||||||
entries []string
|
|
||||||
max int
|
|
||||||
// head contains the index of the element most recently added to the ring.
|
|
||||||
head int
|
|
||||||
// size contains the number of elements in the ring.
|
|
||||||
size int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *stRingBuffer) Add(a string) {
|
|
||||||
if s.entries == nil {
|
|
||||||
const defaultNumEntries = 100
|
|
||||||
s.entries = make([]string, defaultNumEntries)
|
|
||||||
s.max = defaultNumEntries
|
|
||||||
}
|
|
||||||
|
|
||||||
s.head = (s.head + 1) % s.max
|
|
||||||
s.entries[s.head] = a
|
|
||||||
if s.size < s.max {
|
|
||||||
s.size++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NthPreviousEntry returns the value passed to the nth previous call to Add.
|
|
||||||
// If n is zero then the immediately prior value is returned, if one, then the
|
|
||||||
// next most recent, and so on. If such an element doesn't exist then ok is
|
|
||||||
// false.
|
|
||||||
func (s *stRingBuffer) NthPreviousEntry(n int) (value string, ok bool) {
|
|
||||||
if n >= s.size {
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
index := s.head - n
|
|
||||||
if index < 0 {
|
|
||||||
index += s.max
|
|
||||||
}
|
|
||||||
return s.entries[index], true
|
|
||||||
}
|
|
||||||
|
|
||||||
// readPasswordLine reads from reader until it finds \n or io.EOF.
|
|
||||||
// The slice returned does not include the \n.
|
|
||||||
// readPasswordLine also ignores any \r it finds.
|
|
||||||
func readPasswordLine(reader io.Reader) ([]byte, error) {
|
|
||||||
var buf [1]byte
|
|
||||||
var ret []byte
|
|
||||||
|
|
||||||
for {
|
|
||||||
n, err := reader.Read(buf[:])
|
|
||||||
if n > 0 {
|
|
||||||
switch buf[0] {
|
|
||||||
case '\n':
|
|
||||||
return ret, nil
|
|
||||||
case '\r':
|
|
||||||
// remove \r from passwords on Windows
|
|
||||||
default:
|
|
||||||
ret = append(ret, buf[0])
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
if err == io.EOF && len(ret) > 0 {
|
|
||||||
return ret, nil
|
|
||||||
}
|
|
||||||
return ret, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
114
vendor/golang.org/x/crypto/ssh/terminal/util.go
generated
vendored
114
vendor/golang.org/x/crypto/ssh/terminal/util.go
generated
vendored
@@ -1,114 +0,0 @@
|
|||||||
// Copyright 2011 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// +build darwin dragonfly freebsd linux,!appengine netbsd openbsd
|
|
||||||
|
|
||||||
// Package terminal provides support functions for dealing with terminals, as
|
|
||||||
// commonly found on UNIX systems.
|
|
||||||
//
|
|
||||||
// Putting a terminal into raw mode is the most common requirement:
|
|
||||||
//
|
|
||||||
// oldState, err := terminal.MakeRaw(0)
|
|
||||||
// if err != nil {
|
|
||||||
// panic(err)
|
|
||||||
// }
|
|
||||||
// defer terminal.Restore(0, oldState)
|
|
||||||
package terminal // import "golang.org/x/crypto/ssh/terminal"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"golang.org/x/sys/unix"
|
|
||||||
)
|
|
||||||
|
|
||||||
// State contains the state of a terminal.
|
|
||||||
type State struct {
|
|
||||||
termios unix.Termios
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsTerminal returns true if the given file descriptor is a terminal.
|
|
||||||
func IsTerminal(fd int) bool {
|
|
||||||
_, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
|
|
||||||
return err == nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// MakeRaw put the terminal connected to the given file descriptor into raw
|
|
||||||
// mode and returns the previous state of the terminal so that it can be
|
|
||||||
// restored.
|
|
||||||
func MakeRaw(fd int) (*State, error) {
|
|
||||||
termios, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
oldState := State{termios: *termios}
|
|
||||||
|
|
||||||
// This attempts to replicate the behaviour documented for cfmakeraw in
|
|
||||||
// the termios(3) manpage.
|
|
||||||
termios.Iflag &^= unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON
|
|
||||||
termios.Oflag &^= unix.OPOST
|
|
||||||
termios.Lflag &^= unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN
|
|
||||||
termios.Cflag &^= unix.CSIZE | unix.PARENB
|
|
||||||
termios.Cflag |= unix.CS8
|
|
||||||
termios.Cc[unix.VMIN] = 1
|
|
||||||
termios.Cc[unix.VTIME] = 0
|
|
||||||
if err := unix.IoctlSetTermios(fd, ioctlWriteTermios, termios); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &oldState, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetState returns the current state of a terminal which may be useful to
|
|
||||||
// restore the terminal after a signal.
|
|
||||||
func GetState(fd int) (*State, error) {
|
|
||||||
termios, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &State{termios: *termios}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Restore restores the terminal connected to the given file descriptor to a
|
|
||||||
// previous state.
|
|
||||||
func Restore(fd int, state *State) error {
|
|
||||||
return unix.IoctlSetTermios(fd, ioctlWriteTermios, &state.termios)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetSize returns the dimensions of the given terminal.
|
|
||||||
func GetSize(fd int) (width, height int, err error) {
|
|
||||||
ws, err := unix.IoctlGetWinsize(fd, unix.TIOCGWINSZ)
|
|
||||||
if err != nil {
|
|
||||||
return -1, -1, err
|
|
||||||
}
|
|
||||||
return int(ws.Col), int(ws.Row), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// passwordReader is an io.Reader that reads from a specific file descriptor.
|
|
||||||
type passwordReader int
|
|
||||||
|
|
||||||
func (r passwordReader) Read(buf []byte) (int, error) {
|
|
||||||
return unix.Read(int(r), buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadPassword reads a line of input from a terminal without local echo. This
|
|
||||||
// is commonly used for inputting passwords and other sensitive data. The slice
|
|
||||||
// returned does not include the \n.
|
|
||||||
func ReadPassword(fd int) ([]byte, error) {
|
|
||||||
termios, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
newState := *termios
|
|
||||||
newState.Lflag &^= unix.ECHO
|
|
||||||
newState.Lflag |= unix.ICANON | unix.ISIG
|
|
||||||
newState.Iflag |= unix.ICRNL
|
|
||||||
if err := unix.IoctlSetTermios(fd, ioctlWriteTermios, &newState); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
defer unix.IoctlSetTermios(fd, ioctlWriteTermios, termios)
|
|
||||||
|
|
||||||
return readPasswordLine(passwordReader(fd))
|
|
||||||
}
|
|
||||||
58
vendor/golang.org/x/crypto/ssh/terminal/util_plan9.go
generated
vendored
58
vendor/golang.org/x/crypto/ssh/terminal/util_plan9.go
generated
vendored
@@ -1,58 +0,0 @@
|
|||||||
// Copyright 2016 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// Package terminal provides support functions for dealing with terminals, as
|
|
||||||
// commonly found on UNIX systems.
|
|
||||||
//
|
|
||||||
// Putting a terminal into raw mode is the most common requirement:
|
|
||||||
//
|
|
||||||
// oldState, err := terminal.MakeRaw(0)
|
|
||||||
// if err != nil {
|
|
||||||
// panic(err)
|
|
||||||
// }
|
|
||||||
// defer terminal.Restore(0, oldState)
|
|
||||||
package terminal
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"runtime"
|
|
||||||
)
|
|
||||||
|
|
||||||
type State struct{}
|
|
||||||
|
|
||||||
// IsTerminal returns true if the given file descriptor is a terminal.
|
|
||||||
func IsTerminal(fd int) bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// MakeRaw put the terminal connected to the given file descriptor into raw
|
|
||||||
// mode and returns the previous state of the terminal so that it can be
|
|
||||||
// restored.
|
|
||||||
func MakeRaw(fd int) (*State, error) {
|
|
||||||
return nil, fmt.Errorf("terminal: MakeRaw not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetState returns the current state of a terminal which may be useful to
|
|
||||||
// restore the terminal after a signal.
|
|
||||||
func GetState(fd int) (*State, error) {
|
|
||||||
return nil, fmt.Errorf("terminal: GetState not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Restore restores the terminal connected to the given file descriptor to a
|
|
||||||
// previous state.
|
|
||||||
func Restore(fd int, state *State) error {
|
|
||||||
return fmt.Errorf("terminal: Restore not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetSize returns the dimensions of the given terminal.
|
|
||||||
func GetSize(fd int) (width, height int, err error) {
|
|
||||||
return 0, 0, fmt.Errorf("terminal: GetSize not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadPassword reads a line of input from a terminal without local echo. This
|
|
||||||
// is commonly used for inputting passwords and other sensitive data. The slice
|
|
||||||
// returned does not include the \n.
|
|
||||||
func ReadPassword(fd int) ([]byte, error) {
|
|
||||||
return nil, fmt.Errorf("terminal: ReadPassword not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
|
|
||||||
}
|
|
||||||
124
vendor/golang.org/x/crypto/ssh/terminal/util_solaris.go
generated
vendored
124
vendor/golang.org/x/crypto/ssh/terminal/util_solaris.go
generated
vendored
@@ -1,124 +0,0 @@
|
|||||||
// Copyright 2015 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// +build solaris
|
|
||||||
|
|
||||||
package terminal // import "golang.org/x/crypto/ssh/terminal"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"golang.org/x/sys/unix"
|
|
||||||
"io"
|
|
||||||
"syscall"
|
|
||||||
)
|
|
||||||
|
|
||||||
// State contains the state of a terminal.
|
|
||||||
type State struct {
|
|
||||||
termios unix.Termios
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsTerminal returns true if the given file descriptor is a terminal.
|
|
||||||
func IsTerminal(fd int) bool {
|
|
||||||
_, err := unix.IoctlGetTermio(fd, unix.TCGETA)
|
|
||||||
return err == nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadPassword reads a line of input from a terminal without local echo. This
|
|
||||||
// is commonly used for inputting passwords and other sensitive data. The slice
|
|
||||||
// returned does not include the \n.
|
|
||||||
func ReadPassword(fd int) ([]byte, error) {
|
|
||||||
// see also: http://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libast/common/uwin/getpass.c
|
|
||||||
val, err := unix.IoctlGetTermios(fd, unix.TCGETS)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
oldState := *val
|
|
||||||
|
|
||||||
newState := oldState
|
|
||||||
newState.Lflag &^= syscall.ECHO
|
|
||||||
newState.Lflag |= syscall.ICANON | syscall.ISIG
|
|
||||||
newState.Iflag |= syscall.ICRNL
|
|
||||||
err = unix.IoctlSetTermios(fd, unix.TCSETS, &newState)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
defer unix.IoctlSetTermios(fd, unix.TCSETS, &oldState)
|
|
||||||
|
|
||||||
var buf [16]byte
|
|
||||||
var ret []byte
|
|
||||||
for {
|
|
||||||
n, err := syscall.Read(fd, buf[:])
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if n == 0 {
|
|
||||||
if len(ret) == 0 {
|
|
||||||
return nil, io.EOF
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if buf[n-1] == '\n' {
|
|
||||||
n--
|
|
||||||
}
|
|
||||||
ret = append(ret, buf[:n]...)
|
|
||||||
if n < len(buf) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// MakeRaw puts the terminal connected to the given file descriptor into raw
|
|
||||||
// mode and returns the previous state of the terminal so that it can be
|
|
||||||
// restored.
|
|
||||||
// see http://cr.illumos.org/~webrev/andy_js/1060/
|
|
||||||
func MakeRaw(fd int) (*State, error) {
|
|
||||||
termios, err := unix.IoctlGetTermios(fd, unix.TCGETS)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
oldState := State{termios: *termios}
|
|
||||||
|
|
||||||
termios.Iflag &^= unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON
|
|
||||||
termios.Oflag &^= unix.OPOST
|
|
||||||
termios.Lflag &^= unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN
|
|
||||||
termios.Cflag &^= unix.CSIZE | unix.PARENB
|
|
||||||
termios.Cflag |= unix.CS8
|
|
||||||
termios.Cc[unix.VMIN] = 1
|
|
||||||
termios.Cc[unix.VTIME] = 0
|
|
||||||
|
|
||||||
if err := unix.IoctlSetTermios(fd, unix.TCSETS, termios); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &oldState, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Restore restores the terminal connected to the given file descriptor to a
|
|
||||||
// previous state.
|
|
||||||
func Restore(fd int, oldState *State) error {
|
|
||||||
return unix.IoctlSetTermios(fd, unix.TCSETS, &oldState.termios)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetState returns the current state of a terminal which may be useful to
|
|
||||||
// restore the terminal after a signal.
|
|
||||||
func GetState(fd int) (*State, error) {
|
|
||||||
termios, err := unix.IoctlGetTermios(fd, unix.TCGETS)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &State{termios: *termios}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetSize returns the dimensions of the given terminal.
|
|
||||||
func GetSize(fd int) (width, height int, err error) {
|
|
||||||
ws, err := unix.IoctlGetWinsize(fd, unix.TIOCGWINSZ)
|
|
||||||
if err != nil {
|
|
||||||
return 0, 0, err
|
|
||||||
}
|
|
||||||
return int(ws.Col), int(ws.Row), nil
|
|
||||||
}
|
|
||||||
103
vendor/golang.org/x/crypto/ssh/terminal/util_windows.go
generated
vendored
103
vendor/golang.org/x/crypto/ssh/terminal/util_windows.go
generated
vendored
@@ -1,103 +0,0 @@
|
|||||||
// Copyright 2011 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// +build windows
|
|
||||||
|
|
||||||
// Package terminal provides support functions for dealing with terminals, as
|
|
||||||
// commonly found on UNIX systems.
|
|
||||||
//
|
|
||||||
// Putting a terminal into raw mode is the most common requirement:
|
|
||||||
//
|
|
||||||
// oldState, err := terminal.MakeRaw(0)
|
|
||||||
// if err != nil {
|
|
||||||
// panic(err)
|
|
||||||
// }
|
|
||||||
// defer terminal.Restore(0, oldState)
|
|
||||||
package terminal
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"golang.org/x/sys/windows"
|
|
||||||
)
|
|
||||||
|
|
||||||
type State struct {
|
|
||||||
mode uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsTerminal returns true if the given file descriptor is a terminal.
|
|
||||||
func IsTerminal(fd int) bool {
|
|
||||||
var st uint32
|
|
||||||
err := windows.GetConsoleMode(windows.Handle(fd), &st)
|
|
||||||
return err == nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// MakeRaw put the terminal connected to the given file descriptor into raw
|
|
||||||
// mode and returns the previous state of the terminal so that it can be
|
|
||||||
// restored.
|
|
||||||
func MakeRaw(fd int) (*State, error) {
|
|
||||||
var st uint32
|
|
||||||
if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
raw := st &^ (windows.ENABLE_ECHO_INPUT | windows.ENABLE_PROCESSED_INPUT | windows.ENABLE_LINE_INPUT | windows.ENABLE_PROCESSED_OUTPUT)
|
|
||||||
if err := windows.SetConsoleMode(windows.Handle(fd), raw); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &State{st}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetState returns the current state of a terminal which may be useful to
|
|
||||||
// restore the terminal after a signal.
|
|
||||||
func GetState(fd int) (*State, error) {
|
|
||||||
var st uint32
|
|
||||||
if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &State{st}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Restore restores the terminal connected to the given file descriptor to a
|
|
||||||
// previous state.
|
|
||||||
func Restore(fd int, state *State) error {
|
|
||||||
return windows.SetConsoleMode(windows.Handle(fd), state.mode)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetSize returns the dimensions of the given terminal.
|
|
||||||
func GetSize(fd int) (width, height int, err error) {
|
|
||||||
var info windows.ConsoleScreenBufferInfo
|
|
||||||
if err := windows.GetConsoleScreenBufferInfo(windows.Handle(fd), &info); err != nil {
|
|
||||||
return 0, 0, err
|
|
||||||
}
|
|
||||||
return int(info.Size.X), int(info.Size.Y), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadPassword reads a line of input from a terminal without local echo. This
|
|
||||||
// is commonly used for inputting passwords and other sensitive data. The slice
|
|
||||||
// returned does not include the \n.
|
|
||||||
func ReadPassword(fd int) ([]byte, error) {
|
|
||||||
var st uint32
|
|
||||||
if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
old := st
|
|
||||||
|
|
||||||
st &^= (windows.ENABLE_ECHO_INPUT)
|
|
||||||
st |= (windows.ENABLE_PROCESSED_INPUT | windows.ENABLE_LINE_INPUT | windows.ENABLE_PROCESSED_OUTPUT)
|
|
||||||
if err := windows.SetConsoleMode(windows.Handle(fd), st); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
defer windows.SetConsoleMode(windows.Handle(fd), old)
|
|
||||||
|
|
||||||
var h windows.Handle
|
|
||||||
p, _ := windows.GetCurrentProcess()
|
|
||||||
if err := windows.DuplicateHandle(p, windows.Handle(fd), p, &h, 0, false, windows.DUPLICATE_SAME_ACCESS); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
f := os.NewFile(uintptr(h), "stdin")
|
|
||||||
defer f.Close()
|
|
||||||
return readPasswordLine(f)
|
|
||||||
}
|
|
||||||
56
vendor/golang.org/x/net/context/context.go
generated
vendored
56
vendor/golang.org/x/net/context/context.go
generated
vendored
@@ -1,56 +0,0 @@
|
|||||||
// Copyright 2014 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// Package context defines the Context type, which carries deadlines,
|
|
||||||
// cancelation signals, and other request-scoped values across API boundaries
|
|
||||||
// and between processes.
|
|
||||||
// As of Go 1.7 this package is available in the standard library under the
|
|
||||||
// name context. https://golang.org/pkg/context.
|
|
||||||
//
|
|
||||||
// Incoming requests to a server should create a Context, and outgoing calls to
|
|
||||||
// servers should accept a Context. The chain of function calls between must
|
|
||||||
// propagate the Context, optionally replacing it with a modified copy created
|
|
||||||
// using WithDeadline, WithTimeout, WithCancel, or WithValue.
|
|
||||||
//
|
|
||||||
// Programs that use Contexts should follow these rules to keep interfaces
|
|
||||||
// consistent across packages and enable static analysis tools to check context
|
|
||||||
// propagation:
|
|
||||||
//
|
|
||||||
// Do not store Contexts inside a struct type; instead, pass a Context
|
|
||||||
// explicitly to each function that needs it. The Context should be the first
|
|
||||||
// parameter, typically named ctx:
|
|
||||||
//
|
|
||||||
// func DoSomething(ctx context.Context, arg Arg) error {
|
|
||||||
// // ... use ctx ...
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// Do not pass a nil Context, even if a function permits it. Pass context.TODO
|
|
||||||
// if you are unsure about which Context to use.
|
|
||||||
//
|
|
||||||
// Use context Values only for request-scoped data that transits processes and
|
|
||||||
// APIs, not for passing optional parameters to functions.
|
|
||||||
//
|
|
||||||
// The same Context may be passed to functions running in different goroutines;
|
|
||||||
// Contexts are safe for simultaneous use by multiple goroutines.
|
|
||||||
//
|
|
||||||
// See http://blog.golang.org/context for example code for a server that uses
|
|
||||||
// Contexts.
|
|
||||||
package context // import "golang.org/x/net/context"
|
|
||||||
|
|
||||||
// Background returns a non-nil, empty Context. It is never canceled, has no
|
|
||||||
// values, and has no deadline. It is typically used by the main function,
|
|
||||||
// initialization, and tests, and as the top-level Context for incoming
|
|
||||||
// requests.
|
|
||||||
func Background() Context {
|
|
||||||
return background
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO returns a non-nil, empty Context. Code should use context.TODO when
|
|
||||||
// it's unclear which Context to use or it is not yet available (because the
|
|
||||||
// surrounding function has not yet been extended to accept a Context
|
|
||||||
// parameter). TODO is recognized by static analysis tools that determine
|
|
||||||
// whether Contexts are propagated correctly in a program.
|
|
||||||
func TODO() Context {
|
|
||||||
return todo
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user