MCPcopy Index your code
hub / github.com/cardinalby/github-action-ts-run-api

github.com/cardinalby/github-action-ts-run-api @3.1.0

Chat with this repo
repository ↗ · DeepWiki ↗ · release 3.1.0 ↗ · + Follow
404 symbols 861 edges 134 files 18 documented · 4%
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

test publish

TypeScript API for GitHub Action execution and integration/functional testing

Purpose

🔶 Execute your GitHub action locally (or at any other environment).

🔶 Write integration and functional tests for an action, run them locally and on CI.

🔶 Have a short feedback loop without pushing and checking an action behaviour at real GitHub runners every time you change it.

Features

✅ Supports executing JavaScript and Docker actions.

✅ Tested under Windows, Linux and macOS (Intel + Apple Silicon), NodeJS >= 12 locally and on GitHub hosted runners.

✅ Works well with Docker Desktop under Windows and macOS.

✅ Can be used together with any JavaScript test frameworks or alone.

✅ Can execute an explicitly specified JS file or main, pre, post script from action.yml.

✅ Can execute a separate sync or async JS function, isolating its environment (process env, exitCode and working dir), intercepting stdout and stderr output for effective dependencies mocking.

✅ Has a clear JavaScript API with TypeScript declarations and reasonable defaults

✅ Produces warnings about deprecated Actions commands

Setting up an action run option includes:

  • Inputs. Can read default input values from action.yml
  • Saved state
  • Custom environment variables
  • GitHub context
  • GitHub service environment variables
  • Faking GitHub service files (file commands, event payload file)
  • Faking GitHub dirs (workflow, workspace, temp)

Reading results of an action run includes:

  • Reading exit code, stdout and stderr
  • Reading outputs, saved state, warnings, errors, notices and secrets from intercepted stdout
  • Reading exported vars, added paths from faked file commands

Installation

Install for use in tests

npm i github-action-ts-run-api --save-dev

Documentation

Other information:

Quick examples

Test JS action in a child node process

action.yml

name: 'test'
# ...
runs:
  using: 'node22'
  main: 'main.js'

main.js:

const core = require("@actions/core");
const context = require('@actions/github').context;
const fs = require('fs');

core.addPath('newPath');
fs.writeFileSync(
    path.join(process.env.RUNNER_TEMP, 'f.txt'),
    context.payload.pull_request.number.toString()
);

action.test.ts:

import {RunOptions, RunTarget} from 'github-action-ts-run-api';

// You can also test "pre" and "post" scripts
const target = RunTarget.mainJsScript('action.yml');
const options = RunOptions.create()
    // Internally, runner will fake a json file to be picked by @actions/github
    .setGithubContext({payload: {pull_request: {number: 123}}})
    // By default, RUNNER_TEMP is faked for a run and then deleted. Keep it
    .setFakeFsOptions({rmFakedTempDirAfterRun: false});

const res = await target.run(options);

try {
    assert(res.commands.addedPaths === ['newPath']);
    // somewhere in system temp dir
    const pathOfCreatedFile = path.join(res.tempDirPath, 'f.txt');
    // check the contents of a file saved by tested action
    assert(fs.readFileSync(pathOfCreatedFile).toString() === '123');
} finally {
    // we should do it manually because we set rmFakedTempDirAfterRun: false
    // otherwise it is deleted at the end of target.run()
    res.cleanUpFakedDirs();

    // With Jest you can use this instead:
    // This code also gets executed on test timeout 
    // afterAll(() => { 
    //     deleteAllFakedDirs(); 
    // });
}

Test JavaScript function in isolated Action environment

main.js

const core = require("@actions/core");

export async function actionMainFn() {
    core.setOutput('out1', core.getInput('in1'));
    core.setOutput('out2', process.env.ENV2);
    core.exportVariable('v3', core.getState('my_state'));
    // writes to errors and sets process.exitCode to 1
    return new Promise(resolve => setTimeout(() => {
        core.setFailed('err1');
        resolve();
        }, 1000));    
}

main.test.ts:

import {RunOptions, RunTarget} from 'github-action-ts-run-api';
import {actionMainFn} from './main.js';

// Will wait until returned promise fulfills. 
// Use RunTarget.syncFn() for regular functions
const target = RunTarget.asyncFn(actionMainFn);
const options = RunOptions.create()
    .setInputs({in1: 'abc'})
    .setEnv({ENV2: 'def'})
    .setState({my_state: 'ghi'});

const result = await target.run(options);

assert(result.durationMs >= 1000);
assert(result.commands.outputs === {out1: 'abc', out2: 'def'});
assert(result.commands.exportedVars === {v3: 'ghi'});
assert(result.exitCode === 1);
assert(result.runnerWarnings.length === 0);
// changes were isolated inside a function run
assert(process.exitCode !== 1);
assert(result.commands.errors === ['err1']);

Test Docker action

import {RunOptions, RunTarget} from 'github-action-ts-run-api';

const target = RunTarget.dockerAction('action.yml');
const options = RunOptions.create()
    .setInputs({input1: 'val1', input2: 'val2'})    
    .setEnv({ENV1: 'val3'})
    .setWorkingDir('/dir/inside/container')
    .setTimeoutMs(5000)
//  ...
const res = await target.run(options);

console.log(
    res.commands.outputs,
    res.commands.exportedVars,
    res.isSuccessBuild,
    res.isSuccess,
    res.isTimedOut
);

Integration tests examples:

You can find examples for the complicated cases in the library integration tests: * Docker target test * JS file target test * Function target test

Also, check out real actions integration tests:

Extension points exported contracts — how you extend this code

AsyncRunTargetInterface (Interface)
(no doc) [7 implementers]
src/runTarget/AsyncRunTargetInterface.ts
BaseRunMilieuComponentsFactoryInterface (Interface)
(no doc) [3 implementers]
src/runMilieu/BaseRunMilieuComponentsFactoryInterface.ts
DockerRunMilieuComponentsFactoryInterface (Interface)
(no doc) [2 implementers]
src/actionRunner/docker/runMilieu/DockerRunMilieuComponentsFactoryInterface.ts
RunnerDirInterface (Interface)
(no doc) [2 implementers]
src/githubServiceFiles/runnerDir/RunnerDirInterface.ts
StdoutCommandInterface (Interface)
(no doc)
src/stdout/stdoutCommands.ts
GithubServiceEnvInterface (Interface)
(no doc)
src/types/GithubServiceEnvInterface.ts
SpawnAsyncOptions (Interface)
(no doc)
src/utils/spawnAsync.ts
OutputOptionsInterface (Interface)
(no doc)
src/runOptions/OutputOptionsInterface.ts

Core symbols most depended-on inside this repo

create
called by 50
src/runOptions/RunOptions.ts
run
called by 42
src/runTarget/RunTargetInterface.ts
apply
called by 21
src/runOptions/InputsStore.ts
setFakeFsOptions
called by 18
src/runOptions/RunOptions.ts
setInputs
called by 18
src/runOptions/RunOptions.ts
toString
called by 18
src/runResult/warnings/RunnerWarning.ts
setOutputOptions
called by 16
src/runOptions/RunOptions.ts
delete
called by 13
src/githubServiceFiles/FakeFile.ts

Shape

Method 205
Class 103
Function 57
Interface 35
Enum 4

Languages

TypeScript100%

Modules by API surface

src/runOptions/RunOptions.ts19 symbols
src/runMilieu/BaseRunMilieuComponentsFactory.ts16 symbols
src/actionRunner/docker/runMilieu/DockerRunMilieuComponentsFactory.ts14 symbols
src/runOptions/ActionConfigStore.ts13 symbols
src/actionRunner/fn/runMilieu/StdoutInterceptor.ts12 symbols
src/githubServiceFiles/FakeFilesCollection.ts11 symbols
src/utils/WritableStreamBuffer.ts10 symbols
src/actionRunner/jsFile/runTarget/JsFileTarget.ts10 symbols
src/runResult/warnings/RunnerWarning.ts9 symbols
src/stdout/OutputsCommandsCollector.ts8 symbols
src/githubServiceFiles/FakeFile.ts8 symbols
src/actionRunner/docker/runTarget/dockerCli.ts8 symbols

For agents

$ claude mcp add github-action-ts-run-api \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact

Ask about this repo answers extend the page