actions/javascript-action: bootstrapping a JavaScript GitHub Action
Create a JavaScript Action with tests, linting, workflow, publishing, and versioning
At a glance
- What is it?
- The actions/javascript-action repository is a template for building a JavaScript GitHub Action, not a package you install. It ships rollup bundling, jest tests, linting, a coverage badge and a release branch workflow, and it leaves a real gap between what the setup notes promise and what package.json demands.
- Who is it for?
- Adopt this template if you want to ship a Node based action with tests and a checked in bundle and you can live with a Node 24 engine field and a repository that has not published a release since 2020. Do not adopt it if your action needs a different runtime or if you want a maintained library dependency, since the two published releases are from 2019 and 2020.
- 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 88 days ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A starter repository rather than a package to install
actions/javascript-action is a template. You do not add it to a project as a dependency, and there is nothing to import from it at runtime. The README describes it as a way to bootstrap the creation of a JavaScript action, and the workflow is built around the Use this template button at the top of the repository: you click it, choose Create a new repository, pick an owner and a name, and GitHub generates a fresh copy that belongs to you. From that point on, the files in the generated repository are yours to edit.
That framing matters for anyone evaluating it. There is no API surface to adopt and no compatibility promise to track. What you are copying is a directory layout, a set of npm scripts, a bundle configuration and a handful of GitHub workflow files. The published releases of the template, v1.0.0 in November 2019 and v1.0.1 in July 2020, are versions of the template itself rather than versions of a library that other code depends on.
Who it suits is a maintainer who has decided to write an action in JavaScript and wants the plumbing around it settled on day one. The template describes compilation support, tests, a validation workflow, publishing and versioning guidance. The alternative starting point it points at is the Hello world JavaScript action repository, which the README offers as a simpler introduction for newcomers.
From the template button to a working action.yml
The first thing to change is metadata. The action.yml file at the root declares the name, description, inputs and outputs of the action, and the README tells you to update it after copying. The sample action exposes a single input, and the example workflow in the repository passes it as milliseconds.
Before that works, dependencies have to be present and the code has to be packaged. The setup steps in the README are short:
npm installThis resolves the single runtime dependency plus the development toolchain, which includes rollup, jest, eslint, prettier and the @github/local-action utility.
npm run bundleThe bundle script runs the formatter first and then the packaging step, and packaging clears the dist directory before rollup rebuilds it. A reader who runs this in a fresh clone should end up with a populated dist/ directory.
npm testThe test script runs jest with experimental VM modules enabled, and the README shows the expected output listing three cases: one that throws on an invalid number, one that waits 500 milliseconds, and a plain run test. The repository layout backs this up, with a __tests__/ directory, a __fixtures__/ directory and a jest.config.js at the root.
Why dist/ is committed and what npm run all covers
GitHub Actions does not install your dependencies at run time for a JavaScript action. Whatever the runner executes has to be a self contained bundle that you committed. The README makes this point in unusually direct terms: the build step is important, and without it the action will not work correctly when it is used in a workflow.
The bundling is done by rollup, configured in rollup.config.js, with the commonjs and node resolve plugins pulled in as development dependencies so that all dependencies end up inside the final file. The package.json confirms the shape of the output by pointing its exports at ./dist/index.js, and the repository ships a dist/ directory alongside a check-dist.yml workflow whose badge sits at the top of the README. That workflow exists to catch the case where you change source code and forget to rebuild the bundle.
The single command that ties the whole chain together is:
npm run allExpanding the script by hand, it formats, lints, tests, regenerates the coverage badge and packages. The individual pieces are also available on their own: format:write and format:check for prettier, lint for eslint, coverage for the badge generator, and package for the rimraf and rollup run. The bundle script is a short alias for format:write followed by package.
Node 24 in package.json against Node 20 in the setup notes
Here is a genuine inconsistency worth catching before you copy the template. The setup notes in the README state that a reasonably modern version of Node.js is needed and that 20.x or later should work. The package.json disagrees. Its engines field reads node >=24, and its devDependencies require recent majors of eslint, jest and rollup plugins.
Node 20 reached end of life in April 2026, so the engines field is the more accurate statement of what the dependency tree expects, but the two numbers still sit in the same repository and point in different directions. Anyone following the prose and landing on Node 20 will see npm warn about the unmet engine requirement.
The template does offer a way to avoid guessing. A .node-version file sits at the root, and the README explains that nodenv or fnm can read it to switch automatically when you change into the repository. It also states that the same file is used by GitHub Actions in any actions/setup-node steps, so the pin and the CI environment stay aligned if you keep both pointing at the same file. There is also a .devcontainer/ directory for codespaces, and the README mentions codespaces alongside a local clone as supported ways to get started.
Running the action on your own machine with @github/local-action
Committing and pushing to exercise an action is a slow loop, so the template wires in the @github/local-action utility for local runs. The README describes it as a command line tool that stubs or simulates the GitHub Actions Toolkit, which lets you run the action without pushing anything.
The package.json exposes it as an npm script named local-action, and the README shows the same invocation in the terminal:
npx @github/local-action . src/main.js .envThe three positional arguments are the path to the action metadata file, the entrypoint and the dotenv file. The action entrypoint in the source tree is src/main.js, while the bundle output referenced by package.json is dist/index.js.
Environment variables for the local run come from a .env file. The repository ships .env.example, and its comments spell out the convention that inputs follow the INPUT_<name> format and are case sensitive, and that hyphens in an input name must not be converted to underscores. The example file sets a step debug flag and an input value:
ACTIONS_STEP_DEBUG=true
INPUT_MILLISECONDS=2400The same file also lists the GitHub Actions default environment variables, most of them commented out, and notes that setting a value there overrides what the local-action tool supplies. For visual debugging the README points at a Visual Studio Code Debugger configuration in .vscode/launch.json, which you are expected to review and update rather than accept blindly.
Validating with uses: ./ and the bundled workflow files
Once the action exists in your own repository you can reference it from a workflow. The repository demonstrates this in ci.yml, and the README shows the shape of the reference: a checkout step, a step that uses the local path, and a step that prints an output. The local path form, rather than a version tag, is what makes the check meaningful.
steps:
- name: Checkout
id: checkout
uses: actions/checkout@v4
- name: Test Local Action
id: test-action
uses: ./
with:
milliseconds: 1000
- name: Print Output
id: output
run: echo "${{ steps.test-action.outputs.time }}"The with block feeds the milliseconds input, and the run step reads the time output the action sets. The repository's .github/ directory also holds a linter workflow driven by GitHub Super-Linter, a CodeQL analysis workflow and the check-dist workflow, each with a badge in the README. The Super-Linter setup is backed by configuration files at the root, including .markdown-lint.yml, .yaml-lint.yml, .checkov.yml, .licensed.yml and actionlint.yml. The .licenses/ directory holds licence records for the dependency tree, and a CODEOWNERS file is included with a prominent instruction to remove or update it in your copy, since a stale ownership file from the template has no business in a new repository.
Where this template is the wrong tool
The template is a JavaScript and Node template. If your action needs a different runtime, a compiled binary, or a container, the src/ plus rollup plus dist/ arrangement is the wrong shape for it, and the work of rewriting the build and the check-dist workflow falls to you.
There is also a meaningful cost in the surrounding machinery. The single-command path runs a formatter in write mode, so npm run all will rewrite your files rather than fail on a formatting difference. If you want a non destructive check in CI, the separate format:check script is the one to use. The linter setup is strict too, with eslint configured through eslint.config.mjs alongside eslint-plugin-import, eslint-plugin-jest and eslint-plugin-prettier, so code that works but does not match the house style will stop the pipeline.
On the maintenance side the facts are limited and worth stating plainly. The repository is not archived, but the last push was on 2026-07-28, and the most recent published release is v1.0.1 from 2020-07-17, with v1.0.0 dated 2019-11-03. The gap between a recent commit and an old release tag means the template can look current in the commit history while carrying dependencies and scripts that have not been re-released. The README also does not document rollback behaviour for a published action version, so if you need to reason about how a consumer pins or reverts a release, that guidance is not in this repository. The versioning documentation is delegated to the GitHub Actions toolkit, along with the toolkit README for API usage.
Release branches, the merge into main, and the MIT licence
Publishing follows a release branch flow, and the README spells out the sequence. You create a branch, replace the contents of src/ with your own code, add tests under __tests__/, and format, test and build before committing.
git checkout -b releases/v1git add .
git commit -m "My first action is ready!"git push -u origin releases/v1The final step is opening a pull request, taking the feedback, and merging into the main branch, after which the README states that the action is published. The branch name pattern releases/v1 also implies a versioning scheme, and the README points to the toolkit's action versioning document for how to tag and maintain subsequent versions. The action.yml file supplies the version tag that consumers reference, so a copy of the template needs that field adjusted rather than left as inherited.
On licensing, the project is MIT and the package.json repeats the same identifier in its license field, with a LICENSE file at the root. The MIT terms are permissive, but the template also ships the .licensed.yml configuration and the .licenses/ directory, which record the licences of the dependencies bundled into dist/. Bundling means your published artifact carries third party code, so keeping those records accurate as you change src/ is part of shipping the action, not paperwork. This is an observation about obligations to track, not legal advice.
Editorial conclusion
Adopt this template if you want to ship a Node based action with tests and a checked in bundle and you can live with a Node 24 engine field and a repository that has not published a release since 2020. Do not adopt it if your action needs a different runtime or if you want a maintained library dependency, since the two published releases are from 2019 and 2020. Before you build on it, read the engines field in package.json, check whether the dist/ directory and the check-dist workflow still match your bundling step, and confirm that the licences in the .licenses/ directory cover everything you add to src/.
Frequently asked questions
What does the actions/javascript-action repository actually give me?
It is a template repository you copy into a new repository of your own, not a package you install. The copy arrives with rollup bundling into a committed dist/ directory, jest tests, eslint and prettier configuration, Super-Linter, CodeQL and check-dist workflows, and a coverage badge.
Which Node.js version does the javascript-action template need?
The setup notes say 20.x or later should work, but the engines field in package.json requires node >=24. A .node-version file at the root of the repository pins the version for nodenv or fnm and for actions/setup-node in CI.
How do I test a JavaScript action before pushing it?
Run the action locally with npx @github/local-action . src/main.js .env, which stubs the GitHub Actions Toolkit. Environment variables, including inputs in the case sensitive INPUT_<name> format, come from a .env file modelled on .env.example.
How is a new action published from this template?
Put your code in src/, add tests under __tests__/, run npm run all to format, lint, test and package, then commit on a branch such as releases/v1, push it with git push -u origin releases/v1, open a pull request and merge into main.
Official sources
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.
[](https://hysenlabs.com/projects/actions-javascript-action)Was this useful?