CLI tool
actions/toolkit avatar
actions/toolkit

actions/toolkit: the npm packages behind most JavaScript GitHub Actions

The GitHub ToolKit for developing GitHub Actions.

5,859 stars1,810 forksTypeScriptMIT

At a glance

What is it?
actions/toolkit is a Lerna monorepo of @actions/* packages for inputs, exec, globbing, HTTP, tool caching and the Octokit client. It is the standard dependency set for JavaScript actions, and GitHub has frozen outside contributions to it.
Who is it for?
Adopt actions/toolkit if you are writing a JavaScript or TypeScript action and want the same input, output, exec and caching primitives the setup-* actions use; start from the javascript-action template rather than wiring packages by hand. Do not adopt it for a Docker action that never touches the GitHub context, and do not plan contributions: the README states the repository is not taking them.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 2 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 28, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What actions/toolkit actually is, and who ends up depending on it

The repository is not a CLI and not a service. It is a set of npm packages that a JavaScript action imports at runtime, and the README frames the whole project that way: "a set of packages to make creating actions easier." Each package covers one narrow job. @actions/core handles inputs, outputs, results, logging, secrets and variables. @actions/exec runs command line tools and processes their output. @actions/glob searches for files matching glob patterns. @actions/http-client is an HTTP client tuned for actions. @actions/io wraps disk operations such as cp, mv and rmRF. @actions/tool-cache downloads and caches tools, which is how setup-* actions avoid re-downloading a toolchain on every run. @actions/github returns an Octokit client already hydrated with the context the action is running in. @actions/artifact and @actions/cache deal with artifacts and dependency caching, and @actions/attest writes attestations for workflow artifacts.

The audience is narrow and specific: people writing custom actions in JavaScript or TypeScript who need to read a workflow input, shell out to a binary, or call the GitHub API without hand-rolling that plumbing. If you only consume actions written by others, none of this is for you. The packages are published individually, so an action typically depends on two or three of them rather than all ten.

One structural detail matters before you invest time: the README states plainly that the project is not taking contributions, and that GitHub is directing questions to Community Discussions and high-priority bugs to support. It also commits to security updates and fixes for major breaking changes. That is a maintenance posture worth reading carefully rather than assuming a normal open source cadence.

How the packages fit together at runtime

There is no daemon and no framework to boot. An action is a Node process that the runner starts, and the toolkit packages are ordinary libraries inside that process. @actions/core reads the runner's environment and command files to resolve getInput calls, and writes outputs and log annotations back through the same channel. @actions/github builds on Octokit and injects context such as the repository and workflow that triggered the run, which the README demonstrates inside a container example with context.repo.repo.

The heavier packages sit on top of that same process model. @actions/tool-cache downloads a tool and stores it under a directory the runner persists, so a later job can find it again instead of downloading it twice. @actions/cache does the analogous job for arbitrary directories, and the README points readers at it from the tool-cache entry with the line "See @actions/cache for caching workflow dependencies." @actions/exec spawns child processes and captures stdout and stderr, which is why setup-* actions can wrap an installer without parsing shell output themselves.

The repository itself is a Lerna monorepo with Nx in the mix: lerna.json and nx.json sit at the top level, packages live under packages/, and the root package.json drives everything through lerna run. Building, testing and linting are all orchestrated from that root rather than per package by default. That layout is why the versioning guidance lives in docs/action-versioning.md: an action is fetched from the GitHub graph of repos, so how you tag and release it is a separate problem from how you build it.

Installing @actions/core and running it in a real action

The README gives the install line for each package, and they are all the same shape. To read an input and write an output you need @actions/core, which the README installs with:

bash
npm install @actions/core

Once it is in your action's dependencies, import it and call getInput. The README's Hello World walkthrough shows exactly this pattern, reading a value named who-to-greet and printing it:

javascript
const nameToGreet = core.getInput('who-to-greet');
console.log(`Hello ${nameToGreet}!`);

For an action that waits a configurable amount of time, the JavaScript walkthrough reads a milliseconds input inside a run function and logs it before the wait:

javascript
async function run() {
  try {
    const ms = core.getInput('milliseconds');
    console.log(`Waiting ${ms} milliseconds ...`)

The walkthrough then shows the test output a new action should produce, with three passing tests covering an invalid number, a 500 ms wait, and a normal run. If you are starting from nothing, the README points at the javascript-action template and the typescript-action walkthrough rather than asking you to assemble the packages yourself. The TypeScript variant imports the same module with an ES import:

javascript
import * as core from '@actions/core';

If your action ships as a container instead, the README's Octokit example installs production dependencies inside the image and starts Node directly:

docker
FROM node:slim
COPY . .
RUN npm install --production
ENTRYPOINT ["node", "/lib/main.js"]

Inside that container, core.getInput and github.context work the same way, which the README illustrates by logging the repository name from context data.

The contribution freeze, and what it means for a bug you hit

The most consequential limitation is not technical. The README states that the repository is not taking contributions right now, and that resources are being allocated to other areas of Actions. Questions go to Community Discussions, high-priority bugs go to Discussions or support, and security issues follow SECURITY.md. The project does say it will keep providing security updates and fix major breaking changes.

Read that as a support contract with a narrow scope. If @actions/exec mishandles an edge case in your environment, you cannot open a pull request and expect it to land. Your realistic options are a fork, a workaround inside your own action, or a switch to a different library for that one concern. For a small action this rarely matters, because the packages are thin wrappers over Node APIs you can call directly. For a team that has built a lot of internal tooling on @actions/tool-cache or @actions/cache, it means the upgrade path is whatever GitHub ships, on GitHub's schedule.

There is a second, quieter cost: because the packages are versioned and published separately, an action's dependency set drifts. The root package.json carries an overrides block pinning transitive packages such as semver, tar and several @octokit entries, which tells you the maintainers do track dependency risk centrally. Your own action does not inherit those overrides. You are responsible for your own lockfile.

Where a plain Node script or a Docker action is the better choice

The closest alternative is not another toolkit, it is skipping the toolkit. A JavaScript action can call process.env directly for inputs, use Node's child_process for exec, and use fs for file operations. That approach has no dependency to audit, no version drift, and no exposure to the contribution freeze. It costs you the parts that are genuinely fiddly: resolving inputs correctly across runner versions, writing outputs through the command files, and emitting annotations that render in the UI. @actions/core exists precisely because those details are easy to get wrong.

The other real alternative is a Docker action. The README links docs/action-types.md, which "outlines the differences and why you would want to create a JavaScript or a container based action." A container action packages its own runtime, so you are not at the mercy of the Node version on the runner, and you can bring any language. The trade-off is startup time and image size. The README's own container example uses node:slim and runs npm install --production at build time, which is a heavier artifact than a bundled JavaScript action. If your action is a thin wrapper around an API call, the JavaScript route with @actions/github is the shorter path. If it needs a specific toolchain or a non-Node language, the container route wins.

A third option worth naming: if an official setup-* action already exists for your tool, use it. @actions/tool-cache exists to serve those actions, and reimplementing the download-and-cache logic yourself duplicates work GitHub has already done.

Licence and the cost of keeping an action current

The repository is MIT licensed, and LICENSE.md sits at the top level. MIT is permissive: you can depend on the packages, fork them, and ship them inside a proprietary action. The packages are published to npm under the @actions scope, so the licence you actually accept is the one attached to each published package. If you fork a package to work around the contribution freeze, you carry the maintenance yourself from that point, including any security fix GitHub later publishes upstream.

Upgrade cost is the practical question. The root package.json shows the toolchain the maintainers use to build and test the monorepo: TypeScript 5.2, Jest 29, Lerna 6, Nx 16.6, ESLint 8. That matters if you fork the repository rather than consume the packages, because you inherit that build setup and the check-all script that runs format checking, linting, tests and a type check together. Consuming the packages from npm avoids all of it.

For a normal action, the upgrade cost is low and mostly invisible: you bump a version in package.json, rebuild, and commit the bundled output. The thing to watch is the versioning guidance in docs/action-versioning.md, which covers safe releases for actions distributed through the GitHub graph of repos. Getting that wrong is more damaging than a stale dependency, because a bad tag reaches every workflow that references your action.

Editorial conclusion

Adopt actions/toolkit if you are writing a JavaScript or TypeScript action and want the same input, output, exec and caching primitives the setup-* actions use; start from the javascript-action template rather than wiring packages by hand. Do not adopt it for a Docker action that never touches the GitHub context, and do not plan contributions: the README states the repository is not taking them. Before committing, verify the current version and release notes for each @actions/* package you install, and read docs/action-versioning.md before you tag a release.

Frequently asked questions

What packages are in actions/toolkit?

The README lists ten: @actions/core for inputs, outputs, results, logging, secrets and variables; @actions/exec for running CLI tools; @actions/glob for file pattern search; @actions/http-client; @actions/io for disk operations; @actions/tool-cache for downloading and caching tools; @actions/github for an Octokit client with context; @actions/artifact; @actions/cache; and @actions/attest.

How do I install actions/toolkit?

You install individual packages, not the repository. The README gives the npm install line for each one, for example npm install @actions/core, npm install @actions/exec and npm install @actions/github.

Is actions/toolkit accepting contributions?

No. The README states that the repository is not taking contributions at this time, and directs questions and support requests to Community Discussions, high-priority bugs to Discussions or support, and security issues to SECURITY.md. It adds that security updates and fixes for major breaking changes will still be provided.

What is the licence for actions/toolkit?

The repository is MIT licensed, with LICENSE.md at the top level. Each package is also published separately to npm under the @actions scope.

Official sources

  1. actions/toolkit on GitHub
  2. Issues
  3. License: MIT
  4. Project website
  5. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/actions-toolkit.svg)](https://hysenlabs.com/projects/actions-toolkit)