Compare commits

...

85 Commits

Author SHA1 Message Date
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
Danny McCormick a40bce7c8d Publish
Main workflow / Run Windows (push) Has been cancelled
Main workflow / Run Ubuntu (push) Has been cancelled
- @actions/core@1.0.0
 - @actions/exec@1.0.0
 - @actions/github@1.0.0
 - @actions/io@1.0.0
 - @actions/tool-cache@1.0.0
2019-08-07 13:33:06 -04:00
Danny McCormick d7f00ea0fd Update package versions to 1.0.0 (#42)
* Update package versions to 1.0.0

* Fix package-lock
2019-08-07 12:56:34 -04:00
Shawn Napora 46b418103a Quick typo fixes (#41) 2019-08-07 09:56:03 -04:00
Danny McCormick 3004de4b40 Remove neutral exit code references 2019-08-06 09:12:30 -04:00
Danny McCormick da76d1fd39 Update versioning guidance (#40)
* Update versioning guidance

* Missing step
2019-08-06 08:17:49 -04:00
Danny McCormick fc9958ec6a Fix exports 2019-08-05 08:53:51 -04:00
Danny McCormick f9ab88cdc3 Fix link 2019-08-04 14:12:04 -04:00
Danny McCormick 293aa1ae02 Update package docs (#38)
* Update README.md

* Add exec guidance

* Add io guidance

* Add tool-cache guidance

* Readability

* Readability

* Readability

* Nit

* Nit

* Nit
2019-08-04 09:00:04 -04:00
Bryan MacFarlane 0bb10220a7 workflow allows for e2e testing 2019-08-04 08:56:16 -04:00
Bryan MacFarlane 3ce161a815 lower case v1 2019-08-03 12:17:54 -04:00
Bryan MacFarlane 571d130f50 Branching Strategy for Production Modules (#39)
* starting walkthrough docs
2019-08-03 12:15:05 -04:00
Danny McCormick 027a230b99 Correct github interfaces 2019-08-02 15:25:51 -04:00
Danny McCormick dfccb207bf Call out node_modules behavior (#37)
* Update node12-action.md

* Update node12-action.md

* Update node12-action.md
2019-08-01 17:08:30 -04:00
Bryan MacFarlane 3977d56fd3 Create Actions Walkthrough (#36)
* starting walkthrough docs

* versioning
2019-08-01 11:26:17 -04:00
Danny McCormick 4a7f2143e6 Get token from input in doc 2019-07-31 16:19:47 -04:00
Danny McCormick 5218a83722 Check the right variables (#35) 2019-07-30 12:31:36 -04:00
Danny McCormick 2a2b51f939 Add github package (#32)
* Add github package

* Docs

* Feedback

* Accidentally added extra

* Allow octokit to be extended

* Respond to feedback

* Feedback
2019-07-29 13:09:32 -04:00
Danny McCormick 4f5cf60872 Update workflow.yml (#34) 2019-07-25 23:08:57 -04:00
Danny McCormick 0a64f32b4e Update workflow.yml 2019-07-25 23:00:31 -04:00
Danny McCormick 9aacf6aeaa Propose github package spec (#31)
* Create github-package.md

* Add getters for issue/repo

* Update github-package.md
2019-07-22 16:26:28 -04:00
Danny McCormick 5a1b82748e Merge branch 'master' of https://github.com/actions/toolkit 2019-07-11 16:14:13 -04:00
Danny McCormick d78ededdcb Remove legacy workflow 2019-07-11 16:14:02 -04:00
Danny McCormick df0101c5c3 v2 workflow (#30)
* Create workflow.yml

* Try lower version of node

* Try running tests serially

* Start jest directly

* Start jest directly

* Start jest directly

* Start jest directly

* Dont test exec on windows right now

* Dont test exec on windows right now
2019-07-11 16:12:50 -04:00
Danny McCormick d293c20cc9 Dont stamp over stdout (#29) 2019-07-11 15:02:45 -04:00
Bryan MacFarlane c4a488fc74 add logo (#27)
add logo
2019-07-10 14:02:56 -04:00
Danny McCormick d919136160 Fix cp and mv (#26) 2019-07-09 09:56:01 -04:00
Danny McCormick e85d20fbb0 Combine exit into core; Remove toolkit (#21)
* Combine exit into core

* Remove toolkit

* Format

* Format

* Try to fix diff

* Try to fix diff

* Format

* Add gitattributes

* Use unix endings

* gitattributes is not a txt file

* Renormalize line endings
2019-06-25 13:54:41 -04:00
75 changed files with 7017 additions and 1298 deletions
-39
View File
@@ -1,39 +0,0 @@
workflow "CI" {
on = "push"
resolves = ["Format", "Lint", "Test"]
}
action "Dependencies" {
uses = "actions/npm@v2.0.0"
args = "ci"
}
action "Bootstrap" {
needs = "Dependencies"
uses = "actions/npm@v2.0.0"
args = "run bootstrap"
}
action "Compile" {
needs = "Bootstrap"
uses = "actions/npm@v2.0.0"
args = "run build"
}
action "Format" {
needs = "Dependencies"
uses = "actions/npm@v2.0.0"
args = "run format-check"
}
action "Lint" {
needs = "Dependencies"
uses = "actions/npm@v2.0.0"
args = "run lint"
}
action "Test" {
needs = "Compile"
uses = "actions/npm@v2.0.0"
args = "test"
}
+81
View File
@@ -0,0 +1,81 @@
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
macOS:
name: Run macOS
runs-on: macos-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
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
+25 -36
View File
@@ -1,53 +1,42 @@
# Actions Toolkit 🛠
<p align="center">
<img src="res/at-logo.png">
</p>
<p align="center">
<a href="https://github.com/actions/toolkit"><img alt="GitHub Actions status" src="https://github.com/actions/toolkit/workflows/Main%20workflow/badge.svg"></a>
</p>
## GitHub Actions Toolkit
The GitHub Actions ToolKit provides a set of packages to make creating actions easier and drive consistency.
## Packages
The toolkit provides five separate packages. See the docs for each action.
| Package | Description |
| ------- | ----------- |
| [@actions/core](packages/core) | Core functions for setting results, logging, secrets and environment variables |
| [@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/exit](packages/exit) | Provides utilities for exiting from an action |
| [@actions/io](packages/io) | Core functions for CLI filesystem scenarios |
| [@actions/tool-cache](packages/tool-cache) | Functions necessary for downloading and caching tools |
| [@actions/toolkit](packages/toolkit) | A general-purpose toolkit for writing actions |
| [@actions/github](packages/github) | An Octokit client hydrated with the context that the current action is being run in |
## Development
## Creating an Action with the Toolkit
This repository uses [Lerna](https://github.com/lerna/lerna#readme) to manage multiple packages. Read the documentation there to begin contributing.
Actions are units of work which can either run in a container or on the host machine.
Note that before a PR will be accepted, you must ensure:
- all tests are passing
- `npm run format` reports no issues
- `npm run lint` reports no issues
[Choosing an action type](docs/action-types.md): Outlines the differences and why you would want to create a JavaScript or a container based action.
### Useful Scripts
[JavaScript Action Walkthrough using a Template](docs/javascript-action.md): A full walkthrough creating an action using the toolkit along with TypeScript for compile time support and Jest for unit testing. It also covers a branching strategy for versioning and safely testing and releasing an action.
- `npm run bootstrap` This runs `lerna bootstrap` which will install dependencies in this repository's packages and cross-link packages where necessary.
- `npm run build` This compiles TypeScript code in each package (this is especially important if one package relies on changes in another when you're running tests). This is just an alias for `lerna run tsc`.
- `npm run format` This checks that formatting has been applied with Prettier.
- `npm test` This runs all Jest tests in all packages in this repository.
- If you need to run tests for only one package, you can pass normal Jest CLI options:
```console
$ npm test -- packages/toolkit
```
- `npm run create-package [name]` This runs a script that automates a couple of parts of creating a new package.
[Docker Action Walkthrough](docs/container-action.md): Create an action that is delivered as a container and run with docker.
### Creating a Package
[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.
1. In a new branch, create a new Lerna package:
[Versioning](docs/action-versioning.md): Recommendations on versioning, releases and tagging your action.
```console
$ npm run create-package new-package
```
## Contributing
This will ask you some questions about the new package. Start with `0.0.0` as the first version (look generally at some of the other packages for how the package.json is structured).
2. Add `tsc` script to the new package's package.json file:
```json
"scripts": {
"tsc": "tsc"
}
```
3. Start developing 😄 and open a pull request.
We welcome contributions. See [how to contribute](docs/contribute.md).
+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/articles/virtual-environments-for-github-actions#creating-and-using-secrets-encrypted-variables) `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/articles/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/articles/virtual-environments-for-github-actions#creating-and-using-secrets-encrypted-variables) `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/articles/managing-a-workflow-run#downloading-logs-and-artifacts) and [Web Logs](https://help.github.com/en/articles/managing-a-workflow-run#viewing-logs-to-diagnose-failures).
+45
View File
@@ -0,0 +1,45 @@
# Action Types
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 its dependencies packaged up as a container.
Both have access to the workspace and the github event payload and context.
## Why would I choose a Docker action?
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 its dependencies.
Docker actions are currently limited to Linux only.
## Why would I choose a host action?
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 its unit of work on each environment (node8+ubuntu16, node8+windows-2019, etc.)
```yaml
on: push
jobs:
build:
strategy:
matrix:
node: [8.x, 10.x]
os: [ubuntu-16.04, windows-2019]
runs-on: ${{matrix.os}}
actions:
- uses: actions/setup-node@master
with:
version: ${{matrix.node}}
- run: |
npm install
- run: |
npm test
- 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 its PATH or using a setup-* action to acquire it on demand.
+49
View File
@@ -0,0 +1,49 @@
# Versioning
Actions are downloaded and run from the GitHub graph of repos. The workflow references an action use a ref.
Examples:
```yaml
steps:
- uses: actions/setup-node@74bc508
- uses: actions/setup-node@v1
- uses: actions/setup-node@master # not recommended
```
Binding to the immutable sha1 of a released version is the safest for stability and security.
Binding to a specific major version allows for receiving critical fixes and security patches while still maintaining compatibility and the assurance your workflow should still work.
Binding to master is convenient but if a new major version is released which breaks compatibility, your workflow could break.
# 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.
See [Git-Basics-Tagging](https://git-scm.com/book/en/v2/Git-Basics-Tagging)
# Sample Workflow
This illustrates one possible versioning workflow which the walk through covered.
![versioning](assets/action-releases.png)
+1
View File
@@ -0,0 +1 @@
<mxfile modified="2019-09-04T11:27:58.307Z" host="www.draw.io" agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36" etag="MFzGVuAX-y50cwbudjLg" version="11.2.5" type="device" pages="1"><diagram name="Page-1" id="ff44883e-f642-bcb2-894b-16b3d25a3f0b">7Vtbc5s4GP01nj4lA+L+WCdp96E70910uu1TB4OMmWDkEdhx9tfvJ5AMSPIta4Mbx5mJ0R10zncVHll38/VnGi5mf5IYZyNkxOuRdT9CyHds+M8qXuoKxzPqioSmcV1lNhWP6b+YV4puyzTGRadjSUhWpotuZUTyHEdlpy6klDx3u01J1l11ESZYqXiMwkzU3jpN/T9pXM54vekGTcMfOE1mfHEfuXXDJIyeEkqWOV9xhKxp9amb56GYiz9qMQtj8tyqsh5G1h0lpKyv5us7nLHNFRsnxpUv4m5H1nhWzjMomHBZNX/aMtg8ZDA8HMV52V5u23xTYxJFQWAZNnanTngTKPPjGHaWF3OSw9c4WtIVjvmKrcWLMqQlJwMwwRrjPP7I0IRylIVFkUZ15ac0E2Og1B5RlJQ84TuSEVotbxnVZ9MisGSPPSX5ZjVUTVXSlx+beaHwEwoGUIEX7xm9jU3pRZTWafmjdd0aBaVmECuIMeo+C2TIkkZYv7mIS0JIE1zqu/BZ2Ka3puXYfcZkjuHOoQPFWVimq64QhFxqkk2/zdCvJIUbRQYXcFsIKpdvT8i3mKJ+DD6q4Q9ctG6jqapYdRjDTPfUFNvHpp2c0WG/E/kNy1rE+tkm3RaWvYox1n7GoH4YY0mM8WXG1Pd4FsbUC63CbMnvPlLVIEwBxoVR53mWlvhxEVZ7+gzmrcugSrVX1KoYIrS3qVM+bvVRiGR3iWSxicIsTXKm5wBeTFkHYGVrqqnD/nbxYIVpidc7MeStyO9isbHOz42lE11mbRtnbEe9g9cx4Kjm7NLEeY8qf63ZeJVAuxcj0E4gkcjqTaCRKtBoUIE2BxZo27scgbZUcKyrBseRwPHRcODYKjjOVYPjyVpsSFNov5vCI0yhvd8Uuv2YQs8azBS6qkBrWHRFAu3awwl08ovef//7r+Rhtfj+7UtBvkR3N5roY4TcDFYdT+AiqS5omEdwbx9FCyy0aYTvcM5wyicF+wKu4rDABfRZqWEMYLZgl1GYZWRZ7od8gWkKD8pgEYO+NlXjBSnSMiU5quTY9E9kEiWt69kqSCbqEyWNSznD0VOas0elJGa5TLwAJYrzKGWbzyGkAqZvuGAa4QE9KJjAppSyrm4LE1fsbaHgVYrgsC1OAaaPvGGexnG2TbC70nwK2XKdrifja2DTCRc6F2waZ1OTHroi9ee7wzmbWoTefZpdPs0uUu9ybKx+HBsnMG890/GQ7zng1XhBl1u2tnVPClhdBHUp67pn85602+0oOmSartmgolgyTW9MKZkzPEDDp3nydtW7Z3TVu2vZqno3+lTvl5nq3yiMgZP9r1Uedj/Kw5fzHgeeESkTubJNO9/RgXZLPUVBzDEsCFUgzGybJuCPv1214DuSftZEVHafWsFX8FAiqojisMQ1h3GFDtzMuJKLZGucZZi3EOKorr0y+85Jeg/KwO6eRv17clBmqurf7dN3DPYDfVToPA8LtoG/Z8yMpJjZDoaOmcXRXTv68lSLfUXRV3BxySfNQfcFeFBHhF+v94QEPc/u5xiSfZQN36F+juwwuWa/fo6pSYL51y3Pgd/1PIfOppiao9TfS577T6cIWrdDol2h03tcdCgZLzO3d2uDlDSEvDFujQMpeWSQziZp+X190bQvuxbIb+zJp5oH2zXJK+o7wWeqGb6rCuBNQ/ZQPEsxY71G8KZ6bF3nXCfLN5xf9QPZUVTzq/3CoGa2ytYxJgjGFFN24Mmy3oZ08Gzw0BsZH0DNwLAPcLnMea/47cIY2N1jkMBAA8N41oQYOiAhNicrvG+u3vNilv73LMfj7Xfx1r6s0GtezFQTY1tSW5chdJWrxm9KCunYxp/Exsnv8QWBApIOo7MJpZi4hdHuN3euByp/i1+5L5F5Pqx072jJkU3zUzS+7XFYzHSBzcFbtteBb22Io9kPUfc//XwZD09OOx3q55umfIJwsgQWFJtfRNbdm9+dWg//AQ==</diagram></mxfile>
Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

+3
View File
@@ -0,0 +1,3 @@
# Creating a Container Action Using the Toolkit
In progress.
+71
View File
@@ -0,0 +1,71 @@
# Creating a Docker Action
The [container-template](https://github.com/actions/container-template) repo contains the base files to create a Docker action.
# Create a Repo from the Template
Navigate to https://github.com/actions/container-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, users will author:
```yaml
steps:
using: actions/setup-node@master
```
# 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 Container Action'
description: 'Get started with Container actions'
author: 'GitHub'
inputs:
myInput:
description: 'Input to use'
default: 'world'
runs:
using: 'docker'
image: 'Dockerfile'
args:
- ${{ inputs.myInput }}
```
It will be run with docker and the input is mapped into the args
# Change Code
The entry point is in entrypoint.sh
```bash
#!/bin/sh -l
echo "hello $1"
```
# Publish
Simply push your action to publish.
```bash
$ 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)
+40
View File
@@ -0,0 +1,40 @@
## Development
This repository uses [Lerna](https://github.com/lerna/lerna#readme) to manage multiple packages. Read the documentation there to begin contributing.
Note that before a PR will be accepted, you must ensure:
- all tests are passing
- `npm run format` reports no issues
- `npm run lint` reports no issues
### Useful Scripts
- `npm run bootstrap` This runs `lerna bootstrap` which will install dependencies in this repository's packages and cross-link packages where necessary.
- `npm run build` This compiles TypeScript code in each package (this is especially important if one package relies on changes in another when you're running tests). This is just an alias for `lerna run tsc`.
- `npm run format` This checks that formatting has been applied with Prettier.
- `npm test` This runs all Jest tests in all packages in this repository.
- If you need to run tests for only one package, you can pass normal Jest CLI options:
```console
$ npm test -- packages/toolkit
```
- `npm run create-package [name]` This runs a script that automates a couple of parts of creating a new package.
### Creating a Package
1. In a new branch, create a new Lerna package:
```console
$ npm run create-package new-package
```
This will ask you some questions about the new package. Start with `0.0.0` as the first version (look generally at some of the other packages for how the package.json is structured).
2. Add `tsc` script to the new package's package.json file:
```json
"scripts": {
"tsc": "tsc"
}
```
3. Start developing 😄 and open a pull request.
+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)
}
```
+163
View File
@@ -0,0 +1,163 @@
# Creating a JavaScript Action
The [javascript-template](https://github.com/actions/javascript-template) repo contains everything you need to get started.
# Create a Repo from the Template
Navigate to https://github.com/actions/javascript-template
Click on `Use this template` to create the repo for your action. Provide a name such as `myaction` (used in rest of walk through).
![template](assets/node12-template.png)
# Clone and Update
```bash
$ git clone <repolocation>
$ cd myaction
```
Update the `author` element in the package.json file.
# 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 from a distribution branch that **only** has the production dependencies.
The workflow below describes a strategy where you code in master (with node_modules ignored) with a distribution releases/v1 branch users reference via a tag. Actions are self contained referenced from the github graph of repos, downloaded by the runner and run intact at runtime.
## 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. node_modules are ignored in the coding master branch.
```bash
$ npm install
```
## Define Metadata
Your action has a name and a description. Update all fields .
```yaml
name: 'Hello'
description: 'Outputs Hello to a named input'
author: 'me'
inputs:
name:
description: 'the name to say hello to'
default: 'World'
runs:
using: 'node12'
main: 'lib/main.js'
```
The `name` input will be referenced by workflow authors using the `with:` keyword.
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 nameInput = core.getInput('name');
console.log(`Hello ${nameInput}!`);
} catch (error) {
core.setFailed(error.message);
}
}
run();
```
Note that tests are in `__tests__/main.test.ts`. The template uses [jest](https://github.com/facebook/jest) to get you started with unit testing.
## Build and Test
```bash
$ npm run build
> javascript-template-action@0.0.0 build /Users/user/Projects/myaction
> 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 releases/v1 Action
After changing some files, create a releases/v1 branch which we will release
```bash
$ git checkout -b releases/v1
```
> NOTE: We will provide tooling and an action to automate this soon.
Check in 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:
uses: myorg/myaction@releases/v1
with:
name: World!
```
## 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:
uses: myorg/myaction@v1
with:
name: World!
```
+4 -4
View File
@@ -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
*
+59 -100
View File
@@ -724,23 +724,6 @@
"fs-extra": "^7.0.0",
"ssri": "^6.0.1",
"tar": "^4.4.8"
},
"dependencies": {
"tar": {
"version": "4.4.8",
"resolved": "https://registry.npmjs.org/tar/-/tar-4.4.8.tgz",
"integrity": "sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==",
"dev": true,
"requires": {
"chownr": "^1.1.1",
"fs-minipass": "^1.2.5",
"minipass": "^2.3.4",
"minizlib": "^1.1.1",
"mkdirp": "^0.5.0",
"safe-buffer": "^5.1.2",
"yallist": "^3.0.2"
}
}
}
},
"@lerna/github-client": {
@@ -960,23 +943,6 @@
"npmlog": "^4.1.2",
"tar": "^4.4.8",
"temp-write": "^3.4.0"
},
"dependencies": {
"tar": {
"version": "4.4.8",
"resolved": "https://registry.npmjs.org/tar/-/tar-4.4.8.tgz",
"integrity": "sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==",
"dev": true,
"requires": {
"chownr": "^1.1.1",
"fs-minipass": "^1.2.5",
"minipass": "^2.3.4",
"minizlib": "^1.1.1",
"mkdirp": "^0.5.0",
"safe-buffer": "^5.1.2",
"yallist": "^3.0.2"
}
}
}
},
"@lerna/package": {
@@ -3670,10 +3636,13 @@
}
},
"eslint-utils": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.3.1.tgz",
"integrity": "sha512-Z7YjnIldX+2XMcjr7ZkgEsOj/bREONV60qYeB/bjMAqqqZ4zxKyWX+BOUkdmRmA9riiIPVvo5x86m5elviOk0Q==",
"dev": true
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.2.tgz",
"integrity": "sha512-eAZS2sEUMlIeCjBeubdj45dmBHQwPHWyBcT1VSYB7o9x9WRRqKxyUoiXlRjyAwzN7YEzHJlYg0NmzDRWx6GP4Q==",
"dev": true,
"requires": {
"eslint-visitor-keys": "^1.0.0"
}
},
"eslint-visitor-keys": {
"version": "1.0.0",
@@ -4216,9 +4185,7 @@
},
"chownr": {
"version": "1.1.1",
"bundled": true,
"dev": true,
"optional": true
"bundled": true
},
"code-point-at": {
"version": "1.1.0",
@@ -4274,8 +4241,6 @@
"fs-minipass": {
"version": "1.2.5",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"minipass": "^2.2.1"
}
@@ -4395,7 +4360,6 @@
"minipass": {
"version": "2.3.5",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"safe-buffer": "^5.1.2",
@@ -4405,8 +4369,6 @@
"minizlib": {
"version": "1.2.1",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"minipass": "^2.2.1"
}
@@ -4595,7 +4557,6 @@
"safe-buffer": {
"version": "5.1.2",
"bundled": true,
"dev": true,
"optional": true
},
"safer-buffer": {
@@ -4663,21 +4624,6 @@
"dev": true,
"optional": true
},
"tar": {
"version": "4.4.8",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"chownr": "^1.1.1",
"fs-minipass": "^1.2.5",
"minipass": "^2.3.4",
"minizlib": "^1.1.1",
"mkdirp": "^0.5.0",
"safe-buffer": "^5.1.2",
"yallist": "^3.0.2"
}
},
"util-deprecate": {
"version": "1.0.2",
"bundled": true,
@@ -4702,15 +4648,14 @@
"yallist": {
"version": "3.0.3",
"bundled": true,
"dev": true,
"optional": true
}
}
},
"fstream": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz",
"integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=",
"version": "1.0.12",
"resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz",
"integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==",
"dev": true,
"requires": {
"graceful-fs": "^4.1.2",
@@ -6743,9 +6688,9 @@
}
},
"lodash": {
"version": "4.17.11",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
"integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==",
"version": "4.17.15",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
"dev": true
},
"lodash._reinterpolate": {
@@ -6785,12 +6730,12 @@
"dev": true
},
"lodash.template": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.4.0.tgz",
"integrity": "sha1-5zoDhcg1VZF0bgILmWecaQ5o+6A=",
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz",
"integrity": "sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==",
"dev": true,
"requires": {
"lodash._reinterpolate": "~3.0.0",
"lodash._reinterpolate": "^3.0.0",
"lodash.templatesettings": "^4.0.0"
}
},
@@ -7177,9 +7122,9 @@
}
},
"mixin-deep": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz",
"integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==",
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz",
"integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==",
"dev": true,
"requires": {
"for-in": "^1.0.2",
@@ -7336,6 +7281,31 @@
"resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz",
"integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=",
"dev": true
},
"tar": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/tar/-/tar-2.2.2.tgz",
"integrity": "sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA==",
"dev": true,
"requires": {
"block-stream": "*",
"fstream": "^1.0.12",
"inherits": "2"
},
"dependencies": {
"fstream": {
"version": "1.0.12",
"resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz",
"integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==",
"dev": true,
"requires": {
"graceful-fs": "^4.1.2",
"inherits": "~2.0.0",
"mkdirp": ">=0.5 0",
"rimraf": "2"
}
}
}
}
}
},
@@ -7835,21 +7805,6 @@
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz",
"integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==",
"dev": true
},
"tar": {
"version": "4.4.8",
"resolved": "https://registry.npmjs.org/tar/-/tar-4.4.8.tgz",
"integrity": "sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==",
"dev": true,
"requires": {
"chownr": "^1.1.1",
"fs-minipass": "^1.2.5",
"minipass": "^2.3.4",
"minizlib": "^1.1.1",
"mkdirp": "^0.5.0",
"safe-buffer": "^5.1.2",
"yallist": "^3.0.2"
}
}
}
},
@@ -9292,14 +9247,18 @@
}
},
"tar": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz",
"integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=",
"version": "4.4.10",
"resolved": "https://registry.npmjs.org/tar/-/tar-4.4.10.tgz",
"integrity": "sha512-g2SVs5QIxvo6OLp0GudTqEf05maawKUxXru104iaayWA09551tFCTI8f1Asb4lPfkBr91k07iL4c11XO3/b0tA==",
"dev": true,
"requires": {
"block-stream": "*",
"fstream": "^1.0.2",
"inherits": "2"
"chownr": "^1.1.1",
"fs-minipass": "^1.2.5",
"minipass": "^2.3.5",
"minizlib": "^1.2.1",
"mkdirp": "^0.5.0",
"safe-buffer": "^5.1.2",
"yallist": "^3.0.3"
}
},
"temp-dir": {
@@ -9571,9 +9530,9 @@
"dev": true
},
"typescript": {
"version": "3.4.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.4.4.tgz",
"integrity": "sha512-xt5RsIRCEaf6+j9AyOBgvVuAec0i92rgCaS3S+UVf5Z/vF2Hvtsw08wtUTJqp4djwznoAgjSxeCcU4r+CcDBJA==",
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.2.tgz",
"integrity": "sha512-lmQ4L+J6mnu3xweP8+rOrUwzmN+MRAj7TgtJtDaXE5PMyX2kCrklhg3rvOsOIfNeAWMQWO2F1GPc1kMD2vLAfw==",
"dev": true
},
"uglify-js": {
+3 -2
View File
@@ -9,7 +9,8 @@
"format-check": "prettier --check packages/**/*.ts",
"lint": "eslint packages/**/*.ts",
"new-package": "scripts/create-package",
"test": "jest"
"test": "jest",
"test-ci": "jest --testPathIgnorePatterns=\"<rootDir>/packages/exec/__tests__/exec.test.ts\""
},
"devDependencies": {
"@types/jest": "^24.0.11",
@@ -25,6 +26,6 @@
"lerna": "^3.13.3",
"prettier": "^1.17.0",
"ts-jest": "^24.0.2",
"typescript": "^3.4.4"
"typescript": "^3.6.2"
}
}
+91 -1
View File
@@ -4,4 +4,94 @@
## Usage
See [src/core.ts](src/core.ts).
#### Inputs/Outputs
You can use this library to get inputs or set outputs:
```js
const core = require('@actions/core');
const myInput = core.getInput('inputName', { required: true });
// Do stuff
core.setOutput('outputKey', 'outputVal');
```
#### Exporting variables
You can also export variables for future steps. Variables get set in the environment.
```js
const core = require('@actions/core');
// Do stuff
core.exportVariable('envVar', 'Val');
```
#### PATH Manipulation
You can explicitly add items to the path for all remaining steps in a workflow:
```js
const core = require('@actions/core');
core.addPath('pathToTool');
```
#### Exit codes
You should use this library to set the failing exit code for your action:
```js
const core = require('@actions/core');
try {
// Do stuff
}
catch (err) {
// setFailed logs the message and sets a failing exit code
core.setFailed(`Action failed with error ${err}`);
}
```
#### 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');
try {
core.debug('Inside try block');
if (!myInput) {
core.warning('myInput was not set');
}
// Do stuff
}
catch (err) {
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
})
```
+5
View File
@@ -0,0 +1,5 @@
# @actions/core Releases
### 1.0.0
- Initial release
+55 -33
View File
@@ -1,4 +1,3 @@
import {ExitCode} from '@actions/exit'
import * as os from 'os'
import * as path from 'path'
import * as core from '../src/core'
@@ -17,7 +16,7 @@ const testEnvVars = {
// Set inputs
INPUT_MY_INPUT: 'val',
INPUT_MISSING: '',
'INPUT_SPECIAL_CHARS_\'\t"\\': '\'\t"\\ repsonse '
'INPUT_SPECIAL_CHARS_\'\t"\\': '\'\t"\\ response '
}
describe('@actions/core', () => {
@@ -51,34 +50,34 @@ describe('@actions/core', () => {
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 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 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('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('prependPath produces the correct commands and sets the env', () => {
core.addPath('myPath')
@@ -102,7 +101,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('')
})
@@ -111,7 +110,7 @@ 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('setOutput produces the correct command', () => {
@@ -121,18 +120,18 @@ describe('@actions/core', () => {
it('setNeutral sets the correct exit code', () => {
core.setFailed('Failure message')
expect(process.exitCode).toBe(ExitCode.Failure)
expect(process.exitCode).toBe(core.ExitCode.Failure)
})
it('setFailure sets the correct exit code and failure message', () => {
core.setFailed('Failure message')
expect(process.exitCode).toBe(ExitCode.Failure)
expect(process.exitCode).toBe(core.ExitCode.Failure)
assertWriteCalls([`##[error]Failure message${os.EOL}`])
})
it('setFailure escapes the failure message', () => {
core.setFailed('Failure \r\n\nmessage\r')
expect(process.exitCode).toBe(ExitCode.Failure)
expect(process.exitCode).toBe(core.ExitCode.Failure)
assertWriteCalls([`##[error]Failure %0D%0A%0Amessage%0D${os.EOL}`])
})
@@ -156,6 +155,29 @@ describe('@actions/core', () => {
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}`])
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@actions/core",
"version": "0.0.0",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
+4 -6
View File
@@ -1,10 +1,11 @@
{
"name": "@actions/core",
"version": "0.0.0",
"version": "1.1.0",
"description": "Actions core lib",
"keywords": [
"core",
"actions"
"github",
"actions",
"core"
],
"homepage": "https://github.com/actions/toolkit/tree/master/packages/core",
"license": "MIT",
@@ -32,8 +33,5 @@
},
"devDependencies": {
"@types/node": "^12.0.2"
},
"dependencies": {
"@actions/exit": "^0.0.0"
}
}
+2 -2
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,7 +25,7 @@ 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)
}
+59 -8
View File
@@ -1,4 +1,3 @@
import {ExitCode} from '@actions/exit'
import {issue, issueCommand} from './command'
import * as path from 'path'
@@ -11,6 +10,21 @@ export interface InputOptions {
required?: boolean
}
/**
* The code to exit an action
*/
export enum ExitCode {
/**
* A code indicating that the action was successful
*/
Success = 0,
/**
* A code indicating that the action was a failure
*/
Failure = 1
}
//-----------------------------------------------------------------------
// Variables
//-----------------------------------------------------------------------
@@ -32,7 +46,11 @@ export function exportVariable(name: string, val: string): void {
*/
export function exportSecret(name: string, val: string): void {
exportVariable(name, val)
// the runner will error with not implemented
// leaving the function but raising the error earlier
issueCommand('set-secret', {}, val)
throw new Error('Not implemented.')
}
/**
@@ -75,13 +93,6 @@ export function setOutput(name: string, value: string): void {
// Results
//-----------------------------------------------------------------------
/**
* Sets the action status to neutral
*/
export function setNeutral(): void {
process.exitCode = ExitCode.Neutral
}
/**
* Sets the action status to failed.
* When the action exits it will be with an exit code of 1
@@ -119,3 +130,43 @@ export function error(message: string): void {
export function warning(message: string): void {
issue('warning', message)
}
/**
* 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
}
+56 -3
View File
@@ -1,7 +1,60 @@
# `@actions/exec`
> Functions necessary for running tools on the command line
## Usage
See [src/exec.ts](src/exec.ts).
#### Basic
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');
```
#### Args
You can also pass in arg arrays:
```js
const exec = require('@actions/exec');
await exec.exec('node', ['index.js', 'foo=bar']);
```
#### Output/options
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');
let myOutput = '';
let myError = '';
const options = {};
options.listeners = {
stdout: (data: Buffer) => {
myOutput += data.toString();
},
stderr: (data: Buffer) => {
myError += data.toString();
}
};
options.cwd = './lib';
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:
```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']);
```
+5
View File
@@ -0,0 +1,5 @@
# @actions/exec Releases
### 1.0.0
- Initial release
+23 -16
View File
@@ -12,14 +12,19 @@ import * as io from '@actions/io'
const IS_WINDOWS = process.platform === 'win32'
let outstream: stream.Writable
let errstream: stream.Writable
describe('@actions/exec', () => {
beforeAll(() => {
io.mkdirP(getTestTemp())
outstream = fs.createWriteStream(path.join(getTestTemp(), 'my.log'))
errstream = fs.createWriteStream(path.join(getTestTemp(), 'myerr.log'))
})
beforeEach(() => {
process.stdout.write = jest.fn()
process.stderr.write = jest.fn()
outstream.write = jest.fn()
errstream.write = jest.fn()
})
it('Runs exec successfully with arguments split out', async () => {
@@ -45,12 +50,12 @@ describe('@actions/exec', () => {
expect(exitCode).toBe(0)
if (IS_WINDOWS) {
expect(process.stdout.write).toBeCalledWith(
expect(outstream.write).toBeCalledWith(
`[command]${toolpath} /c echo hello${os.EOL}`
)
expect(process.stdout.write).toBeCalledWith(new Buffer(`hello${os.EOL}`))
expect(outstream.write).toBeCalledWith(new Buffer(`hello${os.EOL}`))
} else {
expect(process.stdout.write).toBeCalledWith(
expect(outstream.write).toBeCalledWith(
`[command]${toolpath} -l -a${os.EOL}`
)
}
@@ -75,12 +80,12 @@ describe('@actions/exec', () => {
expect(exitCode).toBe(0)
if (IS_WINDOWS) {
expect(process.stdout.write).toBeCalledWith(
expect(outstream.write).toBeCalledWith(
`[command]${toolpath} /c echo hello${os.EOL}`
)
expect(process.stdout.write).toBeCalledWith(new Buffer(`hello${os.EOL}`))
expect(outstream.write).toBeCalledWith(new Buffer(`hello${os.EOL}`))
} else {
expect(process.stdout.write).toBeCalledWith(
expect(outstream.write).toBeCalledWith(
`[command]${toolpath} -l -a${os.EOL}`
)
}
@@ -105,12 +110,12 @@ describe('@actions/exec', () => {
expect(exitCode).toBe(0)
if (IS_WINDOWS) {
expect(process.stdout.write).toBeCalledWith(
expect(outstream.write).toBeCalledWith(
`[command]${toolpath} /c echo hello${os.EOL}`
)
expect(process.stdout.write).toBeCalledWith(new Buffer(`hello${os.EOL}`))
expect(outstream.write).toBeCalledWith(new Buffer(`hello${os.EOL}`))
} else {
expect(process.stdout.write).toBeCalledWith(
expect(outstream.write).toBeCalledWith(
`[command]${toolpath} -l -a${os.EOL}`
)
}
@@ -140,11 +145,11 @@ describe('@actions/exec', () => {
expect(failed).toBe(true)
if (IS_WINDOWS) {
expect(process.stdout.write).toBeCalledWith(
expect(outstream.write).toBeCalledWith(
`[command]${toolpath} /c non-existent${os.EOL}`
)
} else {
expect(process.stdout.write).toBeCalledWith(
expect(outstream.write).toBeCalledWith(
`[command]${toolpath} -l non-existent${os.EOL}`
)
}
@@ -167,7 +172,7 @@ describe('@actions/exec', () => {
)
expect(exitCode).toBe(0)
expect(process.stdout.write).toBeCalledWith(
expect(outstream.write).toBeCalledWith(
new Buffer('this is output to stderr')
)
})
@@ -191,7 +196,7 @@ describe('@actions/exec', () => {
})
expect(failed).toBe(true)
expect(process.stderr.write).toBeCalledWith(
expect(errstream.write).toBeCalledWith(
new Buffer('this is output to stderr')
)
})
@@ -677,7 +682,9 @@ function getExecOptions(): im.ExecOptions {
env: {},
silent: false,
failOnStdErr: false,
ignoreReturnCode: false
ignoreReturnCode: false,
outStream: outstream,
errStream: errstream
}
}
+2 -2
View File
@@ -1,11 +1,11 @@
{
"name": "@actions/exec",
"version": "0.0.0",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@actions/io": {
"version": "0.0.0",
"version": "1.0.0",
"dev": true
}
}
+5 -4
View File
@@ -1,10 +1,11 @@
{
"name": "@actions/exec",
"version": "0.0.0",
"version": "1.0.1",
"description": "Actions exec lib",
"keywords": [
"exec",
"actions"
"github",
"actions",
"exec"
],
"homepage": "https://github.com/actions/toolkit/tree/master/packages/exec",
"license": "MIT",
@@ -31,6 +32,6 @@
"url": "https://github.com/actions/toolkit/issues"
},
"devDependencies": {
"@actions/io": "^0.0.0"
"@actions/io": "^1.0.1"
}
}
+1 -1
View File
@@ -216,7 +216,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"
-7
View File
@@ -1,7 +0,0 @@
Copyright 2019 GitHub
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-7
View File
@@ -1,7 +0,0 @@
# `@actions/exit`
> TODO: description
## Usage
See [src/exit.ts](src/exit.ts).
-21
View File
@@ -1,21 +0,0 @@
import * as exit from '../src/exit'
/* eslint-disable @typescript-eslint/unbound-method */
it('exits successfully', () => {
jest.spyOn(process, 'exit').mockImplementation()
exit.success()
expect(process.exit).toHaveBeenCalledWith(0)
})
it('exits as a failure', () => {
jest.spyOn(process, 'exit').mockImplementation()
exit.failure()
expect(process.exit).toHaveBeenCalledWith(1)
})
it('exits neutrally', () => {
jest.spyOn(process, 'exit').mockImplementation()
exit.neutral()
expect(process.exit).toHaveBeenCalledWith(78)
})
-5
View File
@@ -1,5 +0,0 @@
{
"name": "@actions/exit",
"version": "0.0.0",
"lockfileVersion": 1
}
-34
View File
@@ -1,34 +0,0 @@
{
"name": "@actions/exit",
"version": "0.0.0",
"description": "Functions for safely exiting from GitHub Actions",
"main": "lib/exit.js",
"keywords": [
"github",
"actions",
"toolkit"
],
"homepage": "https://github.com/actions/toolkit/tree/master/packages/exit",
"license": "MIT",
"directories": {
"lib": "lib",
"test": "__tests__"
},
"files": [
"lib"
],
"publishConfig": {
"access": "public"
},
"repository": {
"type": "git",
"url": "git+https://github.com/actions/toolkit.git"
},
"scripts": {
"test": "echo \"Error: run tests from root\" && exit 1",
"tsc": "tsc"
},
"bugs": {
"url": "https://github.com/actions/toolkit/issues"
}
}
-43
View File
@@ -1,43 +0,0 @@
/**
* The code to exit an action
*/
export enum ExitCode {
/**
* A code indicating that the action was successful
*/
Success = 0,
/**
* A code indicating that the action was a failure
*/
Failure = 1,
/**
* A code indicating that the action is complete, but neither succeeded nor failed
*/
Neutral = 78
}
// TODO: These exit codes may not behave as expected on the new runtime, due to
// complexities of async logging and sync exiting.
/**
* Exit the action as a success.
*/
export function success(): void {
process.exit(ExitCode.Success)
}
/**
* Exit the action as a failure.
*/
export function failure(): void {
process.exit(ExitCode.Failure)
}
/**
* Exit the action neither a success or a failure
*/
export function neutral(): void {
process.exit(ExitCode.Neutral)
}
+50
View File
@@ -0,0 +1,50 @@
# `@actions/github`
> A hydrated Octokit client.
## Usage
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');
const octokit = new github.GitHub(myToken);
const { data: pullRequest } = await octokit.pulls.get({
owner: 'octokit',
repo: 'rest.js',
pull_number: 123,
mediaType: {
format: 'diff'
}
});
console.log(pullRequest);
```
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;
const newIssue = await octokit.issues.create({
...context.repo,
title: 'New issue!',
body: 'Hello Universe!'
});
```
+9
View File
@@ -0,0 +1,9 @@
# @actions/github Releases
### 1.0.1
- Simplify WebPack configs by removing dynamic require - [#101](https://github.com/actions/toolkit/pull/101)
### 1.0.0
- Initial release
@@ -0,0 +1,3 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`@actions/context return error for context.repo when repository doesn't exist 1`] = `"context.repo requires a GITHUB_REPOSITORY environment variable like 'owner/repo'"`;
+80
View File
@@ -0,0 +1,80 @@
import * as path from 'path'
import {Context} from '../src/context'
/* eslint-disable @typescript-eslint/no-require-imports */
describe('@actions/context', () => {
let context: Context
beforeEach(() => {
process.env.GITHUB_EVENT_PATH = path.join(__dirname, 'payload.json')
process.env.GITHUB_REPOSITORY = 'actions/toolkit'
context = new Context()
})
it('returns the payload object', () => {
expect(context.payload).toEqual(require('./payload.json'))
})
it('returns an empty payload if the GITHUB_EVENT_PATH environment variable is falsey', () => {
delete process.env.GITHUB_EVENT_PATH
context = new Context()
expect(context.payload).toEqual({})
})
it('returns attributes from the GITHUB_REPOSITORY', () => {
expect(context.repo).toEqual({owner: 'actions', repo: 'toolkit'})
})
it('returns attributes from the repository payload', () => {
delete process.env.GITHUB_REPOSITORY
context.payload.repository = {
name: 'test',
owner: {login: 'user'}
}
expect(context.repo).toEqual({owner: 'user', repo: 'test'})
})
it("return error for context.repo when repository doesn't exist", () => {
delete process.env.GITHUB_REPOSITORY
context.payload.repository = undefined
expect(() => context.repo).toThrowErrorMatchingSnapshot()
})
it('returns issue attributes from the repository', () => {
expect(context.issue).toEqual({
owner: 'actions',
repo: 'toolkit',
number: 1
})
})
it('works with pullRequest payloads', () => {
delete process.env.GITHUB_REPOSITORY
context.payload = {
pullRequest: {number: 2},
repository: {owner: {login: 'user'}, name: 'test'}
}
expect(context.issue).toEqual({
number: 2,
owner: 'user',
repo: 'test'
})
})
it('works with payload.number payloads', () => {
delete process.env.GITHUB_REPOSITORY
context.payload = {
number: 2,
repository: {owner: {login: 'user'}, name: 'test'}
}
expect(context.issue).toEqual({
number: 2,
owner: 'user',
repo: 'test'
})
})
})
+15
View File
@@ -0,0 +1,15 @@
{
"action": "opened",
"repository": {
"owner": {
"login": "user"
},
"name": "test"
},
"issue": {
"number": 1
},
"sender": {
"type": "User"
}
}
+11
View File
@@ -0,0 +1,11 @@
module.exports = {
clearMocks: true,
moduleFileExtensions: ['js', 'ts'],
testEnvironment: 'node',
testMatch: ['**/*.test.ts'],
testRunner: 'jest-circus/runner',
transform: {
'^.+\\.ts$': 'ts-jest'
},
verbose: true
}
+5250
View File
File diff suppressed because it is too large Load Diff
@@ -1,14 +1,14 @@
{
"name": "@actions/toolkit",
"version": "0.0.0",
"description": "Base actions tools for the GitHub API and GitHub context",
"name": "@actions/github",
"version": "1.1.0",
"description": "Actions github lib",
"keywords": [
"github",
"actions",
"toolkit"
"actions"
],
"homepage": "https://github.com/actions/toolkit/tree/master/packages/toolkit",
"homepage": "https://github.com/actions/toolkit/tree/master/packages/github",
"license": "MIT",
"main": "lib/github.js",
"directories": {
"lib": "lib",
"test": "__tests__"
@@ -24,15 +24,18 @@
"url": "git+https://github.com/actions/toolkit.git"
},
"scripts": {
"test": "echo \"Error: run tests from root\" && exit 1",
"test": "jest",
"build": "tsc",
"tsc": "tsc"
},
"bugs": {
"url": "https://github.com/actions/toolkit/issues"
},
"dependencies": {
"@actions/exit": "^0.0.0",
"@octokit/rest": "^16.25.0",
"signale": "^1.4.0"
"@octokit/graphql": "^2.0.1",
"@octokit/rest": "^16.15.0"
},
"devDependencies": {
"jest": "^24.7.1"
}
}
+36
View File
@@ -0,0 +1,36 @@
/* 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>
}
+71
View File
@@ -0,0 +1,71 @@
// Originally pulled from https://github.com/JasonEtco/actions-toolkit/blob/master/src/context.ts
import {WebhookPayload} from './interfaces'
import {readFileSync, existsSync} from 'fs'
import {EOL} from 'os'
export class Context {
/**
* Webhook payload object that triggered the workflow
*/
payload: WebhookPayload
eventName: string
sha: string
ref: string
workflow: string
action: string
actor: string
/**
* Hydrate the context from the environment
*/
constructor() {
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 {
process.stdout.write(
`GITHUB_EVENT_PATH ${
process.env.GITHUB_EVENT_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
this.workflow = process.env.GITHUB_WORKFLOW as string
this.action = process.env.GITHUB_ACTION as string
this.actor = process.env.GITHUB_ACTOR as string
}
get issue(): {owner: string; repo: string; number: number} {
const payload = this.payload
return {
...this.repo,
number: (payload.issue || payload.pullRequest || payload).number
}
}
get repo(): {owner: string; repo: string} {
if (process.env.GITHUB_REPOSITORY) {
const [owner, repo] = process.env.GITHUB_REPOSITORY.split('/')
return {owner, repo}
}
if (this.payload.repository) {
return {
owner: this.payload.repository.owner.login,
repo: this.payload.repository.name
}
}
throw new Error(
"context.repo requires a GITHUB_REPOSITORY environment variable like 'owner/repo'"
)
}
}
+23
View File
@@ -0,0 +1,23 @@
// Originally pulled from https://github.com/JasonEtco/actions-toolkit/blob/master/src/github.ts
import {GraphQlQueryResponse, Variables, defaults} from '@octokit/graphql'
import Octokit from '@octokit/rest'
import * as Context from './context'
// We need this in order to extend Octokit
Octokit.prototype = new Octokit()
export const context = new Context.Context()
export class GitHub extends Octokit {
graphql: (
query: string,
variables?: Variables
) => Promise<GraphQlQueryResponse>
constructor(token: string, opts: Omit<Octokit.Options, 'auth'> = {}) {
super({...opts, auth: `token ${token}`})
this.graphql = defaults({
headers: {authorization: `token ${token}`}
})
}
}
+39
View File
@@ -0,0 +1,39 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
export interface PayloadRepository {
[key: string]: any
full_name?: string
name: string
owner: {
[key: string]: any
login: string
name?: string
}
html_url?: string
}
export interface WebhookPayload {
[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
}
}
@@ -2,6 +2,7 @@
"extends": "../../tsconfig.json",
"compilerOptions": {
"baseUrl": "./",
"esModuleInterop": true,
"outDir": "./lib",
"rootDir": "./src"
},
+42 -38
View File
@@ -4,46 +4,50 @@
## Usage
#### mkdir -p
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');
```
/**
* Copies a file or folder.
*
* @param source source path
* @param dest destination path
* @param options optional. See CopyOptions.
*/
export function cp(source: string, dest: string, options?: CopyOptions): Promise<void>
/**
* Remove a path recursively with force
*
* @param path path to remove
*/
export function rmRF(path: string): Promise<void>
#### cp/mv
/**
* Make a directory. Creates the full path with folders in between
*
* @param p path to create
* @returns Promise<void>
*/
export function mkdirP(p: string): Promise<void>
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):
/**
* Moves a path.
*
* @param source source path
* @param dest destination path
* @param options optional. See CopyOptions.
*/
export function mv(source: string, dest: string, options?: CopyOptions): Promise<void>
```js
const io = require('@actions/io');
/**
* Returns path of a tool had the tool actually been invoked. Resolves via paths.
*
* @param tool name of the tool
* @param options optional. See WhichOptions.
* @returns Promise<string> path to tool
*/
export function which(tool: string, options?: WhichOptions): Promise<string>
```
// Recursive must be true for directories
const options = { recursive: true, force: false }
await io.cp('path/to/directory', 'path/to/dest', options);
await io.mv('path/to/file', 'path/to/dest');
```
#### rm -rf
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');
await io.rmRF('path/to/file');
```
#### which
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');
const pythonPath: string = await io.which('python', true)
await exec.exec(`"${pythonPath}"`, ['main.py']);
```
+5
View File
@@ -0,0 +1,5 @@
# @actions/io Releases
### 1.0.0
- Initial release
+45 -40
View File
@@ -59,13 +59,7 @@ describe('cp', () => {
await io.mkdirP(root)
await fs.writeFile(sourceFile, 'test file content', {encoding: 'utf8'})
await fs.writeFile(targetFile, 'correct content', {encoding: 'utf8'})
let failed = false
try {
await io.cp(sourceFile, targetFile, {recursive: false, force: false})
} catch {
failed = true
}
expect(failed).toBe(true)
await io.cp(sourceFile, targetFile, {recursive: false, force: false})
expect(await fs.readFile(targetFile, {encoding: 'utf8'})).toBe(
'correct content'
@@ -94,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')
@@ -132,6 +126,43 @@ describe('cp', () => {
expect(thrown).toBe(true)
await assertNotExists(targetFile)
})
it('Copies symlinks correctly', async () => {
// create the following layout
// sourceFolder
// sourceFolder/nested
// sourceFolder/nested/sourceFile
// sourceFolder/symlinkDirectory -> sourceFile
const root: string = path.join(getTestTemp(), 'cp_with_-r_symlinks')
const sourceFolder: string = path.join(root, 'cp_source')
const nestedFolder: string = path.join(sourceFolder, 'nested')
const sourceFile: string = path.join(nestedFolder, 'cp_source_file')
const symlinkDirectory: string = path.join(sourceFolder, 'symlinkDirectory')
const targetFolder: string = path.join(root, 'cp_target')
const targetFile: string = path.join(
targetFolder,
'nested',
'cp_source_file'
)
const symlinkTargetPath: string = path.join(
targetFolder,
'symlinkDirectory',
'cp_source_file'
)
await io.mkdirP(sourceFolder)
await io.mkdirP(nestedFolder)
await fs.writeFile(sourceFile, 'test file content', {encoding: 'utf8'})
await createSymlinkDir(nestedFolder, symlinkDirectory)
await io.cp(sourceFolder, targetFolder, {recursive: true})
expect(await fs.readFile(targetFile, {encoding: 'utf8'})).toBe(
'test file content'
)
expect(await fs.readFile(symlinkTargetPath, {encoding: 'utf8'})).toBe(
'test file content'
)
})
})
describe('mv', () => {
@@ -189,7 +220,7 @@ describe('mv', () => {
)
})
it('moves directory into existing destination with -r', async () => {
it('moves directory into existing destination', async () => {
const root: string = path.join(getTestTemp(), ' mv_with_-r_existing_dest')
const sourceFolder: string = path.join(root, ' mv_source')
const sourceFile: string = path.join(sourceFolder, ' mv_source_file')
@@ -203,7 +234,7 @@ describe('mv', () => {
await io.mkdirP(sourceFolder)
await fs.writeFile(sourceFile, 'test file content', {encoding: 'utf8'})
await io.mkdirP(targetFolder)
await io.mv(sourceFolder, targetFolder, {recursive: true})
await io.mv(sourceFolder, targetFolder)
expect(await fs.readFile(targetFile, {encoding: 'utf8'})).toBe(
'test file content'
@@ -211,10 +242,10 @@ describe('mv', () => {
await assertNotExists(sourceFile)
})
it('moves directory into non-existing destination with -r', async () => {
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')
@@ -223,39 +254,13 @@ describe('mv', () => {
const targetFile: string = path.join(targetFolder, ' mv_source_file')
await io.mkdirP(sourceFolder)
await fs.writeFile(sourceFile, 'test file content', {encoding: 'utf8'})
await io.mv(sourceFolder, targetFolder, {recursive: true})
await io.mv(sourceFolder, targetFolder)
expect(await fs.readFile(targetFile, {encoding: 'utf8'})).toBe(
'test file content'
)
await assertNotExists(sourceFile)
})
it('tries to move directory without -r', async () => {
const root: string = path.join(getTestTemp(), 'mv_without_-r')
const sourceFolder: string = path.join(root, 'mv_source')
const sourceFile: string = path.join(sourceFolder, 'mv_source_file')
const targetFolder: string = path.join(root, 'mv_target')
const targetFile: string = path.join(
targetFolder,
'mv_source',
'mv_source_file'
)
await io.mkdirP(sourceFolder)
await fs.writeFile(sourceFile, 'test file content', {encoding: 'utf8'})
let thrown = false
try {
await io.mv(sourceFolder, targetFolder)
} catch (err) {
thrown = true
}
expect(thrown).toBe(true)
await assertExists(sourceFile)
await assertNotExists(targetFile)
})
})
describe('rmRF', () => {
@@ -316,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)
+1 -1
View File
@@ -1,5 +1,5 @@
{
"name": "@actions/io",
"version": "0.0.0",
"version": "1.0.0",
"lockfileVersion": 1
}
+4 -3
View File
@@ -1,10 +1,11 @@
{
"name": "@actions/io",
"version": "0.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",
+4
View File
@@ -3,12 +3,16 @@ import * as fs from 'fs'
import * as path from 'path'
export const {
chmod,
copyFile,
lstat,
mkdir,
readdir,
readlink,
rename,
rmdir,
stat,
symlink,
unlink
} = fs.promises
+129 -91
View File
@@ -1,5 +1,4 @@
import * as childProcess from 'child_process'
import * as fs from 'fs'
import * as path from 'path'
import {promisify} from 'util'
import * as ioUtil from './io-util'
@@ -16,8 +15,17 @@ export interface CopyOptions {
force?: boolean
}
/**
* Interface for cp/mv options
*/
export interface MoveOptions {
/** Optional. Whether to overwrite existing files in the destination. Defaults to true */
force?: boolean
}
/**
* Copies a file or folder.
* Based off of shelljs - https://github.com/shelljs/shelljs/blob/9237f66c52e5daa40458f94f9565e18e8132f5a6/src/cp.js
*
* @param source source path
* @param dest destination path
@@ -28,7 +36,41 @@ export async function cp(
dest: string,
options: CopyOptions = {}
): Promise<void> {
await move(source, dest, options, {deleteOriginal: false})
const {force, recursive} = readCopyOptions(options)
const destStat = (await ioUtil.exists(dest)) ? await ioUtil.stat(dest) : null
// Dest is an existing file, but not forcing
if (destStat && destStat.isFile() && !force) {
return
}
// If dest is an existing directory, should copy inside.
const newDest: string =
destStat && destStat.isDirectory()
? path.join(dest, path.basename(source))
: dest
if (!(await ioUtil.exists(source))) {
throw new Error(`no such file or directory: ${source}`)
}
const sourceStat = await ioUtil.stat(source)
if (sourceStat.isDirectory()) {
if (!recursive) {
throw new Error(
`Failed to copy. ${source} is a directory, but tried to copy without recursive flag.`
)
} else {
await cpDirRecursive(source, newDest, 0, force)
}
} else {
if (path.relative(source, newDest) === '') {
// a file cannot be copied to itself
throw new Error(`'${newDest}' and '${source}' are the same file`)
}
await copyFile(source, newDest, force)
}
}
/**
@@ -36,14 +78,31 @@ export async function cp(
*
* @param source source path
* @param dest destination path
* @param options optional. See CopyOptions.
* @param options optional. See MoveOptions.
*/
export async function mv(
source: string,
dest: string,
options: CopyOptions = {}
options: MoveOptions = {}
): Promise<void> {
await move(source, dest, options, {deleteOriginal: true})
if (await ioUtil.exists(dest)) {
let destExists = true
if (await ioUtil.isDirectory(dest)) {
// If dest is directory copy src into dest
dest = path.join(dest, path.basename(source))
destExists = await ioUtil.exists(dest)
}
if (destExists) {
if (options.force == null || options.force) {
await rmRF(dest)
} else {
throw new Error('Destination already exists')
}
}
}
await mkdirP(path.dirname(dest))
await ioUtil.rename(source, dest)
}
/**
@@ -167,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[] = []
@@ -198,92 +257,71 @@ export async function which(tool: string, check?: boolean): Promise<string> {
}
}
// Copies contents of source into dest, making any necessary folders along the way.
// Deletes the original copy if deleteOriginal is true
async function copyDirectoryContents(
source: string,
dest: string,
force: boolean,
deleteOriginal = false
): Promise<void> {
if (await ioUtil.isDirectory(source)) {
if (await ioUtil.exists(dest)) {
if (!(await ioUtil.isDirectory(dest))) {
throw new Error(`${dest} is not a directory`)
}
} else {
await mkdirP(dest)
}
// Copy all child files, and directories recursively
const sourceChildren: string[] = await ioUtil.readdir(source)
for (const newSource of sourceChildren) {
const newDest = path.join(dest, path.basename(newSource))
await copyDirectoryContents(
path.resolve(source, newSource),
newDest,
force,
deleteOriginal
)
}
if (deleteOriginal) {
await ioUtil.rmdir(source)
}
} else {
if (force) {
await ioUtil.copyFile(source, dest)
} else {
await ioUtil.copyFile(source, dest, fs.constants.COPYFILE_EXCL)
}
if (deleteOriginal) {
await ioUtil.unlink(source)
}
}
}
async function move(
source: string,
dest: string,
options: CopyOptions = {},
moveOptions: {deleteOriginal: boolean}
): Promise<void> {
const {force, recursive} = readCopyOptions(options)
if (await ioUtil.isDirectory(source)) {
if (!recursive) {
throw new Error(`non-recursive cp failed, ${source} is a directory`)
}
// If directory exists, move source inside it. Otherwise, create it and move contents of source inside.
if (await ioUtil.exists(dest)) {
if (!(await ioUtil.isDirectory(dest))) {
throw new Error(`${dest} is not a directory`)
}
dest = path.join(dest, path.basename(source))
}
await copyDirectoryContents(source, dest, force, moveOptions.deleteOriginal)
} else {
if ((await ioUtil.exists(dest)) && (await ioUtil.isDirectory(dest))) {
dest = path.join(dest, path.basename(source))
}
if (force) {
await ioUtil.copyFile(source, dest)
} else {
await ioUtil.copyFile(source, dest, fs.constants.COPYFILE_EXCL)
}
if (moveOptions.deleteOriginal) {
await ioUtil.unlink(source)
}
}
}
function readCopyOptions(options: CopyOptions): Required<CopyOptions> {
const force = options.force == null ? true : options.force
const recursive = Boolean(options.recursive)
return {force, recursive}
}
async function cpDirRecursive(
sourceDir: string,
destDir: string,
currentDepth: number,
force: boolean
): Promise<void> {
// Ensure there is not a run away recursive copy
if (currentDepth >= 255) return
currentDepth++
await mkdirP(destDir)
const files: string[] = await ioUtil.readdir(sourceDir)
for (const fileName of files) {
const srcFile = `${sourceDir}/${fileName}`
const destFile = `${destDir}/${fileName}`
const srcFileStat = await ioUtil.lstat(srcFile)
if (srcFileStat.isDirectory()) {
// Recurse
await cpDirRecursive(srcFile, destFile, currentDepth, force)
} else {
await copyFile(srcFile, destFile, force)
}
}
// Change the mode for the newly created directory
await ioUtil.chmod(destDir, (await ioUtil.stat(sourceDir)).mode)
}
// Buffered file copy
async function copyFile(
srcFile: string,
destFile: string,
force: boolean
): Promise<void> {
if ((await ioUtil.lstat(srcFile)).isSymbolicLink()) {
// unlink/re-link it
try {
await ioUtil.lstat(destFile)
await ioUtil.unlink(destFile)
} catch (e) {
// Try to override file permission
if (e.code === 'EPERM') {
await ioUtil.chmod(destFile, '0666')
await ioUtil.unlink(destFile)
}
// other errors = it doesn't exist, no work to do
}
// Copy over symlink
const symlinkFull: string = await ioUtil.readlink(srcFile)
await ioUtil.symlink(
symlinkFull,
destFile,
ioUtil.IS_WINDOWS ? 'junction' : null
)
} else if (!(await ioUtil.exists(destFile)) || force) {
await ioUtil.copyFile(srcFile, destFile)
}
}
+76 -1
View File
@@ -4,4 +4,79 @@
## Usage
See [src/tool-cache.ts](src/tool-cache.ts).
#### Download
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');
```
#### Extract
These can then be extracted in platform specific ways:
```js
const tc = require('@actions/tool-cache');
if (process.platform === 'win32') {
const node12Path = tc.downloadTool('http://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
const node12Path = tc.downloadTool('http://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 node12ExtractedFolder = await tc.extractTar(node12Path, 'path/to/extract/to');
}
```
#### Cache
Finally, you can cache these directories in our tool-cache. This is useful if you want to switch back and forth between versions of a tool, or save a tool between runs for private runners (private runners are still in development but are on the roadmap).
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 node12ExtractedFolder = await tc.extractTar(node12Path, 'path/to/extract/to');
const cachedPath = await tc.cacheDir(node12ExtractedFolder, 'node', '12.7.0');
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');
```
#### Find
Finally, you can find directories and files you've previously cached:
```js
const tc = require('@actions/tool-cache');
const core = require('@actions/core');
const nodeDirectory = tc.find('node', '12.x', 'x64');
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');
console.log(`Versions of node available: ${allNodeVersions}`);
```
+10
View File
@@ -0,0 +1,10 @@
# @actions/tool-cache Releases
### 1.1.0
- [Add zip and unzip for macOS](https://github.com/actions/toolkit/pull/49)
- [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.
@@ -7,8 +7,8 @@ import * as exec from '@actions/exec'
const cachePath = path.join(__dirname, 'CACHE')
const tempPath = path.join(__dirname, 'TEMP')
// Set temp and tool directories before importing (used to set global state)
process.env['RUNNER_TEMPDIRECTORY'] = tempPath
process.env['RUNNER_TOOLSDIRECTORY'] = cachePath
process.env['RUNNER_TEMP'] = tempPath
process.env['RUNNER_TOOL_CACHE'] = cachePath
// eslint-disable-next-line import/first
import * as tc from '../src/tool-cache'
@@ -197,6 +197,62 @@ 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.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 +287,9 @@ describe('@actions/tool-cache', function() {
]
await exec.exec(`"${powershellPath}"`, args)
} else {
const zipPath: string = path.join(__dirname, 'externals', 'zip')
const zipBin: string =
process.platform === 'darwin' ? 'zip-darwin' : 'zip'
const zipPath: string = path.join(__dirname, 'externals', zipBin)
await exec.exec(`"${zipPath}`, [zipFile, '-r', '.'], {cwd: stagingDir})
}
@@ -282,7 +340,9 @@ describe('@actions/tool-cache', function() {
]
await exec.exec(`"${powershellPath}"`, args)
} else {
const zipPath = path.join(__dirname, 'externals', 'zip')
const zipBin: string =
process.platform === 'darwin' ? 'zip-darwin' : 'zip'
const zipPath = path.join(__dirname, 'externals', zipBin)
await exec.exec(zipPath, [zipFile, '-r', '.'], {cwd: stagingDir})
}
+22 -22
View File
@@ -1,6 +1,6 @@
{
"name": "@actions/tool-cache",
"version": "0.0.0",
"version": "1.1.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -14,21 +14,21 @@
}
},
"@types/node": {
"version": "12.0.2",
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.0.2.tgz",
"integrity": "sha512-5tabW/i+9mhrfEOUcLDu2xBPsHJ+X5Orqy9FKpale3SjDA17j5AEpYq5vfy3oAeAHGcvANRCO3NV3d2D6q3NiA==",
"version": "12.7.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.7.0.tgz",
"integrity": "sha512-vqcj1MVm2Sla4PpMfYKh1MyDN4D2f/mPIZD7RdAGqEsbE+JxfeqQHHVbRDQ0Nqn8i73gJa1HQ1Pu3+nH4Q0Yiw==",
"dev": true
},
"@types/semver": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-6.0.0.tgz",
"integrity": "sha512-OO0srjOGH99a4LUN2its3+r6CBYcplhJ466yLqs+zvAWgphCpS8hYZEZ797tRDP/QKcqTdb/YCN6ifASoAWkrQ==",
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-6.0.1.tgz",
"integrity": "sha512-ffCdcrEE5h8DqVxinQjo+2d1q+FV5z7iNtPofw3JsrltSoSVlOGaW0rY8XxtO9XukdTn8TaCGWmk2VFGhI70mg==",
"dev": true
},
"@types/uuid": {
"version": "3.4.4",
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-3.4.4.tgz",
"integrity": "sha512-tPIgT0GUmdJQNSHxp0X2jnpQfBSTfGxUMc/2CXBU2mnyTFVYVa2ojpoQ74w0U2yn2vw3jnC640+77lkFFpdVDw==",
"version": "3.4.5",
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-3.4.5.tgz",
"integrity": "sha512-MNL15wC3EKyw1VLF+RoVO4hJJdk9t/Hlv3rt1OL65Qvuadm4BYo6g9ZJQqoq7X8NBFSsQXgAujWciovh2lpVjA==",
"dev": true,
"requires": {
"@types/node": "*"
@@ -97,9 +97,9 @@
"dev": true
},
"lodash": {
"version": "4.17.11",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
"integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==",
"version": "4.17.15",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
"dev": true
},
"minimist": {
@@ -118,9 +118,9 @@
}
},
"ms": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dev": true
},
"nock": {
@@ -167,9 +167,9 @@
"dev": true
},
"semver": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.1.0.tgz",
"integrity": "sha512-kCqEOOHoBcFs/2Ccuk4Xarm/KiWRSLEX9CAZF8xkJ6ZPlIoTZ8V5f7J16vYLJqDbR7KrxTJpR2lqjIEm2Qx9cQ=="
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
},
"tunnel": {
"version": "0.0.4",
@@ -183,9 +183,9 @@
"dev": true
},
"typed-rest-client": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.4.0.tgz",
"integrity": "sha512-f+3+X13CIpkv0WvFERkXq4aH5BYzyeYclf8t+X7oa/YaE80EjYW12kphY0aEQBaL9RzChP0MSbsVhB4X+bzyDw==",
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.5.0.tgz",
"integrity": "sha512-DVZRlmsfnTjp6ZJaatcdyvvwYwbWvR4YDNFDqb+qdTxpvaVP99YCpBkA8rxsLtAPjBVoDe4fNsnMIdZTiPuKWg==",
"requires": {
"tunnel": "0.0.4",
"underscore": "1.8.3"
+10 -9
View File
@@ -1,10 +1,11 @@
{
"name": "@actions/tool-cache",
"version": "0.0.0",
"version": "1.1.1",
"description": "Actions tool-cache lib",
"keywords": [
"exec",
"actions"
"github",
"actions",
"exec"
],
"homepage": "https://github.com/actions/toolkit/tree/master/packages/exec",
"license": "MIT",
@@ -32,17 +33,17 @@
"url": "https://github.com/actions/toolkit/issues"
},
"dependencies": {
"@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",
"@actions/core": "^0.0.0",
"@actions/io": "^0.0.0",
"@actions/exec": "^0.0.0"
"uuid": "^3.3.2"
},
"devDependencies": {
"nock": "^10.0.6",
"@types/nock": "^10.0.3",
"@types/semver": "^6.0.0",
"@types/uuid": "^3.4.4"
"@types/uuid": "^3.4.4",
"nock": "^10.0.6"
}
}
Binary file not shown.
+25 -5
View File
@@ -21,8 +21,8 @@ const IS_WINDOWS = process.platform === 'win32'
const userAgent = 'actions/tool-cache'
// On load grab temp directory and cache directory and remove them from env (currently don't want to expose this)
let tempDirectory: string = process.env['RUNNER_TEMPDIRECTORY'] || ''
let cacheRoot: string = process.env['RUNNER_TOOLSDIRECTORY'] || ''
let tempDirectory: string = process.env['RUNNER_TEMP'] || ''
let cacheRoot: string = process.env['RUNNER_TOOL_CACHE'] || ''
// If directories not found, place them in common temp locations
if (!tempDirectory || !cacheRoot) {
let baseLocation: string
@@ -187,16 +187,21 @@ export async function extract7z(
*
* @param file path to the tar
* @param dest destination directory. Optional.
* @param flags flags for the tar. 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))
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
}
@@ -218,7 +223,11 @@ export async function extractZip(file: string, dest?: string): Promise<string> {
if (IS_WINDOWS) {
await extractZipWin(file, dest)
} else {
await extractZipNix(file, dest)
if (process.platform === 'darwin') {
await extractZipDarwin(file, dest)
} else {
await extractZipNix(file, dest)
}
}
return dest
@@ -250,6 +259,17 @@ async function extractZipNix(file: string, dest: string): Promise<void> {
await exec(`"${unzipPath}"`, [file], {cwd: dest})
}
async function extractZipDarwin(file: string, dest: string): Promise<void> {
const unzipPath = path.join(
__dirname,
'..',
'scripts',
'externals',
'unzip-darwin'
)
await exec(`"${unzipPath}"`, [file], {cwd: dest})
}
/**
* Caches a directory and installs it into the tool cacheDir
*
-7
View File
@@ -1,7 +0,0 @@
Copyright 2019 GitHub
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-7
View File
@@ -1,7 +0,0 @@
# `@actions/toolkit`
> TODO: description
## Usage
See [src/toolkit.ts](src/toolkit.ts).
-39
View File
@@ -1,39 +0,0 @@
import * as exitPkg from '@actions/exit'
import {Signale} from 'signale'
import {Exit} from '../src/exit'
/* eslint-disable @typescript-eslint/unbound-method */
jest.mock('@actions/exit')
const tests: [keyof Exit, keyof Signale][] = [
['success', 'success'],
['neutral', 'info'],
['failure', 'fatal']
]
describe.each(tests)('%s', (method, log) => {
let logger: Signale
let exit: Exit
beforeEach(() => {
// Create a logger to mock
logger = new Signale()
logger.success = jest.fn()
logger.info = jest.fn()
logger.fatal = jest.fn()
process.exit = jest.fn<never, [number]>()
exit = new Exit(logger)
})
it('exits with the expected method', () => {
exit[method]()
expect(exitPkg[method]).toHaveBeenCalled()
})
it('logs the expected message', () => {
exit[method]('hello')
expect(logger[log]).toHaveBeenCalledWith('hello')
})
})
@@ -1,52 +0,0 @@
import * as exitPkg from '@actions/exit'
import {Signale} from 'signale'
import {Toolkit} from '../src/toolkit'
/* eslint-disable @typescript-eslint/unbound-method */
jest.mock('@actions/exit')
describe('.run', () => {
it('runs a sync function', async () => {
const cb = jest.fn(() => true)
const value = await Toolkit.run(cb)
expect(cb).toHaveBeenCalledWith(expect.any(Toolkit))
expect(value).toBe(true)
})
it('runs an async function', async () => {
const cb = jest.fn(async () => true)
const value = await Toolkit.run(cb)
expect(cb).toHaveBeenCalledWith(expect.any(Toolkit))
expect(value).toBe(true)
})
it('logs and fails when an error occurs', async () => {
const err = new Error()
const exitFailure = jest.fn()
await Toolkit.run(async tk => {
tk.exit.failure = exitFailure
throw err
})
expect(exitFailure).toHaveBeenCalledWith(err)
})
})
it('asserts required keys are present', async () => {
const missingKey = '__DOES_NOT_EXIST__'
Reflect.deleteProperty(process.env, missingKey)
const logger = new Signale()
logger.fatal = jest.fn()
jest.spyOn(process, 'exit').mockImplementation()
new Toolkit({logger, requiredEnv: [missingKey]})
expect(exitPkg.failure).toHaveBeenCalled()
expect(logger.fatal)
.toHaveBeenCalledWith(`The following environment variables are required for this action to run:
- __DOES_NOT_EXIST__`)
})
-454
View File
@@ -1,454 +0,0 @@
{
"name": "@actions/toolkit",
"version": "0.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@octokit/endpoint": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-4.0.0.tgz",
"integrity": "sha512-b8sptNUekjREtCTJFpOfSIL4SKh65WaakcyxWzRcSPOk5RxkZJ/S8884NGZFxZ+jCB2rDURU66pSHn14cVgWVg==",
"requires": {
"deepmerge": "3.2.0",
"is-plain-object": "^2.0.4",
"universal-user-agent": "^2.0.1",
"url-template": "^2.0.8"
}
},
"@octokit/request": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-3.0.0.tgz",
"integrity": "sha512-DZqmbm66tq+a9FtcKrn0sjrUpi0UaZ9QPUCxxyk/4CJ2rseTMpAWRf6gCwOSUCzZcx/4XVIsDk+kz5BVdaeenA==",
"requires": {
"@octokit/endpoint": "^4.0.0",
"deprecation": "^1.0.1",
"is-plain-object": "^2.0.4",
"node-fetch": "^2.3.0",
"once": "^1.4.0",
"universal-user-agent": "^2.0.1"
}
},
"@octokit/rest": {
"version": "16.25.0",
"resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-16.25.0.tgz",
"integrity": "sha512-QKIzP0gNYjyIGmY3Gpm3beof0WFwxFR+HhRZ+Wi0fYYhkEUvkJiXqKF56Pf5glzzfhEwOrggfluEld5F/ZxsKw==",
"requires": {
"@octokit/request": "3.0.0",
"atob-lite": "^2.0.0",
"before-after-hook": "^1.4.0",
"btoa-lite": "^1.0.0",
"deprecation": "^1.0.1",
"lodash.get": "^4.4.2",
"lodash.set": "^4.3.2",
"lodash.uniq": "^4.5.0",
"octokit-pagination-methods": "^1.1.0",
"once": "^1.4.0",
"universal-user-agent": "^2.0.0",
"url-template": "^2.0.8"
}
},
"ansi-styles": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"requires": {
"color-convert": "^1.9.0"
}
},
"atob-lite": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/atob-lite/-/atob-lite-2.0.0.tgz",
"integrity": "sha1-D+9a1G8b16hQLGVyfwNn1e5D1pY="
},
"before-after-hook": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-1.4.0.tgz",
"integrity": "sha512-l5r9ir56nda3qu14nAXIlyq1MmUSs0meCIaFAh8HwkFwP1F8eToOuS3ah2VAHHcY04jaYD7FpJC5JTXHYRbkzg=="
},
"btoa-lite": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/btoa-lite/-/btoa-lite-1.0.0.tgz",
"integrity": "sha1-M3dm2hWAEhD92VbCLpxokaudAzc="
},
"chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"requires": {
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
"supports-color": "^5.3.0"
}
},
"color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
"requires": {
"color-name": "1.1.3"
}
},
"color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
},
"cross-spawn": {
"version": "6.0.5",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
"integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
"requires": {
"nice-try": "^1.0.4",
"path-key": "^2.0.1",
"semver": "^5.5.0",
"shebang-command": "^1.2.0",
"which": "^1.2.9"
}
},
"deepmerge": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-3.2.0.tgz",
"integrity": "sha512-6+LuZGU7QCNUnAJyX8cIrlzoEgggTM6B7mm+znKOX4t5ltluT9KLjN6g61ECMS0LTsLW7yDpNoxhix5FZcrIow=="
},
"deprecation": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/deprecation/-/deprecation-1.0.1.tgz",
"integrity": "sha512-ccVHpE72+tcIKaGMql33x5MAjKQIZrk+3x2GbJ7TeraUCZWHoT+KSZpoC+JQFsUBlSTXUrBaGiF0j6zVTepPLg=="
},
"end-of-stream": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz",
"integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==",
"requires": {
"once": "^1.4.0"
}
},
"error-ex": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
"integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
"requires": {
"is-arrayish": "^0.2.1"
}
},
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
},
"execa": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz",
"integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==",
"requires": {
"cross-spawn": "^6.0.0",
"get-stream": "^4.0.0",
"is-stream": "^1.1.0",
"npm-run-path": "^2.0.0",
"p-finally": "^1.0.0",
"signal-exit": "^3.0.0",
"strip-eof": "^1.0.0"
}
},
"figures": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz",
"integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=",
"requires": {
"escape-string-regexp": "^1.0.5"
}
},
"find-up": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
"integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
"requires": {
"locate-path": "^2.0.0"
}
},
"get-stream": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
"integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
"requires": {
"pump": "^3.0.0"
}
},
"graceful-fs": {
"version": "4.1.15",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz",
"integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA=="
},
"has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
},
"is-arrayish": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
"integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0="
},
"is-plain-object": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
"integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
"requires": {
"isobject": "^3.0.1"
}
},
"is-stream": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
"integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ="
},
"isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
"integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA="
},
"isobject": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
"integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8="
},
"json-parse-better-errors": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
"integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw=="
},
"load-json-file": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz",
"integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=",
"requires": {
"graceful-fs": "^4.1.2",
"parse-json": "^4.0.0",
"pify": "^3.0.0",
"strip-bom": "^3.0.0"
}
},
"locate-path": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
"integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=",
"requires": {
"p-locate": "^2.0.0",
"path-exists": "^3.0.0"
}
},
"lodash.get": {
"version": "4.4.2",
"resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
"integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk="
},
"lodash.set": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz",
"integrity": "sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM="
},
"lodash.uniq": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
"integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M="
},
"macos-release": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.2.0.tgz",
"integrity": "sha512-iV2IDxZaX8dIcM7fG6cI46uNmHUxHE4yN+Z8tKHAW1TBPMZDIKHf/3L+YnOuj/FK9il14UaVdHmiQ1tsi90ltA=="
},
"nice-try": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
"integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ=="
},
"node-fetch": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.3.0.tgz",
"integrity": "sha512-MOd8pV3fxENbryESLgVIeaGKrdl+uaYhCSSVkjeOb/31/njTpcis5aWfdqgNlHIrKOLRbMnfPINPOML2CIFeXA=="
},
"npm-run-path": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
"integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=",
"requires": {
"path-key": "^2.0.0"
}
},
"octokit-pagination-methods": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/octokit-pagination-methods/-/octokit-pagination-methods-1.1.0.tgz",
"integrity": "sha512-fZ4qZdQ2nxJvtcasX7Ghl+WlWS/d9IgnBIwFZXVNNZUmzpno91SX5bc5vuxiuKoCtK78XxGGNuSCrDC7xYB3OQ=="
},
"once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"requires": {
"wrappy": "1"
}
},
"os-name": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/os-name/-/os-name-3.1.0.tgz",
"integrity": "sha512-h8L+8aNjNcMpo/mAIBPn5PXCM16iyPGjHNWo6U1YO8sJTMHtEtyczI6QJnLoplswm6goopQkqc7OAnjhWcugVg==",
"requires": {
"macos-release": "^2.2.0",
"windows-release": "^3.1.0"
}
},
"p-finally": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
"integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4="
},
"p-limit": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
"integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==",
"requires": {
"p-try": "^1.0.0"
}
},
"p-locate": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
"integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=",
"requires": {
"p-limit": "^1.1.0"
}
},
"p-try": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
"integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M="
},
"parse-json": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
"integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
"requires": {
"error-ex": "^1.3.1",
"json-parse-better-errors": "^1.0.1"
}
},
"path-exists": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
"integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU="
},
"path-key": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
"integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A="
},
"pify": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
"integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY="
},
"pkg-conf": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-2.1.0.tgz",
"integrity": "sha1-ISZRTKbyq/69FoWW3xi6V4Z/AFg=",
"requires": {
"find-up": "^2.0.0",
"load-json-file": "^4.0.0"
}
},
"pump": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
"integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
"requires": {
"end-of-stream": "^1.1.0",
"once": "^1.3.1"
}
},
"semver": {
"version": "5.7.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz",
"integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA=="
},
"shebang-command": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
"integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
"requires": {
"shebang-regex": "^1.0.0"
}
},
"shebang-regex": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
"integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM="
},
"signal-exit": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
"integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0="
},
"signale": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/signale/-/signale-1.4.0.tgz",
"integrity": "sha512-iuh+gPf28RkltuJC7W5MRi6XAjTDCAPC/prJUpQoG4vIP3MJZ+GTydVnodXA7pwvTKb2cA0m9OFZW/cdWy/I/w==",
"requires": {
"chalk": "^2.3.2",
"figures": "^2.0.0",
"pkg-conf": "^2.1.0"
}
},
"strip-bom": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
"integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM="
},
"strip-eof": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
"integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8="
},
"supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"requires": {
"has-flag": "^3.0.0"
}
},
"universal-user-agent": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-2.0.3.tgz",
"integrity": "sha512-eRHEHhChCBHrZsA4WEhdgiOKgdvgrMIHwnwnqD0r5C6AO8kwKcG7qSku3iXdhvHL3YvsS9ZkSGN8h/hIpoFC8g==",
"requires": {
"os-name": "^3.0.0"
}
},
"url-template": {
"version": "2.0.8",
"resolved": "https://registry.npmjs.org/url-template/-/url-template-2.0.8.tgz",
"integrity": "sha1-/FZaPMy/93MMd19WQflVV5FDnyE="
},
"which": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
"integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
"requires": {
"isexe": "^2.0.0"
}
},
"windows-release": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/windows-release/-/windows-release-3.2.0.tgz",
"integrity": "sha512-QTlz2hKLrdqukrsapKsINzqMgOUpQW268eJ0OaOpJN32h272waxR9fkB9VoWRtK7uKHG5EHJcTXQBD8XZVJkFA==",
"requires": {
"execa": "^1.0.0"
}
},
"wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
}
}
}
-33
View File
@@ -1,33 +0,0 @@
import * as exit from '@actions/exit'
import {Signale} from 'signale'
/**
* A class that wraps some basic methods of exiting from an action
*/
export class Exit {
constructor(private readonly logger: Signale) {}
/**
* Stop the action with a "success" status.
*/
success(message?: string): void {
if (message) this.logger.success(message)
exit.success()
}
/**
* Stop the action with a "neutral" status.
*/
neutral(message?: string): void {
if (message) this.logger.info(message)
exit.neutral()
}
/**
* Stop the action with a "failed" status.
*/
failure(message?: string): void {
if (message) this.logger.fatal(message)
exit.failure()
}
}
-96
View File
@@ -1,96 +0,0 @@
import {LoggerFunc, Signale} from 'signale'
import {Exit} from './exit'
export type ActionFn = (tools: Toolkit) => unknown
/**
* Options used to customize an instance of [[Toolkit]]
*/
export interface ToolkitOptions {
/**
* A custom Signale instance to use
*/
logger?: Signale
/**
* A list of environment variable names this action requires in order to run
*
* If any of them are missing, the action will fail and log the missing keys.
*/
requiredEnv?: string[]
}
/**
* A set of tools for the Actions runtime
*/
export class Toolkit {
/**
* Run an asynchronous function that accepts a toolkit as its argument.
*
* If an error occurs, the error will be logged and the action will exit as a
* failure.
*/
static async run(func: ActionFn, opts?: ToolkitOptions): Promise<void> {
const tools = new Toolkit(opts)
try {
const ret = func(tools)
return ret instanceof Promise ? await ret : ret
} catch (err) {
tools.exit.failure(err)
}
}
/**
* A logger for the toolkit, an instance of [Signale](https://github.com/klaussinani/signale)
*/
readonly logger: Signale & LoggerFunc
/**
* A wrapper around an instance of [[Exit]]
*/
readonly exit: Exit
/**
* The authentication token for the GitHub API
*/
readonly token: string = process.env.GITHUB_TOKEN || ''
constructor(opts: ToolkitOptions = {}) {
const logger = opts.logger || new Signale({config: {underlineLabel: false}})
this.logger = this.wrapLogger(logger)
this.exit = new Exit(this.logger)
if (opts.requiredEnv) {
this.checkRequiredEnv(opts.requiredEnv)
}
}
/**
* Ensure that the given keys are in the environment.
*/
private checkRequiredEnv(keys: string[]): void {
const missingEnv = keys.filter(key => !process.env.hasOwnProperty(key))
if (missingEnv.length === 0) return
const list = missingEnv.map(key => `- ${key}`).join('\n')
this.exit.failure(
`The following environment variables are required for this action to run:\n${list}`
)
}
/**
* Wrap a Signale logger so that its a callable class.
*/
private wrapLogger(logger: Signale): Signale & LoggerFunc {
// Create a callable function
const fn = logger.info.bind(logger)
// Add the log methods onto the function
const wrapped = Object.assign(fn, logger)
// Clone the prototype
Object.setPrototypeOf(wrapped, logger)
return wrapped
}
}
-11
View File
@@ -1,11 +0,0 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"baseUrl": "./",
"outDir": "./lib",
"rootDir": "./src"
},
"include": [
"./src"
]
}
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB