Compare commits

..

140 Commits

Author SHA1 Message Date
eric sciple 1d199224a7 Update action-versioning.md 2019-12-12 23:37:25 -05:00
Bryan MacFarlane d4975510fe update illustration 2019-12-12 14:30:31 -05:00
Bryan MacFarlane 871c495487 update versioning docs for new major versions 2019-12-12 14:21:22 -05:00
Bryan MacFarlane 40a502b14b added matchers to readme 2019-12-12 13:43:34 -05:00
Adam Dobrawy fc45b70f30 Remove draft-only comments from README (#215) 2019-12-11 18:07:02 -05:00
Daniel Anechitoaie 9f6c37ac52 Updated tc.cacheFile example (#226) 2019-12-11 18:05:18 -05:00
Barret Schloerke 80e91ee891 Fix typo (#250) 2019-12-11 18:03:17 -05:00
TriangularIT bedf824517 Small typo (#251) 2019-12-11 18:02:44 -05:00
Peter Evans be9f18b69f Fix documentation links (#217) 2019-12-10 09:11:03 -05:00
Tony Brix 1d7e38e56d fix unzip error (#223) 2019-12-09 14:26:48 -05:00
Bryan MacFarlane aa13e110b1 update illustration 2019-12-09 14:02:57 -05:00
eric sciple 15033a1aed commit package-lock.json 2019-12-09 11:21:43 -05:00
Bryan MacFarlane cd47f1e123 update versioning diagram 2019-12-08 14:34:25 -08:00
Bryan MacFarlane 0f1fef3752 versioning update (#246) 2019-12-07 14:26:07 -08:00
Ross Brodbeck 5fdab2aaf2 Update octokit graphql type dependencies (#228)
* Update GraphQL support in base API
2019-12-06 07:52:04 -05:00
eric sciple 1c12ced7ba bump patch version (#239) 2019-12-04 11:03:36 -05:00
eric sciple 211b25966b Unit test (#236) 2019-12-03 14:18:54 -05:00
Ross Brodbeck d98e55434d Fix test timeouts (#235)
* Fix test timeouts
2019-12-03 13:55:39 -05:00
eric sciple 5c894298f2 toolrunner should which tool before invoking (#220) 2019-11-18 16:20:01 -05:00
Jan Jurzitza 9a3c005162 Clarify that extractTar extracts gzipped tars (#134)
The default downloadTool implementation strips the filename so when passing it to the extractTar function, it doesn't have a way of knowing the format without manually specifying it. However what the extractTar function arguments meant and how to specify them wasn't clear before reading the source code, so the documentation here got updated to reflect that.
2019-11-15 16:12:36 -05:00
Brian Surowiec 225370fc48 Fix remove-matcher syntax (#211) 2019-11-15 16:02:47 -05:00
eric sciple 28a7970270 Merge pull request #212 from actions/users/ericsciple/m161warnings
Update jest and lerna to fix npm install warnings
2019-11-12 12:20:32 -05:00
eric sciple 6c824bd448 Update jest and lerna to fix npm install warnings 2019-11-12 12:13:32 -05:00
Thomas Boop 47357ddfee Document Problem Matcher Commands (#198)
* Add Initial Problem Matcher docs
2019-11-06 16:24:16 -05:00
Josh Gross a465bf5e6d Fix slash in example release branch 2019-11-06 10:16:13 -05:00
Jim Hester 0fbdc19f81 Fix typo (#201) 2019-11-06 10:15:14 -05:00
Bryan MacFarlane 4f11810a00 doc tweak 2019-11-03 14:35:12 -05:00
Josh Gross 46c2a7e41a Fix some typos (#200) 2019-11-03 14:32:46 -05:00
Bryan MacFarlane 626bbe7136 doc tweak 2019-11-03 12:34:23 -05:00
Bryan MacFarlane 1e5fc20bfe update versioning guidance 2019-11-03 12:24:13 -05:00
Thomas Boop a9ebfb1a78 Add Warning about multiline secrets (#196) 2019-11-01 10:05:01 -04:00
Thomas Boop 4a3fe0bcd3 Quote the Commands in order to process on default windows (#191) 2019-10-23 11:06:34 -04:00
Thomas Boop 3d556ddb81 Overwrite tag rather then delete and create (#190) 2019-10-21 16:14:05 -04:00
Bryan MacFarlane a65441cf46 bump core for release and docs (#189) 2019-10-18 15:35:13 -04:00
Manuel Muñoz Solera 565d0bbe18 Adding missing curly Brace in Usage example (#150) 2019-10-14 10:59:46 -04:00
Josh Gross e8d384d3af Merge pull request #149 from actions/users/tihuang/statecommand
add core method to saveState and getState.
2019-10-10 20:04:39 -04:00
Josh Gross 55b188b8c6 Merge pull request #179 from dguo/patch-1
Fix a setup-node warning
2019-10-10 10:25:49 -04:00
Danny Guo 747fa4805a Fix a setup-node warning
setup-node currently outputs:

##[warning]Input 'version' has been deprecated with message: The version property will not be supported after October 1, 2019. Use node-version instead
2019-10-10 08:02:02 -04:00
Bryan MacFarlane 9c0a43bda4 Update issue templates 2019-10-09 09:17:19 -04:00
Bryan MacFarlane e984b2b6bb updating readmes 2019-10-09 09:16:07 -04:00
Bryan MacFarlane c2bb007435 Update issue templates 2019-10-09 09:09:24 -04:00
Bryan MacFarlane 2e4712de6f updating readmes 2019-10-09 08:47:27 -04:00
Tingluo Huang ae706665a1 PR feedback. 2019-10-03 14:48:21 -04:00
Bryan MacFarlane 7b46e3ab34 update readme 2019-10-03 13:51:11 -04:00
Bryan MacFarlane b2151226b6 update workflow paths 2019-10-03 13:07:22 -04:00
Bryan MacFarlane 1643ea2734 update readme 2019-10-03 12:45:11 -04:00
Tingluo Huang 5ce4932391 update doc. 2019-10-03 00:41:30 -04:00
Edward Thomson a1c30dfc53 Add a bug report issue template (#160)
Provide an issue template that will help people locate the GitHub Community forum for GitHub Actions.
2019-10-02 18:17:33 -04:00
Bryan MacFarlane f210cdb256 Update readme (#178)
Updating readme
2019-10-02 17:59:33 -04:00
Bryan MacFarlane 531da1858f fix test timeout (#176)
* fix test timeout
2019-10-02 08:18:38 -04:00
Bryan MacFarlane 9d54cd22ea setSecret (#174)
* setSecret
2019-10-01 17:13:05 -04:00
Bryan MacFarlane 713902387e updating core docs and bumping version (#172)
updating core docs and bumping version
2019-10-01 13:53:09 -04:00
Thomas Boop 05b1b08f77 Update command docs to specify a new line is needed (#171) 2019-10-01 12:56:09 -04:00
Harry Marr 47ccfea021 Add 'repository.directory' to package manifests (#143) 2019-09-24 22:30:51 -04:00
Chad Schulz 46bd5e54fd Wrap example in async function (#157)
As someone not too familiar with async/await JavaScript, I was hung up on this for a bit. If this is too distracting from the example itself, I can understand not integrating it.
2019-09-24 22:27:50 -04:00
Bryan MacFarlane 14ac06ecd8 endgroup typo 2019-09-24 22:14:11 -04:00
Bryan MacFarlane 9b019476db update commands doc 2019-09-24 21:08:02 -04:00
Bryan MacFarlane 1f7964519a bump tool-cache version 2019-09-24 17:20:01 -04:00
Bryan MacFarlane 67eeeea9fa use zip and unzip from path (#161) 2019-09-24 17:07:08 -04:00
Chad Schulz 3116829a9b Add missing } to token example (#153) 2019-09-24 13:49:32 -04:00
Even Alander 547d771ca3 Error in yaml (#158)
The word `you're` in the yaml example made the parsing fail, which baffled me for a while. One fix is to use `"` quotes.
2019-09-24 13:49:11 -04:00
Bryan MacFarlane 4897b2cd3b update typescript walkthrough link 2019-09-21 10:44:36 -04:00
Tingluo Huang 81b71dc6e6 fix lint. 2019-09-19 22:18:51 -04:00
Tingluo Huang 4d15218252 fix lint. 2019-09-19 22:14:12 -04:00
Tingluo Huang b62614fa25 add core method to saveState and getState. 2019-09-19 22:02:45 -04:00
Bryan MacFarlane e2358e2973 update core releases.md 2019-09-18 14:47:11 -04:00
Danny McCormick 14d6a0a2d2 Implement set-secret (#141)
* Implement set-secret

* Update RELEASES.md

* Feedback
2019-09-18 14:25:05 -04:00
Marc Nuri 6fcaac5046 actions/toolkit#127: getInput supports variables with multiple spaces (#129)
* actions/toolkit#127: getInput supports variables with multiple spaces

* actions/toolkit#127: PR comment, update changelog
2019-09-12 13:41:11 -04:00
Warren Buckley b297969f56 Update readme to show how to use the secret GITHUB_TOKEN (#131)
* Update readme to show how to use the secret GITHUB_TOKEN

* Adds in link to docs - which I feel seem to be scattered in repos & official docs
2019-09-12 10:50:19 -04:00
Bryan MacFarlane da04d22321 new template doc links 2019-09-11 03:35:39 -04:00
abelsquidhead 5fd70ca47a step 1. was slightly confusing. For someone that doesn't use git all the time, they might not know they need to go into the .gitignore file. The first time I read this I thought the direction was saying to rename the directory !node_modeules (#125) 2019-09-09 12:01:19 -04:00
Danny McCormick 2c6d31be8f Create commands.md (#105)
* Create commands.md

* Feedback

* Update action-debugging.md
2019-09-09 11:58:49 -04:00
Danny McCormick b3bf422391 Update RELEASES.md 2019-09-09 11:58:37 -04:00
damccorm 4ff3b554b8 Fix tests 2019-09-09 11:57:17 -04:00
Danny McCormick 3dcd65e44b Add github context getting started docs (#80)
* WIP - Add github context getting started docs

* Add testing docs

* Update walkthrough after going through it

* Clean up

* Add markdown types

* Env disclaimer

* Fix broken link to Javascript Action (#103)
2019-09-09 11:52:50 -04:00
Danny McCormick ef4525e9dd Add info for completeness (#111)
* Add info for completeness

* Linting

* Missing import
2019-09-09 11:46:48 -04:00
Danny McCormick a6e7249776 Update to use :: instead of ## (#110)
* Update to use :: instead of ##

* Missed test
2019-09-09 11:46:17 -04:00
Jonathan Leitschuh b529540e0c Don't recommend the use of HTTP to download code (#120) 2019-09-09 09:39:43 -04:00
abelsquidhead f689e65c54 added file name of action.yml file. When first starting out, everything is confusing and a first time actions creator might not know which file to update (#122) 2019-09-07 20:39:41 -04:00
Danny McCormick 7bc0d5222f Need right push target (#119) 2019-09-06 12:50:52 -04:00
Danny McCormick 7f17a6e550 Update RELEASES.md 2019-09-05 16:04:32 -04:00
Danny McCormick 6160df50dc Update RELEASES.md 2019-09-05 13:27:22 -04:00
Jonathan Clem a2ab4bcf78 Publish
Main workflow / Run macOS (push) Has been cancelled
Main workflow / Run Windows (push) Has been cancelled
Main workflow / Run Ubuntu (push) Has been cancelled
- @actions/core@1.1.0
 - @actions/exec@1.0.1
 - @actions/github@1.1.0
 - @actions/io@1.0.1
 - @actions/tool-cache@1.1.1
2019-09-05 11:03:19 -04:00
Jonathan Clem 7772d5f810 Merge pull request #113 from actions/client-options
Accept Octokit.Options in the GitHub constructor
2019-09-05 10:58:15 -04:00
Jonathan Clem 1c4866fa48 Add note about constructor options 2019-09-05 10:52:34 -04:00
Jonathan Clem ebace7edd3 Bump TypeScript to 3.6.2 2019-09-05 10:03:10 -04:00
Jonathan Clem e533651251 Accept Octokit.Options in the GitHub constructor 2019-09-05 09:54:27 -04:00
Jonathan Clem eb4c32847c Merge pull request #98 from actions/core-debug
Core debug
2019-09-05 09:47:05 -04:00
Bryan MacFarlane 020f7034f4 JavaScript walk through update (#107)
* update walkthrough

* review feedback
2019-09-04 17:30:45 -04:00
damccorm 2a1b7d5c7e Merge branch 'master' of https://github.com/actions/toolkit 2019-09-04 14:04:40 -04:00
damccorm eaba9217f8 Bump package version 2019-09-04 14:04:31 -04:00
Danny McCormick f2d01998f0 Update RELEASES.md 2019-09-03 14:42:04 -04:00
Danny McCormick 99d3ad0a64 Use readFileSync instead of require (#101)
* Use readFileSync instead of require

* error handling
2019-08-30 13:02:45 -04:00
Guillaume Clochard ac36ca4405 Small fix for the tool-cache extract example (#99)
* Fix tool-cache extract example

* Use extractZip or extract7z in tool-cache example fix
2019-08-29 11:08:06 -04:00
Jonathan Clem 92e6443cf0 End group in core.group regardless of error thrown 2019-08-28 22:47:37 -04:00
Jonathan Clem 8f9992ca17 Add assertion for return value of core.group 2019-08-28 22:38:30 -04:00
Jonathan Clem 80fc75ef9c Fix readme 2019-08-28 22:36:17 -04:00
Jonathan Clem 8b9dfa809b Add group functions to core 2019-08-28 22:35:27 -04:00
dependabot[bot] e35e0e640b Bump mixin-deep from 1.3.1 to 1.3.2 (#95)
Bumps [mixin-deep](https://github.com/jonschlinkert/mixin-deep) from 1.3.1 to 1.3.2.
- [Release notes](https://github.com/jonschlinkert/mixin-deep/releases)
- [Commits](https://github.com/jonschlinkert/mixin-deep/compare/1.3.1...1.3.2)

Signed-off-by: dependabot[bot] <support@github.com>
2019-08-28 09:45:33 -04:00
dependabot[bot] ccf748b53b Bump lodash.template from 4.4.0 to 4.5.0 (#94)
Bumps [lodash.template](https://github.com/lodash/lodash) from 4.4.0 to 4.5.0.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.4.0...4.5.0)

Signed-off-by: dependabot[bot] <support@github.com>
2019-08-28 09:35:15 -04:00
dependabot[bot] 8caeee5d56 Bump tar from 2.2.1 to 2.2.2 (#93)
Bumps [tar](https://github.com/npm/node-tar) from 2.2.1 to 2.2.2.
- [Release notes](https://github.com/npm/node-tar/releases)
- [Commits](https://github.com/npm/node-tar/compare/v2.2.1...v2.2.2)

Signed-off-by: dependabot[bot] <support@github.com>
2019-08-28 09:34:59 -04:00
dependabot[bot] b26ef29d75 Bump lodash from 4.17.11 to 4.17.15 (#92)
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.11 to 4.17.15.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.11...4.17.15)

Signed-off-by: dependabot[bot] <support@github.com>
2019-08-28 09:34:46 -04:00
dependabot[bot] fba68de49e Bump fstream from 1.0.11 to 1.0.12 (#91)
Bumps [fstream](https://github.com/npm/fstream) from 1.0.11 to 1.0.12.
- [Release notes](https://github.com/npm/fstream/releases)
- [Commits](https://github.com/npm/fstream/compare/v1.0.11...v1.0.12)

Signed-off-by: dependabot[bot] <support@github.com>
2019-08-28 09:34:34 -04:00
dependabot[bot] df04d7dbaf Bump eslint-utils from 1.3.1 to 1.4.2 (#90)
Bumps [eslint-utils](https://github.com/mysticatea/eslint-utils) from 1.3.1 to 1.4.2.
- [Release notes](https://github.com/mysticatea/eslint-utils/releases)
- [Commits](https://github.com/mysticatea/eslint-utils/compare/v1.3.1...v1.4.2)

Signed-off-by: dependabot[bot] <support@github.com>
2019-08-28 09:34:20 -04:00
AEnterprise 37202e8dbc fix syntax error in output example (#84) 2019-08-26 08:41:40 -04:00
Bryan MacFarlane 7cd421b8bc lint fixes 2019-08-25 00:55:22 -04:00
Bryan MacFarlane 17a0aa40f3 taking out some tests 2019-08-25 00:26:39 -04:00
Bryan MacFarlane b7ae833847 bit of branding 2019-08-24 09:24:48 -04:00
Bryan MacFarlane a2078cf37c raising not implemented earlier 2019-08-24 09:22:38 -04:00
Bryan MacFarlane 4ebc9007c0 removing not implemented function from docs 2019-08-24 09:17:35 -04:00
Bryan MacFarlane f66f5629b3 updating keywords 2019-08-23 17:39:40 -04:00
Sven-Hendrik Haase ac007c0698 Fix path (#78)
The current one seems like an uncommon Windows-like notation.
2019-08-23 10:42:51 -04:00
Danny McCormick 6b9630ac94 Add RELEASES.md for each package, bump tool-cache to publish (#67) 2019-08-22 13:14:49 -04:00
Akshay Iyyadurai Balasundaram e54c7a866d Fix: wrong spelling for using action in a workflow (#76) 2019-08-22 13:13:36 -04:00
Scott Brenner 750d949f10 container-action typo fix (#77) 2019-08-22 13:12:47 -04:00
Sven-Hendrik Haase 28803fc3b4 Correct wrong docs (#75)
Docs say it's `using:` but it's actually `uses:`.
2019-08-21 22:31:21 -04:00
Josh Soref ad054c855d Spelling (#72)
* spelling: check in

* spelling: compatibility

* spelling: definitely

* spelling: does not

* spelling: maintaining

* spelling: nonexistent

* spelling: precede

* spelling: response

* spelling: was not
2019-08-21 15:31:44 -04:00
Danny McCormick 7d605994f9 Comment nit 2019-08-21 12:54:24 -04:00
Jason Etcovitch e7914df1c6 Merge pull request #70 from actions/highlight-codeblocks
Highlight codeblocks in markdown files
2019-08-21 07:52:24 -07:00
Jason Etcovitch 1d687b2170 tool-cache/README.md 2019-08-21 01:18:31 -04:00
Jason Etcovitch a796c65f64 io/README.md 2019-08-21 01:18:21 -04:00
Jason Etcovitch 5e3e440c7f exec/README.md 2019-08-21 01:18:00 -04:00
Jason Etcovitch ea2ffbe002 Some more in the docs 2019-08-21 01:16:51 -04:00
Jason Etcovitch db9fd45770 Missed one 2019-08-21 01:16:47 -04:00
Jason Etcovitch 770cf14bde Highlight codeblocks in core/README.md 2019-08-20 22:15:05 -07:00
James M. Greene 00fc8b2580 Fix secrets reference syntax in README (#68) 2019-08-20 15:46:20 -04:00
Sören Wegener e2adf403d6 Typofix in markdown (#63) 2019-08-18 19:38:45 -04:00
Kenny Root 35ed15faaf Fix typo in README.md (#62)
Missed a "k" in "Walkthrough."
2019-08-18 19:28:10 -04:00
James M. Greene 9821b26794 Fix syntax mistake in README (#55) 2019-08-13 18:13:12 -04:00
Danny McCormick 8662b07822 Remove format step 2019-08-13 16:03:45 -04:00
Alif Rachmawadi 2c3e55b8c9 Add more supports for tar extensions (#48)
* added test for extracting .tar.gz

* added ability to extract .tar.xz

* add flags to extract tar

* make use of tempPath

* check file contents and make different content for tar file
2019-08-13 14:26:14 -04:00
Alif Rachmawadi 35cd59e8d5 Added unzip for darwin (#49)
* added unzip for darwin

* add mac builds

* added zip for darwin
2019-08-13 12:39:01 -04:00
Thomas Boop eae6c87114 Add Action Debugging information (#46)
* Add Action Debugging information

* Small verbiage updates

* Update README.md

* minor grammar updates
2019-08-12 17:00:55 -04:00
Danny McCormick 938549d01a Add status badge 2019-08-12 15:09:43 -04:00
Danny McCormick 079812ed8a Add github package to readme 2019-08-12 14:28:37 -04:00
Bryan MacFarlane ac5434c423 update some naming 2019-08-09 15:06:50 -04:00
Danny McCormick 4c46ecfd35 Add debug info 2019-08-09 13:55:59 -04:00
Sascha Wolf 534e4012a4 Docs: Fix link to the versioning file (#45)
The link to `action-versioning.md` was relative which lead to appending an additional `docs` folder to the current path which linked to `docs/docs/action-versioning.md` instead of `docs/action-versioning.md`.

By adding the leading `/` the link now works correctly.
2019-08-09 07:58:16 -04:00
Curtis Gibby 71fe4a8f36 Fix bad apostrophes (#44)
A possessive belonging to an "it" doesn't need an apostrophe. Don't believe me? Ask [the Oatmeal](http://theoatmeal.com/comics/apostrophe) (look for the velociraptor)!
2019-08-08 13:40:24 -04:00
Gregor Martynus aec0ef46e4 [@actions/github] Fix code example, add syntax highlighting (#43)
* [@actions/github] Fix code example, add syntax highlighting

* [@actions/github] add link to @octkit/graphql API

* [@octokit/github] moar syntax highlighting in README
2019-08-07 17:23:44 -04:00
57 changed files with 6926 additions and 2048 deletions
+38
View File
@@ -0,0 +1,38 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.
@@ -0,0 +1,25 @@
---
name: Feature Request
about: Create a request to help us improve
title: ''
labels: enhancement
assignees: ''
---
Thank you 🙇‍♀ for wanting to create an issue in this repository. Before you do, please ensure you are filing the issue in the right place. Issues should only be opened on if the issue **relates to code in this repository**.
* If you have found a security issue [please submit it here](https://hackerone.com/github)
* If you have questions about writing workflows or action files, then please [visit the GitHub Community Forum's Actions Board](https://github.community/t5/GitHub-Actions/bd-p/actions)
* If you are having an issue or question about GitHub Actions then please [contact customer support](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/about-github-actions#contacting-support)
If your issue is relevant to this repository, please include the information below:
**Describe the enhancement**
A clear and concise description of what the features or enhancement you need.
**Code Snippet**
If applicable, add a code snippet to show the api enhancement.
**Additional information**
Add any other context about the feature here.
+47
View File
@@ -0,0 +1,47 @@
name: toolkit-unit-tests
on:
push:
paths-ignore:
- '**.md'
pull_request:
paths-ignore:
- '**.md'
jobs:
build:
name: Build
strategy:
matrix:
runs-on: [ubuntu-latest, macOS-latest, windows-latest]
fail-fast: false
runs-on: ${{ matrix.runs-on }}
steps:
- name: Checkout
uses: actions/checkout@v1
- name: Set Node.js 12.x
uses: actions/setup-node@master
with:
node-version: 12.x
- name: npm install
run: npm install
- name: Bootstrap
run: npm run bootstrap
- name: Compile
run: npm run build
- name: npm test
run: npm test
- name: Lint
run: npm run lint
- name: Format
run: npm run format-check
-58
View File
@@ -1,58 +0,0 @@
name: Main workflow
on: [push]
jobs:
Ubuntu:
name: Run Ubuntu
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@master
- name: Set Node.js 10.x
uses: actions/setup-node@master
with:
version: 10.x
- name: npm install
run: npm install
- name: Bootstrap
run: npm run bootstrap
- name: Compile
run: npm run build
- name: npm test
run: npm test
- name: Lint
run: npm run lint
- name: Format
run: npm run format-check
Windows:
name: Run Windows
runs-on: windows-latest
steps:
- name: Checkout
uses: actions/checkout@master
- name: Set Node.js 10.x
uses: actions/setup-node@master
with:
version: 10.x
- name: npm install
run: npm install
- name: Bootstrap
run: npm run bootstrap
- name: Compile
run: npm run build
# TODO: This currently ignores exec due to issues with Node and spawning on Windows, which I think is exacerbated by Jest.
# It doesn't seem to affect behavior in actions themselves, just when testing with Jest.
# See other similar issues here: https://github.com/nodejs/node/issues/25484
- name: npm test
run: npm run test-ci
+76
View File
@@ -0,0 +1,76 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to make participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies within all project spaces, and it also applies when
an individual is representing the project or its community in public spaces.
Examples of representing a project or community include using an official
project e-mail address, posting via an official social media account, or acting
as an appointed representative at an online or offline event. Representation of
a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at opensource@github.com. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq
+161 -16
View File
@@ -3,35 +3,180 @@
<img src="res/at-logo.png">
</p>
## Toolkit
<p align="center">
<a href="https://github.com/actions/toolkit"><img alt="GitHub Actions status" src="https://github.com/actions/toolkit/workflows/toolkit-unit-tests/badge.svg"></a>
</p>
The toolkit provides a set of packages to make creating actions easier and drive consistency.
## GitHub Actions Toolkit
The GitHub Actions ToolKit provides a set of packages to make creating actions easier.
<br/>
<h3 align="center">Get started with the <a href="https://github.com/actions/javascript-action">javascript-action template</a>!</h3>
<br/>
## Packages
The toolkit provides four separate packages. Since actions are run by pulling actions from the github graph, dependencies including the packages are vendored into your action.
:heavy_check_mark: [@actions/core](packages/core)
| Package | Description |
| ------- | ----------- |
| [@actions/core](packages/core) | Core functions for getting inputs, setting outputs, setting results, logging, secrets and environment variables |
| [@actions/exec](packages/exec) | Functions necessary for running tools on the command line |
| [@actions/io](packages/io) | Core functions for CLI filesystem scenarios |
| [@actions/tool-cache](packages/tool-cache) | Functions necessary for downloading and caching tools |
Provides functions for inputs, outputs, results, logging, secrets and variables. Read more [here](packages/core)
```bash
$ npm install @actions/core --save
```
<br/>
:runner: [@actions/exec](packages/exec)
Provides functions to exec cli tools and process output. Read more [here](packages/exec)
```bash
$ npm install @actions/exec --save
```
<br/>
:pencil2: [@actions/io](packages/io)
Provides disk i/o functions like cp, mv, rmRF, find etc. Read more [here](packages/io)
```bash
$ npm install @actions/io --save
```
<br/>
:hammer: [@actions/tool-cache](packages/tool-cache)
Provides functions for downloading and caching tools. e.g. setup-* actions. Read more [here](packages/tool-cache)
```bash
$ npm install @actions/tool-cache --save
```
<br/>
:octocat: [@actions/github](packages/github)
Provides an Octokit client hydrated with the context that the current action is being run in. Read more [here](packages/github)
```bash
$ npm install @actions/github --save
```
<br/>
## Creating an Action with the Toolkit
Actions are units of work which can either run in a container or on the host machine.
:question: [Choosing an action type](docs/action-types.md)
[Choosing an action type](docs/action-types.md): Outlines the differences and why you would want to create a host or a container based action.
Outlines the differences and why you would want to create a JavaScript or a container based action.
<br/>
<br/>
[JavaScript Action Walthrough](docs/node12-action.md): Create an action which runs on the host using the toolkit
:curly_loop: [Versioning](docs/action-versioning.md)
[Docker Action Walkthrough](docs/container-action.md): Create an action that is delivered as a container and run with docker.
Actions are downloaded and run from the GitHub graph of repos. This contains guidance for versioning actions and safe releases.
<br/>
<br/>
[Docker Action Walkthrough with Octokit](docs/container-action-toolkit.md): Create an action that is delivered as a container which uses the toolkit. This example uses the GitHub context to construct an Octokit client.
:warning: [Problem Matchers](docs/problem-matchers.md)
[Versioning](docs/action-versioning.md): Recommendations on versioning, releases and tagging your action.
Problem Matchers are a way to scan the output of actions for a specified regex pattern and surface that information prominently in the UI.
<br/>
<br/>
<h3><a href="https://github.com/actions/hello-world-javascript-action">Hello World JavaScript Action</a></h3>
Illustrates how to create a simple hello world javascript action.
```javascript
...
const nameToGreet = core.getInput('who-to-greet');
console.log(`Hello ${nameToGreet}!`);
...
```
<br/>
<h3><a href="https://github.com/actions/javascript-action">JavaScript Action Walkthrough</a></h3>
Walkthrough and template for creating a JavaScript Action with tests, linting, workflow, publishing, and versioning.
```javascript
async function run() {
try {
const ms = core.getInput('milliseconds');
console.log(`Waiting ${ms} milliseconds ...`)
...
```
```javascript
PASS ./index.test.js
throws invalid number
wait 500 ms
test runs
Test Suites: 1 passed, 1 total
Tests: 3 passed, 3 total
```
<br/>
<h3><a href="https://github.com/actions/typescript-action">TypeScript Action Walkthrough</a></h3>
Walkthrough creating a TypeScript Action with compilation, tests, linting, workflow, publishing, and versioning.
```javascript
import * as core from '@actions/core';
async function run() {
try {
const ms = core.getInput('milliseconds');
console.log(`Waiting ${ms} milliseconds ...`)
...
```
```javascript
PASS ./index.test.js
throws invalid number
wait 500 ms
test runs
Test Suites: 1 passed, 1 total
Tests: 3 passed, 3 total
```
<br/>
<br/>
<h3><a href="docs/container-action.md">Docker Action Walkthrough</a></h3>
Create an action that is delivered as a container and run with docker.
```docker
FROM alpine:3.10
COPY LICENSE README.md /
COPY entrypoint.sh /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
```
<br/>
<h3><a href="https://github.com/actions/container-toolkit-action">Docker Action Walkthrough with Octokit</a></h3>
Create an action that is delivered as a container which uses the toolkit. This example uses the GitHub context to construct an Octokit client.
```docker
FROM node:slim
COPY . .
RUN npm install --production
ENTRYPOINT ["node", "/lib/main.js"]
```
```javascript
const myInput = core.getInput('myInput');
core.debug(`Hello ${myInput} from inside a container`);
const context = github.context;
console.log(`We can even get context data, like the repo: ${context.repo.repo}`)
```
<br/>
## Contributing
We welcome contributions. See [how to contribute](docs/contribute.md).
We welcome contributions. See [how to contribute](docs/contribute.md).
## Code of Conduct
See [our code of conduct](CODE_OF_CONDUCT.md).
+3
View File
@@ -0,0 +1,3 @@
If you discover a security issue in this repo, please submit it through the [GitHub Security Bug Bounty](https://hackerone.com/github)
Thanks for helping make GitHub Actions safe for everyone.
+26
View File
@@ -0,0 +1,26 @@
# Debugging
If the build logs do not provide enough detail on why a build may be failing, some other options exist to assist with troubleshooting.
## Runner Diagnostic Logs
Runner Diagnostic Logs provide additional log files detailing how the Runner is executing an action.
Each file contains different logging information that corresponds to that process:
* The Runner process coordinates setting up workers to execute jobs.
* The Worker process executes the job.
These files contain the prefix `Runner_` or `Worker_` to indicate the log source.
### How to Access Runner Diagnostic Logs
These log files are enabled by [setting the secret](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/creating-and-using-encrypted-secrets#creating-encrypted-secrets) `ACTIONS_RUNNER_DEBUG` to `true`.
All actions ran while this secret is enabled contain additional diagnostic log files in the `runner-diagnostic-logs` folder of the [log archive](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/managing-a-workflow-run#downloading-logs-and-artifacts).
## Step Debug Logs
Step debug logs increase the verbosity of a job's logs during and after a job's execution to assist with troubleshooting.
Additional log events with the prefix `::debug::` will now also appear in the job's logs.
### How to Access Step Debug Logs
This flag can be enabled by [setting the secret](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/creating-and-using-encrypted-secrets#creating-encrypted-secrets) `ACTIONS_STEP_DEBUG` to `true`.
All actions ran while this secret is enabled will show debug events in the [Downloaded Logs](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/managing-a-workflow-run#downloading-logs-and-artifacts) and [Web Logs](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/managing-a-workflow-run#viewing-logs-to-diagnose-failures).
+4 -4
View File
@@ -3,7 +3,7 @@
There are two types of actions. JavaScript and Docker actions.
- **JavaScript Actions**: JavaScript actions run on the host machine. The unit of work is decoupled from the environment.
- **Docker Actions**: A container action is a container which carries both the unit of work along with the environment and it's dependencies packaged up as a container.
- **Docker Actions**: A container action is a container which carries both the unit of work along with the environment and its dependencies packaged up as a container.
Both have access to the workspace and the github event payload and context.
@@ -11,7 +11,7 @@ Both have access to the workspace and the github event payload and context.
Docker actions carry both the unit of work and the environment.
This creates a more consistent and reliable unit of work where the consumer of the action does not need to worry about the toolsets and it's dependencies.
This creates a more consistent and reliable unit of work where the consumer of the action does not need to worry about the toolsets and its dependencies.
Docker actions are currently limited to Linux only.
@@ -19,7 +19,7 @@ Docker actions are currently limited to Linux only.
JavaScript actions decouple the unit of work from the environment and run directly on the host machine or VM.
Consider a simple example of testing a node lib on node 8, 10 and running a custom action. Each job will setup a node version on the host and custom-action will run it's unit of work on each environment (node8+ubunut16, node8+windows-2019, etc.)
Consider a simple example of testing a node lib on node 8, 10 and running a custom action. Each job will setup a node version on the host and custom-action will run its unit of work on each environment (node8+ubuntu16, node8+windows-2019, etc.)
```yaml
on: push
@@ -42,4 +42,4 @@ jobs:
- uses: actions/custom-action@master
```
JavaScript actions work on any environment that host action runtime is supported on which is currently node 12. However, a host action that runs a toolset expects the environment that it's running on to have that toolset in it's PATH or using a setup-* action to acquire it on demand.
JavaScript actions work on any environment that host action runtime is supported on which is currently node 12. However, a host action that runs a toolset expects the environment that it's running on to have that toolset in its PATH or using a setup-* action to acquire it on demand.
+46 -28
View File
@@ -1,43 +1,61 @@
# Versioning
Actions are downloaded and run from the GitHub graph of repos. The workflow references an action use a ref.
Actions are downloaded and run from the GitHub graph of repos. The workflow references an action using a ref.
Examples:
```yaml
steps:
- uses: actions/setup-node@74bc508
- uses: actions/setup-node@v1
- uses: actions/setup-node@master # not recommended
- uses: actions/javascript-action@v1 # recommended. starter workflows use this
- uses: actions/javascript-action@v1.0.0 # if an action offers specific releases
- uses: actions/javascript-action@41775a4 # binding to a specific sha
```
Binding to the immutable sha1 of a released version is the safest for stability and security.
# Compatibility
Binding to a specific major version allows for receiving critical fixes and security patches while still mainting compatibility and the assurance your workflow should still work.
Binding to a major version is the latest of that major version ( e.g. `v1` == "1.*" )
Binding to master is convenient but if a new major version is released which breaks compatilibility, your workflow could break.
Major versions should guarantee compatibility. A major version can add new capabilities but should not break existing input compatibility or break existing workflows.
Major version binding allows you to take advantage of bug fixes and critical functionality and security fixes. The `master` branch has the latest code and is unstable to bind to. Changes are committed to master before the changes are ready to be released to the marketplace by creating a tag. In addition, a new major version may break compatibility will get implemented in master after branching off the previous major version.
> Warning: do not reference `master` since that is the latest code and may contain breaking changes of the next major version.
```yaml
steps:
- uses: actions/javascript-action@master # do not do this
```
Binding to the immutable sha1 may offer more reliability. However, note that the hosted images toolsets (e.g. ubuntu-latest) move forward and if there is a tool breaking issue, actions may react with a patch to a major version to compensate so binding to a specific SHA may prevent you from getting fixes.
> Recommendation: bind to major versions to get functionality and fixes but reserve binding to a specific release or SHA as a mitigation strategy for unforeseen breaks.
# Recommendations
1. **Don't check node_modules into master**: This will discourage people from attaching to master since the action will fail. You can enforce this by including `node_modules` in your `.gitignore` file.
2. **Create a release branch for each major version**: This will act as an alpha release for that major version. Any time you are ready to publish a new version from master, you should pull those changes into this branch (following the same steps listed below).
```
git checkout -b releases/v1 # If this branch already exists, omit the -b flag
rm -rf node_modules
sed -i '/node_modules/d' .gitignore # Bash command that removes node_modules from .gitignore
npm install --production
git add node_modules .gitignore
git commit -m node_modules
git push origin releases/v1
```
3. **When ready for a stable release, add a major version tag**: Move the major version tag (v1, v2, etc.) to point to the ref of the current release. This will act as the stable release for that major version. You should keep this tag updated to the most recent stable minor/patch release.
```
git checkout releases/v1
git push origin :refs/tags/v1
git tag -fa v1 -m "Update v1 tag"
git push origin v1
```
4. **Create releases for minor and patch version updates**: From the GitHub UI create a release for each minor or patch version update titled with that release version (e.g. v1.2.3).
5. **Compatibility Breaks**: introduce a new major version branch (releases/v2) and tag (v2) if changes will break existing workflows. For example, changing inputs.
1. **Create a GitHub release for each specific version**: Creating a release like [ v1.0.0 ](https://github.com/actions/javascript-action/releases/tag/v1.0.0) allows users to bind back to a specific version if an issue is encountered with the latest major version.
See [Git-Basics-Tagging](https://git-scm.com/book/en/v2/Git-Basics-Tagging)
2. **Publish the specific version to the marketplace**: When you release a specific version, choose the option to "Publish this release to the GitHub Marketplace".
3. **Make the new release available to those binding to the major version tag**: Move the major version tag (v1, v2, etc.) to point to the ref of the current release. This will act as the stable release for that major version. You should keep this tag updated to the most recent stable minor/patch release.
```
git tag --force --annotate -m "Update v1 tag" v1
git push --force origin v1
```
# Major Versions
All releases for a major version should hold compat including input compatibility and behavior compatibility.
Introduce a major version for compatibility breaks and major rewrites of the action.
Ideally, a major version would carry other benefits to the user to entice them to upgrade their workflows. Since updating their workflows will need to be done with an understanding of the changes and what compatibility was broken, introducing a new major version shouldn't be taken lightly.
To get feedback and to set expectations, the new major version can be initially released with `v2-beta` tag to indicate you can try it out but it's still going under some churn. Upon release the `-beta` can be dropped and there's an expectation of compatibility from that point forward.
[An example of v2-beta with checkout](https://github.com/actions/checkout/tree/c170eefc2657d93cc91397be50a299bff978a052#checkout-v2-beta)
# Sample Workflow
This illustrates the versioning workflow covered above.
![versioning](assets/action-releases.png)
+1
View File
@@ -0,0 +1 @@
<mxfile modified="2019-12-12T18:56:00.899Z" host="www.draw.io" agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36" etag="CISsL8yLQ-3TSrXBbF_M" version="12.3.8" type="device" pages="1"><diagram name="Page-1" id="ff44883e-f642-bcb2-894b-16b3d25a3f0b">7VvbctsqFP0aP9YjkISkx9hJcx7amZ7mTNs8dYiEZVpZeBC+9esPyMi64UsdW3Ymdh4Mm5tgrbU3AqdnDyfLR46n488sIkkPWtGyZ9/3IASWi+SXsqzWFhf5a0PMaaQrlYYn+ocULbV1RiOS1SoKxhJBp3VjyNKUhKJmw5yzRb3aiCX1Uac4Ji3DU4iTwtp3S/t3GomxtgMUlAX/EBqP9eA+1FN+weHvmLNZqkfsQXuUf9bFE1z0paeajXHEFhWT/dCzh5wxsU5NlkOSqNUtFq5oJ1bF0/bswVhMEpkBMpkXf9zSGBzSWE6Ok1RUh9vW38h6CcMgsC2HoJGLPwSt/kkkV1ZnU5bKr0E443MS6RErg2cCc6HJIJlgD0ga3Sk0ZT5McJbRcG38SJOijcxVW2SCs99kyBLG8+FtK/9sSgos1bRHLN2MBvOuBF/92PQrM88yY0kq6Oy94re1ya2K3JKKH5V0pZXMlY1UpmjTXucCGTbjITEvLtRKwDwmwlxF96IWvdKtxu6RsAmRTy4rcJJgQed1EWCtmnhTb9P0C6PyQaGlFe4UQtX69jyr3sV6GrpVyR+ZqDxGacpZdRjDADo1xfaxaSdnTNjvRH7Dsgqxnquk28Kyoxhjd0MH5NfpgNwGHdaUPQsd1gPNcTIrHFqLHkki44ZixWJMBXma4ny5FjJ01cmRe+2cNTn4hWMGJr+C8k+LI06dI7bqCCc0TpULk8gRripIwlW6GrnqbxfEc8IFWe5EUJfCBhJuIcxFGcSKKuNq+LK2Y15D62+gaUeqa1PqHi99bEQ4Sqtov3eH3cjZDRoksjuTM2zJud/vX1TR4MKKdrzrUbR9Xc720tB4Th0aH14OGucGTQ2apgu7ZBx0bnHwL+Kgsz8Oom7ioGdfLA6im5yrckbO5eQc/+T3377+Gz/Mp9/++5SxT+Hwg/G1AyVy1MGLTMR5guM0lM92V5TIgTaF8htPFE7pS6a+JFMJzkgm68zbZzQSs6lKhjhJ2Ezsh3xKOJUTVbAUjb6UpsGUZVRQlsJcxcA/kc9FjUMBpw0SgF2idNuv1AACln25DYsRoFtk3BUZd3F6V3js6NTHQ6DvAdeDvufK2OgFdW45xtI9B4Tto6XGRg6hs8Vg43L7+x19yAkWZL2eyon3lKoGOUbxVvdvgT7o25u+eCUw1Hvf2UnnsULCeBJXFDjNWAHasQJ1GtGtW7DYhdDlt1xtrr+xYHG80y/IeW6X7lsNbwsbaB7qtv3GqRUC3bptELTUPMGZEk2TQ1IOokmVqkI1r6pK06aWGpW4qHShd7pgQqMo2eYt6i4ip5d+qIbyFVgn2f017uyQZdieewZFg2A7jV6laNh2ubvfg94PVm5jo24f+CrV1OvpsDrkoq28rtfLHuFsbPLGBy/ZXodXWRDXsB6F7ZV+ETSu1bymOzvULwLg7enpdI4xcFf8bjX8NcOfvybsz+Pz/XffcMFysv2s1bfe3H7WLvezLXUaCLn93rVx9uEEBsGeaz9rBNp09nEKoOvnVvAkqLddydtA3XP9K0PddA10HtTBu0U9gAdovWBGJ6i753PqoA/frVNH1rU59fa10ASnqxyoZYmt3D1bhl9NXOPu+fUgOY5bA8kFBpDsM22VjSB5LZCGcs6YpgoXMVbSk5tRkqlt4QT/kgudL0Em+XulmJ0iVDbPcQ3HSYHbIUqG874RVSKi6QamjPA5DWkay7S+zoMWG6mJczKnbJa9JwiDxqtLYFKa6UjwCAxltvwJ/Prdp/xPA/vhfw==</diagram></mxfile>
Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

+137
View File
@@ -0,0 +1,137 @@
# :: Commands
The [core toolkit package](https://github.com/actions/toolkit/tree/master/packages/core) offers a number of convenience functions for
setting results, logging, registering secrets and exporting variables across actions. Sometimes, however, its useful to be able to do
these things in a script or other tool.
To allow this, we provide a special `::` syntax which, if logged to `stdout` on a new line, will allow the runner to perform special behavior on
your commands. The following commands are all supported:
### Set an environment variable
To set an environment variable for future out of process steps, use `::set-env`:
```sh
echo "::set-env name=FOO::BAR"
```
Running `$FOO` in a future step will now return `BAR`
This is wrapped by the core exportVariable method which sets for future steps but also updates the variable for this step
```javascript
export function exportVariable(name: string, val: string): void {}
```
### PATH Manipulation
To prepend a string to PATH, use `::addPath`:
```sh
echo "::add-path::BAR"
```
Running `$PATH` in a future step will now return `BAR:{Previous Path}`;
This is wrapped by the core addPath method:
```javascript
export function addPath(inputPath: string): void {}
```
### Set outputs
To set an output for the step, use `::set-output`:
```sh
echo "::set-output name=FOO::BAR"
```
Running `steps.[step-id].outputs.FOO` in your Yaml will now give you `BAR`
```yaml
steps:
- name: Set the value
id: step_one
run: echo "::set-output name=FOO::BAR"
- name: Use it
run: echo ${{ steps.step_one.outputs.FOO }}
```
This is wrapped by the core setOutput method:
```javascript
export function setOutput(name: string, value: string): void {}
```
### Register a secret
If a script or action does work to create a secret at runtime, it can be registered with the runner to be masked in logs.
To mask a value in the logs, use `::add-mask`:
```sh
echo "::add-mask::mysecretvalue"
```
This is wrapped by the core setSecret method
```javascript
function setSecret(secret: string): void {}
```
Now, future logs containing BAR will be masked. E.g. running `echo "Hello FOO BAR World"` will now print `Hello FOO **** World`.
**WARNING** The add-mask and setSecret commands only support single line secrets. To register a multiline secrets you must register each line individually otherwise it will not be masked.
**WARNING** Do **not** mask short values if you can avoid it, it could render your output unreadable (and future steps' output as well).
For example, if you mask the letter `l`, running `echo "Hello FOO BAR World"` will now print `He*********o FOO BAR Wor****d`
### Group and Ungroup Log Lines
Emitting a group with a title will instruct the logs to create a collapsable region up to the next ungroup command.
```bash
echo "::group::my title"
echo "::endgroup::"
```
This is wrapped by the core methods:
```javascript
function startGroup(name: string): void {}
function endGroup(): void {}
```
### Problem Matchers
Problems matchers can be used to scan a build's output to automatically surface lines to the user that matches the provided pattern. A file path to a .json Problem Matcher must be provided. See [Problem Matchers](problem-matchers.md) for more information on how to define a Problem Matcher.
```bash
echo "::add-matcher::eslint-compact-problem-matcher.json"
echo "::remove-matcher owner=eslint-compact::"
```
`add-matcher` takes a path to a Problem Matcher file
`remove-matcher` removes a Problem Matcher by owner
### Save State
Save state to be used in the corresponding wrapper (finally) post job entry point.
```bash
echo "::save-state name=FOO::foovalue"
```
### Log Level
Finally, there are several commands to emit different levels of log output:
| log level | example usage |
|---|---|
| [debug](action-debugging.md) | `echo "::debug::My debug message"` |
| warning | `echo "::warning::My warning message"` |
| error | `echo "::error::My error message"` |
### Command Prompt
CMD processes the `"` character differently from other shells when echoing. In CMD, the above snippets should have the `"` characters removed in order to correctly process. For example, the set output command would be:
```cmd
echo ::set-output name=FOO::BAR
```
+2 -2
View File
@@ -14,7 +14,7 @@ Complete creating your repo and clone the repo.
> NOTE: The location of the repo will be how users will reference your action in their workflow file with the using keyword.
e.g. To use https://github.com/actions/setup-node, user's will author:
e.g. To use https://github.com/actions/setup-node, users will author:
```yaml
steps:
@@ -64,7 +64,7 @@ $ git push
The runner will download the action and build the docker container on the fly at runtime.
> Consider versioning your actions with tags. See [versioning](docs/action-versioning.md)
> Consider versioning your actions with tags. See [versioning](/docs/action-versioning.md)
+265 -77
View File
@@ -1,100 +1,288 @@
# Github Package
# Creating an Action using the GitHub Context
In order to support using actions to interact with GitHub, I propose adding a `github` package to the toolkit.
## Goal
Its main purpose will be to provide a hydrated GitHub context/Octokit client with some convenience functions. It is largely pulled from the GitHub utilities provided in https://github.com/JasonEtco/actions-toolkit, though it has been condensed.
In this walkthrough we will learn how to build a basic action using GitHub context data to greet users when they open an issue or PR. In the process we will explore how to access this context and how to make authenticated requests to the GitHub API.
### Spec
Note that a complete version of this action can be found at https://github.com/damccorm/issue-greeter.
##### interfaces.ts
## Prerequisites
This walkthrough assumes that you have gone through the basic [javascript action walkthrough](./javascript-action.md) and have a basic action set up. If not, we recommend you go through that first.
## Installing dependencies
All of the dependencies we need should come packaged for us in this library's core and github packages. To install, run the following in your action:
`npm install @actions/core && npm install @actions/github`
## Metadata
Next, we will need a welcome message and a repo token as an input. Recall that inputs are defined in the `action.yml` metadata file - update your `action.yml` file to define `welcome-message` and `repo-token` as inputs.
```yaml
name: "Welcome"
description: "A basic welcome action"
author: "GitHub"
inputs:
welcome-message:
description: "Message to display when a user opens an issue or PR"
default: "Thanks for opening an issue! Make sure you've followed CONTRIBUTING.md"
repo-token:
description: "Token for the repo. Can be passed in using {{ secrets.GITHUB_TOKEN }}"
required: true
runs:
using: "node12"
main: "lib/main.js"
```
/*
* Interfaces
*/
export interface PayloadRepository {
[key: string]: any
full_name?: string
name: string
owner: {
[key: string]: any
login: string
name?: string
}
html_url?: string
## Action logic
Now that we've installed our dependencies and defined our inputs, we're ready to start writing the action logic in `src/main.ts`! For clarity, we'll structure our action up as follows:
```ts
import * as core from '@actions/core';
import * as github from '@actions/github';
export async function run() {
try {
const welcomeMessage: string = core.getInput('welcome-message');
// TODO - Get context data
// TODO - make request to the GitHub API to comment on the issue
}
catch (error) {
core.setFailed(error.message);
throw error;
}
}
export interface WebhookPayloadWithRepository {
[key: string]: any
repository?: PayloadRepository
issue?: {
[key: string]: any
number: number
html_url?: string
body?: string
}
pull_request?: {
[key: string]: any
number: number
html_url?: string
body?: string
}
sender?: {
[key: string]: any
type: string
}
action?: string
installation?: {
id: number
[key: string]: any
}
run();
```
### Getting context data
For the purpose of this walkthrough, we will need the following pieces of context data:
- the name of the repo that the action is being run on
- the organization/owner of that repo
- the number of the issue that has been opened
Fortunately, the GitHub package provides all of this to us with [a single convenience function](https://github.com/actions/toolkit/blob/ac007c06984bc483fae2ba649788dfc858bc6a8b/packages/github/src/context.ts#L34), so we can simply do:
`const issue: {owner: string; repo: string; number: number} = github.context.issue;`
The context object also contains a number of easily accessed properties, as well as easy access to the full [GitHub payload](https://developer.github.com/v3/activity/events/types/). We can use this to check and make sure we're actually looking at a recently opened issue (and not something else, like a comment on an existing issue):
```ts
if (github.context.payload.action !== 'opened') {
console.log('No issue or PR was opened, skipping');
return;
}
```
##### context.ts
Our whole `src/main.ts` file now looks like:
Contains a GitHub context
```ts
import * as core from '@actions/core';
import * as github from '@actions/github';
```
export class Context {
/**
* Webhook payload object that triggered the workflow
*/
public payload: WebhookPayloadWithRepository
export async function run() {
try {
const welcomeMessage: string = core.getInput('welcome-message', {required: true});
const repoToken: string = core.getInput('repo-token', {required: true});
const issue: {owner: string; repo: string; number: number} = github.context.issue;
/**
* Name of the event that triggered the workflow
*/
public event: string
public sha: string
public ref: string
public workflow: string
public action: string
public actor: string
/**
* Hydrate the context from the environment
*/
constructor ()
public get issue ()
public get repo ()
if (github.context.payload.action !== 'opened') {
console.log('No issue or pull request was opened, skipping');
return;
}
// TODO - make request to the GitHub API to comment on the issue
}
catch (error) {
core.setFailed(error.message);
throw error;
}
}
run();
```
##### github.ts
### Sending requests to the GitHub API
Contains a hydrated Octokit client
Now that we have our context data, we are able to send a request to the GitHub API using the [Octokit REST client](https://github.com/octokit/rest.js). The REST client exposes a number of easy convenience functions, including one for adding comments to issues/PRs (issues and PRs are treated as one concept by the Octokit client):
```ts
const client: github.GitHub = new github.GitHub(repoToken);
await client.issues.createComment({
owner: issue.owner,
repo: issue.repo,
issue_number: issue.number,
body: welcomeMessage
});
```
export class GithubClient extends Octokit {
// For making GraphQL requests
public graphql: (query: string, variables?: Variables) => Promise<GraphQlQueryResponse>
// Calls super and initializes graphql
constructor (token: string)
For more docs on the client, you can visit the [Octokit REST documentation](https://octokit.github.io/rest.js/). Now our action code should be complete:
```ts
import * as core from '@actions/core';
import * as github from '@actions/github';
export async function run() {
try {
const welcomeMessage: string = core.getInput('welcome-message', {required: true});
const repoToken: string = core.getInput('repo-token', {required: true});
const issue: {owner: string; repo: string; number: number} = github.context.issue;
if (github.context.payload.action !== 'opened') {
console.log('No issue or pull request was opened, skipping');
return;
}
const client: github.GitHub = new github.GitHub(repoToken);
await client.issues.createComment({
owner: issue.owner,
repo: issue.repo,
issue_number: issue.number,
body: welcomeMessage
});
}
catch (error) {
core.setFailed(error.message);
throw error;
}
}
run();
```
## Writing unit tests for your action
Next, we're going to write a basic unit test for our action using jest. If you followed the [javascript walkthrough](./javascript-action.md), you should have a file `__tests__/main.test.ts` that runs tests when `npm test` is called. We're going to start by populating that with one test:
```ts
const nock = require('nock');
const path = require('path');
describe('action test suite', () => {
it('It posts a comment on an opened issue', async () => {
// TODO
});
});
```
For the purposes of this walkthrough, we'll focus on populating this test and leave the remaining test coverage as an exercise for the reader.
### Mocking inputs
First, we want to make sure that we can mock our inputs (welcome-message, and repo-token). Actions handles inputs by populating process.env.INPUT_${input name in all caps}, so we can mock that simply by setting those environment variables:
```ts
const nock = require('nock');
const path = require('path');
describe('action test suite', () => {
it('It posts a comment on an opened issue', async () => {
const welcomeMessage = 'hello';
const repoToken = 'token';
process.env['INPUT_WELCOME-MESSAGE'] = welcomeMessage;
process.env['INPUT_REPO-TOKEN'] = repoToken;
// TODO
});
});
```
### Mocking the GitHub context
Mocking the GitHub context is relatively straightforward. Since most of it is simply populated by environment variables, you can just set the corresponding environment variables defined [here](https://github.com/actions/toolkit/blob/ac007c06984bc483fae2ba649788dfc858bc6a8b/packages/github/src/context.ts#L23) and test that it works in that environment. In this case, we can setup our test with:
```ts
const nock = require('nock');
const path = require('path');
describe('action test suite', () => {
it('It posts a comment on an opened issue', async () => {
const welcomeMessage = 'hello';
const repoToken = 'token';
process.env['INPUT_WELCOME-MESSAGE'] = welcomeMessage;
process.env['INPUT_REPO-TOKEN'] = repoToken;
process.env['GITHUB_REPOSITORY'] = 'foo/bar';
process.env['GITHUB_EVENT_PATH'] = path.join(__dirname, 'payload.json');
// TODO
});
});
```
Note that the payload is loaded from GITHUB_EVENT_PATH. Since we set that to `path.join(__dirname, 'payload.json')`, we need to go save our payload there. For the purposes of this test, we can simply save the following to `__tests__/payload.json`:
```json
{
"issue": {
"number": 10
},
"action": "opened"
}
```
Now, calling `github.context.issue` should return `{owner: foo, repo: bar, number: 10}`, and `github.context.payload.action` should get set to 'opened'
> One important detail here is that because the GitHub context loads these environment variables as soon as it is required, you should set them before you require your action. In most cases, this means you need to rerequire your action in every test. If this is a problem, you can get around it by mocking the class directly using jest (or whatever framework you choose).
### Mocking the Octokit Client
To mock the client calls, we recommend using [nock](https://github.com/nock/nock) which allows you to mock the http requests made by the client. First, install nock with `npm install nock --save-dev`.
For this test, we expect the following call:
```ts
client.issues.createComment({
owner: 'foo',
repo: 'bar',
issue_number: 10,
body: 'you posted your first issue'
});
```
From [the GitHub endpoint docs](https://developer.github.com/v3/issues/comments/#create-a-comment), we expect this to get make a POST request to `https://api.github.com/repos/foo/bar/issues/10/comments` with body of `{"body":"hello"}`
We can mock this with:
```ts
const nock = require('nock');
const path = require('path');
describe('action test suite', () => {
it('It posts a comment on an opened issue', async () => {
const welcomeMessage = 'hello';
const repoToken = 'token';
process.env['INPUT_WELCOME-MESSAGE'] = welcomeMessage;
process.env['INPUT_REPO-TOKEN'] = repoToken;
process.env['GITHUB_REPOSITORY'] = 'foo/bar';
process.env['GITHUB_EVENT_PATH'] = path.join(__dirname, 'payload.json');
nock('https://api.github.com')
.persist()
.post('/repos/foo/bar/issues/10/comments', '{\"body\":\"hello\"}')
.reply(200);
const main = require('../src/main');
await main.run();
});
});
```
This will fail if the url or body doesn't exactly match the parameters passed into the nock function. We can now run `npm test` and the test should succeed.
## Build and publish
Now that we've written and unit tested our action, we can build our action with `npm run build` and push it to a repo where it can be consumed by workflows. For more info on versioning your action, see [our versioning docs](./action-versioning.md).
## Next steps
If you're interested in building out this action further, try extending your action to only run on a user's first issue. See our [first-contribution action](https://github.com/actions/first-interaction) for inspiration.
-167
View File
@@ -1,167 +0,0 @@
# Creating a JavaScript Action
The [node12-template](https://github.com/actions/node12-template) repo contains everything you need to get started.
# Create a Repo from the Template
Navigate to https://github.com/actions/node12-template
Click on `Use this template` to create the repo for your action.
![template](assets/node12-template.png)
Complete creating your repo and clone the repo.
> NOTE: The location of the repo will be how users will reference your action in their workflow file with the using keyword.
e.g. To use https://github.com/actions/setup-node, user's will author:
```yaml
steps:
using: actions/setup-node@master
```
# Dev Workflow
The workflow below describes one possible workflow with a branching strategy. Others exist.
> Key Point: the branch that users reference in their workflow files should reference an action that has **only** the production dependencies.
The workflow below describes a strategy where you code in master (with node_modules ignored) with a v1 branch users reference and contains the product references. Actions are self contained referenced on the github graph of repos.
## Install Dependencies
After creating a repo from the template and cloning it, you will be in master. The command below will install the toolkit, other dependencies and dev dependencies
```bash
$ npm install
```
## Define Metadata
Your action has a name and a description. Update the author.
Create inputs that your unit of work will need. These will be what workflow authors set with the `with:` keyword.
```yaml
name: 'My new action'
description: 'A test action'
author: 'GitHub'
inputs:
myInput:
description: 'Input to use'
default: 'world'
runs:
using: 'node12'
main: 'lib/main.js'
```
Note that the action will be run with node 12 (carried by the runner) and the entry point is specified with `main:`
## Change Code and Add Tests
The entry point is in main.ts
```typescript
import * as core from '@actions/core';
async function run() {
try {
const myInput = core.getInput('myInput');
core.debug(`Hello ${myInput}!`);
} catch (error) {
core.setFailed(error.message);
}
}
run();
```
Modify tests in `__tests__\main.test.ts`. The template uses [jest](https://github.com/facebook/jest).
## Format the Code
```bash
$ npm run format
```
## Build and Test
```bash
$ npm run build
> node12-template-action@0.0.0 build /Users/user/Projects/testnode12
> tsc
$ npm test
> jest
PASS __tests__/main.test.ts
TODO - Add a test suite
✓ TODO - Add a test (1ms)
Test Suites: 1 passed, 1 total
...
```
## Commit and Push Changes
```bash
$ git add <whatever only files you added>
$ git commit -m "Message"
```
## Publish a v1-release Action
After changing some files, create a v1-release branch which we will release
```bash
$ git checkout -b v1-release
```
> NOTE: We will provide tooling and an action to automate this soon.
Checkin production dependencies:
1. **Do not ignore node_modules**: Add a `!` in front of the `node_modules` line.
2. **Delete node_modules**: rm -Rf node_modules
3. **Install production dependencies**: npm install --production
4. **Add**: git add node_modules
Simply commit and push your action to publish.
```bash
$ git commit -a -m "publishing v1 of action"
$ git push
```
> NOTE: Consider versioning your actions with tags. See [versioning](action-versioning.md)
## Test End To End
Once the action has a self contained version in the v1-release branch, you can test it by referencing the latest (and potentially unstable) version in the release branch. If you are fixing an issue that someone else is having with your action, you can have them try it before you officially releasing it as the 'v1' version.
```yaml
steps:
using: {org}/{reponame}@v1-release
```
## Release Current Changes as v1
Once you have tested end to end, push a tag of 'v1' to the commit in the release branch.
See [action versioning](action-versioning.md) for more details.
# Users Referencing
Users can now reference your action in their workflows with
```yaml
steps:
using: {org}/{reponame}@v1
```
+107
View File
@@ -0,0 +1,107 @@
# Problem Matchers
Problem Matchers are a way to scan the output of actions for a specified regex pattern and surface that information prominently in the UI. Both [GitHub Annotations](https://developer.github.com/v3/checks/runs/#annotations-object-1) and log file decorations are created when a match is detected.
## Single Line Matchers
Let's consider the ESLint compact output:
```
badFile.js: line 50, col 11, Error - 'myVar' is defined but never used. (no-unused-vars)
```
We can define a problem matcher in json that detects input in that format:
```json
{
"problemMatcher": [
{
"owner": "eslint-compact",
"pattern": [
{
"regexp": "^(.+):\\sline\\s(\\d+),\\scol\\s(\\d+),\\s(Error|Warning|Info)\\s-\\s(.+)\\s\\((.+)\\)$",
"file": 1,
"line": 2,
"column": 3,
"severity": 4,
"message": 5,
"code": 6
}
]
}
]
}
```
The following fields are available for problem matchers:
```
{
owner: An ID field that can be used to remove or replace the problem matcher. **required**
pattern: [
{
regexp: The regex pattern that provides the groups to match against **required**
file: a group number containing the file name
line: a group number containing the line number
column: a group number containing the column information
severity: a group number containing either 'warning' or 'error' case-insensitive. Defaults to `error`
code: a group number containing the error code
message: a group number containing the error message. **required** at least one pattern must set the message
loop: loops until a match is not found, only valid on the last pattern of a multipattern matcher
}
]
}
```
## Multiline Matching
Consider the following output:
```
test.js
1:0 error Missing "use strict" statement strict
5:10 error 'addOne' is defined but never used no-unused-vars
✖ 2 problems (2 errors, 0 warnings)
```
The file name is printed once, yet multiple error lines are printed. The `loop` keyword provides a way to discover multiple errors in outputs.
The eslint-stylish problem matcher defined below catches that output, and creates two annotations from it.
```
{
"problemMatcher": [
{
"owner": "eslint-stylish",
"pattern": [
{
// Matches the 1st line in the output
"regexp": "^([^\\s].*)$",
"file": 1
},
{
// Matches the 2nd and 3rd line in the output
"regexp": "^\\s+(\\d+):(\\d+)\\s+(error|warning|info)\\s+(.*)\\s\\s+(.*)$",
// File is carried through from above, so we define the rest of the groups
"line": 1,
"column": 2,
"severity": 3,
"message": 4,
"code": 5,
"loop": true
}
]
}
]
}
```
The first pattern matches the `test.js` line and records the file information. This line is not decorated in the UI.
The second pattern loops through the remaining lines with `loop: true` until it fails to find a match, and surfaces these lines prominently in the UI.
## Adding and Removing Problem Matchers
Problem Matchers are enabled and removed via the toolkit [commands](commands.md#problem-matchers).
## Duplicate Problem Matchers
Registering two problem-matchers with the same owner will result in only the problem matcher registered last running.
## Examples
Some of the starter actions are already using problem matchers, for example:
- [setup-node](https://github.com/actions/setup-node/tree/master/.github)
- [setup-python](https://github.com/actions/setup-python/tree/master/.github)
- [setup-go](https://github.com/actions/setup-go/tree/master/.github)
- [setup-dotnet](https://github.com/actions/setup-dotnet/tree/master/.github)
+100
View File
@@ -0,0 +1,100 @@
# Github Package
In order to support using actions to interact with GitHub, I propose adding a `github` package to the toolkit.
Its main purpose will be to provide a hydrated GitHub context/Octokit client with some convenience functions. It is largely pulled from the GitHub utilities provided in https://github.com/JasonEtco/actions-toolkit, though it has been condensed.
### Spec
##### interfaces.ts
```ts
/*
* Interfaces
*/
export interface PayloadRepository {
[key: string]: any
full_name?: string
name: string
owner: {
[key: string]: any
login: string
name?: string
}
html_url?: string
}
export interface WebhookPayloadWithRepository {
[key: string]: any
repository?: PayloadRepository
issue?: {
[key: string]: any
number: number
html_url?: string
body?: string
}
pull_request?: {
[key: string]: any
number: number
html_url?: string
body?: string
}
sender?: {
[key: string]: any
type: string
}
action?: string
installation?: {
id: number
[key: string]: any
}
}
```
##### context.ts
Contains a GitHub context
```ts
export class Context {
/**
* Webhook payload object that triggered the workflow
*/
public payload: WebhookPayloadWithRepository
/**
* Name of the event that triggered the workflow
*/
public event: string
public sha: string
public ref: string
public workflow: string
public action: string
public actor: string
/**
* Hydrate the context from the environment
*/
constructor ()
public get issue ()
public get repo ()
}
```
##### github.ts
Contains a hydrated Octokit client
```ts
export class GithubClient extends Octokit {
// For making GraphQL requests
public graphql: (query: string, variables?: Variables) => Promise<GraphQlQueryResponse>
// Calls super and initializes graphql
constructor (token: string)
}
```
@@ -6,7 +6,7 @@ In order to support the node-config action, I propose adding the following into
Holds all the functions necessary for interacting with the runner/environment.
```
```ts
// Logging functions
export function debug(message: string): void
export function warning(message: string): void
@@ -66,7 +66,7 @@ export function setFailed(message: string): void
Holds all the functions necessary for file system manipulation (cli scenarios, not fs replacements):
```
```ts
/**
* Interface for cp/mv options
*/
@@ -132,7 +132,7 @@ export function which(tool: string, options?: WhichOptions): Promise<string>
Holds all the functions necessary for running the tools node-config depends on (aka 7-zip and tar)
```
```ts
/**
* Interface for exec options
*/
@@ -155,7 +155,7 @@ export function exec(commandLine: string, args?: string[], options?: IExecOption
Holds all the functions necessary for downloading and caching node.
```
```ts
/**
* Download a tool from an url and stream it into a file
*
+4958 -1429
View File
File diff suppressed because it is too large Load Diff
+4 -5
View File
@@ -9,8 +9,7 @@
"format-check": "prettier --check packages/**/*.ts",
"lint": "eslint packages/**/*.ts",
"new-package": "scripts/create-package",
"test": "jest",
"test-ci": "jest --testPathIgnorePatterns=\"<rootDir>/packages/exec/__tests__/exec.test.ts\""
"test": "jest"
},
"devDependencies": {
"@types/jest": "^24.0.11",
@@ -21,11 +20,11 @@
"eslint": "^5.16.0",
"eslint-plugin-github": "^2.0.0",
"eslint-plugin-jest": "^22.5.1",
"jest": "^24.7.1",
"jest": "^24.9.0",
"jest-circus": "^24.7.1",
"lerna": "^3.13.3",
"lerna": "^3.18.4",
"prettier": "^1.17.0",
"ts-jest": "^24.0.2",
"typescript": "^3.4.4"
"typescript": "^3.6.2"
}
}
+84 -25
View File
@@ -4,48 +4,55 @@
## Usage
#### Inputs/Outputs
### Import the package
You can use this library to get inputs or set outputs:
```
```js
// javascript
const core = require('@actions/core');
const myInput = core.getInput('inputName', { required: true });
// typescript
import * as core from '@actions/core';
```
// Do stuff
#### Inputs/Outputs
Action inputs can be read with `getInput`. Outputs can be set with `setOutput` which makes them available to be mapped into inputs of other actions to ensure they are decoupled.
```js
const myInput = core.getInput('inputName', { required: true });
core.setOutput('outputKey', 'outputVal');
```
#### Exporting variables/secrets
#### Exporting variables
You can also export variables and secrets for future steps. Variables get set in the environment automatically, while secrets must be scoped into the environment from a workflow using `{{ secret.FOO }}`. Secrets will also be masked from the logs:
```
const core = require('@actions/core');
// Do stuff
Since each step runs in a separate process, you can use `exportVariable` to add it to this step and future steps environment blocks.
```js
core.exportVariable('envVar', 'Val');
core.exportSecret('secretVar', variableWithSecretValue);
```
#### Setting a secret
Setting a secret registers the secret with the runner to ensure it is masked in logs.
```js
core.setSecret('myPassword');
```
#### PATH Manipulation
You can explicitly add items to the path for all remaining steps in a workflow:
To make a tool's path available in the path for the remainder of the job (without altering the machine or containers state), use `addPath`. The runner will prepend the path given to the jobs PATH.
```
const core = require('@actions/core');
core.addPath('pathToTool');
```js
core.addPath('/path/to/mytool');
```
#### Exit codes
You should use this library to set the failing exit code for your action:
You should use this library to set the failing exit code for your action. If status is not set and the script runs to completion, that will lead to a success.
```
```js
const core = require('@actions/core');
try {
@@ -56,13 +63,15 @@ catch (err) {
core.setFailed(`Action failed with error ${err}`);
}
Note that `setNeutral` is not yet implemented in actions V2 but equivalent functionality is being planned.
```
#### Logging
Finally, this library provides some utilities for logging:
Finally, this library provides some utilities for logging. Note that debug logging is hidden from the logs by default. This behavior can be toggled by enabling the [Step Debug Logs](../../docs/action-debugging.md#step-debug-logs).
```
```js
const core = require('@actions/core');
const myInput = core.getInput('input');
@@ -70,12 +79,62 @@ try {
core.debug('Inside try block');
if (!myInput) {
core.warning('myInput wasnt set');
core.warning('myInput was not set');
}
// Do stuff
}
catch (err) {
core.error('Error ${err}, action may still succeed though');
core.error(`Error ${err}, action may still succeed though`);
}
```
This library can also wrap chunks of output in foldable groups.
```js
const core = require('@actions/core')
// Manually wrap output
core.startGroup('Do some function')
doSomeFunction()
core.endGroup()
// Wrap an asynchronous function call
const result = await core.group('Do something async', async () => {
const response = await doSomeHTTPRequest()
return response
})
```
#### Action state
You can use this library to save state and get state for sharing information between a given wrapper action:
**action.yml**
```yaml
name: 'Wrapper action sample'
inputs:
name:
default: 'GitHub'
runs:
using: 'node12'
main: 'main.js'
post: 'cleanup.js'
```
In action's `main.js`:
```js
const core = require('@actions/core');
core.saveState("pidToKill", 12345);
```
In action's `cleanup.js`:
```js
const core = require('@actions/core');
var pid = core.getState("pidToKill");
process.kill(pid);
```
+23
View File
@@ -0,0 +1,23 @@
# @actions/core Releases
### 1.2.0
- saveState and getState functions for wrapper tasks (on finally entry points that run post job)
### 1.1.3
- setSecret added to register a secret with the runner to be masked from the logs
- exportSecret which was not implemented and never worked was removed after clarification from product.
### 1.1.1
- Add support for action input variables with multiple spaces [#127](https://github.com/actions/toolkit/issues/127)
- Switched ## commands to :: commands (should have no noticeable impact) [#110)(https://github.com/actions/toolkit/pull/110)
### 1.1.0
- Added helpers for `group` and `endgroup` [#98](https://github.com/actions/toolkit/pull/98)
### 1.0.0
- Initial release
+61 -48
View File
@@ -16,7 +16,11 @@ const testEnvVars = {
// Set inputs
INPUT_MY_INPUT: 'val',
INPUT_MISSING: '',
'INPUT_SPECIAL_CHARS_\'\t"\\': '\'\t"\\ repsonse '
'INPUT_SPECIAL_CHARS_\'\t"\\': '\'\t"\\ response ',
INPUT_MULTIPLE_SPACES_VARIABLE: 'I have multiple spaces',
// Save inputs
STATE_TEST_1: 'state_val'
}
describe('@actions/core', () => {
@@ -33,50 +37,26 @@ describe('@actions/core', () => {
it('exportVariable produces the correct command and sets the env', () => {
core.exportVariable('my var', 'var val')
assertWriteCalls([`##[set-env name=my var;]var val${os.EOL}`])
assertWriteCalls([`::set-env name=my var,::var val${os.EOL}`])
})
it('exportVariable escapes variable names', () => {
core.exportVariable('special char var \r\n];', 'special val')
expect(process.env['special char var \r\n];']).toBe('special val')
assertWriteCalls([
`##[set-env name=special char var %0D%0A%5D%3B;]special val${os.EOL}`
`::set-env name=special char var %0D%0A%5D%3B,::special val${os.EOL}`
])
})
it('exportVariable escapes variable values', () => {
core.exportVariable('my var2', 'var val\r\n')
expect(process.env['my var2']).toBe('var val\r\n')
assertWriteCalls([`##[set-env name=my var2;]var val%0D%0A${os.EOL}`])
assertWriteCalls([`::set-env name=my var2,::var val%0D%0A${os.EOL}`])
})
it('exportSecret produces the correct commands and sets the env', () => {
core.exportSecret('my secret', 'secret val')
expect(process.env['my secret']).toBe('secret val')
assertWriteCalls([
`##[set-env name=my secret;]secret val${os.EOL}`,
`##[set-secret]secret val${os.EOL}`
])
})
it('exportSecret escapes secret names', () => {
core.exportSecret('special char secret \r\n];', 'special secret val')
expect(process.env['special char secret \r\n];']).toBe('special secret val')
assertWriteCalls([
`##[set-env name=special char secret %0D%0A%5D%3B;]special secret val${
os.EOL
}`,
`##[set-secret]special secret val${os.EOL}`
])
})
it('exportSecret escapes secret values', () => {
core.exportSecret('my secret2', 'secret val\r\n')
expect(process.env['my secret2']).toBe('secret val\r\n')
assertWriteCalls([
`##[set-env name=my secret2;]secret val%0D%0A${os.EOL}`,
`##[set-secret]secret val%0D%0A${os.EOL}`
])
it('setSecret produces the correct command', () => {
core.setSecret('secret val')
assertWriteCalls([`::add-mask::secret val${os.EOL}`])
})
it('prependPath produces the correct commands and sets the env', () => {
@@ -84,7 +64,7 @@ describe('@actions/core', () => {
expect(process.env['PATH']).toBe(
`myPath${path.delimiter}path1${path.delimiter}path2`
)
assertWriteCalls([`##[add-path]myPath${os.EOL}`])
assertWriteCalls([`::add-path::myPath${os.EOL}`])
})
it('getInput gets non-required input', () => {
@@ -101,7 +81,7 @@ describe('@actions/core', () => {
)
})
it('getInput doesnt throw on missing non-required input', () => {
it('getInput does not throw on missing non-required input', () => {
expect(core.getInput('missing', {required: false})).toBe('')
})
@@ -110,59 +90,92 @@ describe('@actions/core', () => {
})
it('getInput handles special characters', () => {
expect(core.getInput('special chars_\'\t"\\')).toBe('\'\t"\\ repsonse')
expect(core.getInput('special chars_\'\t"\\')).toBe('\'\t"\\ response')
})
it('getInput handles multiple spaces', () => {
expect(core.getInput('multiple spaces variable')).toBe(
'I have multiple spaces'
)
})
it('setOutput produces the correct command', () => {
core.setOutput('some output', 'some value')
assertWriteCalls([`##[set-output name=some output;]some value${os.EOL}`])
})
it('setNeutral sets the correct exit code', () => {
core.setFailed('Failure message')
expect(process.exitCode).toBe(core.ExitCode.Failure)
assertWriteCalls([`::set-output name=some output,::some value${os.EOL}`])
})
it('setFailure sets the correct exit code and failure message', () => {
core.setFailed('Failure message')
expect(process.exitCode).toBe(core.ExitCode.Failure)
assertWriteCalls([`##[error]Failure message${os.EOL}`])
assertWriteCalls([`::error::Failure message${os.EOL}`])
})
it('setFailure escapes the failure message', () => {
core.setFailed('Failure \r\n\nmessage\r')
expect(process.exitCode).toBe(core.ExitCode.Failure)
assertWriteCalls([`##[error]Failure %0D%0A%0Amessage%0D${os.EOL}`])
assertWriteCalls([`::error::Failure %0D%0A%0Amessage%0D${os.EOL}`])
})
it('error sets the correct error message', () => {
core.error('Error message')
assertWriteCalls([`##[error]Error message${os.EOL}`])
assertWriteCalls([`::error::Error message${os.EOL}`])
})
it('error escapes the error message', () => {
core.error('Error message\r\n\n')
assertWriteCalls([`##[error]Error message%0D%0A%0A${os.EOL}`])
assertWriteCalls([`::error::Error message%0D%0A%0A${os.EOL}`])
})
it('warning sets the correct message', () => {
core.warning('Warning')
assertWriteCalls([`##[warning]Warning${os.EOL}`])
assertWriteCalls([`::warning::Warning${os.EOL}`])
})
it('warning escapes the message', () => {
core.warning('\r\nwarning\n')
assertWriteCalls([`##[warning]%0D%0Awarning%0A${os.EOL}`])
assertWriteCalls([`::warning::%0D%0Awarning%0A${os.EOL}`])
})
it('startGroup starts a new group', () => {
core.startGroup('my-group')
assertWriteCalls([`::group::my-group${os.EOL}`])
})
it('endGroup ends new group', () => {
core.endGroup()
assertWriteCalls([`::endgroup::${os.EOL}`])
})
it('group wraps an async call in a group', async () => {
const result = await core.group('mygroup', async () => {
process.stdout.write('in my group\n')
return true
})
expect(result).toBe(true)
assertWriteCalls([
`::group::mygroup${os.EOL}`,
'in my group\n',
`::endgroup::${os.EOL}`
])
})
it('debug sets the correct message', () => {
core.debug('Debug')
assertWriteCalls([`##[debug]Debug${os.EOL}`])
assertWriteCalls([`::debug::Debug${os.EOL}`])
})
it('debug escapes the message', () => {
core.debug('\r\ndebug\n')
assertWriteCalls([`##[debug]%0D%0Adebug%0A${os.EOL}`])
assertWriteCalls([`::debug::%0D%0Adebug%0A${os.EOL}`])
})
it('saveState produces the correct command', () => {
core.saveState('state_1', 'some value')
assertWriteCalls([`::save-state name=state_1,::some value${os.EOL}`])
})
it('getState gets wrapper action state', () => {
expect(core.getState('TEST_1')).toBe('state_val')
})
})
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@actions/core",
"version": "1.0.0",
"version": "1.2.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
+6 -4
View File
@@ -1,10 +1,11 @@
{
"name": "@actions/core",
"version": "1.0.0",
"version": "1.2.0",
"description": "Actions core lib",
"keywords": [
"core",
"actions"
"github",
"actions",
"core"
],
"homepage": "https://github.com/actions/toolkit/tree/master/packages/core",
"license": "MIT",
@@ -21,7 +22,8 @@
},
"repository": {
"type": "git",
"url": "git+https://github.com/actions/toolkit.git"
"url": "git+https://github.com/actions/toolkit.git",
"directory": "packages/core"
},
"scripts": {
"test": "echo \"Error: run tests from root\" && exit 1",
+6 -6
View File
@@ -14,7 +14,7 @@ interface CommandProperties {
*
* Examples:
* ##[warning]This is the user warning message
* ##[set-secret name=mypassword]definatelyNotAPassword!
* ##[set-secret name=mypassword]definitelyNotAPassword!
*/
export function issueCommand(
command: string,
@@ -25,11 +25,11 @@ export function issueCommand(
process.stdout.write(cmd.toString() + os.EOL)
}
export function issue(name: string, message: string): void {
export function issue(name: string, message: string = ''): void {
issueCommand(name, {}, message)
}
const CMD_PREFIX = '##['
const CMD_STRING = '::'
class Command {
private readonly command: string
@@ -47,7 +47,7 @@ class Command {
}
toString(): string {
let cmdStr = CMD_PREFIX + this.command
let cmdStr = CMD_STRING + this.command
if (this.properties && Object.keys(this.properties).length > 0) {
cmdStr += ' '
@@ -57,13 +57,13 @@ class Command {
if (val) {
// safely append the val - avoid blowing up when attempting to
// call .replace() if message is not a string for some reason
cmdStr += `${key}=${escape(`${val || ''}`)};`
cmdStr += `${key}=${escape(`${val || ''}`)},`
}
}
}
}
cmdStr += ']'
cmdStr += CMD_STRING
// safely append the message - avoid blowing up when attempting to
// call .replace() if message is not a string for some reason
+79 -8
View File
@@ -1,5 +1,6 @@
import {issue, issueCommand} from './command'
import * as os from 'os'
import * as path from 'path'
/**
@@ -30,7 +31,7 @@ export enum ExitCode {
//-----------------------------------------------------------------------
/**
* sets env variable for this action and future actions in the job
* Sets env variable for this action and future actions in the job
* @param name the name of the variable to set
* @param val the value of the variable
*/
@@ -40,13 +41,11 @@ export function exportVariable(name: string, val: string): void {
}
/**
* exports the variable and registers a secret which will get masked from logs
* @param name the name of the variable to set
* @param val value of the secret
* Registers a secret which will get masked from logs
* @param secret value of the secret
*/
export function exportSecret(name: string, val: string): void {
exportVariable(name, val)
issueCommand('set-secret', {}, val)
export function setSecret(secret: string): void {
issueCommand('add-mask', {}, secret)
}
/**
@@ -67,7 +66,7 @@ export function addPath(inputPath: string): void {
*/
export function getInput(name: string, options?: InputOptions): string {
const val: string =
process.env[`INPUT_${name.replace(' ', '_').toUpperCase()}`] || ''
process.env[`INPUT_${name.replace(/ /g, '_').toUpperCase()}`] || ''
if (options && options.required && !val) {
throw new Error(`Input required and not supplied: ${name}`)
}
@@ -126,3 +125,75 @@ export function error(message: string): void {
export function warning(message: string): void {
issue('warning', message)
}
/**
* Writes info to log with console.log.
* @param message info message
*/
export function info(message: string): void {
process.stdout.write(message + os.EOL)
}
/**
* Begin an output group.
*
* Output until the next `groupEnd` will be foldable in this group
*
* @param name The name of the output group
*/
export function startGroup(name: string): void {
issue('group', name)
}
/**
* End an output group.
*/
export function endGroup(): void {
issue('endgroup')
}
/**
* Wrap an asynchronous function call in a group.
*
* Returns the same type as the function itself.
*
* @param name The name of the group
* @param fn The function to wrap in the group
*/
export async function group<T>(name: string, fn: () => Promise<T>): Promise<T> {
startGroup(name)
let result: T
try {
result = await fn()
} finally {
endGroup()
}
return result
}
//-----------------------------------------------------------------------
// Wrapper action state
//-----------------------------------------------------------------------
/**
* Saves state for current action, the state can only be retrieved by this action's post job execution.
*
* @param name name of the state to store
* @param value value to store
*/
export function saveState(name: string, value: string): void {
issueCommand('save-state', {name}, value)
}
/**
* Gets the value of an state set by this action's main execution.
*
* @param name name of the state to get
* @returns string
*/
export function getState(name: string): string {
return process.env[`STATE_${name}`] || ''
}
+8 -11
View File
@@ -6,7 +6,7 @@
You can use this package to execute your tools on the command line in a cross platform way:
```
```js
const exec = require('@actions/exec');
await exec.exec('node index.js');
@@ -16,7 +16,7 @@ await exec.exec('node index.js');
You can also pass in arg arrays:
```
```js
const exec = require('@actions/exec');
await exec.exec('node', ['index.js', 'foo=bar']);
@@ -26,11 +26,11 @@ await exec.exec('node', ['index.js', 'foo=bar']);
Capture output or specify [other options](https://github.com/actions/toolkit/blob/d9347d4ab99fd507c0b9104b2cf79fb44fcc827d/packages/exec/src/interfaces.ts#L5):
```
```js
const exec = require('@actions/exec');
const myOutput = '';
const myError = '';
let myOutput = '';
let myError = '';
const options = {};
options.listeners = {
@@ -48,13 +48,10 @@ await exec.exec('node', ['index.js', 'foo=bar'], options);
#### Exec tools not in the PATH
You can use it in conjunction with the `which` function from `@actions/io` to execute tools that are not in the PATH:
You can specify the full path for tools not in the PATH:
```
```js
const exec = require('@actions/exec');
const io = require('@actions/io');
const pythonPath: string = await io.which('python', true)
await exec.exec(`"${pythonPath}"`, ['main.py']);
await exec.exec('"/path/to/my-tool"', ['arg1']);
```
+5
View File
@@ -0,0 +1,5 @@
# @actions/exec Releases
### 1.0.0
- Initial release
+229 -3
View File
@@ -121,6 +121,38 @@ describe('@actions/exec', () => {
}
})
it('Runs exec successfully with command from PATH', async () => {
const execOptions = getExecOptions()
const outStream = new StringStream()
execOptions.outStream = outStream
let output = ''
execOptions.listeners = {
stdout: (data: Buffer) => {
output += data.toString()
}
}
let exitCode = 1
let tool: string
let args: string[]
if (IS_WINDOWS) {
tool = 'cmd'
args = ['/c', 'echo', 'hello']
} else {
tool = 'sh'
args = ['-c', 'echo hello']
}
exitCode = await exec.exec(tool, args, execOptions)
expect(exitCode).toBe(0)
const rootedTool = await io.which(tool, true)
expect(outStream.getContents().split(os.EOL)[0]).toBe(
`[command]${rootedTool} ${args.join(' ')}`
)
expect(output.trim()).toBe(`hello`)
})
it('Exec fails with error on bad call', async () => {
const _testExecOptions = getExecOptions()
@@ -298,7 +330,7 @@ describe('@actions/exec', () => {
).toBe(1)
fs.unlinkSync(semaphorePath)
})
}, 10000) // this was timing out on some slower hosted macOS runs at default 5s
it('Handles child process holding streams open and non-zero exit code', async function() {
const semaphorePath = path.join(
@@ -352,7 +384,7 @@ describe('@actions/exec', () => {
).toBe(1)
fs.unlinkSync(semaphorePath)
})
}, 10000) // this was timing out on some slower hosted macOS runs at default 5s
it('Handles child process holding streams open and stderr', async function() {
const semaphorePath = path.join(
@@ -414,7 +446,165 @@ describe('@actions/exec', () => {
fs.unlinkSync(semaphorePath)
})
it('Exec roots relative tool path using unrooted options.cwd', async () => {
let exitCode: number
let command: string
if (IS_WINDOWS) {
command = './print-args-cmd' // let ToolRunner resolve the extension
} else {
command = './print-args-sh.sh'
}
const execOptions = getExecOptions()
execOptions.cwd = 'scripts'
const outStream = new StringStream()
execOptions.outStream = outStream
let output = ''
execOptions.listeners = {
stdout: (data: Buffer) => {
output += data.toString()
}
}
const originalCwd = process.cwd()
try {
process.chdir(__dirname)
exitCode = await exec.exec(`${command} hello world`, [], execOptions)
} catch (err) {
process.chdir(originalCwd)
throw err
}
expect(exitCode).toBe(0)
const toolPath = path.resolve(
__dirname,
execOptions.cwd,
`${command}${IS_WINDOWS ? '.cmd' : ''}`
)
if (IS_WINDOWS) {
expect(outStream.getContents().split(os.EOL)[0]).toBe(
`[command]${process.env.ComSpec} /D /S /C "${toolPath} hello world"`
)
} else {
expect(outStream.getContents().split(os.EOL)[0]).toBe(
`[command]${toolPath} hello world`
)
}
expect(output.trim()).toBe(`args[0]: "hello"${os.EOL}args[1]: "world"`)
})
it('Exec roots relative tool path using rooted options.cwd', async () => {
let command: string
if (IS_WINDOWS) {
command = './print-args-cmd' // let ToolRunner resolve the extension
} else {
command = './print-args-sh.sh'
}
const execOptions = getExecOptions()
execOptions.cwd = path.join(__dirname, 'scripts')
const outStream = new StringStream()
execOptions.outStream = outStream
let output = ''
execOptions.listeners = {
stdout: (data: Buffer) => {
output += data.toString()
}
}
const exitCode = await exec.exec(`${command} hello world`, [], execOptions)
expect(exitCode).toBe(0)
const toolPath = path.resolve(
__dirname,
execOptions.cwd,
`${command}${IS_WINDOWS ? '.cmd' : ''}`
)
if (IS_WINDOWS) {
expect(outStream.getContents().split(os.EOL)[0]).toBe(
`[command]${process.env.ComSpec} /D /S /C "${toolPath} hello world"`
)
} else {
expect(outStream.getContents().split(os.EOL)[0]).toBe(
`[command]${toolPath} hello world`
)
}
expect(output.trim()).toBe(`args[0]: "hello"${os.EOL}args[1]: "world"`)
})
it('Exec roots relative tool path using process.cwd', async () => {
let exitCode: number
let command: string
if (IS_WINDOWS) {
command = 'scripts/print-args-cmd' // let ToolRunner resolve the extension
} else {
command = 'scripts/print-args-sh.sh'
}
const execOptions = getExecOptions()
const outStream = new StringStream()
execOptions.outStream = outStream
let output = ''
execOptions.listeners = {
stdout: (data: Buffer) => {
output += data.toString()
}
}
const originalCwd = process.cwd()
try {
process.chdir(__dirname)
exitCode = await exec.exec(`${command} hello world`, [], execOptions)
} catch (err) {
process.chdir(originalCwd)
throw err
}
expect(exitCode).toBe(0)
const toolPath = path.resolve(
__dirname,
`${command}${IS_WINDOWS ? '.cmd' : ''}`
)
if (IS_WINDOWS) {
expect(outStream.getContents().split(os.EOL)[0]).toBe(
`[command]${process.env.ComSpec} /D /S /C "${toolPath} hello world"`
)
} else {
expect(outStream.getContents().split(os.EOL)[0]).toBe(
`[command]${toolPath} hello world`
)
}
expect(output.trim()).toBe(`args[0]: "hello"${os.EOL}args[1]: "world"`)
})
if (IS_WINDOWS) {
it('Exec roots relative tool path using process.cwd (Windows path separator)', async () => {
let exitCode: number
const command = 'scripts\\print-args-cmd' // let ToolRunner resolve the extension
const execOptions = getExecOptions()
const outStream = new StringStream()
execOptions.outStream = outStream
let output = ''
execOptions.listeners = {
stdout: (data: Buffer) => {
output += data.toString()
}
}
const originalCwd = process.cwd()
try {
process.chdir(__dirname)
exitCode = await exec.exec(`${command} hello world`, [], execOptions)
} catch (err) {
process.chdir(originalCwd)
throw err
}
expect(exitCode).toBe(0)
const toolPath = path.resolve(__dirname, `${command}.cmd`)
expect(outStream.getContents().split(os.EOL)[0]).toBe(
`[command]${process.env.ComSpec} /D /S /C "${toolPath} hello world"`
)
expect(output.trim()).toBe(`args[0]: "hello"${os.EOL}args[1]: "world"`)
})
// Win specific quoting tests
it('execs .exe with verbatim args (Windows)', async () => {
const exePath = process.env.ComSpec
@@ -501,7 +691,7 @@ describe('@actions/exec', () => {
`[command]"${exePath}" myarg1 myarg2`
)
expect(output.trim()).toBe("args[0]: 'myarg1'\r\nargs[1]: 'myarg2'")
})
}, 20000) // slower windows runs timeout, so upping timeout to 20s (from default of 5s)
it('execs .cmd with a space and with verbatim args (Windows)', async () => {
// this test validates the quoting that tool runner adds around the script path.
@@ -568,6 +758,42 @@ describe('@actions/exec', () => {
)
})
it('execs .cmd from path (Windows)', async () => {
// this test validates whether a .cmd is resolved from the PATH when the extension is not specified
const cmd = 'print-args-cmd' // note, not print-args-cmd.cmd
const cmdPath = path.join(__dirname, 'scripts', `${cmd}.cmd`)
const args: string[] = ['my arg 1', 'my arg 2']
const outStream = new StringStream()
let output = ''
const options = {
outStream: <stream.Writable>outStream,
listeners: {
stdout: (data: Buffer) => {
output += data.toString()
}
}
}
const originalPath = process.env['Path']
try {
process.env['Path'] = `${originalPath};${path.dirname(cmdPath)}`
const exitCode = await exec.exec(`${cmd}`, args, options)
expect(exitCode).toBe(0)
expect(outStream.getContents().split(os.EOL)[0]).toBe(
`[command]${
process.env.ComSpec
} /D /S /C "${cmdPath} "my arg 1" "my arg 2""`
)
expect(output.trim()).toBe(
'args[0]: "<quote>my arg 1<quote>"\r\n' +
'args[1]: "<quote>my arg 2<quote>"'
)
} catch (err) {
process.env['Path'] = originalPath
throw err
}
})
it('execs .cmd with arg quoting (Windows)', async () => {
// this test validates .cmd quoting rules are applied, not the default libuv rules
const cmdPath = path.join(
@@ -0,0 +1,12 @@
@echo off
setlocal
set index=0
:check_arg
set arg=%1
if not defined arg goto :eof
set "arg=%arg:"=<quote>%"
echo args[%index%]: "%arg%"
set /a index=%index%+1
shift
goto check_arg
+11
View File
@@ -0,0 +1,11 @@
#!/usr/bin/env bash
# store arguments in a special array
args=("$@")
# get number of elements
ELEMENTS=${#args[@]}
# echo each element
for (( i=0;i<$ELEMENTS;i++)); do
echo "args[$i]: \"${args[${i}]}\""
done
+8 -6
View File
@@ -1,10 +1,11 @@
{
"name": "@actions/exec",
"version": "1.0.0",
"version": "1.0.2",
"description": "Actions exec lib",
"keywords": [
"exec",
"actions"
"github",
"actions",
"exec"
],
"homepage": "https://github.com/actions/toolkit/tree/master/packages/exec",
"license": "MIT",
@@ -21,7 +22,8 @@
},
"repository": {
"type": "git",
"url": "git+https://github.com/actions/toolkit.git"
"url": "git+https://github.com/actions/toolkit.git",
"directory": "packages/exec"
},
"scripts": {
"test": "echo \"Error: run tests from root\" && exit 1",
@@ -30,7 +32,7 @@
"bugs": {
"url": "https://github.com/actions/toolkit/issues"
},
"devDependencies": {
"@actions/io": "^1.0.0"
"dependencies": {
"@actions/io": "^1.0.1"
}
}
+22 -1
View File
@@ -1,8 +1,11 @@
import * as os from 'os'
import * as events from 'events'
import * as child from 'child_process'
import * as path from 'path'
import * as stream from 'stream'
import * as im from './interfaces'
import * as io from '@actions/io'
import * as ioUtil from '@actions/io/lib/io-util'
/* eslint-disable @typescript-eslint/unbound-method */
@@ -216,7 +219,7 @@ export class ToolRunner extends events.EventEmitter {
// command line from libuv quoting rules would look like:
// foo.exe "myarg:\"my val\""
//
// 3) double-up slashes that preceed a quote,
// 3) double-up slashes that precede a quote,
// e.g. hello \world => "hello \world"
// hello\"world => "hello\\""world"
// hello\\"world => "hello\\\\""world"
@@ -392,6 +395,24 @@ export class ToolRunner extends events.EventEmitter {
* @returns number
*/
async exec(): Promise<number> {
// root the tool path if it is unrooted and contains relative pathing
if (
!ioUtil.isRooted(this.toolPath) &&
(this.toolPath.includes('/') ||
(IS_WINDOWS && this.toolPath.includes('\\')))
) {
// prefer options.cwd if it is specified, however options.cwd may also need to be rooted
this.toolPath = path.resolve(
process.cwd(),
this.options.cwd || process.cwd(),
this.toolPath
)
}
// if the tool is only a file name, then resolve it from the PATH
// otherwise verify it exists (add extension on Windows if necessary)
this.toolPath = await io.which(this.toolPath, true)
return new Promise<number>((resolve, reject) => {
this._debug(`exec tool: ${this.toolPath}`)
this._debug('arguments:')
+26 -17
View File
@@ -4,38 +4,47 @@
## Usage
Returns an [Octokit SDK] client. See https://octokit.github.io/rest.js for the API.
Returns an Octokit client. See https://octokit.github.io/rest.js for the API.
```
```js
const github = require('@actions/github');
const core = require('@actions/core');
// This should be a token with access to your repository scoped in as a secret.
const myToken = core.getInput('myToken');
async function run() {
// This should be a token with access to your repository scoped in as a secret.
// The YML workflow will need to set myToken with the GitHub Secret Token
// myToken: ${{ secrets.GITHUB_TOKEN }}
// https://help.github.com/en/actions/automating-your-workflow-with-github-actions/authenticating-with-the-github_token#about-the-github_token-secret
const myToken = core.getInput('myToken');
const octokit = new github.GitHub(myToken);
const octokit = new github.GitHub(myToken);
const pulls = await octokit.pulls.get({
owner: 'octokit',
repo: 'rest.js',
pull_number: 123,
mediaType: {
format: 'diff'
}
});
const { data: pullRequest } = await octokit.pulls.get({
owner: 'octokit',
repo: 'rest.js',
pull_number: 123,
mediaType: {
format: 'diff'
}
});
console.log(pulls);
console.log(pullRequest);
}
run();
```
You can also make GraphQL requests:
You can pass client options (except `auth`, which is handled by the token argument), as specified by [Octokit](https://octokit.github.io/rest.js/), as a second argument to the `GitHub` constructor.
```
You can also make GraphQL requests. See https://github.com/octokit/graphql.js for the API.
```js
const result = await octokit.graphql(query, variables);
```
Finally, you can get the context of the current action:
```
```js
const github = require('@actions/github');
const context = github.context;
+17
View File
@@ -0,0 +1,17 @@
# @actions/github Releases
### 2.0.0
- Upgrade Octokit version to 4.x to include typescript types [#228](https://github.com/actions/toolkit/pull/228)
### 1.1.0
- Accept Octokit.Options in the GitHub constructor [#113](https://github.com/actions/toolkit/pull/113)
### 1.0.1
- Simplify WebPack configs by removing dynamic require - [#101](https://github.com/actions/toolkit/pull/101)
### 1.0.0
- Initial release
+53 -33
View File
@@ -1,6 +1,6 @@
{
"name": "@actions/github",
"version": "1.0.0",
"version": "2.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -384,64 +384,76 @@
}
},
"@octokit/endpoint": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-5.3.1.tgz",
"integrity": "sha512-4mKqSQfeTRFpQMUGIUG1ewdQT64b2YpvjG2dE1x7nhQupdI/AjdgdcIsmPtRFEXlih/uLQLRWJL4FrivpQdC7A==",
"version": "5.5.1",
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-5.5.1.tgz",
"integrity": "sha512-nBFhRUb5YzVTCX/iAK1MgQ4uWo89Gu0TH00qQHoYRCsE12dWcG1OiLd7v2EIo2+tpUKPMOQ62QFy9hy9Vg2ULg==",
"requires": {
"deepmerge": "4.0.0",
"@octokit/types": "^2.0.0",
"is-plain-object": "^3.0.0",
"universal-user-agent": "^3.0.0",
"url-template": "^2.0.8"
"universal-user-agent": "^4.0.0"
},
"dependencies": {
"universal-user-agent": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-3.0.0.tgz",
"integrity": "sha512-T3siHThqoj5X0benA5H0qcDnrKGXzU8TKoX15x/tQHw1hQBvIEBHjxQ2klizYsqBOO/Q+WuxoQUihadeeqDnoA==",
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-4.0.0.tgz",
"integrity": "sha512-eM8knLpev67iBDizr/YtqkJsF3GK8gzDc6st/WKzrTuPtcsOKW/0IdL4cnMBsU69pOx0otavLWBDGTwg+dB0aA==",
"requires": {
"os-name": "^3.0.0"
"os-name": "^3.1.0"
}
}
}
},
"@octokit/graphql": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-2.1.3.tgz",
"integrity": "sha512-XoXJqL2ondwdnMIW3wtqJWEwcBfKk37jO/rYkoxNPEVeLBDGsGO1TCWggrAlq3keGt/O+C/7VepXnukUxwt5vA==",
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.3.1.tgz",
"integrity": "sha512-hCdTjfvrK+ilU2keAdqNBWOk+gm1kai1ZcdjRfB30oA3/T6n53UVJb7w0L5cR3/rhU91xT3HSqCd+qbvH06yxA==",
"requires": {
"@octokit/request": "^5.0.0",
"universal-user-agent": "^2.0.3"
"@octokit/request": "^5.3.0",
"@octokit/types": "^2.0.0",
"universal-user-agent": "^4.0.0"
},
"dependencies": {
"universal-user-agent": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-4.0.0.tgz",
"integrity": "sha512-eM8knLpev67iBDizr/YtqkJsF3GK8gzDc6st/WKzrTuPtcsOKW/0IdL4cnMBsU69pOx0otavLWBDGTwg+dB0aA==",
"requires": {
"os-name": "^3.1.0"
}
}
}
},
"@octokit/request": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.0.1.tgz",
"integrity": "sha512-SHOk/APYpfrzV1RNf7Ux8SZi+vZXhMIB2dBr4TQR6ExMX8R4jcy/0gHw26HLe1dWV7Wxe9WzYyDSEC0XwnoCSQ==",
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.3.1.tgz",
"integrity": "sha512-5/X0AL1ZgoU32fAepTfEoggFinO3rxsMLtzhlUX+RctLrusn/CApJuGFCd0v7GMFhF+8UiCsTTfsu7Fh1HnEJg==",
"requires": {
"@octokit/endpoint": "^5.1.0",
"@octokit/endpoint": "^5.5.0",
"@octokit/request-error": "^1.0.1",
"@octokit/types": "^2.0.0",
"deprecation": "^2.0.0",
"is-plain-object": "^3.0.0",
"node-fetch": "^2.3.0",
"once": "^1.4.0",
"universal-user-agent": "^3.0.0"
"universal-user-agent": "^4.0.0"
},
"dependencies": {
"universal-user-agent": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-3.0.0.tgz",
"integrity": "sha512-T3siHThqoj5X0benA5H0qcDnrKGXzU8TKoX15x/tQHw1hQBvIEBHjxQ2klizYsqBOO/Q+WuxoQUihadeeqDnoA==",
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-4.0.0.tgz",
"integrity": "sha512-eM8knLpev67iBDizr/YtqkJsF3GK8gzDc6st/WKzrTuPtcsOKW/0IdL4cnMBsU69pOx0otavLWBDGTwg+dB0aA==",
"requires": {
"os-name": "^3.0.0"
"os-name": "^3.1.0"
}
}
}
},
"@octokit/request-error": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-1.0.4.tgz",
"integrity": "sha512-L4JaJDXn8SGT+5G0uX79rZLv0MNJmfGa4vb4vy1NnpjSnWDLJRy6m90udGwvMmavwsStgbv2QNkPzzTCMmL+ig==",
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-1.2.0.tgz",
"integrity": "sha512-DNBhROBYjjV/I9n7A8kVkmQNkqFAMem90dSxqvPq57e2hBr7mNTX98y3R2zDpqMQHVRpBDjsvsfIGgBzy+4PAg==",
"requires": {
"@octokit/types": "^2.0.0",
"deprecation": "^2.0.0",
"once": "^1.4.0"
}
@@ -504,6 +516,14 @@
}
}
},
"@octokit/types": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-2.0.2.tgz",
"integrity": "sha512-StASIL2lgT3TRjxv17z9pAqbnI7HGu9DrJlg3sEBFfCLaMEqp+O3IQPUF6EZtQ4xkAu2ml6kMBBCtGxjvmtmuQ==",
"requires": {
"@types/node": ">= 8"
}
},
"@types/babel__core": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.2.tgz",
@@ -570,6 +590,11 @@
"@types/istanbul-lib-report": "*"
}
},
"@types/node": {
"version": "12.12.11",
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.11.tgz",
"integrity": "sha512-O+x6uIpa6oMNTkPuHDa9MhMMehlxLAd5QcOvKRjAFsBVpeFWTOPnXbDvILvFgFFZfQ1xh1EZi1FbXxUix+zpsQ=="
},
"@types/stack-utils": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz",
@@ -1262,11 +1287,6 @@
"integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
"dev": true
},
"deepmerge": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.0.0.tgz",
"integrity": "sha512-YZ1rOP5+kHor4hMAH+HRQnBQHg+wvS1un1hAOuIcxcBy0hzcUf6Jg2a1w65kpoOUnurOfZbERwjI1TfZxNjcww=="
},
"define-properties": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
+6 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@actions/github",
"version": "1.0.0",
"version": "2.0.0",
"description": "Actions github lib",
"keywords": [
"github",
@@ -21,18 +21,21 @@
},
"repository": {
"type": "git",
"url": "git+https://github.com/actions/toolkit.git"
"url": "git+https://github.com/actions/toolkit.git",
"directory": "packages/github"
},
"scripts": {
"test": "jest",
"build": "tsc",
"format": "prettier --write **/*.ts",
"format-check": "prettier --check **/*.ts",
"tsc": "tsc"
},
"bugs": {
"url": "https://github.com/actions/toolkit/issues"
},
"dependencies": {
"@octokit/graphql": "^2.0.1",
"@octokit/graphql": "^4.3.1",
"@octokit/rest": "^16.15.0"
},
"devDependencies": {
-36
View File
@@ -1,36 +0,0 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
declare module '@octokit/graphql' {
export interface GraphQlQueryResponse {
data: {[key: string]: any} | null
errors?: [
{
message: string
path: [string]
extensions: {[key: string]: any}
locations: [
{
line: number
column: number
}
]
}
]
}
export interface GraphQLError {
message: string
locations?: {line: number; column: number}[]
path?: (string | number)[]
extensions?: {
[key: string]: any
}
}
export interface Variables {
[key: string]: any
}
export function defaults(
options: any
): (query: string, variables?: Variables) => Promise<GraphQlQueryResponse>
}
+13 -5
View File
@@ -1,7 +1,7 @@
// Originally pulled from https://github.com/JasonEtco/actions-toolkit/blob/master/src/context.ts
import {WebhookPayload} from './interfaces'
/* eslint-disable @typescript-eslint/no-require-imports */
import {readFileSync, existsSync} from 'fs'
import {EOL} from 'os'
export class Context {
/**
@@ -20,9 +20,17 @@ export class Context {
* Hydrate the context from the environment
*/
constructor() {
this.payload = process.env.GITHUB_EVENT_PATH
? require(process.env.GITHUB_EVENT_PATH)
: {}
this.payload = {}
if (process.env.GITHUB_EVENT_PATH) {
if (existsSync(process.env.GITHUB_EVENT_PATH)) {
this.payload = JSON.parse(
readFileSync(process.env.GITHUB_EVENT_PATH, {encoding: 'utf8'})
)
} else {
const path = process.env.GITHUB_EVENT_PATH
process.stdout.write(`GITHUB_EVENT_PATH ${path} does not exist${EOL}`)
}
}
this.eventName = process.env.GITHUB_EVENT_NAME as string
this.sha = process.env.GITHUB_SHA as string
this.ref = process.env.GITHUB_REF as string
+12 -8
View File
@@ -1,5 +1,11 @@
// Originally pulled from https://github.com/JasonEtco/actions-toolkit/blob/master/src/github.ts
import {GraphQlQueryResponse, Variables, defaults} from '@octokit/graphql'
import {graphql} from '@octokit/graphql'
// we need this type to set up a property on the GitHub object
// that has token authorization
// (it is not exported from octokit by default)
import {graphql as GraphQL} from '@octokit/graphql/dist-types/types'
import Octokit from '@octokit/rest'
import * as Context from './context'
@@ -9,14 +15,12 @@ Octokit.prototype = new Octokit()
export const context = new Context.Context()
export class GitHub extends Octokit {
graphql: (
query: string,
variables?: Variables
) => Promise<GraphQlQueryResponse>
graphql: GraphQL
constructor(token: string) {
super({auth: `token ${token}`})
this.graphql = defaults({
constructor(token: string, opts: Omit<Octokit.Options, 'auth'> = {}) {
super({...opts, auth: `token ${token}`})
this.graphql = graphql.defaults({
headers: {authorization: `token ${token}`}
})
}
+4 -4
View File
@@ -8,7 +8,7 @@
Recursively make a directory. Follows rules specified in [man mkdir](https://linux.die.net/man/1/mkdir) with the `-p` option specified:
```
```js
const io = require('@actions/io');
await io.mkdirP('path/to/make');
@@ -18,7 +18,7 @@ await io.mkdirP('path/to/make');
Copy or move files or folders. Follows rules specified in [man cp](https://linux.die.net/man/1/cp) and [man mv](https://linux.die.net/man/1/mv):
```
```js
const io = require('@actions/io');
// Recursive must be true for directories
@@ -32,7 +32,7 @@ await io.mv('path/to/file', 'path/to/dest');
Remove a file or folder recursively. Follows rules specified in [man rm](https://linux.die.net/man/1/rm) with the `-r` and `-f` rules specified.
```
```js
const io = require('@actions/io');
await io.rmRF('path/to/directory');
@@ -43,7 +43,7 @@ await io.rmRF('path/to/file');
Get the path to a tool and resolves via paths. Follows the rules specified in [man which](https://linux.die.net/man/1/which).
```
```js
const exec = require('@actions/exec');
const io = require('@actions/io');
+5
View File
@@ -0,0 +1,5 @@
# @actions/io Releases
### 1.0.0
- Initial release
+3 -3
View File
@@ -88,7 +88,7 @@ describe('cp', () => {
})
it('copies directory into non-existing destination with -r', async () => {
const root: string = path.join(getTestTemp(), 'cp_with_-r_nonexisting_dest')
const root: string = path.join(getTestTemp(), 'cp_with_-r_nonexistent_dest')
const sourceFolder: string = path.join(root, 'cp_source')
const sourceFile: string = path.join(sourceFolder, 'cp_source_file')
@@ -245,7 +245,7 @@ describe('mv', () => {
it('moves directory into non-existing destination', async () => {
const root: string = path.join(
getTestTemp(),
' mv_with_-r_nonexisting_dest'
' mv_with_-r_nonexistent_dest'
)
const sourceFolder: string = path.join(root, ' mv_source')
const sourceFile: string = path.join(sourceFolder, ' mv_source_file')
@@ -321,7 +321,7 @@ describe('rmRF', () => {
await assertNotExists(testPath)
})
it('removes folder that doesnt exist with rmRF', async () => {
it('removes folder that does not exist with rmRF', async () => {
const testPath = path.join(getTestTemp(), 'testFolder')
await assertNotExists(testPath)
+6 -4
View File
@@ -1,10 +1,11 @@
{
"name": "@actions/io",
"version": "1.0.0",
"version": "1.0.1",
"description": "Actions io lib",
"keywords": [
"io",
"actions"
"github",
"actions",
"io"
],
"homepage": "https://github.com/actions/toolkit/tree/master/packages/io",
"license": "MIT",
@@ -21,7 +22,8 @@
},
"repository": {
"type": "git",
"url": "git+https://github.com/actions/toolkit.git"
"url": "git+https://github.com/actions/toolkit.git",
"directory": "packages/io"
},
"scripts": {
"test": "echo \"Error: run tests from root\" && exit 1",
+2 -2
View File
@@ -226,9 +226,9 @@ export async function which(tool: string, check?: boolean): Promise<string> {
// build the list of directories
//
// Note, technically "where" checks the current directory on Windows. From a task lib perspective,
// Note, technically "where" checks the current directory on Windows. From a toolkit perspective,
// it feels like we should not do this. Checking the current directory seems like more of a use
// case of a shell, and the which() function exposed by the task lib should strive for consistency
// case of a shell, and the which() function exposed by the toolkit should strive for consistency
// across platforms.
const directories: string[] = []
+13 -13
View File
@@ -8,29 +8,29 @@
You can use this to download tools (or other files) from a download URL:
```
```js
const tc = require('@actions/tool-cache');
const node12Path = await tc.downloadTool('http://nodejs.org/dist/v12.7.0/node-v12.7.0-linux-x64.tar.gz');
const node12Path = await tc.downloadTool('https://nodejs.org/dist/v12.7.0/node-v12.7.0-linux-x64.tar.gz');
```
#### Extract
These can then be extracted in platform specific ways:
```
```js
const tc = require('@actions/tool-cache');
if (process.platform === 'win32') {
tc.downloadTool('http://nodejs.org/dist/v12.7.0/node-v12.7.0-win-x64.zip');
const node12Path = tc.downloadTool('https://nodejs.org/dist/v12.7.0/node-v12.7.0-win-x64.zip');
const node12ExtractedFolder = await tc.extractZip(node12Path, 'path/to/extract/to');
// Or alternately
tc.downloadTool('http://nodejs.org/dist/v12.7.0/node-v12.7.0-win-x64.7z');
const node12Path = tc.downloadTool('https://nodejs.org/dist/v12.7.0/node-v12.7.0-win-x64.7z');
const node12ExtractedFolder = await tc.extract7z(node12Path, 'path/to/extract/to');
}
else {
const node12Path = await tc.downloadTool('http://nodejs.org/dist/v12.7.0/node-v12.7.0-linux-x64.tar.gz');
const node12Path = await tc.downloadTool('https://nodejs.org/dist/v12.7.0/node-v12.7.0-linux-x64.tar.gz');
const node12ExtractedFolder = await tc.extractTar(node12Path, 'path/to/extract/to');
}
```
@@ -41,11 +41,11 @@ Finally, you can cache these directories in our tool-cache. This is useful if yo
You'll often want to add it to the path as part of this step:
```
```js
const tc = require('@actions/tool-cache');
const core = require('@actions/core');
const node12Path = await tc.downloadTool('http://nodejs.org/dist/v12.7.0/node-v12.7.0-linux-x64.tar.gz');
const node12Path = await tc.downloadTool('https://nodejs.org/dist/v12.7.0/node-v12.7.0-linux-x64.tar.gz');
const node12ExtractedFolder = await tc.extractTar(node12Path, 'path/to/extract/to');
const cachedPath = await tc.cacheDir(node12ExtractedFolder, 'node', '12.7.0');
@@ -54,17 +54,17 @@ core.addPath(cachedPath);
You can also cache files for reuse.
```
```js
const tc = require('@actions/tool-cache');
tc.cacheFile('path/to/exe', 'destFileName.exe', 'myExeName', '1.1.0');
const cachedPath = await tc.cacheFile('path/to/exe', 'destFileName.exe', 'myExeName', '1.1.0');
```
#### Find
Finally, you can find directories and files you've previously cached:
```
```js
const tc = require('@actions/tool-cache');
const core = require('@actions/core');
@@ -74,7 +74,7 @@ core.addPath(nodeDirectory);
You can even find all cached versions of a tool:
```
```js
const tc = require('@actions/tool-cache');
const allNodeVersions = tc.findAllVersions('node');
+10
View File
@@ -0,0 +1,10 @@
# @actions/tool-cache Releases
### 1.1.2
- [Use zip and unzip from PATH](https://github.com/actions/toolkit/pull/161)
- [Support custom flags for `extractTar`](https://github.com/actions/toolkit/pull/48)
### 1.0.0
- Initial release
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -142,6 +142,36 @@ describe('@actions/tool-cache', function() {
}
})
it('extracts a 7z to a directory that does not exist', async () => {
const tempDir = path.join(__dirname, 'test-install-7z')
const destDir = path.join(tempDir, 'not-exist')
try {
await io.mkdirP(tempDir)
// copy the 7z file to the test dir
const _7zFile: string = path.join(tempDir, 'test.7z')
await io.cp(path.join(__dirname, 'data', 'test.7z'), _7zFile)
// extract/cache
const extPath: string = await tc.extract7z(_7zFile, destDir)
await tc.cacheDir(extPath, 'my-7z-contents', '1.1.0')
const toolPath: string = tc.find('my-7z-contents', '1.1.0')
expect(extPath).toContain('not-exist')
expect(fs.existsSync(toolPath)).toBeTruthy()
expect(fs.existsSync(`${toolPath}.complete`)).toBeTruthy()
expect(fs.existsSync(path.join(toolPath, 'file.txt'))).toBeTruthy()
expect(
fs.existsSync(path.join(toolPath, 'file-with-ç-character.txt'))
).toBeTruthy()
expect(
fs.existsSync(path.join(toolPath, 'folder', 'nested-file.txt'))
).toBeTruthy()
} finally {
await io.rmRF(tempDir)
}
})
it('extract 7z using custom 7z tool', async function() {
const tempDir = path.join(
__dirname,
@@ -197,6 +227,95 @@ describe('@actions/tool-cache', function() {
await io.rmRF(tempDir)
}
})
} else {
it('extract .tar.gz', async () => {
const tempDir = path.join(tempPath, 'test-install-tar.gz')
await io.mkdirP(tempDir)
// copy the .tar.gz file to the test dir
const _tgzFile: string = path.join(tempDir, 'test.tar.gz')
await io.cp(path.join(__dirname, 'data', 'test.tar.gz'), _tgzFile)
// extract/cache
const extPath: string = await tc.extractTar(_tgzFile)
await tc.cacheDir(extPath, 'my-tgz-contents', '1.1.0')
const toolPath: string = tc.find('my-tgz-contents', '1.1.0')
expect(fs.existsSync(toolPath)).toBeTruthy()
expect(fs.existsSync(`${toolPath}.complete`)).toBeTruthy()
expect(fs.existsSync(path.join(toolPath, 'file.txt'))).toBeTruthy()
expect(
fs.existsSync(path.join(toolPath, 'file-with-ç-character.txt'))
).toBeTruthy()
expect(
fs.existsSync(path.join(toolPath, 'folder', 'nested-file.txt'))
).toBeTruthy()
expect(
fs.readFileSync(
path.join(toolPath, 'folder', 'nested-file.txt'),
'utf8'
)
).toBe('folder/nested-file.txt contents')
})
it('extract .tar.gz to a directory that does not exist', async () => {
const tempDir = path.join(tempPath, 'test-install-tar.gz')
const destDir = path.join(tempDir, 'not-exist')
await io.mkdirP(tempDir)
// copy the .tar.gz file to the test dir
const _tgzFile: string = path.join(tempDir, 'test.tar.gz')
await io.cp(path.join(__dirname, 'data', 'test.tar.gz'), _tgzFile)
// extract/cache
const extPath: string = await tc.extractTar(_tgzFile, destDir)
await tc.cacheDir(extPath, 'my-tgz-contents', '1.1.0')
const toolPath: string = tc.find('my-tgz-contents', '1.1.0')
expect(extPath).toContain('not-exist')
expect(fs.existsSync(toolPath)).toBeTruthy()
expect(fs.existsSync(`${toolPath}.complete`)).toBeTruthy()
expect(fs.existsSync(path.join(toolPath, 'file.txt'))).toBeTruthy()
expect(
fs.existsSync(path.join(toolPath, 'file-with-ç-character.txt'))
).toBeTruthy()
expect(
fs.existsSync(path.join(toolPath, 'folder', 'nested-file.txt'))
).toBeTruthy()
expect(
fs.readFileSync(
path.join(toolPath, 'folder', 'nested-file.txt'),
'utf8'
)
).toBe('folder/nested-file.txt contents')
})
it('extract .tar.xz', async () => {
const tempDir = path.join(tempPath, 'test-install-tar.xz')
await io.mkdirP(tempDir)
// copy the .tar.gz file to the test dir
const _txzFile: string = path.join(tempDir, 'test.tar.xz')
await io.cp(path.join(__dirname, 'data', 'test.tar.xz'), _txzFile)
// extract/cache
const extPath: string = await tc.extractTar(_txzFile, undefined, 'x')
await tc.cacheDir(extPath, 'my-txz-contents', '1.1.0')
const toolPath: string = tc.find('my-txz-contents', '1.1.0')
expect(fs.existsSync(toolPath)).toBeTruthy()
expect(fs.existsSync(`${toolPath}.complete`)).toBeTruthy()
expect(fs.existsSync(path.join(toolPath, 'bar.txt'))).toBeTruthy()
expect(
fs.existsSync(path.join(toolPath, 'foo', 'hello.txt'))
).toBeTruthy()
expect(
fs.readFileSync(path.join(toolPath, 'foo', 'hello.txt'), 'utf8')
).toBe('foo/hello: world')
})
}
it('installs a zip and finds it', async () => {
@@ -231,7 +350,7 @@ describe('@actions/tool-cache', function() {
]
await exec.exec(`"${powershellPath}"`, args)
} else {
const zipPath: string = path.join(__dirname, 'externals', 'zip')
const zipPath: string = await io.which('zip')
await exec.exec(`"${zipPath}`, [zipFile, '-r', '.'], {cwd: stagingDir})
}
@@ -282,7 +401,7 @@ describe('@actions/tool-cache', function() {
]
await exec.exec(`"${powershellPath}"`, args)
} else {
const zipPath = path.join(__dirname, 'externals', 'zip')
const zipPath: string = await io.which('zip')
await exec.exec(zipPath, [zipFile, '-r', '.'], {cwd: stagingDir})
}
@@ -307,6 +426,60 @@ describe('@actions/tool-cache', function() {
}
})
it('extract zip to a directory that does not exist', async function() {
const tempDir = path.join(__dirname, 'test-install-zip')
try {
await io.mkdirP(tempDir)
// stage the layout for a zip file:
// file.txt
// folder/nested-file.txt
const stagingDir = path.join(tempDir, 'zip-staging')
await io.mkdirP(path.join(stagingDir, 'folder'))
fs.writeFileSync(path.join(stagingDir, 'file.txt'), '')
fs.writeFileSync(path.join(stagingDir, 'folder', 'nested-file.txt'), '')
// create the zip
const zipFile = path.join(tempDir, 'test.zip')
await io.rmRF(zipFile)
if (IS_WINDOWS) {
const escapedStagingPath = stagingDir.replace(/'/g, "''") // double-up single quotes
const escapedZipFile = zipFile.replace(/'/g, "''")
const powershellPath = await io.which('powershell', true)
const args = [
'-NoLogo',
'-Sta',
'-NoProfile',
'-NonInteractive',
'-ExecutionPolicy',
'Unrestricted',
'-Command',
`$ErrorActionPreference = 'Stop' ; Add-Type -AssemblyName System.IO.Compression.FileSystem ; [System.IO.Compression.ZipFile]::CreateFromDirectory('${escapedStagingPath}', '${escapedZipFile}')`
]
await exec.exec(`"${powershellPath}"`, args)
} else {
const zipPath: string = await io.which('zip')
await exec.exec(zipPath, [zipFile, '-r', '.'], {cwd: stagingDir})
}
const destDir = path.join(tempDir, 'not-exist')
const extPath: string = await tc.extractZip(zipFile, destDir)
await tc.cacheDir(extPath, 'foo', '1.1.0')
const toolPath: string = tc.find('foo', '1.1.0')
expect(extPath).toContain('not-exist')
expect(fs.existsSync(toolPath)).toBeTruthy()
expect(fs.existsSync(`${toolPath}.complete`)).toBeTruthy()
expect(fs.existsSync(path.join(toolPath, 'file.txt'))).toBeTruthy()
expect(
fs.existsSync(path.join(toolPath, 'folder', 'nested-file.txt'))
).toBeTruthy()
} finally {
await io.rmRF(tempDir)
}
})
it('works with a 502 temporary failure', async function() {
nock('http://example.com')
.get('/temp502')
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@actions/tool-cache",
"version": "1.0.0",
"version": "1.1.2",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
+9 -7
View File
@@ -1,10 +1,11 @@
{
"name": "@actions/tool-cache",
"version": "1.0.0",
"version": "1.1.2",
"description": "Actions tool-cache lib",
"keywords": [
"exec",
"actions"
"github",
"actions",
"exec"
],
"homepage": "https://github.com/actions/toolkit/tree/master/packages/exec",
"license": "MIT",
@@ -22,7 +23,8 @@
},
"repository": {
"type": "git",
"url": "git+https://github.com/actions/toolkit.git"
"url": "git+https://github.com/actions/toolkit.git",
"directory": "packages/tool-cache"
},
"scripts": {
"test": "echo \"Error: run tests from root\" && exit 1",
@@ -32,9 +34,9 @@
"url": "https://github.com/actions/toolkit/issues"
},
"dependencies": {
"@actions/core": "^1.0.0",
"@actions/exec": "^1.0.0",
"@actions/io": "^1.0.0",
"@actions/core": "^1.1.0",
"@actions/exec": "^1.0.1",
"@actions/io": "^1.0.1",
"semver": "^6.1.0",
"typed-rest-client": "^1.4.0",
"uuid": "^3.3.2"
Binary file not shown.
+12 -7
View File
@@ -130,7 +130,7 @@ export async function extract7z(
ok(IS_WINDOWS, 'extract7z() not supported on current OS')
ok(file, 'parameter "file" is required')
dest = dest || (await _createExtractFolder(dest))
dest = await _createExtractFolder(dest)
const originalCwd = process.cwd()
process.chdir(dest)
@@ -183,20 +183,25 @@ export async function extract7z(
}
/**
* Extract a tar
* Extract a compressed tar archive
*
* @param file path to the tar
* @param dest destination directory. Optional.
* @param flags flags for the tar command to use for extraction. Defaults to 'xz' (extracting gzipped tars). Optional.
* @returns path to the destination directory
*/
export async function extractTar(file: string, dest?: string): Promise<string> {
export async function extractTar(
file: string,
dest?: string,
flags: string = 'xz'
): Promise<string> {
if (!file) {
throw new Error("parameter 'file' is required")
}
dest = dest || (await _createExtractFolder(dest))
dest = await _createExtractFolder(dest)
const tarPath: string = await io.which('tar', true)
await exec(`"${tarPath}"`, ['xzC', dest, '-f', file])
await exec(`"${tarPath}"`, [flags, '-C', dest, '-f', file])
return dest
}
@@ -213,7 +218,7 @@ export async function extractZip(file: string, dest?: string): Promise<string> {
throw new Error("parameter 'file' is required")
}
dest = dest || (await _createExtractFolder(dest))
dest = await _createExtractFolder(dest)
if (IS_WINDOWS) {
await extractZipWin(file, dest)
@@ -246,7 +251,7 @@ async function extractZipWin(file: string, dest: string): Promise<void> {
}
async function extractZipNix(file: string, dest: string): Promise<void> {
const unzipPath = path.join(__dirname, '..', 'scripts', 'externals', 'unzip')
const unzipPath = await io.which('unzip')
await exec(`"${unzipPath}"`, [file], {cwd: dest})
}