Compare commits

...

242 Commits

Author SHA1 Message Date
Andrey Nering
3084ef129c v2.7.1 2019-11-10 20:36:17 -03:00
Andrey Nering
c0d112f858 Merge pull request #261 from AlbyIanna/AlbyIanna/fix-typo
fix typo in usage.md
2019-11-10 19:58:06 -03:00
Andrey Nering
2265dda84c Merge pull request #249 from bfarayev/feature/bash-autocomplete
Bash autocompletion for task
2019-11-10 19:53:33 -03:00
Alberto Iannaccone
263b094cab fix typo in usage.md 2019-11-08 11:01:57 +01:00
Bakhtiyar Farayev
fbd13614a5 Fix typo and re-organize the folder structure for task completion 2019-11-05 10:30:01 +11:00
Andrey Nering
0494d7ebe3 Update changelog 2019-10-27 18:27:47 -03:00
Andrey Nering
9a8442c946 Update some tools and fix error and calling exit 0
Fixes #251
2019-10-27 18:14:22 -03:00
Andrey Nering
e1dcd0b441 Add a styleguide to documentation site 2019-10-27 17:28:12 -03:00
Andrey Nering
a152db7054 Docs: Mention GitHub Action by the Arduino team 2019-10-24 23:55:21 -03:00
Andrey Nering
b9e092674e Docs: Advice using Go Modules instead of GOPATH 2019-10-24 23:46:29 -03:00
Andrey Nering
4162b5f41d Merge pull request #248 from go-task/improve-github-actions
Improve GitHub Action and also test Go 1.12
2019-10-24 23:30:21 -03:00
Andrey Nering
67ae6f210f Improve GitHub Action and also test Go 1.12 2019-10-24 23:25:06 -03:00
Andrey Nering
f6c5a46626 Update vendor/ directory
Fixes #250
2019-09-26 19:04:13 -03:00
Andrey Nering
d6f7e01c53 Fix Travis 2019-09-22 18:57:42 -03:00
Andrey Nering
46463e4e24 Disabling broken Travis releasing for now
I plan to move this to GitHub Actions. For now, I'll do it manually.
2019-09-22 18:50:16 -03:00
Andrey Nering
5c420f3a34 v2.7.0 2019-09-22 18:21:31 -03:00
Bakhtiyar Farayev
393712ead2 Add initial version of bash autocomplete 2019-09-15 09:06:00 +10:00
Andrey Nering
84da80356d Use stdlib instead of go-homedir 2019-09-08 22:07:48 -03:00
Andrey Nering
bcbb85eac3 Merge pull request #245 from go-task/upgrade-sh
Upgrade mvdan.cc/sh to use edge
2019-09-08 21:59:59 -03:00
Andrey Nering
0e1d8a72e6 Revert "Ensure the $HOME env is being set on Windows"
This reverts commit 52028fc3bc.
2019-09-08 21:56:23 -03:00
Andrey Nering
bbdd698869 Upgrade mvdan.cc/sh to use edge 2019-09-08 21:55:02 -03:00
Andrey Nering
8f684ffa6d Merge pull request #239 from go-task/ci-using-github-actions
Add CI for Linux/Windows/MacOS powered by GitHub Actions
2019-09-07 14:43:00 -03:00
Andrey Nering
9be3666fe7 Trying to fix Travis 2019-09-07 14:40:39 -03:00
Andrey Nering
b7785678f4 CI: Upgrade to Go 1.13 2019-09-07 14:35:48 -03:00
Andrey Nering
d8005b4cf6 Fix typo 2019-09-07 14:32:02 -03:00
Andrey Nering
52028fc3bc Ensure the $HOME env is being set on Windows 2019-09-07 14:29:13 -03:00
Andrey Nering
5285ec23ae Fix summary test on Windows 2019-09-01 22:26:53 -03:00
Andrey Nering
3c882e5c57 Merge branch 'master' into ci-using-github-actions 2019-09-01 22:18:58 -03:00
Andrey Nering
ad569a8a36 Update CHANGELOG 2019-08-25 16:41:54 -03:00
Andrey Nering
0d9fdbaac1 Small improvements to #228 2019-08-25 16:32:25 -03:00
Andrey Nering
f5cd3eab9e Merge pull request #228 from topecongiro/checksum-look-for-generated
Re-run the task if generated files do not exist
2019-08-25 16:32:03 -03:00
Seiichi Uchida
8987cd64a0 Fix a typo 2019-08-21 13:42:19 +09:00
Seiichi Uchida
fac51dcf03 Fix Checksum.IsUpToDate
- Check whether generates exist after the creation of checksum file
- Check whether generates exist if only the user specified generates fields
- Check for each generates field instead of taking it as a whole
2019-08-21 13:35:16 +09:00
Seiichi Uchida
01101a4c9b Remove directory check 2019-08-21 13:34:58 +09:00
Seiichi Uchida
d561e40817 Update glob.go
- Rename glob() to globs()
- Add glob() which handles a single glob pattern
- Change glob() and globs() so that they do not return directoreis
2019-08-21 13:33:12 +09:00
Seiichi Uchida
0cb298ebdf Separate error handlings for readability 2019-08-19 13:00:10 +09:00
Andrey Nering
a149368725 Calling Go directly on Windows 2019-08-15 22:50:28 -03:00
Andrey Nering
afeefe8259 Build on the root directory 2019-08-15 22:45:46 -03:00
Andrey Nering
690d3c27a2 Do we need to use backslash here? 2019-08-15 22:42:06 -03:00
Andrey Nering
3d56ea5ce5 Fix binary calling on Windows 2019-08-15 22:38:36 -03:00
Andrey Nering
fdff7f80a3 Skip linting for now 2019-08-15 22:35:29 -03:00
Andrey Nering
fe6978b107 Fix CI 2019-08-15 22:32:45 -03:00
Andrey Nering
57db6865d2 Build binary first and test all packages 2019-08-15 22:28:55 -03:00
Andrey Nering
d235d5ab28 Add CI for Linux/Windows/MacOS powered by GitHub Actions 2019-08-15 22:24:31 -03:00
Andrey Nering
613dfe06d3 Fix two Goreleaser deprecation messages
https://goreleaser.com/deprecations/#archive

https://goreleaser.com/deprecations/#nfpm
2019-08-11 23:15:08 -03:00
Andrey Nering
a312d61d68 Update CHANGELOG.md 2019-08-11 23:01:25 -03:00
Andrey Nering
e414c1f7b0 Documentation: Improve wording on the Variables section
Thanks @crewjam for the suggestion

Closes #234
2019-08-10 19:50:47 -03:00
Andrey Nering
955359b073 Fix nil panic bug when assigning global var and no var is declared on the Taskfile
Closes #229
Ref #234
2019-08-10 19:38:57 -03:00
Seiichi Uchida
26e0c0887a Re-run the task if generated files do not exist 2019-08-01 13:08:53 +09:00
Andrey Nering
4c295b564a Update CHANGELOG.md 2019-07-21 11:23:49 -03:00
Andrey Nering
2eb52da0db v2.6.0 2019-07-21 11:11:30 -03:00
Andrey Nering
d8bfb3ab13 Add CHANGELOG and documentation for Linux support on Homebrew
Ref: https://github.com/go-task/homebrew-tap/pull/1

Thanks @dawidd6
2019-07-21 11:03:22 -03:00
Andrey Nering
d970e93507 Add --taskfile flag (alias -t) to allow running another Taskfile
Closes #221
2019-07-21 10:57:04 -03:00
Andrey Nering
762714de68 Merge pull request #213 from marco-m/document-dir-creation
Document dir: creation (see PR #211)
2019-06-16 11:03:08 -03:00
Marco Molteni
82a3651a18 Document dir: creation (see PR #211) 2019-06-16 11:33:00 +02:00
Andrey Nering
abe0352de9 Fixed some bugs regarding minor version checks on version:
1. I have forgot to update it on recent releases. Seems that most people just
   use round versions since nobody complained.
2. It's too hard to understand how the github.com/Masterminds/semver package
   works, so I just got rid of it and we're now using plain float checks.
2019-06-15 22:39:35 -03:00
Andrey Nering
4cee4aa5a8 Fix typo 2019-06-15 21:58:37 -03:00
Andrey Nering
9c68c7c50b Add changelog for #205 2019-06-15 21:56:34 -03:00
Andrey Nering
0608782cfa Merge pull request #205 from CypherpunkArmory/add-precondition-to-task
Add Preconditions to Tasks
2019-06-15 21:55:20 -03:00
Andrey Nering
edeaf3794a Merge pull request #212 from ezhukov/patch-1
Add missing "-" in usage.md
2019-06-15 21:21:41 -03:00
Andrey Nering
fe2b8c8afa Post-fixes to #211 2019-06-15 21:12:54 -03:00
Andrey Nering
b66bf58064 Merge branch 'marco-m-209-create-dir' 2019-06-15 20:53:34 -03:00
Andrey Nering
957dfa9cdf Merge branch '209-create-dir' of https://github.com/marco-m/task into marco-m-209-create-dir 2019-06-15 20:52:08 -03:00
Stephen Prater
cc9264854e Change error output 2019-06-11 12:20:56 -07:00
Stephen Prater
d1463b3e24 Fix typos per review 2019-06-11 11:46:22 -07:00
Eugene Zhukov
f1082520e1 Add missing "-" in usage.md 2019-06-11 11:35:10 +03:00
Marco Molteni
733c563194 Protect creation of "dir:" with a mutex 2019-06-10 17:40:20 +02:00
Andrey Nering
0200d043c3 Add funding button via OpenCollective 2019-06-09 21:53:55 -03:00
Marco Molteni
9c475c36e7 Handle the common case when the task directory is not specified
Closes #209
2019-06-06 20:40:31 +02:00
Marco Molteni
c663c5c507 When "dir:" attribute points to a non-existing dir, create it
Closes #209
2019-06-04 18:58:22 +02:00
Marco Molteni
1e93c38307 Task directory: test when "dir:" attribute points to an existing dir 2019-06-04 18:36:35 +02:00
Marco Molteni
81baf808c9 Task directory: test default case (no "dir:" attribute) 2019-06-04 18:24:01 +02:00
Marco Molteni
74537689dc Fix spelling 2019-06-04 08:08:25 +02:00
Stephen Prater
12ab01d5e6 Clarify difference between status and precondition in docs 2019-05-28 13:18:06 -07:00
Stephen Prater
044d3a0ff9 Remove ignore_errors 2019-05-28 13:02:59 -07:00
Stephen Prater
659cae6a4c Apply suggestions from code review
Co-Authored-By: Andrey Nering <andrey.nering@gmail.com>
2019-05-28 12:28:29 -07:00
Stephen Prater
bd5882f0f0 Add Preconditions to Tasks 2019-05-17 13:51:15 -07:00
Andrey Nering
6ff9ba9df9 v2.5.2 2019-05-11 11:28:21 -03:00
Andrey Nering
b2df398a12 go mod vendor 2019-05-11 11:22:47 -03:00
Andrey Nering
83d618e1eb Revert "Upgrade to yaml/go-yaml v3"
This reverts commit 8001fb3915.
2019-05-11 11:22:13 -03:00
Andrey Nering
f0768b3af1 Allow setting global variables through the CLI
Closes #192
2019-05-11 11:06:47 -03:00
Andrey Nering
0233ce52ed v2.5.1 2019-04-27 17:56:30 -03:00
Andrey Nering
6e6f337509 Updated change log 2019-04-27 17:28:58 -03:00
Andrey Nering
1546415b8f Update CHANGELOG.md 2019-04-21 17:16:35 -03:00
Andrey Nering
20725c69bf Merge pull request #200 from go-task/fix-output-issues
Fixes some bugs relatated to commands output handling
2019-04-21 17:05:21 -03:00
Andrey Nering
90613220c6 Fixes some bugs relatated to commands output handling
This seems to fix some of the bugs reported by issues like #114 and #190.

Seems that the standard library's os/exec package has some black magic to
detect if a writer is an actual *os.File, and some stuff are handled
differently, then.

Fixes #114
Fixes #190
2019-04-21 16:55:47 -03:00
Andrey Nering
659fd2ae93 Update Go version on CI 2019-04-13 17:44:55 -03:00
Andrey Nering
29d899f7da Merge pull request #198 from go-task/yaml-v3
Upgrade to go-yaml/yaml v3
2019-04-13 17:29:36 -03:00
Andrey Nering
902a0a01a9 go vendor mod 2019-04-13 17:26:27 -03:00
Andrey Nering
8001fb3915 Upgrade to yaml/go-yaml v3 2019-04-13 17:25:28 -03:00
Andrey Nering
e81e2802f0 Small fix to redirector 2019-03-23 17:48:18 -03:00
Andrey Nering
1ee066ec42 Merge pull request #188 from sosiska/patch-1
Rewrite if-else chain to switch statement
2019-03-23 17:04:31 -03:00
Kirill Motkov
53d54d1c4a Rewrite if-else chain to switch statement 2019-03-19 14:19:21 +03:00
Andrey Nering
10082b60b8 v2.5.0 2019-03-16 10:46:22 -03:00
Andrey Nering
c5b9773922 go mod vendor 2019-03-16 10:42:54 -03:00
Andrey Nering
de11323d28 mvdan.cc/sh: Use v2.6.4 2019-03-16 10:42:23 -03:00
Andrey Nering
9f269e1a95 Migrating from taskfile.org to taskfile.dev 2019-03-04 23:23:30 -03:00
Andrey Nering
e4204168a0 Remove unnecessary extra empty line 2019-03-04 22:56:23 -03:00
Andrey Nering
9c350f8ef1 Update Change Log 2019-03-04 22:56:23 -03:00
Andrey Nering
db19fdac29 Update CNAME 2019-03-04 22:23:39 -03:00
Andrey Nering
d516b238b1 Merge pull request #180 from jaedle/master
Display task summary
2019-03-04 21:44:44 -03:00
Andrey Nering
f9330f6cd9 Merge pull request #182 from GuillaumeAmat/fix-completion
Fix the zsh completion with sub-tasks
2019-03-04 21:39:43 -03:00
jaedle
360da29e1f refactoring 2019-03-04 13:04:04 +01:00
jaedle
9cfac1642a rename method for summary/summaries 2019-03-04 13:03:13 +01:00
jaedle
db90e87d10 rearrange imports 2019-03-04 12:53:06 +01:00
jaedle
b7564080bc add space between tasks 2019-03-04 12:48:26 +01:00
jaedle
1d783bf6c7 refactoring 2019-03-04 12:47:01 +01:00
jaedle
1025c2e3a1 add unit test for spacing between summaries 2019-03-04 12:46:02 +01:00
jaedle
4fd82ab222 refactoring 2019-03-04 12:28:26 +01:00
jaedle
8eadfc1bf6 refactoring 2019-03-04 12:28:11 +01:00
jaedle
f66edbad50 refactoring 2019-03-04 12:27:10 +01:00
jaedle
c7f17b5319 refactoring 2019-03-04 12:25:42 +01:00
jaedle
23c4adcef6 add spacing for tasks 2019-03-04 12:15:40 +01:00
jaedle
808542bed0 remove unnecassry test for multiple summaries 2019-03-04 12:13:13 +01:00
jaedle
93bfd57856 print summary for multiple tasks 2019-03-04 12:09:58 +01:00
jaedle
7e7e1bccba rearrange imports 2019-03-04 12:04:31 +01:00
jaedle
34f6da86c3 rearrange imports 2019-03-04 12:03:28 +01:00
Guillaume AMAT
15c0381c3c Fix the indentation 2019-03-04 07:03:06 +01:00
Guillaume AMAT
c2f4a57e02 Remove \s for MacOS compatibility, use awk instead 2019-03-03 23:32:35 +01:00
Andrey Nering
f945cf2343 Update internal/summary/summary_test.go
Co-Authored-By: jaedle <32975714+jaedle@users.noreply.github.com>
2019-03-03 19:45:00 +01:00
Andrey Nering
5bca3cfd71 Update testdata/summary/Taskfile.yml
Co-Authored-By: jaedle <32975714+jaedle@users.noreply.github.com>
2019-03-03 19:44:27 +01:00
Andrey Nering
26ce4e6886 Update testdata/summary/Taskfile.yml
Co-Authored-By: jaedle <32975714+jaedle@users.noreply.github.com>
2019-03-03 19:44:14 +01:00
Andrey Nering
f5f0e0c376 Update internal/summary/summary.go
Co-Authored-By: jaedle <32975714+jaedle@users.noreply.github.com>
2019-03-03 19:43:57 +01:00
Andrey Nering
9dea1e7f3e Update docs/usage.md
Co-Authored-By: jaedle <32975714+jaedle@users.noreply.github.com>
2019-03-03 19:43:23 +01:00
Andrey Nering
c2e0f8c81f Update docs/usage.md
Co-Authored-By: jaedle <32975714+jaedle@users.noreply.github.com>
2019-03-03 18:56:42 +01:00
Andrey Nering
d341bc25ce Revert "Try out Windows builds in Travis"
This reverts commit fc34d6b56f.

Unfortunately, something seems wrong with Windows build on Travis.
And the output log is unhelpful to debug the problem.
2019-03-03 14:42:33 -03:00
Andrey Nering
0379e2b51b Merge pull request #175 from emirb/patch-1
Try out Windows builds in Travis
2019-03-02 11:18:03 -03:00
Guillaume AMAT
e79026b840 Fix the zsh completion with sub-tasks 2019-03-02 01:40:31 +01:00
Emir Beganović
fc34d6b56f Try out Windows builds in Travis 2019-02-25 09:59:25 +04:00
jaedle
2a1571a99e refactoring 2019-02-24 19:14:15 +01:00
jaedle
c158608255 fix error in documentation 2019-02-24 19:10:44 +01:00
jaedle
3ca590b185 display summary for tasks without summary/description 2019-02-24 19:02:44 +01:00
jaedle
3f8ee21849 print error messsage if no summary or description present 2019-02-24 18:26:16 +01:00
jaedle
845b88a193 print only task name if summary 2019-02-24 18:20:59 +01:00
jaedle
e252972c7f rename test 2019-02-24 17:29:03 +01:00
jaedle
a9012ebfc5 refactoring 2019-02-24 17:28:06 +01:00
jaedle
5cfd9bbbbd refactoring 2019-02-24 17:25:03 +01:00
jaedle
c82a7240bb print task in command section 2019-02-24 17:23:31 +01:00
jaedle
a4a20d92a4 add unit test for full output 2019-02-24 17:20:29 +01:00
jaedle
890996f595 hides commands keywoard if not present 2019-02-24 17:12:22 +01:00
jaedle
474f27c6d3 add unit test for displaying commands 2019-02-24 17:10:59 +01:00
jaedle
33f3894372 add unit tests for summary 2019-02-24 17:05:37 +01:00
jaedle
24436ac76e refactoring 2019-02-24 16:26:46 +01:00
jaedle
3ee66ef705 remove output to own package 2019-02-24 16:25:27 +01:00
jaedle
a1765e1d33 refactoring 2019-02-24 16:17:47 +01:00
jaedle
765e3dbf72 print only commands if present 2019-02-24 16:15:59 +01:00
jaedle
80f5cee599 refactoring 2019-02-24 16:10:43 +01:00
jaedle
4dcb124693 print commands on summary only if commands are present 2019-02-24 16:08:32 +01:00
jaedle
31ecf167cc rename to summary in test fixtures 2019-02-24 15:54:11 +01:00
jaedle
3999480d64 refactoring 2019-02-24 15:45:39 +01:00
Andrey Nering
9dbb503c23 Update vendor directory 2019-02-24 11:45:32 -03:00
Andrey Nering
a98f803d87 Upgrade mvdan.cc/sh 2019-02-24 11:44:53 -03:00
jaedle
9e9ffeb5d5 refactoring 2019-02-24 15:43:45 +01:00
jaedle
33d4ad4d84 rename to summary 2019-02-24 15:38:18 +01:00
jaedle
d05d418c4c renaming field in taskfile to summary 2019-02-24 15:37:02 +01:00
jaedle
06d0af7a1d rename details in Executor to summary 2019-02-24 15:33:09 +01:00
jaedle
9a3b726068 change help to summary 2019-02-24 15:32:24 +01:00
jaedle
2676ab9a59 renamed program flag to summary 2019-02-24 15:31:46 +01:00
jaedle
a1837d553e refactoring 2019-02-24 14:59:19 +01:00
jaedle
fdbc130d8d do not show empty dependencies 2019-02-24 14:55:04 +01:00
jaedle
4b3cea3812 display dependend tasks 2019-02-24 14:53:39 +01:00
jaedle
1c3082ffa6 rename test fixture 2019-02-24 14:48:48 +01:00
jaedle
0446cfdba0 display commands of task 2019-02-24 14:37:14 +01:00
jaedle
db1d3183b6 refatoring 2019-02-24 14:32:47 +01:00
jaedle
fb666394fc refatoring 2019-02-24 14:31:29 +01:00
jaedle
1054c89a9d add missing test fixture file 2019-02-24 14:24:55 +01:00
jaedle
8dd87dc482 refactoring 2019-02-24 14:23:44 +01:00
jaedle
b2edbf05a1 refactoring 2019-02-24 14:20:39 +01:00
jaedle
6fb53a406b remove unusued expectations 2019-02-24 14:18:51 +01:00
jaedle
b05fa0821d move expectations for output to testdata 2019-02-24 14:18:07 +01:00
jaedle
0a808b1212 fix swapped expected and actual parameter 2019-02-24 14:10:46 +01:00
jaedle
f1d83e92a7 print command stub on details 2019-02-24 14:08:27 +01:00
jaedle
31b60f7f60 display task name on details 2019-02-24 14:01:53 +01:00
jaedle
c0f9af5daa refactoring 2019-02-24 12:15:59 +01:00
jaedle
b25a9e8884 refactoring 2019-02-24 12:13:18 +01:00
jaedle
3c0cf3cd55 fix documentation 2019-02-24 12:00:45 +01:00
jaedle
1ac6f17e6a should not surpress empty lines expect on last line 2019-02-24 11:58:44 +01:00
jaedle
399a2b38f3 add documentation for details 2019-02-24 11:52:31 +01:00
jaedle
b97221cdb2 ignore empty lines on description 2019-02-24 11:31:25 +01:00
jaedle
0164bc21ea be more specific in tests about output 2019-02-24 11:28:15 +01:00
jaedle
5a23250d32 simplified tests 2019-02-24 11:25:26 +01:00
jaedle
80d88d9789 refactoring 2019-02-24 11:22:14 +01:00
jaedle
31ead854c7 fix test expectation 2019-02-24 11:19:08 +01:00
jaedle
4b64fcb8a4 add more tests 2019-02-24 11:09:55 +01:00
jaedle
a951f2403d add more tests for details 2019-02-24 11:01:48 +01:00
jaedle
f9adeba7f1 add basic test for details 2019-02-24 09:53:49 +01:00
jaedle
5c823d51d0 revert changes for taskfile 2019-02-24 09:29:19 +01:00
jaedle
9be7521b83 refactoring 2019-02-24 09:28:25 +01:00
jaedle
c73ddc3552 refactoring 2019-02-24 09:27:26 +01:00
jaedle
4b7f058f41 refacotring 2019-02-24 09:25:39 +01:00
jaedle
07221a1b20 output detailed task description 2019-02-24 09:24:57 +01:00
jaedle
13614fb3c4 add details flag for cli 2019-02-24 08:51:20 +01:00
jaedle
4fa983bde7 ignore ide configuration 2019-02-24 08:24:09 +01:00
Andrey Nering
9cb1db8c0a Docs: Fix wrong URL 2019-02-21 21:57:21 -03:00
Andrey Nering
5738436d55 v2.4.0 2019-02-21 21:28:10 -03:00
Andrey Nering
5e49b38c33 Mitigate execext.Expand problems on Windows
Closes #170

Co-authored-by: mikynov <micnov@gmail.com>
2019-02-21 21:22:40 -03:00
Andrey Nering
0c94adaff9 Update CHANGELOG.md 2019-02-21 21:06:46 -03:00
Andrey Nering
f8a6c5d06c Fix execext.Expand for file names with spaces
Fixes #176
2019-02-21 20:59:17 -03:00
Andrey Nering
21e66c7c02 Docs: Update theme color 2019-02-09 10:48:48 -02:00
Andrey Nering
902f0d3ac4 Don't persist new checksum on the disk if dry mode is enabled
Fixes #166
2019-02-09 10:44:35 -02:00
Andrey Nering
713ecd35f6 Pass context as an argument 2019-02-09 10:16:13 -02:00
Andrey Nering
27b35157cd Indentation fix 2019-02-09 10:15:38 -02:00
Andrey Nering
f8fb639870 Update documentation and changelog to mention the new --output flag
Ref #173
2019-02-09 10:01:41 -02:00
Andrey Nering
14f41ae619 Merge pull request #173 from kjdev/master
Add execute output style options
2019-02-07 19:51:49 -02:00
kj
a026d72924 Add execute output style options 2019-02-05 15:42:57 +09:00
Andrey Nering
2cb070f5b3 Merge pull request #172 from go-task/allow-calling-root-task-from-included
Allow calling a task of the root Taskfile from within an included Taskfile
2019-02-02 21:26:22 -02:00
Andrey Nering
1dec956e99 Allow calling a task of the root Taskfile from within an included Taskfile
Fixes #161
2019-02-02 21:22:08 -02:00
Tim Foerster
310394aa60 task: Fix merge behavior 2019-02-02 17:19:20 -02:00
Andrey Nering
468ff18243 Merge pull request #164 from saromanov/fix-error-message
taskfile: return defined error when taskfile.yml is not found
2019-01-22 22:16:45 -02:00
Sergey
44a63580f0 taskfile: missing task: prefix to the error message 2019-01-23 02:01:53 +05:00
Andrey Nering
4ac1fa43aa Merge pull request #165 from 0xflotus/patch-1
fixed docs
2019-01-21 23:00:01 -02:00
0xflotus
6f992a3cf7 fixed suppressed 2019-01-21 13:36:04 +01:00
0xflotus
fd4ce656d5 fixed Snapcraft 2019-01-21 13:33:19 +01:00
Sergey
9ed2dca427 taskfile: return defined error when taskfile.yml is not found 2019-01-21 14:56:14 +05:00
Andrey Nering
dfb804fe3f Update vendor/ 2019-01-19 19:25:49 -02:00
Andrey Nering
4f2a84b426 Upgrade some libs 2019-01-19 19:24:49 -02:00
Andrey Nering
14a127b6b3 Pin mattn/go-zglob version 2019-01-19 19:08:30 -02:00
Andrey Nering
06000533fb Pin mvdan/sh version 2019-01-19 19:07:11 -02:00
Andrey Nering
7722aba403 v2.3.0 2019-01-02 13:44:27 -02:00
Andrey Nering
4817d8c67f Move documentation tasks to its own Taskfile 2019-01-02 13:42:06 -02:00
Andrey Nering
9a062d90d1 Merge pull request #159 from go-task/global-environment-variables-#138
Add ability to globally set environment variables
2019-01-02 13:29:14 -02:00
Andrey Nering
959eb45373 Docs: Fix some typos 2019-01-02 13:25:58 -02:00
Andrey Nering
a42f2af9eb Documentation and changelog for global environment variables 2019-01-02 13:21:21 -02:00
Andrey Nering
4ddad68212 Merge global environment variables when merging tasks 2019-01-02 13:20:12 -02:00
Andrey Nering
aac6c5a1c7 Add hability to globally set environment variables
Closes #138
2019-01-02 12:06:12 -02:00
Andrey Nering
5572e31fd4 Merge pull request #157 from frederikhors/patch-2
Misprint
2018-12-27 10:49:24 -02:00
frederikhors
233b8bf81a Misprint 2018-12-27 01:11:36 +01:00
Andrey Nering
2ae3810f80 Merge pull request #156 from frederikhors/patch-1
Misprint
2018-12-26 10:38:20 -02:00
frederikhors
736165876c Misprint 2018-12-26 12:04:00 +01:00
Andrey Nering
61b3fca9a3 Merge pull request #154 from go-task/upgrade-mvdan-sh
Upgrade mvdan/sh
2018-12-24 15:30:45 -02:00
Andrey Nering
469863b7b3 go mod vendor 2018-12-24 15:26:33 -02:00
Andrey Nering
5238bc55fd Upgrade mvdan.cc/sh
Fixes #153
2018-12-24 15:25:19 -02:00
Andrey Nering
57a01aa6ff Fix failing test
There was some breaking changes described at
https://github.com/mvdan/sh/issues/335#issuecomment-447605295
2018-12-24 15:19:53 -02:00
Andrey Nering
9361dbc39e Merge branch 'master' into upgrade-mvdan-sh 2018-12-24 15:08:29 -02:00
Andrey Nering
11d257cb26 Changelog: Mention Scoop 2018-12-24 15:03:30 -02:00
Andrey Nering
a928ab75e3 Docs: Give more visibility to sponsors and contributors 2018-12-24 15:00:14 -02:00
Andrey Nering
55a240c82e Improve documentation on Scoop
Updates #152
2018-12-24 14:42:49 -02:00
Andrey Nering
f8aedf438b Merge pull request #152 from lambdalisue/docs-scoop
Add Scoop
2018-12-24 14:32:53 -02:00
Andrey Nering
9f1bb9a42e Add CHANGELOG.md
This was generated in a semi-automated way using the existing GitHub releases,
fetch thought the GitHub API.
2018-12-15 16:14:39 -02:00
Andrey Nering
0ed7274610 go mod vendor 2018-12-15 15:44:17 -02:00
Andrey Nering
df032b09a7 Upgrade mvdan/sh 2018-12-15 15:43:40 -02:00
lambdalisue
780bd08490 Add Scoop
Task is now available on Scoop for Windows users.
https://github.com/lukesampson/scoop-extras/pull/1485
2018-12-06 22:21:03 +09:00
376 changed files with 87290 additions and 20588 deletions

1
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1 @@
open_collective: task

30
.github/workflows/test.yml vendored Normal file
View File

@@ -0,0 +1,30 @@
name: Test
on: [push, pull_request]
jobs:
test:
name: Test
strategy:
matrix:
go-version: [1.12.x, 1.13.x]
platform: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{matrix.platform}}
steps:
- name: Set up Go ${{matrix.go-version}}
uses: actions/setup-go@v1
with:
go-version: ${{matrix.go-version}}
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v1
- name: Download Go modules
run: go mod download
env:
GOPROXY: https://proxy.golang.org
- name: Build
run: go build -o ./bin/task -v ./cmd/task
- name: Test
run: ./bin/task test

8
.gitignore vendored
View File

@@ -18,3 +18,11 @@
dist/
.DS_Store
# intellij idea/goland
.idea/
# exuberant ctags
tags
/bin

View File

@@ -14,12 +14,11 @@ build:
env:
- CGO_ENABLED=0
archive:
name_template: "{{.Binary}}_{{.Os}}_{{.Arch}}"
format_overrides:
- goos: windows
format: zip
archives:
- name_template: "{{.Binary}}_{{.Os}}_{{.Arch}}"
format_overrides:
- goos: windows
format: zip
release:
draft: true
@@ -30,15 +29,15 @@ snapshot:
checksum:
name_template: "task_checksums.txt"
nfpm:
vendor: Task
homepage: https://github.com/go-task/task
maintainer: Andrey Nering <andrey.nering@gmail.com>
description: Simple task runner written in Go
license: MIT
conflicts:
- taskwarrior
formats:
- deb
- rpm
name_template: "{{.ProjectName}}_{{.Os}}_{{.Arch}}"
nfpms:
- vendor: Task
homepage: https://github.com/go-task/task
maintainer: Andrey Nering <andrey.nering@gmail.com>
description: Simple task runner written in Go
license: MIT
conflicts:
- taskwarrior
formats:
- deb
- rpm
name_template: "{{.ProjectName}}_{{.Os}}_{{.Arch}}"

View File

@@ -1,8 +1,11 @@
language: go
go:
- 1.10.x
- 1.11.x
- 1.12.x
- 1.13.x
env:
- GO111MODULE=on
addons:
apt:
@@ -10,13 +13,5 @@ addons:
- rpm
script:
- go install github.com/go-task/task/cmd/task
- 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

216
CHANGELOG.md Normal file
View File

@@ -0,0 +1,216 @@
# Changelog
## 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
- Fixed panic bug when assigning a global variable
([#229](https://github.com/go-task/task/issues/229), [#243](https://github.com/go-task/task/issues/234)).
- A task with `method: checksum` will now re-run if generated files are deleted
([#228](https://github.com/go-task/task/pull/228), [#238](https://github.com/go-task/task/issues/238)).
## v2.6.0 - 2019-07-21
- Fixed some bugs regarding minor version checks on `version:`.
- Add `preconditions:` to task
([#205](https://github.com/go-task/task/pull/205)).
- Create directory informed on `dir:` if it doesn't exist
([#209](https://github.com/go-task/task/issues/209), [#211](https://github.com/go-task/task/pull/211)).
- We now have a `--taskfile` flag (alias `-t`), which can be used to run
another Taskfile (other than the default `Taskfile.yml`)
([#221](https://github.com/go-task/task/pull/221)).
- It's now possible to install Task using Homebrew on Linux
([go-task/homebrew-tap#1](https://github.com/go-task/homebrew-tap/pull/1)).
## v2.5.2 - 2019-05-11
- Reverted YAML upgrade due issues with CRLF on Windows
([#201](https://github.com/go-task/task/issues/201), [go-yaml/yaml#450](https://github.com/go-yaml/yaml/issues/450)).
- Allow setting global variables through the CLI
([#192](https://github.com/go-task/task/issues/192)).
## 2.5.1 - 2019-04-27
- Fixed some issues with interactive command line tools, where sometimes
the output were not being shown, and similar issues
([#114](https://github.com/go-task/task/issues/114), [#190](https://github.com/go-task/task/issues/190), [#200](https://github.com/go-task/task/pull/200)).
- Upgraded [go-yaml/yaml](https://github.com/go-yaml/yaml) from v2 to v3.
## v2.5.0 - 2019-03-16
- We moved from the taskfile.org domain to the new fancy taskfile.dev domain.
While stuff is being redirected, we strongly recommend to everyone that use
[this install script](https://taskfile.dev/#/installation?id=install-script)
to use the new taskfile.dev domain on scripts from now on.
- Fixed to the ZSH completion
([#182](https://github.com/go-task/task/pull/182)).
- Add [`--summary` flag along with `summary:` task attribute](https://taskfile.org/#/usage?id=display-summary-of-task)
([#180](https://github.com/go-task/task/pull/180)).
## v2.4.0 - 2019-02-21
- Allow calling a task of the root Taskfile from an included Taskfile
by prefixing it with `:`
([#161](https://github.com/go-task/task/issues/161), [#172](https://github.com/go-task/task/issues/172)),
- Add flag to override the `output` option
([#173](https://github.com/go-task/task/pull/173));
- Fix bug where Task was persisting the new checksum on the disk when the Dry
Mode is enabled
([#166](https://github.com/go-task/task/issues/166));
- Fix file timestamp issue when the file name has spaces
([#176](https://github.com/go-task/task/issues/176));
- Mitigating path expanding issues on Windows
([#170](https://github.com/go-task/task/pull/170)).
## v2.3.0 - 2019-01-02
- On Windows, Task can now be installed using [Scoop](https://scoop.sh/)
([#152](https://github.com/go-task/task/pull/152));
- Fixed issue with file/directory globing
([#153](https://github.com/go-task/task/issues/153));
- Added ability to globally set environment variables
(
[#138](https://github.com/go-task/task/pull/138),
[#159](https://github.com/go-task/task/pull/159)
).
## v2.2.1 - 2018-12-09
- This repository now uses Go Modules (#143). We'll still keep the `vendor` directory in sync for some time, though;
- Fixing a bug when the Taskfile has no tasks but includes another Taskfile (#150);
- Fix a bug when calling another task or a dependency in an included Taskfile (#151).
## v2.2.0 - 2018-10-25
- Added support for [including other Taskfiles](https://taskfile.org/#/usage?id=including-other-taskfiles) (#98)
- This should be considered experimental. For now, only including local files is supported, but support for including remote Taskfiles is being discussed. If you have any feedback, please comment on #98.
- Task now have a dedicated documentation site: https://taskfile.org
- Thanks to [Docsify](https://docsify.js.org/) for making this pretty easy. To check the source code, just take a look at the [docs](https://github.com/go-task/task/tree/master/docs) directory of this repository. Contributions to the documentation is really appreciated.
## v2.1.1 - 2018-09-17
- Fix suggestion to use `task --init` not being shown anymore (when a `Taskfile.yml` is not found)
- Fix error when using checksum method and no file exists for a source glob (#131)
- Fix signal handling when the `--watch` flag is given (#132)
## v2.1.0 - 2018-08-19
- Add a `ignore_error` option to task and command (#123)
- Add a dry run mode (`--dry` flag) (#126)
## v2.0.3 - 2018-06-24
- Expand environment variables on "dir", "sources" and "generates" (#116)
- Fix YAML merging syntax (#112)
- Add ZSH completion (#111)
- Implement new `output` option. Please check out the [documentation](https://github.com/go-task/task#output-syntax)
## v2.0.2 - 2018-05-01
- Fix merging of YAML anchors (#112)
## v2.0.1 - 2018-03-11
- Fixes panic on `task --list`
## v2.0.0 - 2018-03-08
Version 2.0.0 is here, with a new Taskfile format.
Please, make sure to read the [Taskfile versions](https://github.com/go-task/task/blob/master/TASKFILE_VERSIONS.md) document, since it describes in depth what changed for this version.
* New Taskfile version 2 (https://github.com/go-task/task/issues/77)
* Possibility to have global variables in the `Taskfile.yml` instead of `Taskvars.yml` (https://github.com/go-task/task/issues/66)
* Small improvements and fixes
## v1.4.4 - 2017-11-19
- Handle SIGINT and SIGTERM (#75);
- List: print message with there's no task with description;
- Expand home dir ("~" symbol) on paths (#74);
- Add Snap as an installation method;
- Move examples to its own repo;
- Watch: also walk on tasks called on on "cmds", and not only on "deps";
- Print logs to stderr instead of stdout (#68);
- Remove deprecated `set` keyword;
- Add checksum based status check, alternative to timestamp based.
## v1.4.3 - 2017-09-07
- Allow assigning variables to tasks at run time via CLI (#33)
- Added suport for multiline variables from sh (#64)
- Fixes env: remove square braces and evaluate shell (#62)
- Watch: change watch library and few fixes and improvements
- When use watching, cancel and restart long running process on file change (#59 and #60)
## v1.4.2 - 2017-07-30
- Flag to set directory of execution
- Always echo command if is verbose mode
- Add silent mode to disable echoing of commands
- Fixes and improvements of variables (#56)
## v1.4.1 - 2017-07-15
- Allow use of YAML for dynamic variables instead of $ prefix
- `VAR: {sh: echo Hello}` instead of `VAR: $echo Hello`
- Add `--list` (or `-l`) flag to print existing tasks
- OS specific Taskvars file (e.g. `Taskvars_windows.yml`, `Taskvars_linux.yml`, etc)
- Consider task up-to-date on equal timestamps (#49)
- Allow absolute path in generates section (#48)
- Bugfix: allow templating when calling deps (#42)
- Fix panic for invalid task in cyclic dep detection
- Better error output for dynamic variables in Taskvars.yml (#41)
- Allow template evaluation in parameters
## v1.4.0 - 2017-07-06
- Cache dynamic variables
- Add verbose mode (`-v` flag)
- Support to task parameters (overriding vars) (#31) (#32)
- Print command, also when "set:" is specified (#35)
- Improve task command help text (#35)
## v1.3.1 - 2017-06-14
- Fix glob not working on commands (#28)
- Add ExeExt template function
- Add `--init` flag to create a new Taskfile
- Add status option to prevent task from running (#27)
- Allow interpolation on `generates` and `sources` attributes (#26)
## v1.3.0 - 2017-04-24
- Migrate from os/exec.Cmd to a native Go sh/bash interpreter
- This is a potentially breaking change if you use Windows.
- Now, `cmd` is not used anymore on Windows. Always use Bash-like syntax for your commands, even on Windows.
- Add "ToSlash" and "FromSlash" to template functions
- Use functions defined on github.com/Masterminds/sprig
- Do not redirect stdin while running variables commands
- Using `context` and `errgroup` packages (this will make other tasks to be cancelled, if one returned an error)
## v1.2.0 - 2017-04-02
- More tests and Travis integration
- Watch a task (experimental)
- Possibility to call another task
- Fix "=" not being reconized in variables/environment variables
- Tasks can now have a description, and help will print them (#10)
- Task dependencies now run concurrently
- Support for a default task (#16)
## v1.1.0 - 2017-03-08
- Support for YAML, TOML and JSON (#1)
- Support running command in another directory (#4)
- `--force` or `-f` flag to force execution of task even when it's up-to-date
- Detection of cyclic dependencies (#5)
- Support for variables (#6, #9, #14)
- Operation System specific commands and variables (#13)
## v1.0.0 - 2017-02-28
- Add LICENSE file

View File

@@ -3,10 +3,20 @@
# Task
Task is a task runner / build tool that aims to be simpler and easier to use
than, for example, [GNU Make][make].
than, for example, [GNU Make](https://www.gnu.org/software/make/).
See [taskfile.dev](https://taskfile.dev) for documentation.
---
See [taskfile.org](https://taskfile.org) for documentation.
## Sponsors
[make]: https://www.gnu.org/software/make/
[![Sponsors](https://opencollective.com/task/sponsors.svg?width=890)](https://opencollective.com/task)
## Backers
[![Backers](https://opencollective.com/task/backers.svg?width=890)](https://opencollective.com/task)
## Contributors
[![Contributors](https://opencollective.com/task/contributors.svg?width=890)](https://github.com/go-task/task/graphs/contributors)

View File

@@ -1,5 +1,8 @@
version: '2'
includes:
docs: ./docs
vars:
GIT_COMMIT:
sh: git log -n 1 --format=%h
@@ -83,13 +86,3 @@ tasks:
cmds:
- echo '{{.GO_PACKAGES}}'
silent: true
docs:install:
desc: Installs docsify to work the on the documentation site
cmds:
- npm install docsify-cli -g
docs:serve:
desc: Serves the documentation site locally
cmds:
- docsify serve docs

View File

@@ -0,0 +1,421 @@
// 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
`)

View File

@@ -5,6 +5,7 @@ import (
"log"
"os"
"os/signal"
"path/filepath"
"syscall"
"github.com/go-task/task/v2"
@@ -17,7 +18,7 @@ var (
version = "master"
)
const usage = `Usage: task [-ilfwvsd] [--init] [--list] [--force] [--watch] [--verbose] [--silent] [--dir] [--dry] [task...]
const usage = `Usage: task [-ilfwvsd] [--init] [--list] [--force] [--watch] [--verbose] [--silent] [--dir] [--taskfile] [--dry] [--summary] [task...]
Runs the specified task(s). Falls back to the "default" task if no task name
was specified, or lists all tasks if an unknown task name was specified.
@@ -56,7 +57,10 @@ func main() {
verbose bool
silent bool
dry bool
summary bool
dir string
entrypoint string
output string
)
pflag.BoolVar(&versionFlag, "version", false, "show Task version")
@@ -68,7 +72,10 @@ func main() {
pflag.BoolVarP(&verbose, "verbose", "v", false, "enables verbose mode")
pflag.BoolVarP(&silent, "silent", "s", false, "disables echoing")
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.StringVarP(&dir, "dir", "d", "", "sets directory of execution")
pflag.StringVarP(&entrypoint, "taskfile", "t", "", `choose which Taskfile to run. Defaults to "Taskfile.yml"`)
pflag.StringVarP(&output, "output", "o", "", "sets output style: [interleaved|group|prefixed]")
pflag.Parse()
if versionFlag {
@@ -87,24 +94,32 @@ func main() {
return
}
ctx := context.Background()
if !watch {
ctx = getSignalContext()
if dir != "" && entrypoint != "" {
log.Fatal("task: You can't set both --dir and --taskfile")
return
}
if entrypoint != "" {
dir = filepath.Dir(entrypoint)
entrypoint = filepath.Base(entrypoint)
} else {
entrypoint = "Taskfile.yml"
}
e := task.Executor{
Force: force,
Watch: watch,
Verbose: verbose,
Silent: silent,
Dir: dir,
Dry: dry,
Context: ctx,
Force: force,
Watch: watch,
Verbose: verbose,
Silent: silent,
Dir: dir,
Dry: dry,
Entrypoint: entrypoint,
Summary: summary,
Stdin: os.Stdin,
Stdout: os.Stdout,
Stderr: os.Stderr,
OutputStyle: output,
}
if err := e.Setup(); err != nil {
log.Fatal(err)
@@ -121,19 +136,24 @@ func main() {
arguments = []string{"default"}
}
calls, err := args.Parse(arguments...)
if err != nil {
log.Fatal(err)
calls, globals := args.Parse(arguments...)
for name, value := range globals {
e.Taskfile.Vars[name] = value
}
ctx := context.Background()
if !watch {
ctx = getSignalContext()
}
if status {
if err = e.Status(calls...); err != nil {
if err := e.Status(ctx, calls...); err != nil {
log.Fatal(err)
}
return
}
if err := e.Run(calls...); err != nil {
if err := e.Run(ctx, calls...); err != nil {
log.Fatal(err)
}
}

21
completion/bash/task.bash Normal file
View 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' | sed 's/^\* //' | awk '{ print $1 }');
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

2
completion/zsh/_task Normal file → Executable file
View File

@@ -5,7 +5,7 @@ function __list() {
local -a scripts
if [ -f Taskfile.yml ]; then
scripts=($(task -l | sed '1d' | sed 's/://' | awk '{ print $2 }'))
scripts=($(task -l | sed '1d' | sed 's/^\* //' | awk '{ print $1 }' | sed 's/:$//' | sed 's/:/\\:/'))
_describe 'script' scripts
fi
}

View File

@@ -1 +1 @@
taskfile.org
taskfile.dev

View File

@@ -28,8 +28,8 @@ guide to check the full schema documentation and Task features.
## Features
- [Easy installation](installation.md): just download a single binary, add to
$PATH and you're done! Or you can also install using [Homebrew][homebrew] or
[Snapcraft][snapcraft] if you want;
$PATH and you're done! Or you can also install using [Homebrew][homebrew],
[Snapcraft][snapcraft], or [Scoop][scoop] if you want;
- Available on CIs: by adding [this simple command](installation.md#install-script)
to install on your CI script and you're done to use Task as part of your CI pipeline;
- Truly cross-platform: while most build tools only work well on Linux or macOS,
@@ -38,9 +38,22 @@ guide to check the full schema documentation and Task features.
if a given set of files haven't changed since last run (based either on its
timestamp or content).
## Sponsors
[![Sponsors](https://opencollective.com/task/sponsors.svg?width=890)](https://opencollective.com/task)
## Backers
[![Backers](https://opencollective.com/task/backers.svg?width=890)](https://opencollective.com/task)
## Contributors
[![Contributors](https://opencollective.com/task/contributors.svg?width=890)](https://github.com/go-task/task/graphs/contributors)
[make]: https://www.gnu.org/software/make/
[go]: https://golang.org/
[yaml]: http://yaml.org/
[homebrew]: https://brew.sh/
[snapcraft]: https://snapcraft.io/
[scoop]: https://scoop.sh/
[sh]: https://mvdan.cc/sh

12
docs/Taskfile.yml Normal file
View File

@@ -0,0 +1,12 @@
version: '2'
tasks:
install:
desc: Installs docsify to work the on the documentation site
cmds:
- npm install docsify-cli -g
serve:
desc: Serves the documentation site locally
cmds:
- docsify serve docs

View File

@@ -1,8 +1,8 @@
- [Installation](installation.md)
- [Usage](usage.md)
- [Styleguide](styleguide.md)
- [Taskfile Versions](taskfile_versions.md)
- [Examples](examples.md)
- [Releasing Task](releasing_task.md)
- [Alternative Task Runners](alternative_task_runners.md)
- [Sponsors and Backers](sponsors_and_backers.md)
- [![Github](https://icongram.jgog.in/simple/github.svg?color=808080&size=16)Github](https://github.com/go-task/task)

View File

@@ -33,7 +33,7 @@
name: 'Task',
repo: 'go-task/task',
ga: 'UA-126286662-1',
themeColor: '#83d0f2',
themeColor: '#00add8',
loadSidebar: true,
auto2top: true,
maxLevel: 3,

View File

@@ -8,13 +8,15 @@ The `task_checksums.txt` file contains the sha256 checksum for each file.
## Homebrew
If you're on macOS and have [Homebrew][homebrew] installed, getting Task is
as simple as running:
If you're on macOS or Linux and have [Homebrew][homebrew] installed, getting
Task is as simple as running:
```bash
brew install go-task/tap/go-task
```
> This installation method is only currently supported on amd64 architectures.
## Snap
Task is available for [Snapcraft][snapcraft], but keep in mind that your
@@ -25,21 +27,29 @@ right:
sudo snap install task
```
## Scoop
If you're on Windows and have [Scoop][scoop] installed, use `extras` bucket
to install Task like:
```cmd
scoop bucket add extras
scoop install task
```
This installation method is community owned. After a new release of Task, it
may take some time until it's available on Scoop.
## Go
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)
instead, which is faster and more stable, since it'll just download the latest
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:
```bash
@@ -47,7 +57,7 @@ git clone https://github.com/go-task/task
cd task
# compiling binary to $GOPATH/bin
go install -v
go install -v ./cmd/task
# compiling it to another location
# use -o ./task.exe on Windows
@@ -59,18 +69,32 @@ Both methods requires having the [Go][go] environment properly setup locally.
## Install script
We also have a [install script][installscript], which is very useful on
scanarios like CIs. Many thanks to [godownloader][godownloader] for allowing
scenarios like CIs. Many thanks to [godownloader][godownloader] for allowing
easily generating this script.
```bash
curl -s https://taskfile.org/install.sh | sh
curl -sL https://taskfile.dev/install.sh | sh
```
> 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/
[snapcraft]: https://snapcraft.io/
[homebrew]: https://brew.sh/
[installscript]: https://github.com/go-task/task/blob/master/install-task.sh
[releases]: https://github.com/go-task/task/releases
[godownloader]: https://github.com/goreleaser/godownloader
[scoop]: https://scoop.sh/

View File

@@ -1,6 +1,6 @@
# Releasing Task
The release process of Task is done is done with the help of
The release process of Task is done with the help of
[GoReleaser][goreleaser]. You can test the release process locally by calling
the `test-release` task of the Taskfile.
@@ -22,7 +22,14 @@ the binaries:
* Updating the current version on [snapcraft.yaml][snapcraftyaml];
* Moving both `i386` and `amd64` new artifacts to the stable channel on
the [Snapscraft dashboard][snapcraftdashboard]
the [Snapcraft dashboard][snapcraftdashboard]
# Scoop
Scoop is a community owned installation method. Scoop owners usually take care
of updating versions there by editing
[this file](https://github.com/lukesampson/scoop-extras/blob/master/bucket/task.json).
If you think its Task version is outdated, open an issue to let us know.
[goreleaser]: https://goreleaser.com/#continuous_integration
[homebrewtap]: https://github.com/go-task/homebrew-tap

View File

@@ -1,16 +0,0 @@
# Sponsors and Backers
## Sponsors
[![Sponsors](https://opencollective.com/task/sponsors.svg?width=890)][opencollective]
## Backers
[![Backers](https://opencollective.com/task/backers.svg?width=890)][opencollective]
## Contributors
[![Contributors](https://opencollective.com/task/contributors.svg?width=890)][contributors]
[opencollective]: https://opencollective.com/task
[contributors]: https://github.com/go-task/task/graphs/contributors

213
docs/styleguide.md Normal file
View 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 to specially to 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:` and `expansions:`
- `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.

View File

@@ -141,6 +141,21 @@ includes:
docker: ./DockerTasks.yml
```
## Version 2.6
Version 2.6 comes with `preconditions` stanza in tasks.
```yaml
version: '2'
tasks:
upload_environment:
preconditions:
- test -f .env
cmds:
- aws s3 cp .env s3://myenvironment
```
Please check the [documentation][includes]
[output]: usage.md#output-syntax

View File

@@ -31,23 +31,41 @@ interpreter. So you can write sh/bash commands and it will work even on
Windows, where `sh` or `bash` are usually not available. Just remember any
executable called must be available by the OS or in PATH.
If you ommit a task name, "default" will be assumed.
If you omit a task name, "default" will be assumed.
## Environment
You can specify environment variables that are added when running a command:
You can use `env` to set custom environment variables for a specific task:
```yaml
version: '2'
tasks:
build:
greet:
cmds:
- echo $hallo
- echo $GREETING
env:
hallo: welt
GREETING: Hey, there!
```
Additionally, you can set globally environment variables, that'll be available
to all tasks:
```yaml
version: '2'
env:
GREETING: Hey, there!
tasks:
greet:
cmds:
- echo $GREETING
```
> NOTE: `env` supports expansion and retrieving output from a shell command
> just like variables, as you can see on the [Variables](#variables) section.
## Operating System specific tasks
If you add a `Taskfile_{{GOOS}}.yml` you can override or amend your Taskfile
@@ -83,7 +101,7 @@ Keep in mind that the version of the files should match. Also, when redefining
a task the whole task is replaced, properties of the task are not merged.
It's also possible to have an OS specific `Taskvars.yml` file, like
`Taskvars_windows.yml`, `Taskfile_linux.yml`, or `Taskvars_darwin.yml`. See the
`Taskvars_windows.yml`, `Taskvars_linux.yml`, or `Taskvars_darwin.yml`. See the
[variables section](#variables) below.
## Including other Taskfiles
@@ -130,6 +148,8 @@ tasks:
- caddy
```
If the directory doesn't exist, `task` creates it.
## Task dependencies
You may have tasks that depend on others. Just pointing them on `deps` will
@@ -240,6 +260,10 @@ tasks:
The above syntax is also supported in `deps`.
> NOTE: If you want to call a task declared in the root Taskfile from within an
> [included Taskfile](#including-other-taskfiles), add a leading `:` like this:
> `task: :task-name`.
## Prevent unnecessary work
If a task generates something, you can inform Task the source and generated
@@ -322,12 +346,61 @@ up-to-date.
Also, `task --status [tasks]...` will exit with a non-zero exit code if any of
the tasks are not up-to-date.
If you need a certain set of conditions to be _true_ you can use the
`preconditions` stanza. `preconditions` are very similar to `status`
lines except they support `sh` expansion and they SHOULD all return 0.
```yaml
version: '2'
tasks:
generate-files:
cmds:
- mkdir directory
- touch directory/file1.txt
- touch directory/file2.txt
# test existence of files
preconditions:
- test -f .env
- sh: "[ 1 = 0 ]"
msg: "One doesn't equal Zero, Halting"
```
Preconditions can set specific failure messages that can tell
a user what steps to take using the `msg` field.
If a task has a dependency on a sub-task with a precondition, and that
precondition is not met - the calling task will fail. Note that a task
executed with a failing precondition will not run unless `--force` is
given.
Unlike `status` which will skip a task if it is up to date, and continue
executing tasks that depend on it, a `precondition` will fail a task, along
with any other tasks that depend on it.
```yaml
version: '2'
tasks:
task_will_fail:
preconditions:
- sh: "exit 1"
task_will_also_fail:
deps:
- task_will_fail
task_will_still_fail:
cmds:
- task: task_will_fail
- echo "I will not run"
```
## Variables
When doing interpolation of variables, Task will look for the below.
They are listed below in order of importance (e.g. most important first):
- Variables declared locally in the task
- Variables declared in the task definition
- Variables given while calling a task from another.
(See [Calling another task](#calling-another-task) above)
- Variables declared in the `vars:` option in the `Taskfile`
@@ -349,6 +422,12 @@ right before.
$ task write-file FILE=file.txt "CONTENT=Hello, World!" print "MESSAGE=All done!"
```
If you want to set global variables using this syntax, give it before any task:
```bash
$ task OUTPUT=file.txt generate-file
```
Example of locally declared vars:
```yaml
@@ -357,7 +436,7 @@ version: '2'
tasks:
print-var:
cmds:
echo "{{.VAR}}"
- echo "{{.VAR}}"
vars:
VAR: Hello!
```
@@ -455,7 +534,7 @@ Task also adds the following functions:
- `catLines`: Replaces Unix (\n) and Windows (\r\n) styled newlines with a space.
- `toSlash`: Does nothing on Unix, but on Windows converts a string from `\`
path format to `/`.
- `fromSlash`: Oposite of `toSlash`. Does nothing on Unix, but on Windows
- `fromSlash`: Opposite of `toSlash`. Does nothing on Unix, but on Windows
converts a string from `\` path format to `/`.
- `exeExt`: Returns the right executable extension for the current OS
(`".exe"` for Windows, `""` for others).
@@ -488,7 +567,7 @@ tasks:
## Help
Running `task --list` (or `task -l`) lists all tasks with a description.
The following taskfile:
The following Taskfile:
```yaml
version: '2'
@@ -520,6 +599,51 @@ would print the following output:
* test: Run all the go tests.
```
## Display summary of task
Running `task --summary task-name` will show a summary of a task
The following Taskfile:
```yaml
version: '2'
tasks:
release:
deps: [build]
summary: |
Release your project to github
It will build your project before starting the release it.
Please make sure that you have set GITHUB_TOKEN before starting.
cmds:
- your-release-tool
build:
cmds:
- your-build-tool
```
with running ``task --summary release`` would print the following output:
```
task: release
Release your project to github
It will build your project before starting the release it.
Please make sure that you have set GITHUB_TOKEN before starting.
dependencies:
- build
commands:
- your-release-tool
```
If a summary is missing, the description will be printed.
If the task does not have a summary or a description, a warning is printed.
Please note: *showing the summary will not execute the command*.
## Silent mode
Silent mode disables echoing of commands before Task runs it.
@@ -575,7 +699,7 @@ tasks:
* 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`:
```yaml
version: '2'
@@ -620,7 +744,7 @@ tasks:
- echo "Hello World"
```
`ignore_error` can also be set for a task, which mean errors will be supressed
`ignore_error` can also be set for a task, which mean errors will be suppressed
for all commands. But keep in mind this option won't propagate to other tasks
called either by `deps` or `cmds`!
@@ -686,6 +810,8 @@ $ task default
[print-baz] baz
```
> The `output` option can also be specified by the `--output` or `-o` flags.
## Watch tasks
If you give a `--watch` or `-w` argument, task will watch for file changes

22
go.mod
View File

@@ -1,24 +1,20 @@
module github.com/go-task/task/v2
require (
github.com/Masterminds/semver v1.4.2
github.com/Masterminds/semver v1.4.2 // indirect
github.com/Masterminds/sprig v2.16.0+incompatible
github.com/aokoli/goutils v1.0.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/google/uuid v1.0.0 // indirect
github.com/huandu/xstrings v1.1.0 // indirect
github.com/imdario/mergo v0.3.6 // indirect
github.com/kr/pretty v0.1.0 // indirect
github.com/mattn/go-zglob v0.0.0-20180803001819-2ea3427bfa53
github.com/mitchellh/go-homedir v1.0.0
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/radovskyb/watcher v1.0.2
github.com/mattn/go-zglob v0.0.1
github.com/radovskyb/watcher v1.0.5
github.com/spf13/pflag v1.0.3
github.com/stretchr/testify v1.2.2
golang.org/x/crypto v0.0.0-20180830192347-182538f80094 // indirect
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d // indirect
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f
golang.org/x/sys v0.0.0-20180831094639-fa5fdf94c789 // indirect
gopkg.in/yaml.v2 v2.2.1
mvdan.cc/sh v0.0.0-20180829163519-3a244a89e2e5
github.com/stretchr/testify v1.4.0
golang.org/x/sync v0.0.0-20190423024810-112230192c58
gopkg.in/yaml.v2 v2.2.2
mvdan.cc/sh/v3 v3.0.0-alpha3
)
go 1.13

51
go.sum
View File

@@ -4,6 +4,7 @@ github.com/Masterminds/sprig v2.16.0+incompatible h1:QZbMUPxRQ50EKAq3LFMnxddMu88
github.com/Masterminds/sprig v2.16.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o=
github.com/aokoli/goutils v1.0.1 h1:7fpzNGoJ3VA8qcrm++XEE1QUe0mIwNeLa02Nwq7RDkg=
github.com/aokoli/goutils v1.0.1/go.mod h1:SijmP0QR8LtwsmDs8Yii5Z/S4trXFGFC2oO5g9DP+DQ=
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/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/google/uuid v1.0.0 h1:b4Gk+7WdP/d3HZH8EJsZpvV7EtDOgaZLtnaNGIu1adA=
@@ -17,29 +18,39 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
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/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/mattn/go-zglob v0.0.0-20180803001819-2ea3427bfa53 h1:tGfIHhDghvEnneeRhODvGYOt305TPwingKt6p90F4MU=
github.com/mattn/go-zglob v0.0.0-20180803001819-2ea3427bfa53/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo=
github.com/mitchellh/go-homedir v1.0.0 h1:vKb8ShqSby24Yrqr/yDYkuFz8d0WUjys40rvnGC8aR0=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
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/pkg/diff v0.0.0-20190915214748-c72a55596e99/go.mod h1:kFj35MyHn14a6pIgWhm46KVjJr5CHys3eEYxkuKD1EI=
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/radovskyb/watcher v1.0.2 h1:9L5TsZUbo1nKhQEQPtICVc+x9UZQ6VPdBepLHyGw/bQ=
github.com/radovskyb/watcher v1.0.2/go.mod h1:78okwvY5wPdzcb1UYnip1pvrZNIVEIh/Cm+ZuvsUYIg=
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/rogpeppe/go-internal v1.3.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/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
golang.org/x/crypto v0.0.0-20180830192347-182538f80094 h1:rVTAlhYa4+lCfNxmAIEOGQRoD23UqP72M3+rSWVGDTg=
golang.org/x/crypto v0.0.0-20180830192347-182538f80094/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d h1:g9qWBGx4puODJTMVyoPrpoxPFgVGd+z1DZwjfRu4d0I=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180831094639-fa5fdf94c789 h1:T8D7l6WB3tLu+VpKvw06ieD/OhBi1XpJmG1U/FtttZg=
golang.org/x/sys v0.0.0-20180831094639-fa5fdf94c789/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7 h1:0hQKqeLdqlt5iIwVOBErRisrHJAN57yOiPRQItI20fU=
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190910064555-bbd175535a8b h1:3S2h5FadpNr0zUUCVZjlKIEYF+KaX/OBplTGo89CYHI=
golang.org/x/sys v0.0.0-20190910064555-bbd175535a8b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
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/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
mvdan.cc/sh v0.0.0-20180829163519-3a244a89e2e5 h1:FKi9XtQO5aNipfQ/qnnLCoM6gdFwPQY702RRbNRxjK8=
mvdan.cc/sh v0.0.0-20180829163519-3a244a89e2e5/go.mod h1:IeeQbZq+x2SUGBensq/jge5lLQbS3XT2ktyp3wrt4x8=
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-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/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
mvdan.cc/sh/v3 v3.0.0-alpha3 h1:P9Kvg+bAwBksU9ATd8MNl1FEsObQ0yNsahZYLA4HRPs=
mvdan.cc/sh/v3 v3.0.0-alpha3/go.mod h1:VowSI5HikxXG/UBAS3NOTV4cDOWB+uTj+Qa7src2EYo=

View File

@@ -8,7 +8,7 @@ import (
"path/filepath"
)
const defaultTaskfile = `# https://taskfile.org
const defaultTaskfile = `# https://taskfile.dev
version: '2'

View File

@@ -1,36 +1,43 @@
package args
import (
"errors"
"strings"
"github.com/go-task/task/v2/internal/taskfile"
)
var (
// ErrVariableWithoutTask is returned when variables are given before any task
ErrVariableWithoutTask = errors.New("task: variable given before any task")
)
// Parse parses command line argument: tasks and vars of each task
func Parse(args ...string) ([]taskfile.Call, error) {
func Parse(args ...string) ([]taskfile.Call, taskfile.Vars) {
var calls []taskfile.Call
var globals taskfile.Vars
for _, arg := range args {
if !strings.Contains(arg, "=") {
calls = append(calls, taskfile.Call{Task: arg})
continue
}
if len(calls) < 1 {
return nil, ErrVariableWithoutTask
}
if globals == nil {
globals = taskfile.Vars{}
}
if calls[len(calls)-1].Vars == nil {
calls[len(calls)-1].Vars = make(taskfile.Vars)
}
name, value := splitVar(arg)
globals[name] = taskfile.Var{Static: value}
} else {
if calls[len(calls)-1].Vars == nil {
calls[len(calls)-1].Vars = make(taskfile.Vars)
}
pair := strings.SplitN(arg, "=", 2)
calls[len(calls)-1].Vars[pair[0]] = taskfile.Var{Static: pair[1]}
name, value := splitVar((arg))
calls[len(calls)-1].Vars[name] = taskfile.Var{Static: value}
}
}
return calls, nil
return calls, globals
}
func splitVar(s string) (string, string) {
pair := strings.SplitN(s, "=", 2)
return pair[0], pair[1]
}

View File

@@ -12,13 +12,13 @@ import (
func TestArgs(t *testing.T) {
tests := []struct {
Args []string
Expected []taskfile.Call
Err error
Args []string
ExpectedCalls []taskfile.Call
ExpectedGlobals taskfile.Vars
}{
{
Args: []string{"task-a", "task-b", "task-c"},
Expected: []taskfile.Call{
ExpectedCalls: []taskfile.Call{
{Task: "task-a"},
{Task: "task-b"},
{Task: "task-c"},
@@ -26,7 +26,7 @@ func TestArgs(t *testing.T) {
},
{
Args: []string{"task-a", "FOO=bar", "task-b", "task-c", "BAR=baz", "BAZ=foo"},
Expected: []taskfile.Call{
ExpectedCalls: []taskfile.Call{
{
Task: "task-a",
Vars: taskfile.Vars{
@@ -45,7 +45,7 @@ func TestArgs(t *testing.T) {
},
{
Args: []string{"task-a", "CONTENT=with some spaces"},
Expected: []taskfile.Call{
ExpectedCalls: []taskfile.Call{
{
Task: "task-a",
Vars: taskfile.Vars{
@@ -55,16 +55,22 @@ func TestArgs(t *testing.T) {
},
},
{
Args: []string{"FOO=bar", "task-a"},
Err: args.ErrVariableWithoutTask,
Args: []string{"FOO=bar", "task-a", "task-b"},
ExpectedCalls: []taskfile.Call{
{Task: "task-a"},
{Task: "task-b"},
},
ExpectedGlobals: taskfile.Vars{
"FOO": {Static: "bar"},
},
},
}
for i, test := range tests {
t.Run(fmt.Sprintf("TestArgs%d", i+1), func(t *testing.T) {
calls, err := args.Parse(test.Args...)
assert.Equal(t, test.Err, err)
assert.Equal(t, test.Expected, calls)
calls, globals := args.Parse(test.Args...)
assert.Equal(t, test.ExpectedCalls, calls)
assert.Equal(t, test.ExpectedGlobals, globals)
})
}
}

View File

@@ -5,10 +5,13 @@ import (
"errors"
"io"
"os"
"path/filepath"
"strings"
"mvdan.cc/sh/interp"
"mvdan.cc/sh/syntax"
"mvdan.cc/sh/v3/expand"
"mvdan.cc/sh/v3/interp"
"mvdan.cc/sh/v3/shell"
"mvdan.cc/sh/v3/syntax"
)
// RunCommandOptions is the options for the RunCommand func
@@ -41,17 +44,12 @@ func RunCommand(ctx context.Context, opts *RunCommandOptions) error {
if len(environ) == 0 {
environ = os.Environ()
}
env, err := interp.EnvFromList(environ)
if err != nil {
return err
}
r, err := interp.New(
interp.Dir(opts.Dir),
interp.Env(env),
interp.Env(expand.ListEnviron(environ...)),
interp.Module(interp.DefaultExec),
interp.Module(interp.OpenDevImpls(interp.DefaultOpen)),
interp.WithOpenModules(interp.OpenDevImpls),
interp.StdIO(opts.Stdin, opts.Stdout, opts.Stderr),
)
@@ -64,9 +62,24 @@ func RunCommand(ctx context.Context, opts *RunCommandOptions) error {
// IsExitError returns true the given error is an exis status error
func IsExitError(err error) bool {
switch err.(type) {
case interp.ExitStatus, interp.ShellExitStatus:
case interp.ExitStatus:
return true
default:
return false
}
}
// Expand is a helper to mvdan.cc/shell.Fields that returns the first field
// if available.
func Expand(s string) (string, error) {
s = filepath.ToSlash(s)
s = strings.Replace(s, " ", `\ `, -1)
fields, err := shell.Fields(s, nil)
if err != nil {
return "", err
}
if len(fields) > 0 {
return fields[0], nil
}
return "", nil
}

View File

@@ -7,7 +7,7 @@ import (
type Group struct{}
func (Group) WrapWriter(w io.Writer, _ string) io.WriteCloser {
func (Group) WrapWriter(w io.Writer, _ string) io.Writer {
return &groupWriter{writer: w}
}

View File

@@ -6,18 +6,6 @@ import (
type Interleaved struct{}
func (Interleaved) WrapWriter(w io.Writer, _ string) io.WriteCloser {
return nopWriterCloser{w: w}
}
type nopWriterCloser struct {
w io.Writer
}
func (wc nopWriterCloser) Write(p []byte) (int, error) {
return wc.w.Write(p)
}
func (wc nopWriterCloser) Close() error {
return nil
func (Interleaved) WrapWriter(w io.Writer, _ string) io.Writer {
return w
}

View File

@@ -5,5 +5,5 @@ import (
)
type Output interface {
WrapWriter(w io.Writer, prefix string) io.WriteCloser
WrapWriter(w io.Writer, prefix string) io.Writer
}

View File

@@ -3,6 +3,7 @@ package output_test
import (
"bytes"
"fmt"
"io"
"testing"
"github.com/go-task/task/v2/internal/output"
@@ -24,7 +25,7 @@ func TestInterleaved(t *testing.T) {
func TestGroup(t *testing.T) {
var b bytes.Buffer
var o output.Output = output.Group{}
var w = o.WrapWriter(&b, "")
var w = o.WrapWriter(&b, "").(io.WriteCloser)
fmt.Fprintln(w, "foo\nbar")
assert.Equal(t, "", b.String())
@@ -37,7 +38,7 @@ func TestGroup(t *testing.T) {
func TestPrefixed(t *testing.T) {
var b bytes.Buffer
var o output.Output = output.Prefixed{}
var w = o.WrapWriter(&b, "prefix")
var w = o.WrapWriter(&b, "prefix").(io.WriteCloser)
t.Run("simple use cases", func(t *testing.T) {
b.Reset()

View File

@@ -9,7 +9,7 @@ import (
type Prefixed struct{}
func (Prefixed) WrapWriter(w io.Writer, prefix string) io.WriteCloser {
func (Prefixed) WrapWriter(w io.Writer, prefix string) io.Writer {
return &prefixWriter{writer: w, prefix: prefix}
}
@@ -34,12 +34,12 @@ func (pw *prefixWriter) Close() error {
func (pw *prefixWriter) writeOutputLines(force bool) error {
for {
line, err := pw.buff.ReadString('\n')
if err == nil {
switch line, err := pw.buff.ReadString('\n'); err {
case nil:
if err = pw.writeLine(line); err != nil {
return err
}
} else if err == io.EOF {
case io.EOF:
// if this line was not a complete line, re-add to the buffer
if !force && !strings.HasSuffix(line, "\n") {
_, err = pw.buff.WriteString(line)
@@ -47,7 +47,7 @@ func (pw *prefixWriter) writeOutputLines(force bool) error {
}
return pw.writeLine(line)
} else {
default:
return err
}
}

View File

@@ -14,9 +14,11 @@ import (
// Checksum validades if a task is up to date by calculating its source
// files checksum
type Checksum struct {
Dir string
Task string
Sources []string
Dir string
Task string
Sources []string
Generates []string
Dry bool
}
// IsUpToDate implements the Checker interface
@@ -26,7 +28,7 @@ func (c *Checksum) IsUpToDate() (bool, error) {
data, _ := ioutil.ReadFile(checksumFile)
oldMd5 := strings.TrimSpace(string(data))
sources, err := glob(c.Dir, c.Sources)
sources, err := globs(c.Dir, c.Sources)
if err != nil {
return false, err
}
@@ -36,10 +38,29 @@ func (c *Checksum) IsUpToDate() (bool, error) {
return false, nil
}
_ = os.MkdirAll(filepath.Join(c.Dir, ".task", "checksum"), 0755)
if err = ioutil.WriteFile(checksumFile, []byte(newMd5+"\n"), 0644); err != nil {
return false, err
if !c.Dry {
_ = os.MkdirAll(filepath.Join(c.Dir, ".task", "checksum"), 0755)
if err = ioutil.WriteFile(checksumFile, []byte(newMd5+"\n"), 0644); err != nil {
return false, err
}
}
if len(c.Generates) > 0 {
// For each specified 'generates' field, check whether the files actually exist
for _, g := range c.Generates {
generates, err := glob(c.Dir, g)
if os.IsNotExist(err) {
return false, nil
}
if err != nil {
return false, err
}
if len(generates) == 0 {
return false, nil
}
}
}
return oldMd5 == newMd5, nil
}
@@ -47,21 +68,14 @@ func (c *Checksum) checksum(files ...string) (string, error) {
h := md5.New()
for _, f := range files {
// also sum the filename, so checksum changes for renaming a file
if _, err := io.Copy(h, strings.NewReader(filepath.Base(f))); err != nil {
return "", err
}
f, err := os.Open(f)
if err != nil {
return "", err
}
info, err := f.Stat()
if err != nil {
return "", err
}
if info.IsDir() {
continue
}
// also sum the filename, so checksum changes for renaming a file
if _, err = io.Copy(h, strings.NewReader(info.Name())); err != nil {
return "", err
}
if _, err = io.Copy(h, f); err != nil {
return "", err
}

View File

@@ -1,28 +1,50 @@
package status
import (
"os"
"path/filepath"
"sort"
"github.com/go-task/task/v2/internal/execext"
"github.com/mattn/go-zglob"
"mvdan.cc/sh/shell"
)
func glob(dir string, globs []string) (files []string, err error) {
func globs(dir string, globs []string) ([]string, error) {
files := make([]string, 0)
for _, g := range globs {
if !filepath.IsAbs(g) {
g = filepath.Join(dir, g)
}
g, err = shell.Expand(g, nil)
if err != nil {
return nil, err
}
f, err := zglob.Glob(g)
f, err := glob(dir, g)
if err != nil {
continue
}
files = append(files, f...)
}
sort.Strings(files)
return
return files, nil
}
func glob(dir string, g string) ([]string, error) {
files := make([]string, 0)
if !filepath.IsAbs(g) {
g = filepath.Join(dir, g)
}
g, err := execext.Expand(g)
if err != nil {
return nil, err
}
fs, err := zglob.Glob(g)
if err != nil {
return nil, err
}
for _, f := range fs {
info, err := os.Stat(f)
if err != nil {
return nil, err
}
if info.IsDir() {
continue
}
files = append(files, f)
}
return files, nil
}

View File

@@ -19,11 +19,11 @@ func (t *Timestamp) IsUpToDate() (bool, error) {
return false, nil
}
sources, err := glob(t.Dir, t.Sources)
sources, err := globs(t.Dir, t.Sources)
if err != nil {
return false, nil
}
generates, err := glob(t.Dir, t.Generates)
generates, err := globs(t.Dir, t.Generates)
if err != nil {
return false, nil
}

103
internal/summary/summary.go Normal file
View File

@@ -0,0 +1,103 @@
package summary
import (
"strings"
"github.com/go-task/task/v2/internal/logger"
"github.com/go-task/task/v2/internal/taskfile"
)
func PrintTasks(l *logger.Logger, t *taskfile.Taskfile, c []taskfile.Call) {
for i, call := range c {
printSpaceBetweenSummaries(l, i)
PrintTask(l, t.Tasks[call.Task])
}
}
func printSpaceBetweenSummaries(l *logger.Logger, i int) {
spaceRequired := i > 0
if !spaceRequired {
return
}
l.Outf("")
l.Outf("")
}
func PrintTask(l *logger.Logger, t *taskfile.Task) {
printTaskName(l, t)
printTaskDescribingText(t, l)
printTaskDependencies(l, t)
printTaskCommands(l, t)
}
func printTaskDescribingText(t *taskfile.Task, l *logger.Logger) {
if hasSummary(t) {
printTaskSummary(l, t)
} else if hasDescription(t) {
printTaskDescription(l, t)
} else {
printNoDescriptionOrSummary(l)
}
}
func hasSummary(t *taskfile.Task) bool {
return t.Summary != ""
}
func printTaskSummary(l *logger.Logger, t *taskfile.Task) {
lines := strings.Split(t.Summary, "\n")
for i, line := range lines {
notLastLine := i+1 < len(lines)
if notLastLine || line != "" {
l.Outf(line)
}
}
}
func printTaskName(l *logger.Logger, t *taskfile.Task) {
l.Outf("task: %s", t.Task)
l.Outf("")
}
func hasDescription(t *taskfile.Task) bool {
return t.Desc != ""
}
func printTaskDescription(l *logger.Logger, t *taskfile.Task) {
l.Outf(t.Desc)
}
func printNoDescriptionOrSummary(l *logger.Logger) {
l.Outf("(task does not have description or summary)")
}
func printTaskDependencies(l *logger.Logger, t *taskfile.Task) {
if len(t.Deps) == 0 {
return
}
l.Outf("")
l.Outf("dependencies:")
for _, d := range t.Deps {
l.Outf(" - %s", d.Task)
}
}
func printTaskCommands(l *logger.Logger, t *taskfile.Task) {
if len(t.Cmds) == 0 {
return
}
l.Outf("")
l.Outf("commands:")
for _, c := range t.Cmds {
isCommand := c.Cmd != ""
if isCommand {
l.Outf(" - %s", c.Cmd)
} else {
l.Outf(" - Task: %s", c.Task)
}
}
}

View File

@@ -0,0 +1,173 @@
package summary_test
import (
"bytes"
"strings"
"testing"
"github.com/go-task/task/v2/internal/logger"
"github.com/go-task/task/v2/internal/summary"
"github.com/go-task/task/v2/internal/taskfile"
"github.com/stretchr/testify/assert"
)
func TestPrintsDependenciesIfPresent(t *testing.T) {
buffer, l := createDummyLogger()
task := &taskfile.Task{
Deps: []*taskfile.Dep{
{Task: "dep1"},
{Task: "dep2"},
{Task: "dep3"},
},
}
summary.PrintTask(&l, task)
assert.Contains(t, buffer.String(), "\ndependencies:\n - dep1\n - dep2\n - dep3\n")
}
func createDummyLogger() (*bytes.Buffer, logger.Logger) {
buffer := &bytes.Buffer{}
l := logger.Logger{
Stderr: buffer,
Stdout: buffer,
Verbose: false,
}
return buffer, l
}
func TestDoesNotPrintDependenciesIfMissing(t *testing.T) {
buffer, l := createDummyLogger()
task := &taskfile.Task{
Deps: []*taskfile.Dep{},
}
summary.PrintTask(&l, task)
assert.NotContains(t, buffer.String(), "dependencies:")
}
func TestPrintTaskName(t *testing.T) {
buffer, l := createDummyLogger()
task := &taskfile.Task{
Task: "my-task-name",
}
summary.PrintTask(&l, task)
assert.Contains(t, buffer.String(), "task: my-task-name\n")
}
func TestPrintTaskCommandsIfPresent(t *testing.T) {
buffer, l := createDummyLogger()
task := &taskfile.Task{
Cmds: []*taskfile.Cmd{
{Cmd: "command-1"},
{Cmd: "command-2"},
{Task: "task-1"},
},
}
summary.PrintTask(&l, task)
assert.Contains(t, buffer.String(), "\ncommands:\n")
assert.Contains(t, buffer.String(), "\n - command-1\n")
assert.Contains(t, buffer.String(), "\n - command-2\n")
assert.Contains(t, buffer.String(), "\n - Task: task-1\n")
}
func TestDoesNotPrintCommandIfMissing(t *testing.T) {
buffer, l := createDummyLogger()
task := &taskfile.Task{
Cmds: []*taskfile.Cmd{},
}
summary.PrintTask(&l, task)
assert.NotContains(t, buffer.String(), "commands")
}
func TestLayout(t *testing.T) {
buffer, l := createDummyLogger()
task := &taskfile.Task{
Task: "sample-task",
Summary: "line1\nline2\nline3\n",
Deps: []*taskfile.Dep{
{Task: "dependency"},
},
Cmds: []*taskfile.Cmd{
{Cmd: "command"},
},
}
summary.PrintTask(&l, task)
assert.Equal(t, expectedOutput(), buffer.String())
}
func expectedOutput() string {
expected := `task: sample-task
line1
line2
line3
dependencies:
- dependency
commands:
- command
`
return expected
}
func TestPrintDescriptionAsFallback(t *testing.T) {
buffer, l := createDummyLogger()
taskWithoutSummary := &taskfile.Task{
Desc: "description",
}
taskWithSummary := &taskfile.Task{
Desc: "description",
Summary: "summary",
}
taskWithoutSummaryOrDescription := &taskfile.Task{}
summary.PrintTask(&l, taskWithoutSummary)
assert.Contains(t, buffer.String(), "description")
buffer.Reset()
summary.PrintTask(&l, taskWithSummary)
assert.NotContains(t, buffer.String(), "description")
buffer.Reset()
summary.PrintTask(&l, taskWithoutSummaryOrDescription)
assert.Contains(t, buffer.String(), "\n(task does not have description or summary)\n")
}
func TestPrintAllWithSpaces(t *testing.T) {
buffer, l := createDummyLogger()
t1 := &taskfile.Task{Task: "t1"}
t2 := &taskfile.Task{Task: "t2"}
t3 := &taskfile.Task{Task: "t3"}
tasks := make(taskfile.Tasks, 3)
tasks["t1"] = t1
tasks["t2"] = t2
tasks["t3"] = t3
summary.PrintTasks(&l,
&taskfile.Taskfile{Tasks: tasks},
[]taskfile.Call{{Task: "t1"}, {Task: "t2"}, {Task: "t3"}})
assert.True(t, strings.HasPrefix(buffer.String(), "task: t1"))
assert.Contains(t, buffer.String(), "\n(task does not have description or summary)\n\n\ntask: t2")
assert.Contains(t, buffer.String(), "\n(task does not have description or summary)\n\n\ntask: t3")
}

View File

@@ -35,6 +35,13 @@ func Merge(t1, t2 *Taskfile, namespaces ...string) error {
t1.Vars[k] = v
}
if t1.Env == nil {
t1.Env = make(Vars)
}
for k, v := range t2.Env {
t1.Env[k] = v
}
if t1.Tasks == nil {
t1.Tasks = make(Tasks)
}
@@ -59,5 +66,8 @@ func Merge(t1, t2 *Taskfile, namespaces ...string) error {
}
func taskNameWithNamespace(taskName string, namespaces ...string) string {
if strings.HasPrefix(taskName, ":") {
return strings.TrimPrefix(taskName, ":")
}
return strings.Join(append(namespaces, taskName), NamespaceSeparator)
}

View File

@@ -0,0 +1,45 @@
package taskfile
import (
"errors"
"fmt"
)
var (
// ErrCantUnmarshalPrecondition is returned for invalid precond YAML.
ErrCantUnmarshalPrecondition = errors.New("task: Can't unmarshal precondition value")
)
// Precondition represents a precondition necessary for a task to run
type Precondition struct {
Sh string
Msg string
}
// UnmarshalYAML implements yaml.Unmarshaler interface.
func (p *Precondition) UnmarshalYAML(unmarshal func(interface{}) error) error {
var cmd string
if err := unmarshal(&cmd); err == nil {
p.Sh = cmd
p.Msg = fmt.Sprintf("`%s` failed", cmd)
return nil
}
var sh struct {
Sh string
Msg string
}
if err := unmarshal(&sh); err != nil {
return err
}
p.Sh = sh.Sh
p.Msg = sh.Msg
if p.Msg == "" {
p.Msg = fmt.Sprintf("%s failed", sh.Sh)
}
return nil
}

View File

@@ -0,0 +1,48 @@
package taskfile_test
import (
"testing"
"github.com/go-task/task/v2/internal/taskfile"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v2"
)
func TestPreconditionParse(t *testing.T) {
tests := []struct {
content string
v interface{}
expected interface{}
}{
{
"test -f foo.txt",
&taskfile.Precondition{},
&taskfile.Precondition{Sh: `test -f foo.txt`, Msg: "`test -f foo.txt` failed"},
},
{
"sh: '[ 1 = 0 ]'",
&taskfile.Precondition{},
&taskfile.Precondition{Sh: "[ 1 = 0 ]", Msg: "[ 1 = 0 ] failed"},
},
{`
sh: "[ 1 = 2 ]"
msg: "1 is not 2"
`,
&taskfile.Precondition{},
&taskfile.Precondition{Sh: "[ 1 = 2 ]", Msg: "1 is not 2"},
},
{`
sh: "[ 1 = 2 ]"
msg: "1 is not 2"
`,
&taskfile.Precondition{},
&taskfile.Precondition{Sh: "[ 1 = 2 ]", Msg: "1 is not 2"},
},
}
for _, test := range tests {
err := yaml.Unmarshal([]byte(test.content), test.v)
assert.NoError(t, err)
assert.Equal(t, test.expected, test.v)
}
}

View File

@@ -12,14 +12,16 @@ import (
"gopkg.in/yaml.v2"
)
// ErrIncludedTaskfilesCantHaveIncludes is returned when a included Taskfile contains includes
var ErrIncludedTaskfilesCantHaveIncludes = errors.New("task: Included Taskfiles can't have includes. Please, move the include to the main Taskfile")
var (
// ErrIncludedTaskfilesCantHaveIncludes is returned when a included Taskfile contains includes
ErrIncludedTaskfilesCantHaveIncludes = errors.New("task: Included Taskfiles can't have includes. Please, move the include to the main Taskfile")
)
// Taskfile reads a Taskfile for a given directory
func Taskfile(dir string) (*taskfile.Taskfile, error) {
path := filepath.Join(dir, "Taskfile.yml")
func Taskfile(dir string, entrypoint string) (*taskfile.Taskfile, error) {
path := filepath.Join(dir, entrypoint)
if _, err := os.Stat(path); err != nil {
return nil, fmt.Errorf(`No Taskfile.yml found. Use "task --init" to create a new one`)
return nil, fmt.Errorf(`task: No Taskfile found on "%s". Use "task --init" to create a new one`, path)
}
t, err := readTaskfile(path)
if err != nil {

View File

@@ -1,22 +1,24 @@
package taskfile
// Tasks representas a group of tasks
// Tasks represents a group of tasks
type Tasks map[string]*Task
// Task represents a task
type Task struct {
Task string
Cmds []*Cmd
Deps []*Dep
Desc string
Sources []string
Generates []string
Status []string
Dir string
Vars Vars
Env Vars
Silent bool
Method string
Prefix string
IgnoreError bool `yaml:"ignore_error"`
Task string
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"`
}

View File

@@ -7,6 +7,7 @@ type Taskfile struct {
Output string
Includes map[string]string
Vars Vars
Env Vars
Tasks Tasks
}
@@ -23,6 +24,7 @@ func (tf *Taskfile) UnmarshalYAML(unmarshal func(interface{}) error) error {
Output string
Includes map[string]string
Vars Vars
Env Vars
Tasks Tasks
}
if err := unmarshal(&taskfile); err != nil {
@@ -33,9 +35,13 @@ func (tf *Taskfile) UnmarshalYAML(unmarshal func(interface{}) error) error {
tf.Output = taskfile.Output
tf.Includes = taskfile.Includes
tf.Vars = taskfile.Vars
tf.Env = taskfile.Env
tf.Tasks = taskfile.Tasks
if tf.Expansions <= 0 {
tf.Expansions = 2
}
if tf.Vars == nil {
tf.Vars = make(Vars)
}
return nil
}

View File

@@ -1,46 +0,0 @@
package version
import (
"github.com/Masterminds/semver"
)
var (
v1 = mustVersion("1")
v2 = mustVersion("2")
v21 = mustVersion("2.1")
v22 = mustVersion("2.2")
v23 = mustVersion("2.3")
)
// IsV1 returns if is a given Taskfile version is version 1
func IsV1(v *semver.Constraints) bool {
return v.Check(v1)
}
// IsV2 returns if is a given Taskfile version is at least version 2
func IsV2(v *semver.Constraints) bool {
return v.Check(v2)
}
// IsV21 returns if is a given Taskfile version is at least version 2.1
func IsV21(v *semver.Constraints) bool {
return v.Check(v21)
}
// IsV22 returns if is a given Taskfile version is at least version 2.2
func IsV22(v *semver.Constraints) bool {
return v.Check(v22)
}
// IsV23 returns if is a given Taskfile version is at least version 2.3
func IsV23(v *semver.Constraints) bool {
return v.Check(v23)
}
func mustVersion(s string) *semver.Version {
v, err := semver.NewVersion(s)
if err != nil {
panic(err)
}
return v
}

31
precondition.go Normal file
View File

@@ -0,0 +1,31 @@
package task
import (
"context"
"errors"
"github.com/go-task/task/v2/internal/execext"
"github.com/go-task/task/v2/internal/taskfile"
)
var (
// ErrPreconditionFailed is returned when a precondition fails
ErrPreconditionFailed = errors.New("task: precondition not met")
)
func (e *Executor) areTaskPreconditionsMet(ctx context.Context, t *taskfile.Task) (bool, error) {
for _, p := range t.Preconditions {
err := execext.RunCommand(ctx, &execext.RunCommandOptions{
Command: p.Sh,
Dir: t.Dir,
Env: getEnviron(t),
})
if err != nil {
e.Logger.Errf("task: %s", p.Msg)
return false, ErrPreconditionFailed
}
}
return true, nil
}

View File

@@ -10,13 +10,13 @@ import (
)
// Status returns an error if any the of given tasks is not up-to-date
func (e *Executor) Status(calls ...taskfile.Call) error {
func (e *Executor) Status(ctx context.Context, calls ...taskfile.Call) error {
for _, call := range calls {
t, err := e.CompiledTask(call)
if err != nil {
return err
}
isUpToDate, err := isTaskUpToDate(e.Context, t)
isUpToDate, err := e.isTaskUpToDate(ctx, t)
if err != nil {
return err
}
@@ -27,12 +27,12 @@ func (e *Executor) Status(calls ...taskfile.Call) error {
return nil
}
func isTaskUpToDate(ctx context.Context, t *taskfile.Task) (bool, error) {
func (e *Executor) isTaskUpToDate(ctx context.Context, t *taskfile.Task) (bool, error) {
if len(t.Status) > 0 {
return isTaskUpToDateStatus(ctx, t)
return e.isTaskUpToDateStatus(ctx, t)
}
checker, err := getStatusChecker(t)
checker, err := e.getStatusChecker(t)
if err != nil {
return false, err
}
@@ -40,15 +40,15 @@ func isTaskUpToDate(ctx context.Context, t *taskfile.Task) (bool, error) {
return checker.IsUpToDate()
}
func statusOnError(t *taskfile.Task) error {
checker, err := getStatusChecker(t)
func (e *Executor) statusOnError(t *taskfile.Task) error {
checker, err := e.getStatusChecker(t)
if err != nil {
return err
}
return checker.OnError()
}
func getStatusChecker(t *taskfile.Task) (status.Checker, error) {
func (e *Executor) getStatusChecker(t *taskfile.Task) (status.Checker, error) {
switch t.Method {
case "", "timestamp":
return &status.Timestamp{
@@ -58,9 +58,11 @@ func getStatusChecker(t *taskfile.Task) (status.Checker, error) {
}, nil
case "checksum":
return &status.Checksum{
Dir: t.Dir,
Task: t.Task,
Sources: t.Sources,
Dir: t.Dir,
Task: t.Task,
Sources: t.Sources,
Generates: t.Generates,
Dry: e.Dry,
}, nil
case "none":
return status.None{}, nil
@@ -69,7 +71,7 @@ func getStatusChecker(t *taskfile.Task) (status.Checker, error) {
}
}
func isTaskUpToDateStatus(ctx context.Context, t *taskfile.Task) (bool, error) {
func (e *Executor) isTaskUpToDateStatus(ctx context.Context, t *taskfile.Task) (bool, error) {
for _, s := range t.Status {
err := execext.RunCommand(ctx, &execext.RunCommandOptions{
Command: s,
@@ -77,8 +79,10 @@ func isTaskUpToDateStatus(ctx context.Context, t *taskfile.Task) (bool, error) {
Env: getEnviron(t),
})
if err != nil {
e.Logger.VerboseOutf("task: status command %s exited non-zero: %s", s, err)
return false, nil
}
e.Logger.VerboseOutf("task: status command %s exited zero", s)
}
return true, nil
}

163
task.go
View File

@@ -2,9 +2,12 @@ package task
import (
"context"
"errors"
"fmt"
"io"
"os"
"strconv"
"sync"
"sync/atomic"
"github.com/go-task/task/v2/internal/compiler"
@@ -13,11 +16,10 @@ import (
"github.com/go-task/task/v2/internal/execext"
"github.com/go-task/task/v2/internal/logger"
"github.com/go-task/task/v2/internal/output"
"github.com/go-task/task/v2/internal/summary"
"github.com/go-task/task/v2/internal/taskfile"
"github.com/go-task/task/v2/internal/taskfile/read"
"github.com/go-task/task/v2/internal/taskfile/version"
"github.com/Masterminds/semver"
"golang.org/x/sync/errgroup"
)
@@ -30,30 +32,33 @@ const (
// Executor executes a Taskfile
type Executor struct {
Taskfile *taskfile.Taskfile
Dir string
Force bool
Watch bool
Verbose bool
Silent bool
Dry bool
Context context.Context
Dir string
Entrypoint string
Force bool
Watch bool
Verbose bool
Silent bool
Dry bool
Summary bool
Stdin io.Reader
Stdout io.Writer
Stderr io.Writer
Logger *logger.Logger
Compiler compiler.Compiler
Output output.Output
Logger *logger.Logger
Compiler compiler.Compiler
Output output.Output
OutputStyle string
taskvars taskfile.Vars
taskCallCount map[string]*int32
mkdirMutexMap map[string]*sync.Mutex
}
// Run runs Task
func (e *Executor) Run(calls ...taskfile.Call) error {
func (e *Executor) Run(ctx context.Context, calls ...taskfile.Call) error {
// check if given tasks exist
for _, c := range calls {
if _, ok := e.Taskfile.Tasks[c.Task]; !ok {
@@ -63,12 +68,17 @@ func (e *Executor) Run(calls ...taskfile.Call) error {
}
}
if e.Summary {
summary.PrintTasks(e.Logger, e.Taskfile, calls)
return nil
}
if e.Watch {
return e.watchTasks(calls...)
}
for _, c := range calls {
if err := e.RunTask(e.Context, c); err != nil {
if err := e.RunTask(ctx, c); err != nil {
return err
}
}
@@ -77,8 +87,12 @@ func (e *Executor) Run(calls ...taskfile.Call) error {
// Setup setups Executor's internal state
func (e *Executor) Setup() error {
if e.Entrypoint == "" {
e.Entrypoint = "Taskfile.yml"
}
var err error
e.Taskfile, err = read.Taskfile(e.Dir)
e.Taskfile, err = read.Taskfile(e.Dir, e.Entrypoint)
if err != nil {
return err
}
@@ -87,14 +101,6 @@ func (e *Executor) Setup() error {
return err
}
v, err := semver.NewConstraint(e.Taskfile.Version)
if err != nil {
return fmt.Errorf(`task: could not parse taskfile version "%s": %v`, e.Taskfile.Version, err)
}
if e.Context == nil {
e.Context = context.Background()
}
if e.Stdin == nil {
e.Stdin = os.Stdin
}
@@ -109,14 +115,30 @@ func (e *Executor) Setup() error {
Stderr: e.Stderr,
Verbose: e.Verbose,
}
switch {
case version.IsV1(v):
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)
}
// consider as equal to the greater version if round
if v == 2.0 {
v = 2.6
}
if v < 1 {
return fmt.Errorf(`task: Taskfile version should be greater or equal to v1`)
}
if v > 2.6 {
return fmt.Errorf(`task: Taskfile versions greater than v2.6 not implemented in the version of Task`)
}
if v < 2 {
e.Compiler = &compilerv1.CompilerV1{
Dir: e.Dir,
Vars: e.taskvars,
Logger: e.Logger,
}
case version.IsV2(v), version.IsV21(v), version.IsV22(v):
} else { // v >= 2
e.Compiler = &compilerv2.CompilerV2{
Dir: e.Dir,
Taskvars: e.taskvars,
@@ -124,16 +146,18 @@ func (e *Executor) Setup() error {
Expansions: e.Taskfile.Expansions,
Logger: e.Logger,
}
case version.IsV23(v):
return fmt.Errorf(`task: Taskfile versions greater than v2.3 not implemented in the version of Task`)
}
if !version.IsV21(v) && e.Taskfile.Output != "" {
if v < 2.1 && e.Taskfile.Output != "" {
return fmt.Errorf(`task: Taskfile option "output" is only available starting on Taskfile version v2.1`)
}
if !version.IsV22(v) && 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`)
}
if e.OutputStyle != "" {
e.Taskfile.Output = e.OutputStyle
}
switch e.Taskfile.Output {
case "", "interleaved":
e.Output = output.Interleaved{}
@@ -145,8 +169,8 @@ func (e *Executor) Setup() error {
return fmt.Errorf(`task: output option "%s" not recognized`, e.Taskfile.Output)
}
if !version.IsV21(v) {
err := fmt.Errorf(`task: Taskfile option "ignore_error" is only available starting on Taskfile version v2.1`)
if v <= 2.1 {
err := errors.New(`task: Taskfile option "ignore_error" is only available starting on Taskfile version v2.1`)
for _, task := range e.Taskfile.Tasks {
if task.IgnoreError {
@@ -160,9 +184,19 @@ func (e *Executor) Setup() error {
}
}
if v < 2.6 {
for _, task := range e.Taskfile.Tasks {
if len(task.Preconditions) > 0 {
return errors.New(`task: Task option "preconditions" is only available starting on Taskfile version v2.6`)
}
}
}
e.taskCallCount = make(map[string]*int32, len(e.Taskfile.Tasks))
e.mkdirMutexMap = make(map[string]*sync.Mutex, len(e.Taskfile.Tasks))
for k := range e.Taskfile.Tasks {
e.taskCallCount[k] = new(int32)
e.mkdirMutexMap[k] = &sync.Mutex{}
}
return nil
}
@@ -182,11 +216,17 @@ func (e *Executor) RunTask(ctx context.Context, call taskfile.Call) error {
}
if !e.Force {
upToDate, err := isTaskUpToDate(ctx, t)
preCondMet, err := e.areTaskPreconditionsMet(ctx, t)
if err != nil {
return err
}
if upToDate {
upToDate, err := e.isTaskUpToDate(ctx, t)
if err != nil {
return err
}
if upToDate && preCondMet {
if !e.Silent {
e.Logger.Errf(`task: Task "%s" is up to date`, t.Task)
}
@@ -194,9 +234,13 @@ func (e *Executor) RunTask(ctx context.Context, call taskfile.Call) error {
}
}
if err := e.mkdir(t); err != nil {
e.Logger.Errf("task: cannot make directory %q: %v", t.Dir, err)
}
for i := range t.Cmds {
if err := e.runCommand(ctx, t, call, i); err != nil {
if err2 := statusOnError(t); err2 != nil {
if err2 := e.statusOnError(t); err2 != nil {
e.Logger.VerboseErrf("task: error cleaning status on error: %v", err2)
}
@@ -211,6 +255,23 @@ func (e *Executor) RunTask(ctx context.Context, call taskfile.Call) error {
return nil
}
func (e *Executor) mkdir(t *taskfile.Task) error {
if t.Dir == "" {
return nil
}
mutex := e.mkdirMutexMap[t.Task]
mutex.Lock()
defer mutex.Unlock()
if _, err := os.Stat(t.Dir); os.IsNotExist(err) {
if err := os.MkdirAll(t.Dir, 0755); err != nil {
return err
}
}
return nil
}
func (e *Executor) runDeps(ctx context.Context, t *taskfile.Task) error {
g, ctx := errgroup.WithContext(ctx)
@@ -218,7 +279,11 @@ func (e *Executor) runDeps(ctx context.Context, t *taskfile.Task) error {
d := d
g.Go(func() error {
return e.RunTask(ctx, taskfile.Call{Task: d.Task, Vars: d.Vars})
err := e.RunTask(ctx, taskfile.Call{Task: d.Task, Vars: d.Vars})
if err != nil {
return err
}
return nil
})
}
@@ -230,7 +295,11 @@ func (e *Executor) runCommand(ctx context.Context, t *taskfile.Task, call taskfi
switch {
case cmd.Task != "":
return e.RunTask(ctx, taskfile.Call{Task: cmd.Task, Vars: cmd.Vars})
err := e.RunTask(ctx, taskfile.Call{Task: cmd.Task, Vars: cmd.Vars})
if err != nil {
return err
}
return nil
case cmd.Cmd != "":
if e.Verbose || (!cmd.Silent && !t.Silent && !e.Silent) {
e.Logger.Errf(cmd.Cmd)
@@ -242,8 +311,18 @@ func (e *Executor) runCommand(ctx context.Context, t *taskfile.Task, call taskfi
stdOut := e.Output.WrapWriter(e.Stdout, t.Prefix)
stdErr := e.Output.WrapWriter(e.Stderr, t.Prefix)
defer stdOut.Close()
defer stdErr.Close()
defer func() {
if _, ok := stdOut.(*os.File); !ok {
if closer, ok := stdOut.(io.Closer); ok {
closer.Close()
}
}
if _, ok := stdErr.(*os.File); !ok {
if closer, ok := stdErr.(io.Closer); ok {
closer.Close()
}
}
}()
err := execext.RunCommand(ctx, &execext.RunCommandOptions{
Command: cmd.Cmd,
@@ -268,9 +347,9 @@ func getEnviron(t *taskfile.Task) []string {
return nil
}
envs := os.Environ()
environ := os.Environ()
for k, v := range t.Env.ToStringMap() {
envs = append(envs, fmt.Sprintf("%s=%s", k, v))
environ = append(environ, fmt.Sprintf("%s=%s", k, v))
}
return envs
return environ
}

View File

@@ -2,17 +2,18 @@ package task_test
import (
"bytes"
"context"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"runtime"
"strings"
"testing"
"github.com/go-task/task/v2"
"github.com/go-task/task/v2/internal/taskfile"
"github.com/mitchellh/go-homedir"
"github.com/stretchr/testify/assert"
)
@@ -40,7 +41,7 @@ func (fct fileContentTest) Run(t *testing.T) {
Stderr: ioutil.Discard,
}
assert.NoError(t, e.Setup(), "e.Setup()")
assert.NoError(t, e.Run(taskfile.Call{Task: fct.Target}), "e.Run(target)")
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: fct.Target}), "e.Run(target)")
for name, expectContent := range fct.Files {
t.Run(fct.name(name), func(t *testing.T) {
@@ -61,7 +62,8 @@ func TestEnv(t *testing.T) {
Target: "default",
TrimSpace: false,
Files: map[string]string{
"env.txt": "GOOS='linux' GOARCH='amd64' CGO_ENABLED='0'\n",
"local.txt": "GOOS='linux' GOARCH='amd64' CGO_ENABLED='0'\n",
"global.txt": "FOO='foo' BAR='overriden' BAZ='baz'\n",
},
}
tt.Run(t)
@@ -177,7 +179,7 @@ func TestVarsInvalidTmpl(t *testing.T) {
Stderr: ioutil.Discard,
}
assert.NoError(t, e.Setup(), "e.Setup()")
assert.EqualError(t, e.Run(taskfile.Call{Task: target}), expectError, "e.Run(target)")
assert.EqualError(t, e.Run(context.Background(), taskfile.Call{Task: target}), expectError, "e.Run(target)")
}
func TestParams(t *testing.T) {
@@ -229,12 +231,12 @@ func TestDeps(t *testing.T) {
Stderr: ioutil.Discard,
}
assert.NoError(t, e.Setup())
assert.NoError(t, e.Run(taskfile.Call{Task: "default"}))
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "default"}))
for _, f := range files {
f = filepath.Join(dir, f)
if _, err := os.Stat(f); err != nil {
t.Errorf("File %s should exists", f)
t.Errorf("File %s should exist", f)
}
}
}
@@ -246,7 +248,7 @@ func TestStatus(t *testing.T) {
_ = os.Remove(file)
if _, err := os.Stat(file); err == nil {
t.Errorf("File should not exists: %v", err)
t.Errorf("File should not exist: %v", err)
}
var buff bytes.Buffer
@@ -257,35 +259,79 @@ func TestStatus(t *testing.T) {
Silent: true,
}
assert.NoError(t, e.Setup())
assert.NoError(t, e.Run(taskfile.Call{Task: "gen-foo"}))
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "gen-foo"}))
if _, err := os.Stat(file); err != nil {
t.Errorf("File should exists: %v", err)
t.Errorf("File should exist: %v", err)
}
e.Silent = false
assert.NoError(t, e.Run(taskfile.Call{Task: "gen-foo"}))
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "gen-foo"}))
if buff.String() != `task: Task "gen-foo" is up to date`+"\n" {
t.Errorf("Wrong output message: %s", buff.String())
}
}
func TestPrecondition(t *testing.T) {
const dir = "testdata/precondition"
var buff bytes.Buffer
e := &task.Executor{
Dir: dir,
Stdout: &buff,
Stderr: &buff,
}
// A precondition that has been met
assert.NoError(t, e.Setup())
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "foo"}))
if buff.String() != "" {
t.Errorf("Got Output when none was expected: %s", buff.String())
}
// A precondition that was not met
assert.Error(t, e.Run(context.Background(), taskfile.Call{Task: "impossible"}))
if buff.String() != "task: 1 != 0 obviously!\n" {
t.Errorf("Wrong output message: %s", buff.String())
}
buff.Reset()
// Calling a task with a precondition in a dependency fails the task
assert.Error(t, e.Run(context.Background(), taskfile.Call{Task: "depends_on_impossible"}))
if buff.String() != "task: 1 != 0 obviously!\n" {
t.Errorf("Wrong output message: %s", buff.String())
}
buff.Reset()
// Calling a task with a precondition in a cmd fails the task
assert.Error(t, e.Run(context.Background(), taskfile.Call{Task: "executes_failing_task_as_cmd"}))
if buff.String() != "task: 1 != 0 obviously!\n" {
t.Errorf("Wrong output message: %s", buff.String())
}
buff.Reset()
}
func TestGenerates(t *testing.T) {
var srcTask = "sub/src.txt"
var relTask = "rel.txt"
var absTask = "abs.txt"
const (
srcTask = "sub/src.txt"
relTask = "rel.txt"
absTask = "abs.txt"
fileWithSpaces = "my text file.txt"
)
// This test does not work with a relative dir.
dir, err := filepath.Abs("testdata/generates")
assert.NoError(t, err)
var srcFile = filepath.Join(dir, srcTask)
for _, task := range []string{srcTask, relTask, absTask} {
for _, task := range []string{srcTask, relTask, absTask, fileWithSpaces} {
path := filepath.Join(dir, task)
_ = os.Remove(path)
if _, err := os.Stat(path); err == nil {
t.Errorf("File should not exists: %v", err)
t.Errorf("File should not exist: %v", err)
}
}
@@ -297,19 +343,19 @@ func TestGenerates(t *testing.T) {
}
assert.NoError(t, e.Setup())
for _, theTask := range []string{relTask, absTask} {
for _, theTask := range []string{relTask, absTask, fileWithSpaces} {
var destFile = filepath.Join(dir, theTask)
var upToDate = fmt.Sprintf("task: Task \"%s\" is up to date\n", srcTask) +
fmt.Sprintf("task: Task \"%s\" is up to date\n", theTask)
// Run task for the first time.
assert.NoError(t, e.Run(taskfile.Call{Task: theTask}))
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: theTask}))
if _, err := os.Stat(srcFile); err != nil {
t.Errorf("File should exists: %v", err)
t.Errorf("File should exist: %v", err)
}
if _, err := os.Stat(destFile); err != nil {
t.Errorf("File should exists: %v", err)
t.Errorf("File should exist: %v", err)
}
// Ensure task was not incorrectly found to be up-to-date on first run.
if buff.String() == upToDate {
@@ -318,7 +364,7 @@ func TestGenerates(t *testing.T) {
buff.Reset()
// Re-run task to ensure it's now found to be up-to-date.
assert.NoError(t, e.Run(taskfile.Call{Task: theTask}))
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: theTask}))
if buff.String() != upToDate {
t.Errorf("Wrong output message: %s", buff.String())
}
@@ -349,14 +395,14 @@ func TestStatusChecksum(t *testing.T) {
}
assert.NoError(t, e.Setup())
assert.NoError(t, e.Run(taskfile.Call{Task: "build"}))
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "build"}))
for _, f := range files {
_, err := os.Stat(filepath.Join(dir, f))
assert.NoError(t, err)
}
buff.Reset()
assert.NoError(t, e.Run(taskfile.Call{Task: "build"}))
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "build"}))
assert.Equal(t, `task: Task "build" is up to date`+"\n", buff.String())
}
@@ -366,7 +412,7 @@ func TestInit(t *testing.T) {
_ = os.Remove(file)
if _, err := os.Stat(file); err == nil {
t.Errorf("Taskfile.yml should not exists")
t.Errorf("Taskfile.yml should not exist")
}
if err := task.InitTaskfile(ioutil.Discard, dir); err != nil {
@@ -374,7 +420,7 @@ func TestInit(t *testing.T) {
}
if _, err := os.Stat(file); err != nil {
t.Errorf("Taskfile.yml should exists")
t.Errorf("Taskfile.yml should exist")
}
}
@@ -387,7 +433,7 @@ func TestCyclicDep(t *testing.T) {
Stderr: ioutil.Discard,
}
assert.NoError(t, e.Setup())
assert.IsType(t, &task.MaximumTaskCallExceededError{}, e.Run(taskfile.Call{Task: "task-1"}))
assert.IsType(t, &task.MaximumTaskCallExceededError{}, e.Run(context.Background(), taskfile.Call{Task: "task-1"}))
}
func TestTaskVersion(t *testing.T) {
@@ -423,16 +469,16 @@ func TestTaskIgnoreErrors(t *testing.T) {
}
assert.NoError(t, e.Setup())
assert.NoError(t, e.Run(taskfile.Call{Task: "task-should-pass"}))
assert.Error(t, e.Run(taskfile.Call{Task: "task-should-fail"}))
assert.NoError(t, e.Run(taskfile.Call{Task: "cmd-should-pass"}))
assert.Error(t, e.Run(taskfile.Call{Task: "cmd-should-fail"}))
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "task-should-pass"}))
assert.Error(t, e.Run(context.Background(), taskfile.Call{Task: "task-should-fail"}))
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "cmd-should-pass"}))
assert.Error(t, e.Run(context.Background(), taskfile.Call{Task: "cmd-should-fail"}))
}
func TestExpand(t *testing.T) {
const dir = "testdata/expand"
home, err := homedir.Dir()
home, err := os.UserHomeDir()
if err != nil {
t.Errorf("Couldn't get $HOME: %v", err)
}
@@ -444,7 +490,7 @@ func TestExpand(t *testing.T) {
Stderr: &buff,
}
assert.NoError(t, e.Setup())
assert.NoError(t, e.Run(taskfile.Call{Task: "pwd"}))
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "pwd"}))
assert.Equal(t, home, strings.TrimSpace(buff.String()))
}
@@ -463,7 +509,7 @@ func TestDry(t *testing.T) {
Dry: true,
}
assert.NoError(t, e.Setup())
assert.NoError(t, e.Run(taskfile.Call{Task: "build"}))
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "build"}))
assert.Equal(t, "touch file.txt", strings.TrimSpace(buff.String()))
if _, err := os.Stat(file); err == nil {
@@ -471,6 +517,32 @@ func TestDry(t *testing.T) {
}
}
// TestDryChecksum tests if the checksum file is not being written to disk
// if the dry mode is enabled.
func TestDryChecksum(t *testing.T) {
const dir = "testdata/dry_checksum"
checksumFile := filepath.Join(dir, ".task/checksum/default")
_ = os.Remove(checksumFile)
e := task.Executor{
Dir: dir,
Stdout: ioutil.Discard,
Stderr: ioutil.Discard,
Dry: true,
}
assert.NoError(t, e.Setup())
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "default"}))
_, err := os.Stat(checksumFile)
assert.Error(t, err, "checksum file should not exist")
e.Dry = false
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "default"}))
_, err = os.Stat(checksumFile)
assert.NoError(t, err, "checksum file should exist")
}
func TestIncludes(t *testing.T) {
tt := fileContentTest{
Dir: "testdata/includes",
@@ -510,3 +582,102 @@ func TestIncludesDependencies(t *testing.T) {
}
tt.Run(t)
}
func TestIncludesCallingRoot(t *testing.T) {
tt := fileContentTest{
Dir: "testdata/includes_call_root_task",
Target: "included:call-root",
TrimSpace: true,
Files: map[string]string{
"root_task.txt": "root task",
},
}
tt.Run(t)
}
func TestSummary(t *testing.T) {
const dir = "testdata/summary"
var buff bytes.Buffer
e := task.Executor{
Dir: dir,
Stdout: &buff,
Stderr: &buff,
Summary: true,
Silent: true,
}
assert.NoError(t, e.Setup())
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "task-with-summary"}, taskfile.Call{Task: "other-task-with-summary"}))
data, err := ioutil.ReadFile(filepath.Join(dir, "task-with-summary.txt"))
assert.NoError(t, err)
expectedOutput := string(data)
if runtime.GOOS == "windows" {
expectedOutput = strings.Replace(expectedOutput, "\r\n", "\n", -1)
}
assert.Equal(t, expectedOutput, buff.String())
}
func TestWhenNoDirAttributeItRunsInSameDirAsTaskfile(t *testing.T) {
const expected = "dir"
const dir = "testdata/" + expected
var out bytes.Buffer
e := &task.Executor{
Dir: dir,
Stdout: &out,
Stderr: &out,
}
assert.NoError(t, e.Setup())
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "whereami"}))
// got should be the "dir" part of "testdata/dir"
got := strings.TrimSuffix(filepath.Base(out.String()), "\n")
assert.Equal(t, expected, got, "Mismatch in the working directory")
}
func TestWhenDirAttributeAndDirExistsItRunsInThatDir(t *testing.T) {
const expected = "exists"
const dir = "testdata/dir/explicit_exists"
var out bytes.Buffer
e := &task.Executor{
Dir: dir,
Stdout: &out,
Stderr: &out,
}
assert.NoError(t, e.Setup())
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "whereami"}))
got := strings.TrimSuffix(filepath.Base(out.String()), "\n")
assert.Equal(t, expected, got, "Mismatch in the working directory")
}
func TestWhenDirAttributeItCreatesMissingAndRunsInThatDir(t *testing.T) {
const expected = "createme"
const dir = "testdata/dir/explicit_doesnt_exist/"
const toBeCreated = dir + expected
const target = "whereami"
var out bytes.Buffer
e := &task.Executor{
Dir: dir,
Stdout: &out,
Stderr: &out,
}
// Ensure that the directory to be created doesn't actually exist.
_ = os.Remove(toBeCreated)
if _, err := os.Stat(toBeCreated); err == nil {
t.Errorf("Directory should not exist: %v", err)
}
assert.NoError(t, e.Setup())
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: target}))
got := strings.TrimSuffix(filepath.Base(out.String()), "\n")
assert.Equal(t, expected, got, "Mismatch in the working directory")
// Clean-up after ourselves only if no error.
_ = os.Remove(toBeCreated)
}

7
testdata/dir/Taskfile.yml vendored Normal file
View File

@@ -0,0 +1,7 @@
version: '2'
tasks:
whereami:
cmds:
- pwd
silent: true

View File

@@ -0,0 +1,8 @@
version: '2'
tasks:
whereami:
dir: createme
cmds:
- pwd
silent: true

View File

@@ -0,0 +1,8 @@
version: '2'
tasks:
whereami:
dir: exists
cmds:
- pwd
silent: true

View File

9
testdata/dry_checksum/Taskfile.yml vendored Normal file
View File

@@ -0,0 +1,9 @@
version: '2'
tasks:
default:
cmds:
- echo "Working..."
sources:
- source.txt
method: checksum

1
testdata/dry_checksum/source.txt vendored Normal file
View File

@@ -0,0 +1 @@
Something...

View File

@@ -1 +1 @@
env.txt
*.txt

View File

@@ -1,10 +1,33 @@
default:
vars:
AMD64: amd64
env:
GOOS: linux
GOARCH: "{{.AMD64}}"
CGO_ENABLED:
sh: echo '0'
cmds:
- echo "GOOS='$GOOS' GOARCH='$GOARCH' CGO_ENABLED='$CGO_ENABLED'" > env.txt
version: '2'
vars:
BAZ:
sh: echo baz
env:
FOO: foo
BAR: bar
BAZ: "{{.BAZ}}"
tasks:
default:
cmds:
- task: local
- task: global
local:
vars:
AMD64: amd64
env:
GOOS: linux
GOARCH: "{{.AMD64}}"
CGO_ENABLED:
sh: echo '0'
cmds:
- echo "GOOS='$GOOS' GOARCH='$GOARCH' CGO_ENABLED='$CGO_ENABLED'" > local.txt
global:
env:
BAR: overriden
cmds:
- echo "FOO='$FOO' BAR='$BAR' BAZ='$BAZ'" > global.txt

View File

@@ -29,3 +29,13 @@ sub/src.txt:
- echo "hello world" > sub/src.txt
status:
- test -f sub/src.txt
'my text file.txt':
desc: generate file with spaces in the name
deps: [sub/src.txt]
cmds:
- cat sub/src.txt > 'my text file.txt'
sources:
- sub/src.txt
generates:
- 'my text file.txt'

View File

@@ -0,0 +1 @@
*.txt

View File

@@ -0,0 +1,9 @@
version: '2'
includes:
included: Taskfile2.yml
tasks:
root-task:
cmds:
- echo "root task" > root_task.txt

View File

@@ -0,0 +1,6 @@
version: '2'
tasks:
call-root:
cmds:
- task: :root-task

19
testdata/precondition/Taskfile.yml vendored Normal file
View File

@@ -0,0 +1,19 @@
version: '2'
tasks:
foo:
preconditions:
- test -f foo.txt
impossible:
preconditions:
- sh: "[ 1 = 0 ]"
msg: "1 != 0 obviously!"
depends_on_impossible:
deps:
- impossible
executes_failing_task_as_cmd:
cmds:
- task: impossible

0
testdata/precondition/foo.txt vendored Normal file
View File

26
testdata/summary/Taskfile.yml vendored Normal file
View File

@@ -0,0 +1,26 @@
version: 2
tasks:
task-with-summary:
deps: [dependend-task-1, dependend-task-2]
summary: |
summary of task-with-summary - line 1
line 2
line 3
cmds:
- echo 'task-with-summary was executed'
- echo 'another command'
- exit 0
other-task-with-summary:
summary: summary of other-task-with-summary
cmds:
- echo 'other-task-with-summary was executed'
dependend-task-1:
cmds:
- echo 'dependend-task-1 was executed'
dependend-task-2:
cmds:
- echo 'dependend-task-2 was executed'

22
testdata/summary/task-with-summary.txt vendored Normal file
View File

@@ -0,0 +1,22 @@
task: task-with-summary
summary of task-with-summary - line 1
line 2
line 3
dependencies:
- dependend-task-1
- dependend-task-2
commands:
- echo 'task-with-summary was executed'
- echo 'another command'
- exit 0
task: other-task-with-summary
summary of other-task-with-summary
commands:
- echo 'other-task-with-summary was executed'

View File

@@ -3,10 +3,9 @@ package task
import (
"path/filepath"
"github.com/go-task/task/v2/internal/execext"
"github.com/go-task/task/v2/internal/taskfile"
"github.com/go-task/task/v2/internal/templater"
"mvdan.cc/sh/shell"
)
// CompiledTask returns a copy of a task, but replacing variables in almost all
@@ -31,13 +30,13 @@ func (e *Executor) CompiledTask(call taskfile.Call) (*taskfile.Task, error) {
Status: r.ReplaceSlice(origTask.Status),
Dir: r.Replace(origTask.Dir),
Vars: nil,
Env: r.ReplaceVars(origTask.Env),
Env: nil,
Silent: origTask.Silent,
Method: r.Replace(origTask.Method),
Prefix: r.Replace(origTask.Prefix),
IgnoreError: origTask.IgnoreError,
}
new.Dir, err = shell.Expand(new.Dir, nil)
new.Dir, err = execext.Expand(new.Dir)
if err != nil {
return nil, err
}
@@ -47,6 +46,14 @@ func (e *Executor) CompiledTask(call taskfile.Call) (*taskfile.Task, error) {
if new.Prefix == "" {
new.Prefix = new.Task
}
new.Env = make(taskfile.Vars, len(e.Taskfile.Env)+len(origTask.Env))
for k, v := range r.ReplaceVars(e.Taskfile.Env) {
new.Env[k] = v
}
for k, v := range r.ReplaceVars(origTask.Env) {
new.Env[k] = v
}
for k, v := range new.Env {
static, err := e.Compiler.HandleDynamicVar(v)
if err != nil {
@@ -77,5 +84,15 @@ func (e *Executor) CompiledTask(call taskfile.Call) (*taskfile.Task, error) {
}
}
if len(origTask.Preconditions) > 0 {
new.Preconditions = make([]*taskfile.Precondition, len(origTask.Preconditions))
for i, precond := range origTask.Preconditions {
new.Preconditions[i] = &taskfile.Precondition{
Sh: r.Replace(precond.Sh),
Msg: r.Replace(precond.Msg),
}
}
}
return &new, r.Err()
}

View File

@@ -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.

View File

@@ -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.

View File

@@ -1 +0,0 @@
module github.com/mitchellh/go-homedir

View File

@@ -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
}

View File

@@ -15,7 +15,8 @@ Events contain the `os.FileInfo` of the file or directory that the event is base
[Watcher Command](#command)
# Update
Event.Path for Rename and Move events is now returned in the format of `fromPath -> toPath`
- Added new file filter hooks (Including a built in regexp filtering hook) [Dec 12, 2018]
- Event.Path for Rename and Move events is now returned in the format of `fromPath -> toPath`
#### Chmod event is not supported under windows.
@@ -68,6 +69,11 @@ func main() {
// Only notify rename and move events.
w.FilterOps(watcher.Rename, watcher.Move)
// Only files that match the regular expression during file listings
// will be watched.
r := regexp.MustCompile("^abc$")
w.AddFilterHook(watcher.RegexFilterHook(r, false))
go func() {
for {
select {
@@ -128,6 +134,8 @@ Usage of watcher:
command to run when an event occurs
-dotfiles
watch dot files (default true)
-ignore string
comma separated list of paths to ignore
-interval string
watcher poll interval (default "100ms")
-keepalive

12
vendor/github.com/radovskyb/watcher/ishidden.go generated vendored Normal file
View File

@@ -0,0 +1,12 @@
// +build !windows
package watcher
import (
"path/filepath"
"strings"
)
func isHiddenFile(path string) (bool, error) {
return strings.HasPrefix(filepath.Base(path), "."), nil
}

View File

@@ -0,0 +1,21 @@
// +build windows
package watcher
import (
"syscall"
)
func isHiddenFile(path string) (bool, error) {
pointer, err := syscall.UTF16PtrFromString(path)
if err != nil {
return false, err
}
attributes, err := syscall.GetFileAttributes(pointer)
if err != nil {
return false, err
}
return attributes&syscall.FILE_ATTRIBUTE_HIDDEN != 0, nil
}

View File

@@ -4,6 +4,6 @@ package watcher
import "os"
func SameFile(fi1, fi2 os.FileInfo) bool {
func sameFile(fi1, fi2 os.FileInfo) bool {
return os.SameFile(fi1, fi2)
}

View File

@@ -4,7 +4,7 @@ package watcher
import "os"
func SameFile(fi1, fi2 os.FileInfo) bool {
func sameFile(fi1, fi2 os.FileInfo) bool {
return fi1.ModTime() == fi2.ModTime() &&
fi1.Size() == fi2.Size() &&
fi1.Mode() == fi2.Mode() &&

View File

@@ -6,6 +6,7 @@ import (
"io/ioutil"
"os"
"path/filepath"
"regexp"
"strings"
"sync"
"time"
@@ -24,6 +25,10 @@ var (
// ErrWatchedFileDeleted is an error that occurs when a file or folder that was
// being watched has been deleted.
ErrWatchedFileDeleted = errors.New("error: watched file or folder deleted")
// ErrSkip is less of an error, but more of a way for path hooks to skip a file or
// directory.
ErrSkip = errors.New("error: skipping file")
)
// An Op is a type that is used to describe what type
@@ -69,16 +74,43 @@ type Event struct {
// String returns a string depending on what type of event occurred and the
// file name associated with the event.
func (e Event) String() string {
if e.FileInfo != nil {
pathType := "FILE"
if e.IsDir() {
pathType = "DIRECTORY"
}
return fmt.Sprintf("%s %q %s [%s]", pathType, e.Name(), e.Op, e.Path)
if e.FileInfo == nil {
return "???"
}
return "???"
pathType := "FILE"
if e.IsDir() {
pathType = "DIRECTORY"
}
return fmt.Sprintf("%s %q %s [%s]", pathType, e.Name(), e.Op, e.Path)
}
// FilterFileHookFunc is a function that is called to filter files during listings.
// If a file is ok to be listed, nil is returned otherwise ErrSkip is returned.
type FilterFileHookFunc func(info os.FileInfo, fullPath string) error
// RegexFilterHook is a function that accepts or rejects a file
// for listing based on whether it's filename or full path matches
// a regular expression.
func RegexFilterHook(r *regexp.Regexp, useFullPath bool) FilterFileHookFunc {
return func(info os.FileInfo, fullPath string) error {
str := info.Name()
if useFullPath {
str = fullPath
}
// Match
if r.MatchString(str) {
return nil
}
// No match.
return ErrSkip
}
}
// Watcher describes a process that watches files for changes.
type Watcher struct {
Event chan Event
Error chan error
@@ -88,6 +120,7 @@ type Watcher struct {
// mu protects the following.
mu *sync.Mutex
ffh []FilterFileHookFunc
running bool
names map[string]bool // bool for recursive or not.
files map[string]os.FileInfo // map of files.
@@ -125,6 +158,13 @@ func (w *Watcher) SetMaxEvents(delta int) {
w.mu.Unlock()
}
// AddFilterHook
func (w *Watcher) AddFilterHook(f FilterFileHookFunc) {
w.mu.Lock()
w.ffh = append(w.ffh, f)
w.mu.Unlock()
}
// IgnoreHiddenFiles sets the watcher to ignore any file or directory
// that starts with a dot.
func (w *Watcher) IgnoreHiddenFiles(ignore bool) {
@@ -157,7 +197,13 @@ func (w *Watcher) Add(name string) (err error) {
// If name is on the ignored list or if hidden files are
// ignored and name is a hidden file or directory, simply return.
_, ignored := w.ignored[name]
if ignored || (w.ignoreHidden && strings.HasPrefix(name, ".")) {
isHidden, err := isHiddenFile(name)
if err != nil {
return err
}
if ignored || (w.ignoreHidden && isHidden) {
return nil
}
@@ -200,18 +246,36 @@ func (w *Watcher) list(name string) (map[string]os.FileInfo, error) {
// Add all of the files in the directory to the file list as long
// as they aren't on the ignored list or are hidden files if ignoreHidden
// is set to true.
outer:
for _, fInfo := range fInfoList {
path := filepath.Join(name, fInfo.Name())
_, ignored := w.ignored[path]
if ignored || (w.ignoreHidden && strings.HasPrefix(fInfo.Name(), ".")) {
isHidden, err := isHiddenFile(path)
if err != nil {
return nil, err
}
if ignored || (w.ignoreHidden && isHidden) {
continue
}
for _, f := range w.ffh {
err := f(fInfo, path)
if err == ErrSkip {
continue outer
}
if err != nil {
return nil, err
}
}
fileList[path] = fInfo
}
return fileList, nil
}
// Add adds either a single file or directory recursively to the file list.
// AddRecursive adds either a single file or directory recursively to the file list.
func (w *Watcher) AddRecursive(name string) (err error) {
w.mu.Lock()
defer w.mu.Unlock()
@@ -242,10 +306,27 @@ func (w *Watcher) listRecursive(name string) (map[string]os.FileInfo, error) {
if err != nil {
return err
}
for _, f := range w.ffh {
err := f(info, path)
if err == ErrSkip {
return nil
}
if err != nil {
return err
}
}
// If path is ignored and it's a directory, skip the directory. If it's
// ignored and it's a single file, skip the file.
_, ignored := w.ignored[path]
if ignored || (w.ignoreHidden && strings.HasPrefix(info.Name(), ".")) {
isHidden, err := isHiddenFile(path)
if err != nil {
return err
}
if ignored || (w.ignoreHidden && isHidden) {
if info.IsDir() {
return filepath.SkipDir
}
@@ -292,7 +373,7 @@ func (w *Watcher) Remove(name string) (err error) {
return nil
}
// Remove removes either a single file or a directory recursively from
// RemoveRecursive removes either a single file or a directory recursively from
// the file's list.
func (w *Watcher) RemoveRecursive(name string) (err error) {
w.mu.Lock()
@@ -346,11 +427,17 @@ func (w *Watcher) Ignore(paths ...string) (err error) {
return nil
}
// WatchedFiles returns a map of files added to a Watcher.
func (w *Watcher) WatchedFiles() map[string]os.FileInfo {
w.mu.Lock()
defer w.mu.Unlock()
return w.files
files := make(map[string]os.FileInfo)
for k, v := range w.files {
files[k] = v
}
return files
}
// fileInfo is an implementation of os.FileInfo that can be used
@@ -560,7 +647,7 @@ func (w *Watcher) pollEvents(files map[string]os.FileInfo, evt chan Event,
// Check for renames and moves.
for path1, info1 := range removes {
for path2, info2 := range creates {
if SameFile(info1, info2) {
if sameFile(info1, info2) {
e := Event{
Op: Move,
Path: fmt.Sprintf("%s -> %s", path1, path2),
@@ -606,6 +693,7 @@ func (w *Watcher) Wait() {
w.wg.Wait()
}
// Close stops a Watcher and unlocks its mutex, then sends a close signal.
func (w *Watcher) Close() {
w.mu.Lock()
if !w.running {

View File

@@ -1,22 +1,21 @@
Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell
MIT License
Please consider promoting this project if you find it useful.
Copyright (c) 2012-2018 Mat Ryer and Tyler Bunnell
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:
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 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.
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.

View File

@@ -113,6 +113,17 @@ func Errorf(t TestingT, err error, msg string, args ...interface{}) bool {
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.
//
// 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...)...)
}
// 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
// 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...)...)
}
// 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 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...)...)
}
// 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.
//
// 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...)...)
}
// 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
// elements given in the specified subset(array, slice...).
//

View File

@@ -215,6 +215,28 @@ func (a *Assertions) Errorf(err error, msg string, args ...interface{}) bool {
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.
//
// 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...)
}
// 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
// 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...)
}
// 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 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...)
}
// 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.
//
// 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...)
}
// 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
// elements given in the specified subset(array, slice...).
//

View 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
}

View File

@@ -18,6 +18,7 @@ import (
"github.com/davecgh/go-spew/spew"
"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
@@ -39,7 +40,7 @@ type ValueAssertionFunc func(TestingT, interface{}, ...interface{}) bool
// for table driven tests.
type BoolAssertionFunc func(TestingT, bool, ...interface{}) bool
// ValuesAssertionFunc is a common function prototype when validating an error value. Can be useful
// ErrorAssertionFunc is a common function prototype when validating an error value. Can be useful
// for table driven tests.
type ErrorAssertionFunc func(TestingT, error, ...interface{}) bool
@@ -179,7 +180,11 @@ func messageFromMsgAndArgs(msgAndArgs ...interface{}) string {
return ""
}
if len(msgAndArgs) == 1 {
return msgAndArgs[0].(string)
msg := msgAndArgs[0]
if msgAsStr, ok := msg.(string); ok {
return msgAsStr
}
return fmt.Sprintf("%+v", msg)
}
if len(msgAndArgs) > 1 {
return fmt.Sprintf(msgAndArgs[0].(string), msgAndArgs[1:]...)
@@ -346,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
// representations appropriate to be presented to the user.
//
@@ -415,6 +451,17 @@ func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
return Fail(t, "Expected value not to be nil.", msgAndArgs...)
}
// containsKind checks if a specified kind in the slice of kinds.
func containsKind(kinds []reflect.Kind, kind reflect.Kind) bool {
for i := 0; i < len(kinds); i++ {
if kind == kinds[i] {
return true
}
}
return false
}
// isNil checks if a specified object is nil or not, without Failing.
func isNil(object interface{}) bool {
if object == nil {
@@ -423,7 +470,14 @@ func isNil(object interface{}) bool {
value := reflect.ValueOf(object)
kind := value.Kind()
if kind >= reflect.Chan && kind <= reflect.Slice && value.IsNil() {
isNilableKind := containsKind(
[]reflect.Kind{
reflect.Chan, reflect.Func,
reflect.Interface, reflect.Map,
reflect.Ptr, reflect.Slice},
kind)
if isNilableKind && value.IsNil() {
return true
}
@@ -457,14 +511,14 @@ func isEmpty(object interface{}) bool {
// collection types are empty when they have no element
case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice:
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:
if objValue.IsNil() {
return true
}
deref := objValue.Elem().Interface()
return isEmpty(deref)
// for all other types, compare against the zero value
// for all other types, compare against the zero value
default:
zero := reflect.Zero(objValue.Type())
return reflect.DeepEqual(object, zero.Interface())
@@ -607,7 +661,7 @@ func NotEqual(t TestingT, expected, actual interface{}, msgAndArgs ...interface{
func includeElement(list interface{}, element interface{}) (ok, found bool) {
listValue := reflect.ValueOf(list)
elementValue := reflect.ValueOf(element)
listKind := reflect.TypeOf(list).Kind()
defer func() {
if e := recover(); e != nil {
ok = false
@@ -615,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())
}
if reflect.TypeOf(list).Kind() == reflect.Map {
if listKind == reflect.Map {
mapKeys := listValue.MapKeys()
for i := 0; i < len(mapKeys); i++ {
if ObjectsAreEqual(mapKeys[i].Interface(), element) {
@@ -1315,6 +1370,24 @@ func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{
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) {
t := reflect.TypeOf(v)
k := t.Kind()
@@ -1327,7 +1400,7 @@ func typeAndKind(v interface{}) (reflect.Type, reflect.Kind) {
}
// diff returns a diff of both values as long as both are of the same type and
// are a struct, map, slice or array. Otherwise it returns an empty string.
// are a struct, map, slice, array or string. Otherwise it returns an empty string.
func diff(expected interface{}, actual interface{}) string {
if expected == nil || actual == nil {
return ""
@@ -1345,12 +1418,12 @@ func diff(expected interface{}, actual interface{}) string {
}
var e, a string
if ek != reflect.String {
if et != reflect.TypeOf("") {
e = spewConfig.Sdump(expected)
a = spewConfig.Sdump(actual)
} else {
e = expected.(string)
a = actual.(string)
e = reflect.ValueOf(expected).String()
a = reflect.ValueOf(actual).String()
}
diff, _ := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{
@@ -1392,3 +1465,34 @@ var spewConfig = spew.ConfigState{
type tHelper interface {
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()
}()
}
}
}

View File

@@ -10,6 +10,7 @@ package scrypt // import "golang.org/x/crypto/scrypt"
import (
"crypto/sha256"
"errors"
"math/bits"
"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,
// 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) {
w0 := tmp[0] ^ in[0]
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
for i := 0; i < 8; i += 2 {
u := x0 + x12
x4 ^= u<<7 | u>>(32-7)
u = x4 + x0
x8 ^= u<<9 | u>>(32-9)
u = x8 + x4
x12 ^= u<<13 | u>>(32-13)
u = x12 + x8
x0 ^= u<<18 | u>>(32-18)
x4 ^= bits.RotateLeft32(x0+x12, 7)
x8 ^= bits.RotateLeft32(x4+x0, 9)
x12 ^= bits.RotateLeft32(x8+x4, 13)
x0 ^= bits.RotateLeft32(x12+x8, 18)
u = x5 + x1
x9 ^= u<<7 | u>>(32-7)
u = x9 + x5
x13 ^= u<<9 | u>>(32-9)
u = x13 + x9
x1 ^= u<<13 | u>>(32-13)
u = x1 + x13
x5 ^= u<<18 | u>>(32-18)
x9 ^= bits.RotateLeft32(x5+x1, 7)
x13 ^= bits.RotateLeft32(x9+x5, 9)
x1 ^= bits.RotateLeft32(x13+x9, 13)
x5 ^= bits.RotateLeft32(x1+x13, 18)
u = x10 + x6
x14 ^= u<<7 | u>>(32-7)
u = x14 + x10
x2 ^= u<<9 | u>>(32-9)
u = x2 + x14
x6 ^= u<<13 | u>>(32-13)
u = x6 + x2
x10 ^= u<<18 | u>>(32-18)
x14 ^= bits.RotateLeft32(x10+x6, 7)
x2 ^= bits.RotateLeft32(x14+x10, 9)
x6 ^= bits.RotateLeft32(x2+x14, 13)
x10 ^= bits.RotateLeft32(x6+x2, 18)
u = x15 + x11
x3 ^= u<<7 | u>>(32-7)
u = x3 + x15
x7 ^= u<<9 | u>>(32-9)
u = x7 + x3
x11 ^= u<<13 | u>>(32-13)
u = x11 + x7
x15 ^= u<<18 | u>>(32-18)
x3 ^= bits.RotateLeft32(x15+x11, 7)
x7 ^= bits.RotateLeft32(x3+x15, 9)
x11 ^= bits.RotateLeft32(x7+x3, 13)
x15 ^= bits.RotateLeft32(x11+x7, 18)
u = x0 + x3
x1 ^= u<<7 | u>>(32-7)
u = x1 + x0
x2 ^= u<<9 | u>>(32-9)
u = x2 + x1
x3 ^= u<<13 | u>>(32-13)
u = x3 + x2
x0 ^= u<<18 | u>>(32-18)
x1 ^= bits.RotateLeft32(x0+x3, 7)
x2 ^= bits.RotateLeft32(x1+x0, 9)
x3 ^= bits.RotateLeft32(x2+x1, 13)
x0 ^= bits.RotateLeft32(x3+x2, 18)
u = x5 + x4
x6 ^= u<<7 | u>>(32-7)
u = x6 + x5
x7 ^= u<<9 | u>>(32-9)
u = x7 + x6
x4 ^= u<<13 | u>>(32-13)
u = x4 + x7
x5 ^= u<<18 | u>>(32-18)
x6 ^= bits.RotateLeft32(x5+x4, 7)
x7 ^= bits.RotateLeft32(x6+x5, 9)
x4 ^= bits.RotateLeft32(x7+x6, 13)
x5 ^= bits.RotateLeft32(x4+x7, 18)
u = x10 + x9
x11 ^= u<<7 | u>>(32-7)
u = x11 + x10
x8 ^= u<<9 | u>>(32-9)
u = x8 + x11
x9 ^= u<<13 | u>>(32-13)
u = x9 + x8
x10 ^= u<<18 | u>>(32-18)
x11 ^= bits.RotateLeft32(x10+x9, 7)
x8 ^= bits.RotateLeft32(x11+x10, 9)
x9 ^= bits.RotateLeft32(x8+x11, 13)
x10 ^= bits.RotateLeft32(x9+x8, 18)
u = x15 + x14
x12 ^= u<<7 | u>>(32-7)
u = x12 + x15
x13 ^= u<<9 | u>>(32-9)
u = x13 + x12
x14 ^= u<<13 | u>>(32-13)
u = x14 + x13
x15 ^= u<<18 | u>>(32-18)
x12 ^= bits.RotateLeft32(x15+x14, 7)
x13 ^= bits.RotateLeft32(x12+x15, 9)
x14 ^= bits.RotateLeft32(x13+x12, 13)
x15 ^= bits.RotateLeft32(x14+x13, 18)
}
x0 += w0
x1 += w1

View File

@@ -7,6 +7,7 @@ package terminal
import (
"bytes"
"io"
"strconv"
"sync"
"unicode/utf8"
)
@@ -159,6 +160,10 @@ func bytesToKey(b []byte, pasteActive bool) (rune, []byte) {
return keyClearScreen, b[1:]
case 23: // ^W
return keyDeleteWord, b[1:]
case 14: // ^N
return keyDown, b[1:]
case 16: // ^P
return keyUp, b[1:]
}
}
@@ -267,34 +272,44 @@ func (t *Terminal) moveCursorToPos(pos int) {
}
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:]
m := []rune{}
// 1 unit up can be expressed as ^[[A or ^[A
// 5 units up can be expressed as ^[[5A
if up == 1 {
m = append(m, keyEscape, '[', 'A')
} else if up > 1 {
m = append(m, keyEscape, '[')
m = append(m, []rune(strconv.Itoa(up))...)
m = append(m, 'A')
}
t.queue(movement)
if down == 1 {
m = append(m, keyEscape, '[', 'B')
} else if down > 1 {
m = append(m, keyEscape, '[')
m = append(m, []rune(strconv.Itoa(down))...)
m = append(m, 'B')
}
if right == 1 {
m = append(m, keyEscape, '[', 'C')
} else if right > 1 {
m = append(m, keyEscape, '[')
m = append(m, []rune(strconv.Itoa(right))...)
m = append(m, 'C')
}
if left == 1 {
m = append(m, keyEscape, '[', 'D')
} else if left > 1 {
m = append(m, keyEscape, '[')
m = append(m, []rune(strconv.Itoa(left))...)
m = append(m, 'D')
}
t.queue(m)
}
func (t *Terminal) clearLineToRight() {

View File

@@ -2,7 +2,7 @@
// 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
// +build aix darwin dragonfly freebsd linux,!appengine netbsd openbsd
// Package terminal provides support functions for dealing with terminals, as
// commonly found on UNIX systems.
@@ -25,7 +25,7 @@ type State struct {
termios unix.Termios
}
// IsTerminal returns true if the given file descriptor is a terminal.
// IsTerminal returns whether the given file descriptor is a terminal.
func IsTerminal(fd int) bool {
_, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
return err == nil

12
vendor/golang.org/x/crypto/ssh/terminal/util_aix.go generated vendored Normal file
View File

@@ -0,0 +1,12 @@
// Copyright 2018 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 aix
package terminal
import "golang.org/x/sys/unix"
const ioctlReadTermios = unix.TCGETS
const ioctlWriteTermios = unix.TCSETS

View File

@@ -21,7 +21,7 @@ import (
type State struct{}
// IsTerminal returns true if the given file descriptor is a terminal.
// IsTerminal returns whether the given file descriptor is a terminal.
func IsTerminal(fd int) bool {
return false
}

View File

@@ -17,7 +17,7 @@ type State struct {
termios unix.Termios
}
// IsTerminal returns true if the given file descriptor is a terminal.
// IsTerminal returns whether the given file descriptor is a terminal.
func IsTerminal(fd int) bool {
_, err := unix.IoctlGetTermio(fd, unix.TCGETA)
return err == nil

View File

@@ -26,7 +26,7 @@ type State struct {
mode uint32
}
// IsTerminal returns true if the given file descriptor is a terminal.
// IsTerminal returns whether the given file descriptor is a terminal.
func IsTerminal(fd int) bool {
var st uint32
err := windows.GetConsoleMode(windows.Handle(fd), &st)
@@ -64,13 +64,15 @@ func Restore(fd int, state *State) error {
return windows.SetConsoleMode(windows.Handle(fd), state.mode)
}
// GetSize returns the dimensions of the given terminal.
// GetSize returns the visible dimensions of the given terminal.
//
// These dimensions don't include any scrollback buffer height.
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
return int(info.Window.Right - info.Window.Left + 1), int(info.Window.Bottom - info.Window.Top + 1), nil
}
// ReadPassword reads a line of input from a terminal without local echo. This

3
vendor/golang.org/x/net/AUTHORS generated vendored
View File

@@ -1,3 +0,0 @@
# This source code refers to The Go Authors for copyright purposes.
# The master list of authors is in the main Go distribution,
# visible at http://tip.golang.org/AUTHORS.

View File

@@ -1,3 +0,0 @@
# This source code was written by the Go contributors.
# The master list of contributors is in the main Go distribution,
# visible at http://tip.golang.org/CONTRIBUTORS.

27
vendor/golang.org/x/net/LICENSE generated vendored
View File

@@ -1,27 +0,0 @@
Copyright (c) 2009 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

22
vendor/golang.org/x/net/PATENTS generated vendored
View File

@@ -1,22 +0,0 @@
Additional IP Rights Grant (Patents)
"This implementation" means the copyrightable works distributed by
Google as part of the Go project.
Google hereby grants to You a perpetual, worldwide, non-exclusive,
no-charge, royalty-free, irrevocable (except as stated in this section)
patent license to make, have made, use, offer to sell, sell, import,
transfer and otherwise run, modify and propagate the contents of this
implementation of Go, where such license applies only to those patent
claims, both currently owned or controlled by Google and acquired in
the future, licensable by Google that are necessarily infringed by this
implementation of Go. This grant does not include claims that would be
infringed only as a consequence of further modification of this
implementation. If you or your agent or exclusive licensee institute or
order or agree to the institution of patent litigation against any
entity (including a cross-claim or counterclaim in a lawsuit) alleging
that this implementation of Go or any code incorporated within this
implementation of Go constitutes direct or contributory patent
infringement, or inducement of patent infringement, then any patent
rights granted to you under this License for this implementation of Go
shall terminate as of the date such litigation is filed.

View File

@@ -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
}

View File

@@ -1,72 +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.
// +build go1.7
package context
import (
"context" // standard library's context, as of Go 1.7
"time"
)
var (
todo = context.TODO()
background = context.Background()
)
// Canceled is the error returned by Context.Err when the context is canceled.
var Canceled = context.Canceled
// DeadlineExceeded is the error returned by Context.Err when the context's
// deadline passes.
var DeadlineExceeded = context.DeadlineExceeded
// WithCancel returns a copy of parent with a new Done channel. The returned
// context's Done channel is closed when the returned cancel function is called
// or when the parent context's Done channel is closed, whichever happens first.
//
// Canceling this context releases resources associated with it, so code should
// call cancel as soon as the operations running in this Context complete.
func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
ctx, f := context.WithCancel(parent)
return ctx, CancelFunc(f)
}
// WithDeadline returns a copy of the parent context with the deadline adjusted
// to be no later than d. If the parent's deadline is already earlier than d,
// WithDeadline(parent, d) is semantically equivalent to parent. The returned
// context's Done channel is closed when the deadline expires, when the returned
// cancel function is called, or when the parent context's Done channel is
// closed, whichever happens first.
//
// Canceling this context releases resources associated with it, so code should
// call cancel as soon as the operations running in this Context complete.
func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) {
ctx, f := context.WithDeadline(parent, deadline)
return ctx, CancelFunc(f)
}
// WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)).
//
// Canceling this context releases resources associated with it, so code should
// call cancel as soon as the operations running in this Context complete:
//
// func slowOperationWithTimeout(ctx context.Context) (Result, error) {
// ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond)
// defer cancel() // releases resources if slowOperation completes before timeout elapses
// return slowOperation(ctx)
// }
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {
return WithDeadline(parent, time.Now().Add(timeout))
}
// WithValue returns a copy of parent in which the value associated with key is
// val.
//
// Use context Values only for request-scoped data that transits processes and
// APIs, not for passing optional parameters to functions.
func WithValue(parent Context, key interface{}, val interface{}) Context {
return context.WithValue(parent, key, val)
}

View File

@@ -1,20 +0,0 @@
// Copyright 2017 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 go1.9
package context
import "context" // standard library's context, as of Go 1.7
// A Context carries a deadline, a cancelation signal, and other values across
// API boundaries.
//
// Context's methods may be called by multiple goroutines simultaneously.
type Context = context.Context
// A CancelFunc tells an operation to abandon its work.
// A CancelFunc does not wait for the work to stop.
// After the first call, subsequent calls to a CancelFunc do nothing.
type CancelFunc = context.CancelFunc

View File

@@ -1,300 +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.
// +build !go1.7
package context
import (
"errors"
"fmt"
"sync"
"time"
)
// An emptyCtx is never canceled, has no values, and has no deadline. It is not
// struct{}, since vars of this type must have distinct addresses.
type emptyCtx int
func (*emptyCtx) Deadline() (deadline time.Time, ok bool) {
return
}
func (*emptyCtx) Done() <-chan struct{} {
return nil
}
func (*emptyCtx) Err() error {
return nil
}
func (*emptyCtx) Value(key interface{}) interface{} {
return nil
}
func (e *emptyCtx) String() string {
switch e {
case background:
return "context.Background"
case todo:
return "context.TODO"
}
return "unknown empty Context"
}
var (
background = new(emptyCtx)
todo = new(emptyCtx)
)
// Canceled is the error returned by Context.Err when the context is canceled.
var Canceled = errors.New("context canceled")
// DeadlineExceeded is the error returned by Context.Err when the context's
// deadline passes.
var DeadlineExceeded = errors.New("context deadline exceeded")
// WithCancel returns a copy of parent with a new Done channel. The returned
// context's Done channel is closed when the returned cancel function is called
// or when the parent context's Done channel is closed, whichever happens first.
//
// Canceling this context releases resources associated with it, so code should
// call cancel as soon as the operations running in this Context complete.
func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
c := newCancelCtx(parent)
propagateCancel(parent, c)
return c, func() { c.cancel(true, Canceled) }
}
// newCancelCtx returns an initialized cancelCtx.
func newCancelCtx(parent Context) *cancelCtx {
return &cancelCtx{
Context: parent,
done: make(chan struct{}),
}
}
// propagateCancel arranges for child to be canceled when parent is.
func propagateCancel(parent Context, child canceler) {
if parent.Done() == nil {
return // parent is never canceled
}
if p, ok := parentCancelCtx(parent); ok {
p.mu.Lock()
if p.err != nil {
// parent has already been canceled
child.cancel(false, p.err)
} else {
if p.children == nil {
p.children = make(map[canceler]bool)
}
p.children[child] = true
}
p.mu.Unlock()
} else {
go func() {
select {
case <-parent.Done():
child.cancel(false, parent.Err())
case <-child.Done():
}
}()
}
}
// parentCancelCtx follows a chain of parent references until it finds a
// *cancelCtx. This function understands how each of the concrete types in this
// package represents its parent.
func parentCancelCtx(parent Context) (*cancelCtx, bool) {
for {
switch c := parent.(type) {
case *cancelCtx:
return c, true
case *timerCtx:
return c.cancelCtx, true
case *valueCtx:
parent = c.Context
default:
return nil, false
}
}
}
// removeChild removes a context from its parent.
func removeChild(parent Context, child canceler) {
p, ok := parentCancelCtx(parent)
if !ok {
return
}
p.mu.Lock()
if p.children != nil {
delete(p.children, child)
}
p.mu.Unlock()
}
// A canceler is a context type that can be canceled directly. The
// implementations are *cancelCtx and *timerCtx.
type canceler interface {
cancel(removeFromParent bool, err error)
Done() <-chan struct{}
}
// A cancelCtx can be canceled. When canceled, it also cancels any children
// that implement canceler.
type cancelCtx struct {
Context
done chan struct{} // closed by the first cancel call.
mu sync.Mutex
children map[canceler]bool // set to nil by the first cancel call
err error // set to non-nil by the first cancel call
}
func (c *cancelCtx) Done() <-chan struct{} {
return c.done
}
func (c *cancelCtx) Err() error {
c.mu.Lock()
defer c.mu.Unlock()
return c.err
}
func (c *cancelCtx) String() string {
return fmt.Sprintf("%v.WithCancel", c.Context)
}
// cancel closes c.done, cancels each of c's children, and, if
// removeFromParent is true, removes c from its parent's children.
func (c *cancelCtx) cancel(removeFromParent bool, err error) {
if err == nil {
panic("context: internal error: missing cancel error")
}
c.mu.Lock()
if c.err != nil {
c.mu.Unlock()
return // already canceled
}
c.err = err
close(c.done)
for child := range c.children {
// NOTE: acquiring the child's lock while holding parent's lock.
child.cancel(false, err)
}
c.children = nil
c.mu.Unlock()
if removeFromParent {
removeChild(c.Context, c)
}
}
// WithDeadline returns a copy of the parent context with the deadline adjusted
// to be no later than d. If the parent's deadline is already earlier than d,
// WithDeadline(parent, d) is semantically equivalent to parent. The returned
// context's Done channel is closed when the deadline expires, when the returned
// cancel function is called, or when the parent context's Done channel is
// closed, whichever happens first.
//
// Canceling this context releases resources associated with it, so code should
// call cancel as soon as the operations running in this Context complete.
func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) {
if cur, ok := parent.Deadline(); ok && cur.Before(deadline) {
// The current deadline is already sooner than the new one.
return WithCancel(parent)
}
c := &timerCtx{
cancelCtx: newCancelCtx(parent),
deadline: deadline,
}
propagateCancel(parent, c)
d := deadline.Sub(time.Now())
if d <= 0 {
c.cancel(true, DeadlineExceeded) // deadline has already passed
return c, func() { c.cancel(true, Canceled) }
}
c.mu.Lock()
defer c.mu.Unlock()
if c.err == nil {
c.timer = time.AfterFunc(d, func() {
c.cancel(true, DeadlineExceeded)
})
}
return c, func() { c.cancel(true, Canceled) }
}
// A timerCtx carries a timer and a deadline. It embeds a cancelCtx to
// implement Done and Err. It implements cancel by stopping its timer then
// delegating to cancelCtx.cancel.
type timerCtx struct {
*cancelCtx
timer *time.Timer // Under cancelCtx.mu.
deadline time.Time
}
func (c *timerCtx) Deadline() (deadline time.Time, ok bool) {
return c.deadline, true
}
func (c *timerCtx) String() string {
return fmt.Sprintf("%v.WithDeadline(%s [%s])", c.cancelCtx.Context, c.deadline, c.deadline.Sub(time.Now()))
}
func (c *timerCtx) cancel(removeFromParent bool, err error) {
c.cancelCtx.cancel(false, err)
if removeFromParent {
// Remove this timerCtx from its parent cancelCtx's children.
removeChild(c.cancelCtx.Context, c)
}
c.mu.Lock()
if c.timer != nil {
c.timer.Stop()
c.timer = nil
}
c.mu.Unlock()
}
// WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)).
//
// Canceling this context releases resources associated with it, so code should
// call cancel as soon as the operations running in this Context complete:
//
// func slowOperationWithTimeout(ctx context.Context) (Result, error) {
// ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond)
// defer cancel() // releases resources if slowOperation completes before timeout elapses
// return slowOperation(ctx)
// }
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {
return WithDeadline(parent, time.Now().Add(timeout))
}
// WithValue returns a copy of parent in which the value associated with key is
// val.
//
// Use context Values only for request-scoped data that transits processes and
// APIs, not for passing optional parameters to functions.
func WithValue(parent Context, key interface{}, val interface{}) Context {
return &valueCtx{parent, key, val}
}
// A valueCtx carries a key-value pair. It implements Value for that key and
// delegates all other calls to the embedded Context.
type valueCtx struct {
Context
key, val interface{}
}
func (c *valueCtx) String() string {
return fmt.Sprintf("%v.WithValue(%#v, %#v)", c.Context, c.key, c.val)
}
func (c *valueCtx) Value(key interface{}) interface{} {
if c.key == key {
return c.val
}
return c.Context.Value(key)
}

View File

@@ -1,109 +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.
// +build !go1.9
package context
import "time"
// A Context carries a deadline, a cancelation signal, and other values across
// API boundaries.
//
// Context's methods may be called by multiple goroutines simultaneously.
type Context interface {
// Deadline returns the time when work done on behalf of this context
// should be canceled. Deadline returns ok==false when no deadline is
// set. Successive calls to Deadline return the same results.
Deadline() (deadline time.Time, ok bool)
// Done returns a channel that's closed when work done on behalf of this
// context should be canceled. Done may return nil if this context can
// never be canceled. Successive calls to Done return the same value.
//
// WithCancel arranges for Done to be closed when cancel is called;
// WithDeadline arranges for Done to be closed when the deadline
// expires; WithTimeout arranges for Done to be closed when the timeout
// elapses.
//
// Done is provided for use in select statements:
//
// // Stream generates values with DoSomething and sends them to out
// // until DoSomething returns an error or ctx.Done is closed.
// func Stream(ctx context.Context, out chan<- Value) error {
// for {
// v, err := DoSomething(ctx)
// if err != nil {
// return err
// }
// select {
// case <-ctx.Done():
// return ctx.Err()
// case out <- v:
// }
// }
// }
//
// See http://blog.golang.org/pipelines for more examples of how to use
// a Done channel for cancelation.
Done() <-chan struct{}
// Err returns a non-nil error value after Done is closed. Err returns
// Canceled if the context was canceled or DeadlineExceeded if the
// context's deadline passed. No other values for Err are defined.
// After Done is closed, successive calls to Err return the same value.
Err() error
// Value returns the value associated with this context for key, or nil
// if no value is associated with key. Successive calls to Value with
// the same key returns the same result.
//
// Use context values only for request-scoped data that transits
// processes and API boundaries, not for passing optional parameters to
// functions.
//
// A key identifies a specific value in a Context. Functions that wish
// to store values in Context typically allocate a key in a global
// variable then use that key as the argument to context.WithValue and
// Context.Value. A key can be any type that supports equality;
// packages should define keys as an unexported type to avoid
// collisions.
//
// Packages that define a Context key should provide type-safe accessors
// for the values stores using that key:
//
// // Package user defines a User type that's stored in Contexts.
// package user
//
// import "golang.org/x/net/context"
//
// // User is the type of value stored in the Contexts.
// type User struct {...}
//
// // key is an unexported type for keys defined in this package.
// // This prevents collisions with keys defined in other packages.
// type key int
//
// // userKey is the key for user.User values in Contexts. It is
// // unexported; clients use user.NewContext and user.FromContext
// // instead of using this key directly.
// var userKey key = 0
//
// // NewContext returns a new Context that carries value u.
// func NewContext(ctx context.Context, u *User) context.Context {
// return context.WithValue(ctx, userKey, u)
// }
//
// // FromContext returns the User value stored in ctx, if any.
// func FromContext(ctx context.Context) (*User, bool) {
// u, ok := ctx.Value(userKey).(*User)
// return u, ok
// }
Value(key interface{}) interface{}
}
// A CancelFunc tells an operation to abandon its work.
// A CancelFunc does not wait for the work to stop.
// After the first call, subsequent calls to a CancelFunc do nothing.
type CancelFunc func()

View File

@@ -7,9 +7,8 @@
package errgroup
import (
"context"
"sync"
"golang.org/x/net/context"
)
// A Group is a collection of goroutines working on subtasks that are part of

View File

@@ -14,7 +14,7 @@ migrating the build system to use containers so the builds are reproducible.
This is being done on an OS-by-OS basis. Please update this documentation as
components of the build system change.
### Old Build System (currently for `GOOS != "Linux" || GOARCH == "sparc64"`)
### Old Build System (currently for `GOOS != "linux"`)
The old build system generates the Go files based on the C header files
present on your system. This means that files
@@ -32,9 +32,9 @@ To build the files for your current OS and architecture, make sure GOOS and
GOARCH are set correctly and run `mkall.sh`. This will generate the files for
your specific system. Running `mkall.sh -n` shows the commands that will be run.
Requirements: bash, perl, go
Requirements: bash, go
### New Build System (currently for `GOOS == "Linux" && GOARCH != "sparc64"`)
### New Build System (currently for `GOOS == "linux"`)
The new build system uses a Docker container to generate the go files directly
from source checkouts of the kernel and various system libraries. This means
@@ -52,14 +52,14 @@ system and have your GOOS and GOARCH set accordingly. Running `mkall.sh` will
then generate all of the files for all of the GOOS/GOARCH pairs in the new build
system. Running `mkall.sh -n` shows the commands that will be run.
Requirements: bash, perl, go, docker
Requirements: bash, go, docker
## Component files
This section describes the various files used in the code generation process.
It also contains instructions on how to modify these files to add a new
architecture/OS or to add additional syscalls, types, or constants. Note that
if you are using the new build system, the scripts cannot be called normally.
if you are using the new build system, the scripts/programs cannot be called normally.
They must be called from within the docker container.
### asm files
@@ -81,8 +81,8 @@ each GOOS/GOARCH pair.
### mksysnum
Mksysnum is a script located at `${GOOS}/mksysnum.pl` (or `mksysnum_${GOOS}.pl`
for the old system). This script takes in a list of header files containing the
Mksysnum is a Go program located at `${GOOS}/mksysnum.go` (or `mksysnum_${GOOS}.go`
for the old system). This program takes in a list of header files containing the
syscall number declarations and parses them to produce the corresponding list of
Go numeric constants. See `zsysnum_${GOOS}_${GOARCH}.go` for the generated
constants.
@@ -92,14 +92,14 @@ new installation of the target OS (or updating the source checkouts for the
new build system). However, depending on the OS, you make need to update the
parsing in mksysnum.
### mksyscall.pl
### mksyscall.go
The `syscall.go`, `syscall_${GOOS}.go`, `syscall_${GOOS}_${GOARCH}.go` are
hand-written Go files which implement system calls (for unix, the specific OS,
or the specific OS/Architecture pair respectively) that need special handling
and list `//sys` comments giving prototypes for ones that can be generated.
The mksyscall.pl script takes the `//sys` and `//sysnb` comments and converts
The mksyscall.go program takes the `//sys` and `//sysnb` comments and converts
them into syscalls. This requires the name of the prototype in the comment to
match a syscall number in the `zsysnum_${GOOS}_${GOARCH}.go` file. The function
prototype can be exported (capitalized) or not.
@@ -160,7 +160,7 @@ signal numbers, and constants. Generated by `mkerrors.sh` (see above).
### `zsyscall_${GOOS}_${GOARCH}.go`
A file containing all the generated syscalls for a specific GOOS and GOARCH.
Generated by `mksyscall.pl` (see above).
Generated by `mksyscall.go` (see above).
### `zsysnum_${GOOS}_${GOARCH}.go`

Some files were not shown because too many files have changed in this diff Show More