Giter Club home page Giter Club logo

actions-toolkit's Introduction

GitHub Actions Toolkit

An opinionated toolkit for building GitHub Actions in Node.js
Usage โ€ข API โ€ข How to test your Action โ€ข FAQ

GitHub Actions status Codecov

This toolkit is an opinionated alternative to (and wrapper around) the official toolkit. actions/toolkit makes many features optional in the interest of performance, so you may prefer to use it instead of this library.

Usage

Installation

$ npm install actions-toolkit
const { Toolkit } = require('actions-toolkit')
const tools = new Toolkit()

Bootstrap a new action

$ npx actions-toolkit my-cool-action

This will create a new folder my-cool-action with the following files:

โ”œโ”€โ”€ Dockerfile
โ”œโ”€โ”€ action.yml
โ”œโ”€โ”€ index.js
โ”œโ”€โ”€ index.test.js
โ””โ”€โ”€ package.json

API

Toolkit options

event (optional)

An optional list of events that this action works with. If omitted, the action will run for any event - if present, the action will exit with a failing status code for any event that is not allowed.

const tools = new Toolkit({
  event: ['issues', 'pull_requests']
})

You can also pass a single string:

const tools = new Toolkit({
  event: 'issues'
})

And/or strings that include an action (what actually happened to trigger this event) for even more specificity:

const tools = new Toolkit({
  event: ['issues.opened']
})

secrets (optional)

You can choose to pass a list of secrets that must be included in the workflow that runs your Action. This ensures that your Action has the secrets it needs to function correctly:

const tools = new Toolkit({
  secrets: ['SUPER_SECRET_KEY']
})

If any of the listed secrets are missing, the Action will fail and log a message.

token (optional)

You can pass a custom token used for authenticating with the GitHub API:

const tools = new Toolkit({
  token: '1234567890abcdefghi'
})

The github_token input or process.env.GITHUB_TOKEN will be used if no token was passed.

Toolkit.run

Run an asynchronous function that receives an instance of Toolkit as its argument. If the function throws an error (or returns a rejected promise), Toolkit.run will log the error and exit the action with a failure status code.

The toolkit instance can be configured by passing Toolkit options as the second argument to Toolkit.run.

Toolkit.run(async tools => {
  // Action code
}, { event: 'push' })

tools.github

Returns an Octokit SDK client authenticated for this repository. See https://octokit.github.io/rest.js for the API.

const newIssue = await tools.github.issues.create({
  ...tools.context.repo,
  title: 'New issue!',
  body: 'Hello Universe!'
})

You can also make GraphQL requests:

const result = await tools.github.graphql(query, variables)

See https://github.com/octokit/graphql.js for more details on how to leverage the GraphQL API.

Note: To make this function, you must pass a GitHub API token to your action. You can do this in the workflow - both of these are automatically used if they exist:

uses: your/action@v1
with:
  github_token: ${{ github.token }}
env:
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

tools.log

This library comes with a slightly-customized instance of Signale, a great logging utility. Check out their docs for the full list of methods. You can use those methods in your action:

tools.log('Welcome to this example!')
tools.log.info('Gonna try this...')
try {
  risky()
  tools.log.success('We did it!')
} catch (error) {
  tools.log.fatal(error)
}

In the GitHub Actions output, this is the result:

โ„น  info      Welcome to this example!
โ„น  info      Gonna try this...
โœ–  fatal     Error: Something bad happened!
    at Object.<anonymous> (/index.js:5:17)
    at Module._compile (internal/modules/cjs/loader.js:734:30)

tools.inputs

GitHub Actions workflows can define some "inputs" - options that can be passed to the action:

uses: JasonEtco/example-action@v1
with:
  foo: bar

You can access those using tools.inputs:

console.log(tools.inputs.foo) // -> 'bar'

Note! This is not a plain object, it's an instance of Proxy, so be aware that there may be some differences.


tools.outputs

GitHub Actions workflows can define some "outputs" - options that can be passed to the next actions. You can access those using tools.outputs:

tools.outputs.foo = 'bar'

Note! This is not a plain object, it's an instance of Proxy, so be aware that there may be some differences.


tools.command(command, (args, match) => Promise)

Respond to a slash-command posted in a GitHub issue, comment, pull request, pull request review or commit comment. Arguments to the slash command are parsed by minimist. You can use a slash command in a larger comment, but the command must be at the start of the line:

Hey, let's deploy this!
/deploy --app example --container node:alpine
tools.command('deploy', async (args: ParsedArgs, match: RegExpExecArray) => {
  console.log(args)
  // -> { app: 'example', container: 'node:alpine' }
})

The handler will run multiple times for each match:

/deploy 1
/deploy 2
/deploy 3
let i = 0
await tools.command('deploy', () => { i++ })
console.log(i)
// -> 3

tools.getPackageJSON()

Get the package.json file in the project root and returns it as an object.

const pkg = tools.getPackageJSON()

tools.readFile(path, [encoding = 'utf8'])

Get the contents of a file in the repository. Should be used with actions/checkout to clone the repository in the actions workflow.

const contents = await tools.readFile('example.md')

tools.exec

Run a CLI command in the workspace. This uses @actions/exec under the hood so check there for the full usage.

const result = await tools.exec('npm audit')

tools.token

The GitHub API token being used to authenticate requests.


tools.workspace

A path to a clone of the repository.


tools.exit

A collection of methods to end the action's process and tell GitHub what status to set (success, neutral or failure). Internally, these methods call process.exit with the appropriate exit code. You can pass an optional message to each one to be logged before exiting. This can be used like an early return:

if (someCheck) tools.exit.neutral('No _action_ necessary!')
if (anError) tools.exit.failure('We failed!')
tools.exit.success('We did it team!')

tools.context

tools.context.action

The name of the action

tools.context.actor

The actor that triggered the workflow (usually a user's login)

tools.context.event

The name of the event that triggered the workflow

tools.context.payload

A JSON object of the webhook payload object that triggered the workflow

tools.context.ref

The Git ref at which the action was triggered

tools.context.sha

The Git sha at which the action was triggered

tools.context.workflow

The name of the workflow that was triggered.

tools.context.issue

The owner, repo, and issue_number params for making API requests against an issue or pull request.

tools.context.pullRequest

The owner, repo, and pull_number params for making API requests against a pull request.

tools.context.repo

The owner and repo params for making API requests against a repository. This uses the GITHUB_REPOSITORY environment variable under the hood.

How to test your GitHub Actions

Similar to building CLIs, GitHub Actions usually works by running a file with node <file>; this means that writing a complete test suite can be tricky. Here's a pattern for writing tests using actions-toolkit, by mocking Toolkit.run:

index.js
const { Toolkit } = require('actions-toolkit')
Toolkit.run(async tools => {
  tools.log.success('Yay!')
})
index.test.js
const { Toolkit } = require('actions-toolkit')
describe('tests', () => {
  let action

  beforeAll(() => {
    // Mock `Toolkit.run` to redefine `action` when its called
    Toolkit.run = fn => { action = fn }
    // Require the index.js file, after we've mocked `Toolkit.run`
    require('./index.js')
  })

  it('logs successfully', async () => {
    // Create a fake instance of `Toolkit`
    const fakeTools = new Toolkit()
    // Mock the logger, or whatever else you need
    fakeTools.log.success = jest.fn()
    await action(fakeTools)
    expect(fakeTools.log.success).toHaveBeenCalled()
  })
})

You can then mock things by tweaking environment variables and redefining tools.context.payload. You can check out this repo's tests as an example.

Motivation

actions-toolkit is a wrapper around some fantastic open source libraries, and provides some helper methods for dealing with the GitHub Actions runtime. Actions all run in Docker containers, so this library aims to help you focus on your code and not the runtime. You can learn more about building Actions in Node.js to get started!

After building a GitHub Action in Node.js, it was clear to me that I was writing code that other actions will want to use. Reading files from the repository, making requests to the GitHub API, or running arbitrary executables on the project, etc.

So, I thought it'd be useful to build those out into a library to help you build actions in Node.js ๐ŸŽ‰

FAQ

Aren't these just wrappers around existing functions?

Yep! I just didn't want to rewrite them for my next Action, so here we are.

What's the difference between this and actions/toolkit?

This library was the inspiration for the official toolkit. Nowadays, it's an opinionated alternative. My goal for the library is to make building simple actions easy, while the official toolkit needs to support more complicated use-cases (like performance and scaling concerns).

actions-toolkit's People

Contributors

abouroubi avatar ataylorme avatar banyan avatar bdougie avatar danez avatar dependabot[bot] avatar evdama avatar jasonetco avatar jclem avatar kentaro-m avatar kevinpollet avatar lannonbr avatar likern avatar macklinu avatar mheap avatar outsideris avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

actions-toolkit's Issues

Specific events causes error logs

I understand this is the current approach for handling events but I think it should end without a failure.

if present, the action will exit with a failing status code for any event that is not allowed.

This causes some confusion in the following scenario:

{
  event: [ 'issues.opened' ],
}

In GitHub we cannot specify to run an action on issues.opened we can only run an action on issues. This means that all the actions that run on issues are going to get an error log. Which causes confusion like seen in this issue: alex-page/add-new-issue-project#3

โœ–  error     Event `issues.assigned` is not supported by this action.

I think these actions should log with a neutral state if the user is using a specific action and they are using the parent event.

I can do a simple work around which is nice:

if( tools.context.payload.action !== 'opened' ){
  tools.exit.neutral( `Event ${ action } is not supported by this action.` )
}

Let me know if you think this is an issue, happy to close it if there is some functionality I don't understand or reasoning for this approach.

Describe tools.arguments further

In the docs, it may be useful further to describe how to write this when interacting with the .workflow files:

as seen in the GitHub Actions docs, the args field could be written here:

args = ["container:release", "--app", "web"]

With actions-toolkit, I think this would be converted to this I believe:

{ _: ['container:release'], app: 'web' }

Is this correct? If so, I think having this in the docs for actions-toolkit would be useful.

Throw error when requiredEnvVars are missing

๐Ÿ‘‹ @JasonEtco thanks for providing this utility, it's super helpful!

What are your thoughts on throwing an error (saying, e.g. GitHub Actions environment unavailable) when the required environment variables are missing? My thoughts are this would make it a little more explicit when the Actions environment isn't set, and would allow me to decide how to proceed (should I abort, or try and do something useful).

private warnForMissingEnvVars () {
const requiredEnvVars = [
'HOME',
'GITHUB_WORKFLOW',
'GITHUB_ACTION',
'GITHUB_ACTOR',
'GITHUB_REPOSITORY',
'GITHUB_EVENT_NAME',
'GITHUB_EVENT_PATH',
'GITHUB_WORKSPACE',
'GITHUB_SHA',
'GITHUB_REF',
'GITHUB_TOKEN'
]
const requiredButMissing = requiredEnvVars.filter(key => !process.env.hasOwnProperty(key))
if (requiredButMissing.length > 0) {
// This isn't being run inside of a GitHub Action environment!
const list = requiredButMissing.map(key => `- ${key}`).join('\n')
const warning = `There are environment variables missing from this runtime, but would be present on GitHub.\n${list}`
// tslint:disable-next-line:no-console
console.warn(warning)
this.warning = warning
}
}

Ability to leverage graphql previews of the API is not advertised in README

Context

I am trying to leverage your toolkit to create action on a repo. I understand you are leveraging Octokit, but adding a preview header was not as clear but I figured it out.

My example

Toolkit.run(async tools => {

  const headers = {"Accept": "special-feature"}
  const results = await tools.github.graphql(query, {headers})
  ...
}

If it is cool with you, I might add a mention of headers to the README.md

Debug mode

Here's a thought for folks building Actions: would it be useful to have a "Debug mode" for Actions using actions-toolkit? Here's a rough idea for the feature's I'd want it to have:

  • Post payloads to Smee.io (something similar to JasonEtco/smee-action, but built into actions-toolkit)
  • Additional, verbose logging
    • Log before require-ing event.json
    • Log the contents of event.json
    • Log all GITHUB_* environment variables (aside from GITHUB_TOKEN)
    • Maybe log a tree of the workspace?

I'd have it enabled by the DEBUG environment variable:

action "issue" {
  uses = "JasonEtco/create-an-issue@master"
  env = {
    DEBUG = true
  }
}

What do y'all think? Would this be useful, or unnecessary?

Add methods for exiting the action

I think it'd be cool to add methods to exit the action with the appropriate exit code. For a successful action, you can just let the action end on its own, but for a failure or a neutral status you need to pass the appropriate code. Here's what I'm thinking:

class Toolkit {
  public fail (message: string) {
    console.error(message)
    process.exit(1)
  }
  public neutral (message: string) {
    console.log(message)
    process.exit(78)
  }
}

Especially for the neutral status, remembering the code you want to use and actually calling process.exit is a weird scenario (at least in my experience).

Should this be nested under something like toolkit.end.method or toolkit.exit.method? Is this even a helpful thing (I think yes)?

A "store" for sharing information across actions

Sharing data between actions can be done, but its a little tricky. A workflow has a shared file-system (in /github/workspace), so data can be written and read from a file across actions.

However, this is can be kinda tricky to get right. I'd love to build a "store" of sorts, that just reads/writes from a namespaced file. Something like:

tools.store.set('my-key', { foo: true })
tools.store.get('my-key')

This could operate inside of a .txt file, namespaced to the workflow's check run id or the sha or the workflow name (lots of options).

It's definitely possible that a library like this already exists - in which case, we should use it!

Specify some input or env that allows a user to override the default GITHUB_REPOSITORY env var

Is your feature request related to a problem? Please describe.
Using this toolkit and your create-issue action, I am unable to create an issue in a different repository than the action's repo. Our team is attempting to put as much automation in its own repository as possible to separate repo concerns and this functionality would be great!

Describe the solution you'd like
I can pass env vars of repo and owner, or a repository value with a / delimited string

Describe alternatives you've considered
I tried to override the GITHUB_REPOSITORY var. Looking at the Context class, it does look like process.env.GITHUB_REPOSITORY is checked. Unfortunately, the actions documentation explicitly says that GITHUB_ variables will be ignored, so I'm not sure how else to proceed.

Additional context
I do apologize if I missed a setting somewhere that facilitates this! For context, here is the relevant portion of our action using create-issue:

      - name: Create an issue
        uses: JasonEtco/create-an-issue@v2
        env:
          GITHUB_TOKEN: ${{ secrets.CREATE_ISSUE_TOKEN }}
          GITHUB_REPOSITORY: github/heart-services <-- this doesn't get overwritten
          START_DATE: ${{ steps.date.outputs.start }}
          END_DATE: ${{ steps.date.outputs.end }}
          USER_NAME: ${{ steps.get_first_responder.outputs.user }}
          LABELS: first-responder
        with:
          filename: .github/ISSUE_TEMPLATE/first_responder.md
          assignees: ${{ steps.get_first_responder.outputs.user }}

Thank you ๐Ÿ˜„ . Assuming you think this could be a useful feature, I'm happy to open a PR with whatever approach you think is best.

Integrate Signale logs with GH actions syntax

As of now, you can use the built-in logger to show errors, warnings, debug messages, and so on, but they don't use the GH actions syntax, and so they don't appear in the report.

I'd like to integrate the already existing methods with the proper syntax (you can add it as a prefix in Signale) and to add a couple of methods (such as groups)

It's still possible to create your own Signale instance or to just log them manually, but I think that since you're already providing a custom logger it would be nice to provide this kind of feature too.

I work on this myself and then open a PR if you think this would be a nice addition to the package.
Let me know!

Document toolkit#getFile

I noticed this isn't documented in the README at all for some reason - it should be!

Here's the code:

/**
* Gets the contents file in your project's workspace
*
* ```js
* const myFile = tools.getFile('README.md')
* ```
*
* @param filename - Name of the file
* @param encoding - Encoding (usually utf8)
*/
public getFile (filename: string, encoding = 'utf8') {
const pathToFile = path.join(this.workspace, filename)
if (!fs.existsSync(pathToFile)) throw new Error(`File ${filename} could not be found in your project's workspace.`)
return fs.readFileSync(pathToFile, encoding)
}

Marking this as good first issue - if you're interested in contributing, that'd be wonderful!

Only allow certain events

Events that trigger actions are defined in workflows, but there's nothing prevent me from using, for example, an action that is designed to inspect a push payload triggered by an issues event. So, it'd be nice to build a declarative way to fail an action run unless the event trigger matches what the action was expecting.

The real goal here is to avoid situations where a user is expecting an action to work for any event, but can't figure out why it won't with the event they're using.

Here's what I'm thinking:

interface ToolkitOpts {
  only?: string[]
}
class Toolkit {
  constructor (opts: ToolkitOpts) {
    const event = process.env.GITHUB_EVENT
    if (Array.isArray(opts) && !opts.includes(event)) {
      console.error(`Event ${event} is not supported by this action.`)
      process.exit(1)
    }
  }
}

Creating a pull request review

I want to have a slash command like /emergency-approve that will create an approved review by someone (either the author itself, if that's possible, or by a bot user).
While the usage isn't relevant, I know people have opinions on such a behaviour so I'll just say that this is to allow a designated person(s) to bypass the requirement of an approved review to merge a pull request to a protected branch, in case we must deploy an urgent fix.

I started by creating a filter of a issue comment directly in github actions, pointing to another filter of the hardcoded username of the designated user, and when I got to the actual action of creating the approval I tried messing around with https://github.com/actions/github, but it only have comments, labels, and assignments.
So I forked it and tried duplicating and adjusting the comment logic, but I can't seem to understand how do I use the context to have the relevant needed information (repo, pull request) but also provide an event of type APPROVE and maybe an approve comment saying it was an emergency approval.

This is the code I got to (in the forked https://github.com/actions/github repo) but I guess I can do everything directly by creating a complete action using this repo.

async function doApprove() {
  filterAction(tools.arguments.action)
  const body = tools.arguments._.slice(1).join(' ')
  tools.log.info('reason', body)
  return checkStatus(
    await tools.github.pulls.createReview(tools.context.issue({body})) // this is where I don't understand how to add the additional information, specifically the "event" of value "APPROVE"
  )
}

Not all feather icons are supported by actions

The new questionnaire allows you to choose unsupported feather icons. If the documentation is correct, I don't think we support these icons:

chrome
codepen
coffee
figma
frown
github
gitlab
instagram
key
linkedin
meh
mouse-pointer
pen-tool
slack
smile
trello
twitter
x-octagon
youtube

The documentation over here lists the icons that are supported right now.

A logo for this library

I'd like this library to have a logo! Here's where it'd go in the README:

image

Motivation

I read somewhere (I can't find the post but would absolutely love to re-read) that talked about the power of branding for open source projects. Having an image in your mind is a lot more memorable than a name or a line of code. Plus, it adds some legitimacy to show that the project has gotten real attention.

Inspiration

  • The ๐Ÿ›  emoji
  • The play button logo for GitHub Actions:

How you can help

If you're interested in contributing this, that'd be amazing. You can drop some potential directions in this issue and we can talk about them!

Can we set outputs ?

Github Actions can have outputs, can we do that with this toolkit ?

If not, is there any restirction on importing the @actions/core in the same file that uses actions-toolkit ?

minimist argument as string with space breaking

I have an argument --test="hello world".

Using minimist not through this library the output is

{ _: [], test: 'hello world' }

Using this library it separates the string values into

{ _: [ 'world\'' ],  test: '\'hello' }

I noticed you parse the arguments, but I think something seems a bit off.

Add CLI questionnaire

Similar to npm init, it could be a nice developer experience to interactively bootstrap an actions-toolkit action. Being asked by the CLI to give the action a name, description, icon, etc. could be a nice way to inform developers about the documented label metadata during the setup process.

This could use a library like inquirer or prompts to interactively prompt the user for input. We could default to what labels currently exist in the Dockerfile template if the user doesn't choose one, at least as a start.

# Labels for GitHub to read your action
LABEL "com.github.actions.name"="Your action name"
LABEL "com.github.actions.description"="A description of your action"
# Here all of the available icons: https://feathericons.com/
LABEL "com.github.actions.icon"="play"
# And all of the available colors: https://developer.github.com/actions/creating-github-actions/creating-a-docker-container/#label
LABEL "com.github.actions.color"="gray-dark"

We could also add a flag for skipping or answering yes to all prompts to bypass this flow.

Thoughts on this? Happy to spec this out more if you're interested / would like a PR but need more clarifying information.

Show Table of Contents for API

I've noticed that some folks aren't making full use of APIs provided by actions-toolkit, and I'm assuming its because they don't know about a certain method/property. It'd be nice to provide a table of contents that show, at a high level, the things that actions-toolkit can do. Something like:

  • API
    • Authenticated GitHub API client (tools.createOctokit)
    • Parsing arguments (tools.arguments)
    • Reading files (tools.getFile)
    • In-repo configuration (tools.config)

Better support for arguments

Currently, arguments are accessed via process.argv. It'd be great to serialize those and provide a nicer API for consuming arguments by the workflow. As a process variable, it's extremely hard to test.

More specifically, any additional arguments are usually (but not only) passed as process.argv[2] or higher, because 0 is the Node executable, and 1 is the file that's being run (like node file.js). How this fairs in an npm start script, not sure!


Some possible APIs:

As an array of args: (this is super easy to implement)

// $ node file.js some-arg
console.log(tools.arguments)
// => ['file.js', 'some-arg']

As --option flags: (a lib probably exists to do this)

// $ node file.js --foo true --bar="baz"
console.log(tools.arguments)
// => { foo: true, bar: 'baz' }

Provide a logger

I'm not sure what the goals here are, but I find that when I'm debugging an action its difficult to visually parse the logs. I'd like to explore a better logging solution, one that actions-toolkit can help with.

Something like this, which shows the logger name and where in the code the logs come from:

tools.log('Something happened!')
// => [action index.js:12:3] Something happened!

Some loggers

Add docs on testing your action

Writing tests for GitHub Actions is tough to get the hang of. I want to add documentation to show how I've done this using actions-toolkit. I'll link to create-an-issue, where I've done this fairly well. Some key highlights:

// Mock the path to the workspace
tools.workspace = path.join(__dirname, 'fixtures')
// Mock the payload
tools.context.payload = { repository: { owner: { login: 'JasonEtco' }, name: 'waddup' } }
// Mock the GitHub client
tools.github = github

And allowing for a "mocked" Toolkit instance to be passed to your app code. This can usually be done by having an entrypoint JS file that just calls your actual code with a regular Toolkit, or an optional passed Toolkit that you can mock:

From validate-semantic-release:

module.exports = tools => {
  if (!tools) tools = new Toolkit({ event: 'release' })  

Release workflow won't publish tagged versions

I realized that the workflow added in #27 doesn't actually publish tags to NPM. So it'll validate that, for example, v1.1.0-beta.1 is in a prerelease GitHub release, but won't publish it as --tag beta. This is a limitation of the current workflow strategy; that information would need to be passed to args here:

uses = "actions/npm@master"
args = "publish"
secrets = ["NPM_AUTH_TOKEN"]

I think that what I'll do is use tools.store.set('tag', tag) (or just write tag to a file) inside of validate-release, and read that in a custom publish action instead of using actions/npm.

CLI tool to bootstrap a new Action

I'd love to run a command to bootstrap the various files I know I'll need (like a Dockerfile):

./node_modules/.bin/actions-toolkit init

This would generate a Dockerfile:

FROM node:10-slim

LABEL "com.github.actions.name"="Create an issue"
LABEL "com.github.actions.description"="Creates a new issue using a template with front matter."
LABEL "com.github.actions.icon"="gear"
LABEL "com.github.actions.color"="red"

LABEL "repository"="http://github.com/JasonEtco/create-an-issue"
LABEL "homepage"="http://github.com/JasonEtco/create-an-issue"
LABEL "maintainer"="Jason Etcovitch <[email protected]>"

WORKDIR /
COPY package*.json ./
RUN npm ci
COPY . .

ENTRYPOINT ["node", "/entrypoint.js"]

It could also generate an entrypoint.js file:

const { Toolkit } = require('actions-toolkit')
const tools = new Toolkit()

This could use inquirer to fill in some of those fields, like create-probot-app.

Possible EventEmitter memory leak detected in tests

Running npm test will log the following warning:

(node:42520) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 exit listeners added. Use emitter.setMaxListeners() to increase limit

This was discussed in #54 but can be investigated as its own issue.

EDIT: Was able to get a stack trace by running node --trace-warnings node_modules/.bin/jest:

PASS tests/exit.test.ts
PASS tests/context.test.ts
PASS tests/store.test.ts
(node:16225) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 exit listeners added. Use emitter.setMaxListeners() to increase limit
    at _addListener (events.js:243:17)
    at process.addListener (events.js:259:10)
    at new Store (/Users/macklinu/Documents/GitHub/actions-toolkit/src/store.ts:47:13)
    at new Toolkit (/Users/macklinu/Documents/GitHub/actions-toolkit/src/index.ts:79:18)
    at Object.<anonymous> (/Users/macklinu/Documents/GitHub/actions-toolkit/tests/index.test.ts:11:15)
    at Object.asyncJestLifecycle (/Users/macklinu/Documents/GitHub/actions-toolkit/node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:53:37)
    at resolve (/Users/macklinu/Documents/GitHub/actions-toolkit/node_modules/jest-jasmine2/build/queueRunner.js:41:12)
    at new Promise (<anonymous>)
    at mapper (/Users/macklinu/Documents/GitHub/actions-toolkit/node_modules/jest-jasmine2/build/queueRunner.js:26:19)
    at promise.then (/Users/macklinu/Documents/GitHub/actions-toolkit/node_modules/jest-jasmine2/build/queueRunner.js:71:41)
PASS tests/index.test.ts

Test Suites: 4 passed, 4 total
Tests:       39 passed, 39 total
Snapshots:   13 passed, 13 total
Time:        2.508s
Ran all test suites.

Issue looks to be with this exit handler:

process.on('exit', () => {
if (this.cache.keys().length > 0) {
this.save()
}
})

Add a `secrets` option to the constructor

Similar to the events option from #36, I'd like the Toolkit constructor to have a secrets option that does exactly the same kind of thing: will exit with a failure if the required secrets are not present. This should be a declarative, clear way of saying "this action won't work without these secrets."

new Toolkit({
  secrets: ['GITHUB_TOKEN']
})

Release workflow doesn't support 2FA

The workflow added in #27 doesn't work because of a limitation in NPM's authentication strategy. Currently, you can either have 2FA for authorization only (login), or for everything (including publishing libraries) - unfortunately, that applies to tokens as well.

So during the action run, it attempts to publish and asks for an OTP (one-time-password) - which of course I can't provide.

Memorializing this Twitter conversation.

/command support

It'd be great to provide a /command abstraction - something like:

// In an issue/PR comment, `/pizza dominos pepperoni`
toolkit.command('pizza', async args => {
  // args -> ['dominos', 'pepperoni']
})

Or maybe...

// on instead of command
toolkit.on('pizza', async args => console.log(args))

Memorializing a conversation in this tweet:

image


I think that @mscoutermarsh should work on this ๐Ÿ˜Ž๐Ÿ˜ˆ

Latest @octokit/rest has a .graphql method so no need to add it

Latest @octokit/rest is built on @octokit/core, so when you update you can remove your workaround

export class GitHub extends Octokit {
public graphql: typeof graphql
constructor (token: string) {
super({ auth: `token ${token}` })
this.graphql = graphql.defaults({
headers: { authorization: `token ${token}` }
})
}
}

But I'm also not sure why you keep exposing that API instead of telling people how to load their own octokit instance. It will reduce your maintenance and support overhead, and there is maybe one extra line of code for the users

GraphQL support

Would be great to use @octokit/graphql and provide an authenticated GraphQL method. Something like:

import graphql from '@octokit/graphql'

class Toolkit {
  async graphql (query: string, variables: object) {
    return graphql(query, {
      headers: { authorization: `token ${this.token}` },
      ...variables
    })
  })
}

Or, adding it through an Octokit plugin.

Stop warning about `HOME` not being present on Windows runners

As of now, the toolkit is creating a warning every time the HOME env variable is not present, as it's part of the required env variables:

const requiredEnvVars = [
'HOME',
'GITHUB_WORKFLOW',
'GITHUB_ACTION',
'GITHUB_ACTOR',
'GITHUB_REPOSITORY',
'GITHUB_EVENT_NAME',
'GITHUB_EVENT_PATH',
'GITHUB_WORKSPACE',
'GITHUB_SHA'
]

A user of mine has reported here that Windows runners don't use the HOME variable, even if they are original runners from GitHub.

I think the HOME variable could be removed from the list, since it doesn't seem to be actually used by the toolkit. Also, if one really needed to work with paths, there are a lot of other variables that GitHub provides in their runners, like: GITHUB_WORKSPACE, GITHUB_ACTION_PATH, GITHUB_EVENT_PATH, GITHUB_PATH, ...

tolkit

Is your feature request related to a problem? Please describe.

A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

Describe the solution you'd like

A clear and concise description of what you want to happen.

Describe alternatives you've considered

A clear and concise description of any alternative solutions or features you've considered.

Additional context

Add any other context or screenshots about the feature request here.https://www.github.com/Queenmariehatcher/queenmariehatcher/tree/master/docs%2Fspecs%2Fpackage-specs.md

Error regarding missing js-yaml dependency

We've just received the 3.0 update in our pipeline. However, actions that use actions-toolkit now all fail with the following error:

Error: Cannot find module 'js-yaml'
Require stack:
- /node_modules/actions-toolkit/lib/index.js
- /entrypoint.js
    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:980:15)
    at Function.Module._load (internal/modules/cjs/loader.js:862:27)
    at Module.require (internal/modules/cjs/loader.js:1040:19)
    at require (internal/modules/cjs/helpers.js:72:18)
    at Object.<anonymous> (/node_modules/actions-toolkit/lib/index.js:54:33)
    at Module._compile (internal/modules/cjs/loader.js:1151:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1171:10)
    at Module.load (internal/modules/cjs/loader.js:1000:32)
    at Function.Module._load (internal/modules/cjs/loader.js:899:14)
    at Module.require (internal/modules/cjs/loader.js:1040:19) {
  code: 'MODULE_NOT_FOUND',
  requireStack: [ '/node_modules/actions-toolkit/lib/index.js', '/entrypoint.js' ]
}

Does that ring a bell?

Note about Architectural Context & Added Value

Since Github Actions is an entirely new concept it's hard for people to grasp how actions-toolkit fits in this context of github actions. Is it a way to write actions inside Docker Containers using node.js? Is it an extension to the textual way of writing actions on github? Is it something optional entirely, and where is it executed after all?

People don't know yet what to make of github actions, let alone some utility library (is it one?) like actions-toolkit. It would be great if there were a few sentences at the beginning telling a new user why he would want to look into actions-toolkit and what the added value of using it might be.

Support use with GitHub Enterprise servers

The .github property of the Toolkit class instances lets me interact with GitHub cloud but it does not allow me to interact with a GitHub Enterprise Server. This causes issues when attempting to use this package in a GitHub action running from GitHub Enterprise

A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
Without passing a 'baseUrl' property into the constructor of Octokit, it will default to using GitHub Cloud's API endpoint, rather than the API endpoint on the GitHub Enterprise Server.

The baseUrl parameter should be added here:

this.github = new Octokit({ auth: `token ${this.token}` })

Describe the solution you'd like

Allow an additional parameter to be passed into the ToolKit constructor that might allow me to pass in the correct API endpoint URL or detect the URL from the environment variables GitHub Enterprise provides:

  process.env.GITHUB_API_URL == 'https://{hostname of GitHub Enterprise Server}/api/v3'

Describe alternatives you've considered

I have not found a work around. I replaced the .github property with my own correctly configured OctoKit class instance

Logging Issue

Given the following code:

tools.log.info({
  owner: process.env.GITHUB_ACTOR,
  repo: process.env.GITHUB_REPOSITORY.split('/').pop(),
  tag: tagName,
  message: process.env.hasOwnProperty('TAG_MESSAGE')
    ? process.env.TAG_MESSAGE
    : `Version ${pkg.version}`,
  sha: process.env.GITHUB_SHA,
  type: 'commit'
})

Actual Output: Version 1.0.0

Expected Output: (object)

{
  "owner": "...",
  "repo": "....",
  "tag": "1.0.0",
  "message": "Version 1.0.0",
  "sha": "...",
  "type": "commit"
}

It appears as though message is being picked up by the logger instead of dumping out the object. It works with other objects. It only fails when message is an attribute of the object.

Automated release workflow

Publishing this library is pretty manual right now. I'd like to dogfood and use GitHub Actions to automatically publish the npm package when I create a new release on GitHub (because Release Drafter is still great).

I'm imagining something like this:

workflow "Publish a release to npm" {
  on = "release"
  resolves = ["npm publish"]
}

# Checks that the release is not a draft
# and it is attached to a valid semver tag
action "validate release" {
  uses = "./.github/actions/validate-release"
}

action "npm ci" {
  needs = ["find tag"]
  uses = "actions/npm@master"
  args = "ci"
}

action "npm publish" {
  needs = ["npm ci"]
  uses = "actions/npm@master"
  args = "publish"
  secrets = ["NPM_TOKEN"]
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.