Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a40bce7c8d | |||
| d7f00ea0fd | |||
| 46b418103a | |||
| 3004de4b40 | |||
| da76d1fd39 | |||
| fc9958ec6a | |||
| f9ab88cdc3 | |||
| 293aa1ae02 | |||
| 0bb10220a7 | |||
| 3ce161a815 | |||
| 571d130f50 | |||
| 027a230b99 | |||
| dfccb207bf | |||
| 3977d56fd3 | |||
| 4a7f2143e6 | |||
| 5218a83722 | |||
| 2a2b51f939 | |||
| 4f5cf60872 | |||
| 0a64f32b4e | |||
| 9aacf6aeaa | |||
| 5a1b82748e | |||
| d78ededdcb | |||
| df0101c5c3 | |||
| d293c20cc9 | |||
| c4a488fc74 | |||
| d919136160 | |||
| e85d20fbb0 |
@@ -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"
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
name: Main workflow
|
||||
on: [push]
|
||||
jobs:
|
||||
Ubuntu:
|
||||
name: Run Ubuntu
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@master
|
||||
|
||||
- name: Set Node.js 10.x
|
||||
uses: actions/setup-node@master
|
||||
with:
|
||||
version: 10.x
|
||||
|
||||
- name: npm install
|
||||
run: npm install
|
||||
|
||||
- name: Bootstrap
|
||||
run: npm run bootstrap
|
||||
|
||||
- name: Compile
|
||||
run: npm run build
|
||||
|
||||
- name: npm test
|
||||
run: npm test
|
||||
|
||||
- name: Lint
|
||||
run: npm run lint
|
||||
|
||||
- name: Format
|
||||
run: npm run format-check
|
||||
Windows:
|
||||
name: Run Windows
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@master
|
||||
|
||||
- name: Set Node.js 10.x
|
||||
uses: actions/setup-node@master
|
||||
with:
|
||||
version: 10.x
|
||||
|
||||
- name: npm install
|
||||
run: npm install
|
||||
|
||||
- name: Bootstrap
|
||||
run: npm run bootstrap
|
||||
|
||||
- name: Compile
|
||||
run: npm run build
|
||||
|
||||
# TODO: This currently ignores exec due to issues with Node and spawning on Windows, which I think is exacerbated by Jest.
|
||||
# It doesn't seem to affect behavior in actions themselves, just when testing with Jest.
|
||||
# See other similar issues here: https://github.com/nodejs/node/issues/25484
|
||||
- name: npm test
|
||||
run: npm run test-ci
|
||||
@@ -1,53 +1,37 @@
|
||||
# Actions Toolkit 🛠
|
||||
|
||||
<p align="center">
|
||||
<img src="res/at-logo.png">
|
||||
</p>
|
||||
|
||||
## Toolkit
|
||||
|
||||
The toolkit provides a set of packages to make creating actions easier and drive consistency.
|
||||
|
||||
## Packages
|
||||
|
||||
The toolkit provides four separate packages. Since actions are run by pulling actions from the github graph, dependencies including the packages are vendored into your action.
|
||||
|
||||
| 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 |
|
||||
|
||||
## 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 host or a container based action.
|
||||
|
||||
### Useful Scripts
|
||||
[JavaScript Action Walthrough](docs/node12-action.md): Create an action which runs on the host using the toolkit
|
||||
|
||||
- `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).
|
||||
@@ -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 it's 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 it's 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 it's unit of work on each environment (node8+ubunut16, 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 it's PATH or using a setup-* action to acquire it on demand.
|
||||
@@ -0,0 +1,43 @@
|
||||
# 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 mainting compatibility and the assurance your workflow should still work.
|
||||
|
||||
Binding to master is convenient but if a new major version is released which breaks compatilibility, 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)
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 54 KiB |
@@ -0,0 +1,3 @@
|
||||
# Creating a Container Action Using the Toolkit
|
||||
|
||||
In progress.
|
||||
@@ -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.
|
||||
|
||||

|
||||
|
||||
Complete creating your repo and clone the repo.
|
||||
|
||||
> NOTE: The location of the repo will be how users will reference your action in their workflow file with the using keyword.
|
||||
|
||||
e.g. To use https://github.com/actions/setup-node, user's will author:
|
||||
|
||||
```yaml
|
||||
steps:
|
||||
using: actions/setup-node@master
|
||||
```
|
||||
|
||||
# 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)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -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
|
||||
|
||||
```
|
||||
/*
|
||||
* 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
|
||||
|
||||
```
|
||||
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
|
||||
|
||||
```
|
||||
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)
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,167 @@
|
||||
# Creating a JavaScript Action
|
||||
|
||||
The [node12-template](https://github.com/actions/node12-template) repo contains everything you need to get started.
|
||||
|
||||
# Create a Repo from the Template
|
||||
|
||||
Navigate to https://github.com/actions/node12-template
|
||||
|
||||
Click on `Use this template` to create the repo for your action.
|
||||
|
||||

|
||||
|
||||
Complete creating your repo and clone the repo.
|
||||
|
||||
> NOTE: The location of the repo will be how users will reference your action in their workflow file with the using keyword.
|
||||
|
||||
e.g. To use https://github.com/actions/setup-node, user's will author:
|
||||
|
||||
```yaml
|
||||
steps:
|
||||
using: actions/setup-node@master
|
||||
```
|
||||
|
||||
# Dev Workflow
|
||||
|
||||
The workflow below describes one possible workflow with a branching strategy. Others exist.
|
||||
|
||||
> Key Point: the branch that users reference in their workflow files should reference an action that has **only** the production dependencies.
|
||||
|
||||
The workflow below describes a strategy where you code in master (with node_modules ignored) with a v1 branch users reference and contains the product references. Actions are self contained referenced on the github graph of repos.
|
||||
|
||||
## Install Dependencies
|
||||
|
||||
After creating a repo from the template and cloning it, you will be in master. The command below will install the toolkit, other dependencies and dev dependencies
|
||||
|
||||
```bash
|
||||
$ npm install
|
||||
```
|
||||
|
||||
## Define Metadata
|
||||
|
||||
Your action has a name and a description. Update the author.
|
||||
|
||||
Create inputs that your unit of work will need. These will be what workflow authors set with the `with:` keyword.
|
||||
|
||||
```yaml
|
||||
name: 'My new action'
|
||||
description: 'A test action'
|
||||
author: 'GitHub'
|
||||
inputs:
|
||||
myInput:
|
||||
description: 'Input to use'
|
||||
default: 'world'
|
||||
runs:
|
||||
using: 'node12'
|
||||
main: 'lib/main.js'
|
||||
|
||||
```
|
||||
|
||||
Note that the action will be run with node 12 (carried by the runner) and the entry point is specified with `main:`
|
||||
|
||||
## Change Code and Add Tests
|
||||
|
||||
The entry point is in main.ts
|
||||
|
||||
```typescript
|
||||
import * as core from '@actions/core';
|
||||
|
||||
async function run() {
|
||||
try {
|
||||
const myInput = core.getInput('myInput');
|
||||
core.debug(`Hello ${myInput}!`);
|
||||
} catch (error) {
|
||||
core.setFailed(error.message);
|
||||
}
|
||||
}
|
||||
|
||||
run();
|
||||
```
|
||||
|
||||
Modify tests in `__tests__\main.test.ts`. The template uses [jest](https://github.com/facebook/jest).
|
||||
|
||||
## Format the Code
|
||||
|
||||
```bash
|
||||
$ npm run format
|
||||
```
|
||||
|
||||
## Build and Test
|
||||
|
||||
```bash
|
||||
$ npm run build
|
||||
|
||||
> node12-template-action@0.0.0 build /Users/user/Projects/testnode12
|
||||
> tsc
|
||||
|
||||
$ npm test
|
||||
|
||||
> jest
|
||||
|
||||
PASS __tests__/main.test.ts
|
||||
TODO - Add a test suite
|
||||
✓ TODO - Add a test (1ms)
|
||||
|
||||
Test Suites: 1 passed, 1 total
|
||||
...
|
||||
```
|
||||
|
||||
## Commit and Push Changes
|
||||
|
||||
```bash
|
||||
$ git add <whatever only files you added>
|
||||
$ git commit -m "Message"
|
||||
```
|
||||
|
||||
## Publish a v1-release Action
|
||||
|
||||
After changing some files, create a v1-release branch which we will release
|
||||
|
||||
```bash
|
||||
$ git checkout -b v1-release
|
||||
```
|
||||
|
||||
> NOTE: We will provide tooling and an action to automate this soon.
|
||||
|
||||
Checkin production dependencies:
|
||||
1. **Do not ignore node_modules**: Add a `!` in front of the `node_modules` line.
|
||||
2. **Delete node_modules**: rm -Rf node_modules
|
||||
3. **Install production dependencies**: npm install --production
|
||||
4. **Add**: git add node_modules
|
||||
|
||||
|
||||
Simply commit and push your action to publish.
|
||||
|
||||
```bash
|
||||
$ git commit -a -m "publishing v1 of action"
|
||||
$ git push
|
||||
```
|
||||
|
||||
> NOTE: Consider versioning your actions with tags. See [versioning](action-versioning.md)
|
||||
|
||||
## Test End To End
|
||||
|
||||
Once the action has a self contained version in the v1-release branch, you can test it by referencing the latest (and potentially unstable) version in the release branch. If you are fixing an issue that someone else is having with your action, you can have them try it before you officially releasing it as the 'v1' version.
|
||||
|
||||
```yaml
|
||||
steps:
|
||||
using: {org}/{reponame}@v1-release
|
||||
```
|
||||
|
||||
## Release Current Changes as v1
|
||||
|
||||
Once you have tested end to end, push a tag of 'v1' to the commit in the release branch.
|
||||
|
||||
See [action versioning](action-versioning.md) for more details.
|
||||
|
||||
# Users Referencing
|
||||
|
||||
Users can now reference your action in their workflows with
|
||||
|
||||
```yaml
|
||||
steps:
|
||||
using: {org}/{reponame}@v1
|
||||
```
|
||||
|
||||
|
||||
|
||||
+2
-1
@@ -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",
|
||||
|
||||
+75
-1
@@ -4,4 +4,78 @@
|
||||
|
||||
## Usage
|
||||
|
||||
See [src/core.ts](src/core.ts).
|
||||
#### Inputs/Outputs
|
||||
|
||||
You can use this library to get inputs or set outputs:
|
||||
|
||||
```
|
||||
const core = require('@actions/core');
|
||||
|
||||
const myInput = core.getInput('inputName', { required: true });
|
||||
|
||||
// Do stuff
|
||||
|
||||
core.setOutput('outputKey', 'outputVal');
|
||||
```
|
||||
|
||||
#### Exporting variables/secrets
|
||||
|
||||
You can also export variables and secrets for future steps. Variables get set in the environment automatically, while secrets must be scoped into the environment from a workflow using `{{ secret.FOO }}`. Secrets will also be masked from the logs:
|
||||
|
||||
```
|
||||
const core = require('@actions/core');
|
||||
|
||||
// Do stuff
|
||||
|
||||
core.exportVariable('envVar', 'Val');
|
||||
core.exportSecret('secretVar', variableWithSecretValue);
|
||||
```
|
||||
|
||||
#### PATH Manipulation
|
||||
|
||||
You can explicitly add items to the path for all remaining steps in a workflow:
|
||||
|
||||
```
|
||||
const core = require('@actions/core');
|
||||
|
||||
core.addPath('pathToTool');
|
||||
```
|
||||
|
||||
#### Exit codes
|
||||
|
||||
You should use this library to set the failing exit code for your action:
|
||||
|
||||
```
|
||||
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:
|
||||
|
||||
```
|
||||
const core = require('@actions/core');
|
||||
|
||||
const myInput = core.getInput('input');
|
||||
try {
|
||||
core.debug('Inside try block');
|
||||
|
||||
if (!myInput) {
|
||||
core.warning('myInput wasnt set');
|
||||
}
|
||||
|
||||
// Do stuff
|
||||
}
|
||||
catch (err) {
|
||||
core.error('Error ${err}, action may still succeed though');
|
||||
}
|
||||
```
|
||||
|
||||
@@ -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'
|
||||
@@ -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}`])
|
||||
})
|
||||
|
||||
|
||||
Generated
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@actions/core",
|
||||
"version": "0.0.0",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@actions/core",
|
||||
"version": "0.0.0",
|
||||
"version": "1.0.0",
|
||||
"description": "Actions core lib",
|
||||
"keywords": [
|
||||
"core",
|
||||
@@ -32,8 +32,5 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^12.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@actions/exit": "^0.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
//-----------------------------------------------------------------------
|
||||
@@ -75,13 +89,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
|
||||
|
||||
+56
-3
@@ -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:
|
||||
|
||||
```
|
||||
const exec = require('@actions/exec');
|
||||
|
||||
await exec.exec('node index.js');
|
||||
```
|
||||
|
||||
#### Args
|
||||
|
||||
You can also pass in arg arrays:
|
||||
|
||||
```
|
||||
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):
|
||||
|
||||
```
|
||||
const exec = require('@actions/exec');
|
||||
|
||||
const myOutput = '';
|
||||
const 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:
|
||||
|
||||
```
|
||||
const exec = require('@actions/exec');
|
||||
const io = require('@actions/io');
|
||||
|
||||
const pythonPath: string = await io.which('python', true)
|
||||
|
||||
await exec.exec(`"${pythonPath}"`, ['main.py']);
|
||||
```
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Generated
+2
-2
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@actions/exec",
|
||||
"version": "0.0.0",
|
||||
"version": "1.0.0",
|
||||
"description": "Actions exec lib",
|
||||
"keywords": [
|
||||
"exec",
|
||||
@@ -31,6 +31,6 @@
|
||||
"url": "https://github.com/actions/toolkit/issues"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@actions/io": "^0.0.0"
|
||||
"@actions/io": "^1.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
@@ -1,7 +0,0 @@
|
||||
# `@actions/exit`
|
||||
|
||||
> TODO: description
|
||||
|
||||
## Usage
|
||||
|
||||
See [src/exit.ts](src/exit.ts).
|
||||
@@ -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)
|
||||
})
|
||||
Generated
-5
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"name": "@actions/exit",
|
||||
"version": "0.0.0",
|
||||
"lockfileVersion": 1
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
# `@actions/github`
|
||||
|
||||
> A hydrated Octokit client.
|
||||
|
||||
## Usage
|
||||
|
||||
Returns an [Octokit SDK] client. See https://octokit.github.io/rest.js for the API.
|
||||
|
||||
```
|
||||
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 pulls = await octokit.pulls.get({
|
||||
owner: 'octokit',
|
||||
repo: 'rest.js',
|
||||
pull_number: 123,
|
||||
mediaType: {
|
||||
format: 'diff'
|
||||
}
|
||||
});
|
||||
|
||||
console.log(pulls);
|
||||
```
|
||||
|
||||
You can also make GraphQL requests:
|
||||
|
||||
```
|
||||
const result = await octokit.graphql(query, variables);
|
||||
```
|
||||
|
||||
Finally, you can get the context of the current action:
|
||||
|
||||
```
|
||||
const github = require('@actions/github');
|
||||
|
||||
const context = github.context;
|
||||
|
||||
const newIssue = await octokit.issues.create({
|
||||
...context.repo,
|
||||
title: 'New issue!',
|
||||
body: 'Hello Universe!'
|
||||
});
|
||||
```
|
||||
@@ -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'"`;
|
||||
@@ -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'
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"action": "opened",
|
||||
"repository": {
|
||||
"owner": {
|
||||
"login": "user"
|
||||
},
|
||||
"name": "test"
|
||||
},
|
||||
"issue": {
|
||||
"number": 1
|
||||
},
|
||||
"sender": {
|
||||
"type": "User"
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
Generated
+5250
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.0.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
@@ -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>
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
// Originally pulled from https://github.com/JasonEtco/actions-toolkit/blob/master/src/context.ts
|
||||
import {WebhookPayload} from './interfaces'
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-require-imports */
|
||||
|
||||
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 = process.env.GITHUB_EVENT_PATH
|
||||
? require(process.env.GITHUB_EVENT_PATH)
|
||||
: {}
|
||||
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'"
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
super({auth: `token ${token}`})
|
||||
this.graphql = defaults({
|
||||
headers: {authorization: `token ${token}`}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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:
|
||||
|
||||
```
|
||||
/**
|
||||
* 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>
|
||||
const io = require('@actions/io');
|
||||
|
||||
/**
|
||||
* Remove a path recursively with force
|
||||
*
|
||||
* @param path path to remove
|
||||
*/
|
||||
export function rmRF(path: string): Promise<void>
|
||||
await io.mkdirP('path/to/make');
|
||||
```
|
||||
|
||||
/**
|
||||
* 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>
|
||||
#### cp/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>
|
||||
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):
|
||||
|
||||
/**
|
||||
* 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>
|
||||
```
|
||||
```
|
||||
const io = require('@actions/io');
|
||||
|
||||
// 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.
|
||||
|
||||
```
|
||||
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).
|
||||
|
||||
```
|
||||
const exec = require('@actions/exec');
|
||||
const io = require('@actions/io');
|
||||
|
||||
const pythonPath: string = await io.which('python', true)
|
||||
|
||||
await exec.exec(`"${pythonPath}"`, ['main.py']);
|
||||
```
|
||||
|
||||
@@ -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'
|
||||
@@ -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,7 +242,7 @@ 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'
|
||||
@@ -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', () => {
|
||||
|
||||
Generated
+1
-1
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "@actions/io",
|
||||
"version": "0.0.0",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 1
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@actions/io",
|
||||
"version": "0.0.0",
|
||||
"version": "1.0.0",
|
||||
"description": "Actions io lib",
|
||||
"keywords": [
|
||||
"io",
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
+127
-89
@@ -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)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
```
|
||||
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:
|
||||
|
||||
```
|
||||
const tc = require('@actions/tool-cache');
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
tc.downloadTool('http://nodejs.org/dist/v12.7.0/node-v12.7.0-win-x64.zip');
|
||||
const node12ExtractedFolder = await tc.extractZip(node12Path, 'path/to/extract/to');
|
||||
|
||||
// Or alternately
|
||||
tc.downloadTool('http://nodejs.org/dist/v12.7.0/node-v12.7.0-win-x64.7z');
|
||||
const 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:
|
||||
|
||||
```
|
||||
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.
|
||||
|
||||
```
|
||||
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:
|
||||
|
||||
```
|
||||
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:
|
||||
|
||||
```
|
||||
const tc = require('@actions/tool-cache');
|
||||
|
||||
const allNodeVersions = tc.findAllVersions('node');
|
||||
console.log(`Versions of node available: ${allNodeVersions}`);
|
||||
```
|
||||
|
||||
@@ -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'
|
||||
|
||||
Generated
+22
-22
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@actions/tool-cache",
|
||||
"version": "0.0.0",
|
||||
"version": "1.0.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"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@actions/tool-cache",
|
||||
"version": "0.0.0",
|
||||
"version": "1.0.0",
|
||||
"description": "Actions tool-cache lib",
|
||||
"keywords": [
|
||||
"exec",
|
||||
@@ -32,17 +32,17 @@
|
||||
"url": "https://github.com/actions/toolkit/issues"
|
||||
},
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.0.0",
|
||||
"@actions/exec": "^1.0.0",
|
||||
"@actions/io": "^1.0.0",
|
||||
"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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
@@ -1,7 +0,0 @@
|
||||
# `@actions/toolkit`
|
||||
|
||||
> TODO: description
|
||||
|
||||
## Usage
|
||||
|
||||
See [src/toolkit.ts](src/toolkit.ts).
|
||||
@@ -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__`)
|
||||
})
|
||||
Generated
-454
@@ -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="
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": "./",
|
||||
"outDir": "./lib",
|
||||
"rootDir": "./src"
|
||||
},
|
||||
"include": [
|
||||
"./src"
|
||||
]
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 49 KiB |
Reference in New Issue
Block a user