Use Docker target to run/test your docker action. action.yml
file should have runs.using
key equal docker
.
Builds and runs specified docker image. Works with native docker on Linux (including GitHub runners) and Docker Desktop on MacOS and Windows.
Unlike other targets, docker target has fixed path of faked dirs and files inside a container. But relying on them is not recommended. You should use GitHub environment variables values instead.
Environment variable | Value | Description |
---|---|---|
RUNNER_TEMP | /home/runner/work/_temp |
Temp dir |
GITHUB_WORKSPACE | /github/workspace |
The default working directory on the runner for steps, and the default location of a repository when using the checkout action. |
GITHUB_EVENT_PATH | /github/workflow/event.json |
The path of the file with the complete webhook event payload. |
GITHUB_ENV | /github/file_commands/ENV |
file for exported vars |
GITHUB_PATH | /github/file_commands/PATH |
file for added paths |
GITHUB_OUTPUT | /github/file_commands/OUTPUT |
file for setting outputs |
GITHUB_STATE | /github/file_commands/STATE |
file for setting state |
As in other targets, temporary created dirs used by default to fake these paths.
As usual, you can specify
existing host dirs instead (options.setTempDir()
, options.setWorkspaceDir()
).
In both cases dirs will be mounted as volumes to a fixed paths in container and corresponding env variables will point to these paths.
import {RunTarget, RunOptions} from 'github-action-ts-run-api';
// Read the details below
const dockerOptions = {};
// Use Dockerfile from `runs.image` key of action.yml
const target = RunTarget.dockerAction('path/to/action.yml', dockerOptions);
const result = target.run(RunOptions.create());
This approach can be used if you want to perform an integration test run not the whole action, but only a part of it. In this case you can create a separate Dockerfile that will have only needed functionality and run tests against it.
import {RunTarget, RunOptions} from 'github-action-ts-run-api';
// Read the details below
const dockerOptions = {};
// No action config was specified.
// * Default input values will not be applied
// * Container args will not be passed to a container
const target1 = RunTarget.dockerFile('path/to/Dockerfile', undefined, dockerOptions);
// Read action config from action.yml file.
// * Default input values will be used from the inputs section.
// * Container args defined in the action config will be passed to a container
const target2 = RunTarget.dockerFile('actionSrc/file.js', 'path/to/action.yml', dockerOptions);
// Pass already parsed action config.
// * Default input values will be used from the inputs section.
// * Container args defined in the action config will be passed to a container
const target3 = RunTarget.dockerFile('actionSrc/file.js', parsedYmlObject, dockerOptions);
const result = target.run(RunOptions.create());
Passing the optional dockerOptions
argument is demonstrated in the examples above.
It has 2 fields that you can set:
In the documentation, GitHub recommends running Docker action under root user in a container.
While testing your action on local Linux machine it can cause troubles if root user in container creates files in volumes mounted to the container. Since you normally operate as a non-root user on your development machine, testing an action you will not be able to access/delete created files.
For this reason Docker target by default runs a container under current user on Linux. On macOS and Windows systems where Docker Desktop is normally used, it will be still run under root by default, because Docker Desktop doesn't map file permissions directly to host files, and they will be still available for a local user.
true
(default): run a container with uid and gid of a current user (only for Linux).false
: run under root user (uid = 0, gid = 0).
A name of the docker network to attach a container to.
undefined
(default): attach to default bridge network- string name: attach to a network by name
This option can be used to mock some external services by containers with stub HTTP servers.
Contains a result of a spawning of child docker build
command.
After a second and subsequent run()
calls can be undefined
, because image id has been cached.
Contains a result of a spawning of child docker run
command or undefined
if
the build command failed first.
Indicates whether docker build
command was successful.
Indicates whether both docker build
and docker run
commands were successful.
Usage examples can be found in DockerTarget.test.ts.
🔻 Docker Desktop for Windows and MacOS behaves differently from native docker on Linux. Be aware!
🔻 Windows and MacOS GitHub hosted runners don't have installed docker.
🔻 Faked dirs and command files are mounted as volumes.
Returns the host that can be used inside container to access the Docker host machine.
See "Stubbing GitHub API by local NodeJS HTTP server" example below.
The wrapper that starts/stops a docker compose file around a callback.
Performs docker compose up, then runs callback
function and after its promise fulfilled,
runs docker compose down.
See "Stubbing GitHub API by HTTP server container" example below.
This approach relates to stubbing any external service:
- Read base API URL from environment variable, use default if variable is not set
- For GitHub API there are dedicated
GITHUB_API_URL
,GITHUB_SERVER_URL
,GITHUB_GRAPHQL_URL
variables. - Start local HTTP stub server, pass its address to the needed env variable.
Show example code
entrypoint.sh:
echo "::set-output name=out1::$(curl "$GITHUB_API_URL"/repos/owner/repo/releases)"
action.test.ts:
import {RunTarget, RunOptions, getDockerHostName} from 'github-action-ts-run-api';
const port = 8234;
const server = http.createServer((req, res) => {
if (req.url === '/repos/owner/repo/releases') {
res.writeHead(200);
res.end('fake_response');
} else {
res.writeHead(404);
res.end();
}
}).listen(port);
try {
const target = RunTarget.dockerAction('path/to/action.yml');
const res = await target.run(RunOptions.create()
// Using '172.17.0.1' or 'host.docker.internal'
// the action container can access the host
.setGithubContext({apiUrl: `http://${getDockerHostName()}:${port}`})
);
assert(res.commands.outputs.out1 === 'fake_response');
} finally {
server.close();
}
You can find actual working code in DockerTarget.test.ts.
It's the similar approach to the described above:
- Use a docker container defined as a service in docker-compose.yml to run your stub server.
- Define a named network in docker-compose.yml.
- Attach an action container to the network and make requests to the stub service by name.
Show example code
entrypoint.sh:
echo "::set-output name=out1::$(curl "$GITHUB_API_URL"/repos/owner/repo/releases)"
docker-compose.yml:
version: "3.5"
services:
fake-server:
build:
# Image with stub GitHub API HTTP server
context: httpServerDir
ports:
- "80:80"
networks:
default:
# Assign a name to the network to connect
# the tested action container to it
name: testNet
action.test.ts:
import {RunTarget, RunOptions, withDockerCompose} from 'github-action-ts-run-api';
await withDockerCompose(
'path/to/docker-compose.yml',
async () => {
const target = RunTarget.dockerAction(
'path/to/action.yml',
// network name defined in docker-compose.yml
{network: 'testNet'}
);
const res = await target.run(RunOptions.create()
// service name defined in docker-compose.yml
.setGithubContext({apiUrl: `http://fake-server:80`})
);
assert(res.commands.outputs.out1 === 'fake_response');
});
You can find actual working code in DockerTarget.test.ts.