toolkit Documentation

The GitHub ToolKit for developing GitHub Actions.

5,774 stars TypeScript TypeScript 1 file Β· ~9,367 tokens
RAW Doc

File: README.md

GitHub Actions Toolkit

The GitHub Actions ToolKit provides a set of packages to make creating actions easier.

Get started with the [javascript-action template](https://github.com/actions/javascript-action)!

Packages

:heavy_check_mark: @actions/core

Provides functions for inputs, outputs, results, logging, secrets and variables. Read more here

bash
npm install @actions/core

:runner: @actions/exec

Provides functions to exec cli tools and process output. Read more here

bash
npm install @actions/exec

:ice_cream: @actions/glob

Provides functions to search for files matching glob patterns. Read more here

bash
npm install @actions/glob

:phone: @actions/http-client

A lightweight HTTP client optimized for building actions. Read more here

bash
npm install @actions/http-client

:pencil2: @actions/io

Provides disk i/o functions like cp, mv, rmRF, which etc. Read more here

bash
npm install @actions/io

:hammer: @actions/tool-cache

Provides functions for downloading and caching tools. e.g. setup-* actions. Read more here

See @actions/cache for caching workflow dependencies.

bash
npm install @actions/tool-cache

:octocat: @actions/github

Provides an Octokit client hydrated with the context that the current action is being run in. Read more here

bash
npm install @actions/github

:floppy_disk: @actions/artifact

Provides functions to interact with actions artifacts. Read more here

bash
npm install @actions/artifact

:dart: @actions/cache

Provides functions to cache dependencies and build outputs to improve workflow execution time. Read more here

bash
npm install @actions/cache

:lock_with_ink_pen: @actions/attest

Provides functions to write attestations for workflow artifacts. Read more here

bash
npm install @actions/attest

Creating an Action with the Toolkit

:question: Choosing an action type

Outlines the differences and why you would want to create a JavaScript or a container based action.

:curly_loop: Versioning

Actions are downloaded and run from the GitHub graph of repos. This contains guidance for versioning actions and safe releases.

:warning: Problem Matchers

Problem Matchers are a way to scan the output of actions for a specified regex pattern and surface that information prominently in the UI.

:warning: Proxy Server Support

Self-hosted runners can be configured to run behind proxy servers.

[Hello World JavaScript Action](https://github.com/actions/hello-world-javascript-action)

Illustrates how to create a simple hello world javascript action.

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

[JavaScript Action Walkthrough](https://github.com/actions/javascript-action)

Walkthrough and template for creating a JavaScript Action with tests, linting, workflow, publishing, and versioning.

javascript
async function run() {
  try {
    const ms = core.getInput('milliseconds');
    console.log(`Waiting ${ms} milliseconds ...`)
    ...
javascript
PASS ./index.test.js
  βœ“ throws invalid number
  βœ“ wait 500 ms
  βœ“ test runs

Test Suites: 1 passed, 1 total
Tests:       3 passed, 3 total

[TypeScript Action Walkthrough](https://github.com/actions/typescript-action)

Walkthrough creating a TypeScript Action with compilation, tests, linting, workflow, publishing, and versioning.

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

async function run() {
  try {
    const ms = core.getInput('milliseconds');
    console.log(`Waiting ${ms} milliseconds ...`)
    ...
javascript
PASS ./index.test.js
  βœ“ throws invalid number
  βœ“ wait 500 ms
  βœ“ test runs

Test Suites: 1 passed, 1 total
Tests:       3 passed, 3 total

[Docker Action Walkthrough](docs/container-action.md)

Create an action that is delivered as a container and run with docker.

docker
FROM alpine:3.10
COPY LICENSE README.md /
COPY entrypoint.sh /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]

[Docker Action Walkthrough with Octokit](https://github.com/actions/container-toolkit-action)

Create an action that is delivered as a container which uses the toolkit. This example uses the GitHub context to construct an Octokit client.

docker
FROM node:slim
COPY . .
RUN npm install --production
ENTRYPOINT ["node", "/lib/main.js"]
javascript
const myInput = core.getInput('myInput');
core.debug(`Hello ${myInput} from inside a container`);

const context = github.context;
console.log(`We can even get context data, like the repo: ${context.repo.repo}`)

Note

Thank you for your interest in this GitHub repo, however, right now we are not taking contributions.

We continue to focus our resources on strategic areas that help our customers be successful while making developers' lives easier. While GitHub Actions remains a key part of this vision, we are allocating resources towards other areas of Actions and are not taking contributions to this repository at this time. The GitHub public roadmap is the best place to follow along for any updates on features we’re working on and what stage they’re in.

We are taking the following steps to better direct requests related to GitHub Actions, including:

  1. We will be directing questions and support requests to our Community Discussions area

  2. High Priority bugs can be reported through Community Discussions or you can report these to our support team https://support.github.com/contact/bug-report.

  3. Security Issues should be handled as per our security.md.

We will still provide security updates for this project and fix major breaking changes during this time.

You are welcome to still raise bugs in this repo.

Code of Conduct

See our code of conduct.


File: docs/adrs/0381-glob-module.md

ADR 381: `glob` module

Date: 2019-12-05

Status: Accepted

Context

This ADR proposes adding a glob function to the toolkit.

First party actions should have a consistent glob experience.

Related to artifact upload/download v2.

Decision

New module

Create a new module @actions/glob that can be versioned at it's own pace - not tied to @actions/io.

Signature

js
/**
 * Constructs a globber from patterns
 *
 * @param patterns  Patterns separated by newlines
 * @param options   Glob options
 */
export function create(
  patterns: string,
  options?: GlobOptions
): Promise<Globber> {}

/**
 * Used to match files and directories
 */
export interface Globber {
  /**
   * Returns the search path preceding the first glob segment, from each pattern.
   * Duplicates and descendants of other paths are filtered out.
   *
   * Example 1: The patterns `/foo/*` and `/bar/*` returns `/foo` and `/bar`.
   *
   * Example 2: The patterns `/foo/*` and `/foo/bar/*` returns `/foo`.
   */
  getSearchPaths(): string[]

  /**
   * Returns files and directories matching the glob patterns.
   *
   * Order of the results is not guaranteed.
   */
  glob(): Promise<string[]>

  /**
   * Returns files and directories matching the glob patterns.
   *
   * Order of the results is not guaranteed.
   */
  globGenerator(): AsyncGenerator<string, void>
}

/**
 * Options to control globbing behavior
 */
export interface GlobOptions {
  /**
   * Indicates whether to follow symbolic links. Generally should set to false
   * when deleting files.
   *
   * @default true
   */
  followSymbolicLinks?: boolean

  /**
   * Indicates whether directories that match a glob pattern, should implicitly
   * cause all descendant paths to be matched.
   *
   * For example, given the directory `my-dir`, the following glob patterns
   * would produce the same results: `my-dir/**`, `my-dir/`, `my-dir`
   *
   * @default true
   */
  implicitDescendants?: boolean

  /**
   * Indicates whether broken symbolic should be ignored and omitted from the
   * result set. Otherwise an error will be thrown.
   *
   * @default true
   */
  omitBrokenSymbolicLinks?: boolean
}

Toolkit usage

Example, do not follow symbolic links:

js
const patterns = core.getInput('path')
const globber = glob.create(patterns, {followSymbolicLinks: false})
const files = globber.glob()

Example, iterator:

js
const patterns = core.getInput('path')
const globber = glob.create(patterns)
for await (const file of this.globGenerator()) {
  console.log(file)
}

Action usage

Actions should follow symbolic links by default.

Users can opt-out.

Example:

yaml
jobs:
  build:
    steps:
      - uses: actions/upload-artifact@v1
        with:
          path: |
            **/*.tar.gz
            **/*.pkg
          follow-symbolic-links: false    # opt out, should default to true

HashFiles function

Hash files should not follow symbolic links by default.

User can opt-in by specifying flag --follow-symbolic-links.

Example:

yaml
jobs:
  build:
    steps:
      - uses: actions/cache@v1
        with:
          hash: ${{ hashFiles('--follow-symbolic-links', '**/package-lock.json') }}

Glob behavior

Patterns *, ?, [...], ** (globstar) are supported.

With the following behaviors:

  • File names that begin with . may be included in the results
  • Case insensitive on Windows
  • Directory separator / and \ both supported on Windows

Note:

  • Refer here for more information about Bash glob patterns.
  • Refer here for more information about Bash glob options.

Tilde expansion

Support basic tilde expansion, for current user HOME replacement only.

For example, on macOS:

  • ~ may expand to /Users/johndoe
  • ~/foo may expand to /Users/johndoe/foo

Note:

  • Refer here for more information about Bash tilde expansion.
  • All other forms of tilde expansion are not supported.
  • Use os.homedir() to resolve the HOME path

Root and normalize paths

An unrooted pattern will be rooted using the current working directory, prior to searching. Additionally the search path will be normalized prior to searching (relative pathing removed, slashes normalized on Windows, extra slashes removed).

The two side effects are:

  1. Rooted and normalized paths are always returned
  2. The pattern ** will include the working directory in the results

These side effects diverge from Bash behavior. Whereas Bash is designed to be a shell, we are designing an API. This decision is intended to improve predictability of the API results.

Note:

  • In Bash, the results are not rooted when the pattern is relative.
  • In Bash, the results are not normalized. For example, the results from ./* may look like: ./foo ./bar
  • In Bash, the results from the pattern ** does not include the working directory. However the results from /foo/** would include the directory /foo. Also the results from foo/** would include the directory foo.

Comments

Patterns that begin with # are treated as comments.

Exclude patterns

Leading ! changes the meaning of an include pattern to exclude.

Note:

  • Multiple leading ! flips the meaning.

Escaping

Wrapping special characters in [] can be used to escape literal glob characters in a file name. For example the literal file name hello[a-z] can be escaped as hello[[]a-z].

On Linux/macOS \ is also treated as an escape character.

Consequences

  • Publish new module @actions/glob
  • Publish docs for the module (add link from ./README.md to new doc ./packages/glob/README.md)

File: docs/adrs/README.md

ADRs

ADR, short for "Architecture Decision Record" is a way of capturing important architectural decisions, along with their context and consequences.

This folder includes ADRs for the actions toolkit. ADRs are proposed in the form of a pull request, and they commonly follow this format:

  • Title: short present tense imperative phrase, less than 50 characters, like a git commit message.

  • Status: proposed, accepted, rejected, deprecated, superseded, etc.

  • Context: what is the issue that we're seeing that is motivating this decision or change.

  • Decision: what is the change that we're actually proposing or doing.

  • Consequences: what becomes easier or more difficult to do because of this change.


  • More information about ADRs can be found here.

File: docs/specs/github-package.md

Github Package

In order to support using actions to interact with GitHub, I propose adding a github package to the toolkit.

Its main purpose will be to provide a hydrated GitHub context/Octokit client with some convenience functions. It is largely pulled from the GitHub utilities provided in https://github.com/JasonEtco/actions-toolkit, though it has been condensed.

Spec

interfaces.ts

ts
/*
 * Interfaces
 */
 
export interface PayloadRepository {
  [key: string]: any
  full_name?: string
  name: string
  owner: {
    [key: string]: any
    login: string
    name?: string
  }
  html_url?: string
}

export interface WebhookPayloadWithRepository {
  [key: string]: any
  repository?: PayloadRepository
  issue?: {
    [key: string]: any
    number: number
    html_url?: string
    body?: string
  }
  pull_request?: {
    [key: string]: any
    number: number
    html_url?: string
    body?: string
  }
  sender?: {
    [key: string]: any
    type: string
  }
  action?: string
  installation?: {
    id: number
    [key: string]: any
  }
}

context.ts

Contains a GitHub context

ts
export class Context {
   /**
   * Webhook payload object that triggered the workflow
   */
  public payload: WebhookPayloadWithRepository

  /**
   * Name of the event that triggered the workflow
   */
  public event: string
  public sha: string
  public ref: string
  public workflow: string
  public action: string
  public actor: string
  
  /**
   * Hydrate the context from the environment
   */
  constructor ()
  
  public get issue ()
  
  public get repo ()
}

github.ts

Contains a hydrated Octokit client

ts
export class GithubClient extends Octokit {
 // For making GraphQL requests
 public graphql: (query: string, variables?: Variables) => Promise<GraphQlQueryResponse>
 
 // Calls super and initializes graphql
 constructor (token: string)
}

File: docs/specs/package-specs.md

Package Specs

In order to support the node-config action, I propose adding the following into 4 libraries (tool-cache, core, io, exec), with tool-cache having dependencies on the other 3 libs:

Core spec

Holds all the functions necessary for interacting with the runner/environment.

ts
// Logging functions
export function debug(message: string): void
export function warning(message: string): void
export function error(message: string): void

/**
 * sets env variable for this action and future actions in the job
 * @param name the name of the variable to set
 * @param val the value of the variable
 */
export function exportVariable(name: string, val: string): void

/**
 * exports the variable and registers a secret which will get masked from logs
 * @param name the name of the variable to set
 * @param val value of the secret
 */
export function exportSecret(name: string, val: string): void

/**
 * Prepends inputPath to the PATH
 * @param inputPath
 */
export function addPath(inputPath: string): void

/**
 * Interface for getInput options
 */
export interface InputOptions {
    /** Optional. Whether the input is required. If required and not present, will throw. Defaults to false */
    required?: bool;
}

/**
 * Gets the value of an input.  The value is also trimmed.
 * 
 * @param     name     name of the input to get
 * @param     options  optional. See InputOptions.
 * @returns   string
 */
export function getInput(name: string, options?: InputOptions): string | undefined

/**
 * sets the status of the action to neutral
 * @param message 
 */
export function setNeutral(message: string): void

/**
 * sets the status of the action to failed
 * @param message 
 */
export function setFailed(message: string): void

IO spec

Holds all the functions necessary for file system manipulation (cli scenarios, not fs replacements):

ts
/**
 * Interface for cp/mv options
 */
export interface CopyOptions {
    /** Optional. Whether to recursively copy all subdirectories. Defaults to false */
    recursive?: boolean;
    /** Optional. Whether to overwrite existing files in the destination. Defaults to true */
    force?: boolean;
}

/**
 * Copies a file or folder.
 * 
 * @param     source    source path
 * @param     dest      destination path
 * @param     options   optional. See CopyOptions.
 */
export function cp(source: string, dest: string, options?: CopyOptions): Promise<void>

/**
 * Remove a path recursively with force
 * 
 * @param     path     path to remove
 */
export function rmRF(path: string): Promise<void>

/**
 * Make a directory.  Creates the full path with folders in between
 * 
 * @param     p       path to create
 * @returns   Promise<void>
 */
export function mkdirP(p: string): Promise<void>

/**
 * Moves a path.
 *
 * @param     source    source path
 * @param     dest      destination path
 * @param     options   optional. See CopyOptions.
 */
export function mv(source: string, dest: string, options?: CopyOptions): Promise<void>

/**
 * Interface for which options
 */
export interface WhichOptions {
    /** Optional. Whether to check if tool exists. If true, will throw if it fails. Defaults to false */
    check?: boolean;
}

/**
 * Returns path of a tool had the tool actually been invoked.  Resolves via paths.
 * 
 * @param     tool              name of the tool
 * @param     options           optional. See WhichOptions.
 * @returns   Promise<string>   path to tool
 */
export function which(tool: string, options?: WhichOptions): Promise<string>

Exec spec

Holds all the functions necessary for running the tools node-config depends on (aka 7-zip and tar)

ts
/**
 * Interface for exec options
 */
export interface IExecOptions

/**
* Exec a command.
* Output will be streamed to the live console.
* Returns promise with return code
* 
* @param     commandLine        command to execute
* @param     args               optional additional arguments
* @param     options            optional exec options.  See IExecOptions
* @returns   Promise<number>    return code
*/
export function exec(commandLine: string, args?: string[], options?: IExecOptions): Promise<number>

Tool-Cache spec:

Holds all the functions necessary for downloading and caching node.

ts
/**
 * Download a tool from an url and stream it into a file
 *
 * @param url       url of tool to download
 * @returns         path to downloaded tool
 */
export async function downloadTool(url: string): Promise<string>

/**
 * Extract a .7z file
 *
 * @param file     path to the .7z file
 * @param dest     destination directory. Optional.
 * @returns        path to the destination directory
 */
export async function extract7z(file: string, dest?: string): Promise<string>

/**
 * Extract a tar
 *
 * @param file     path to the tar
 * @param dest     destination directory. Optional.
 * @returns        path to the destination directory
 */
export async function extractTar(file: string, destination?: string): Promise<string>

/**
 * Caches a directory and installs it into the tool cacheDir
 *
 * @param sourceDir    the directory to cache into tools
 * @param tool          tool name
 * @param version       version of the tool.  semver format
 * @param arch          architecture of the tool.  Optional.  Defaults to machine architecture
 */
export async function cacheDir(sourceDir: string, tool: string, version: string, arch?: string): Promise<string>

/**
 * finds the path to a tool in the local installed tool cache
 *
 * @param toolName      name of the tool
 * @param versionSpec   version of the tool
 * @param arch          optional arch.  defaults to arch of computer
 */
export function find(toolName: string, versionSpec: string, arch?: string): string

File: docs/action-debugging.md

Debugging

If the job logs do not provide enough detail on why a job may be failing, some other options exist to assist with troubleshooting.

Step Debug Logs

This is the primary way for customers to debug job failures caused by failed steps.

Step debug logs increase the verbosity of a job's logs during and after a job's execution to assist with troubleshooting.

Additional log events with the prefix ::debug:: will now also appear in the job's logs, these log events are provided by the Action's author and the runner process.

How to Access Step Debug Logs

This flag can be enabled by setting the secret ACTIONS_STEP_DEBUG to true.

All actions ran while this secret is enabled will show debug events in the Downloaded Logs and Web Logs.

Runner Diagnostic Logs

Runner Diagnostic Logs provide additional log files detailing how the Runner is executing an action.

You need the runner diagnostic logs only if you think there is an infrastructure problem with GitHub Actions and you want the product team to check the logs.

Each file contains different logging information that corresponds to that process:

  • The Runner process coordinates setting up workers to execute jobs.
  • The Worker process executes the job.

These files contain the prefix Runner_ or Worker_ to indicate the log source.

How to Access Runner Diagnostic Logs

These log files are enabled by setting the secret ACTIONS_RUNNER_DEBUG to true.

All actions ran while this secret is enabled contain additional diagnostic log files in the runner-diagnostic-logs folder of the log archive.


File: docs/action-types.md

Action Types

There are two types of actions. JavaScript and Docker actions.

  • JavaScript Actions: JavaScript actions run on the host machine. The unit of work is decoupled from the environment.
  • Docker Actions: A container action is a container which carries both the unit of work along with the environment and its dependencies packaged up as a container.

Both have access to the workspace and the github event payload and context.

Why would I choose a Docker action?

Docker actions carry both the unit of work and the environment.

This creates a more consistent and reliable unit of work where the consumer of the action does not need to worry about the toolsets and its dependencies.

Docker actions are currently limited to Linux only.

Why would I choose a host action?

JavaScript actions decouple the unit of work from the environment and run directly on the host machine or VM.

Consider a simple example of testing a node lib on node 8, 10 and running a custom action. Each job will setup a node version on the host and custom-action will run its unit of work on each environment (node8+ubuntu16, node8+windows-2019, etc.)

yaml
on: push

jobs:
  build:
    strategy: 
      matrix:
        node: [8.x, 10.x]
        os: [ubuntu-16.04, windows-2019]
    runs-on: ${{matrix.os}}
    actions:
    - uses: actions/setup-node@v5
      with:
        version: ${{matrix.node}}
    - run: | 
        npm install
    - run: |
        npm test
    - uses: actions/custom-action@v1

JavaScript actions work on any environment that host action runtime is supported on which is currently node 12. However, a host action that runs a toolset expects the environment that it's running on to have that toolset in its PATH or using a setup-* action to acquire it on demand.


File: docs/action-versioning.md

Versioning

Actions are downloaded and run from the GitHub graph of repos. The workflow references an action using a ref.

Examples:

yaml
steps:
    - uses: actions/javascript-action@v1        # recommended. starter workflows use this
    - uses: actions/[email protected]    # if an action offers specific releases 
    - uses: actions/javascript-action@41775a4da8ffae865553a738ab8ac1cd5a3c0044 # sha

Compatibility

Binding to a major version is the latest of that major version ( e.g. v1 == "1.*" )

Major versions should guarantee compatibility. A major version can add net new capabilities but should not break existing input compatibility or break existing workflows.

Major version binding allows you to take advantage of bug fixes and critical functionality and security fixes. The main branch has the latest code and is unstable to bind to since changes get committed to main and released to the market place by creating a tag. In addition, a new major version carrying breaking changes will get implemented in main after branching off the previous major version.

Warning: do not reference main since that is the latest code and can be carrying breaking changes of the next major version.

yaml
steps:
    - uses: actions/javascript-action@main  # do not do this

Binding to the immutable full sha1 may offer more reliability. However, note that the hosted images toolsets (e.g. ubuntu-latest) move forward and if there is a tool breaking issue, actions may react with a patch to a major version to compensate so binding to a specific SHA may prevent you from getting fixes.

Recommendation: bind to major versions to get functionality and fixes but reserve binding to a specific release or SHA as a mitigation strategy for unforeseen breaks.

Recommendations

  1. Create a GitHub release for each specific version: Creating a release like v1.0.0 allows users to bind back to a specific version if an issue is encountered with the latest major version.

  2. Publish the specific version to the marketplace: When you release a specific version, choose the option to "Publish this Action to the GitHub Marketplace".

  3. Make the new release available to those binding to the major version tag: Move the major version tag (v1, v2, etc.) to point to the ref of the current release. This will act as the stable release for that major version. You should keep this tag updated to the most recent stable minor/patch release.

text
git tag -fa v1 -m "Update v1 tag"
git push origin v1 --force

Major Versions

All releases for a major version should hold compat including input compatibility and behavior compatibility.

Introduce a major version for compatibility breaks and major rewrites of the action.

Ideally, a major version would carry other benefits to the user to entice them to upgrade their workflows. Since updating their workflows will need to be done with an understanding of the changes and what compatibility was broken, introducing a new major version shouldn't be taken lightly.

To get feedback and to set expectations, the new major version can be initially released with v2-beta tag to indicate you can try it out but it's still going under some churn. Upon release the -beta can be dropped and there's an expectation of compatibility from that point forward.

An example of v2-beta with checkout

Sample Workflow

This illustrates the versioning workflow covered above.


File: docs/commands.md

:: Commands

The core toolkit package offers a number of convenience functions for
setting results, logging, registering secrets and exporting variables across actions. Sometimes, however, its useful to be able to do
these things in a script or other tool.

To allow this, we provide a special :: syntax which, if logged to stdout on a new line, will allow the runner to perform special behavior on
your commands. The following commands are all supported:

Set outputs

To set an output for the step, use ::set-output:

sh
echo "::set-output name=FOO::BAR"

Running steps.[step-id].outputs.FOO in your Yaml will now give you BAR

yaml
steps:
  - name: Set the value
    id: step_one
    run: echo "::set-output name=FOO::BAR"
  - name: Use it
    run: echo ${{ steps.step_one.outputs.FOO }}

This is wrapped by the core setOutput method:

javascript
export function setOutput(name: string, value: string): void {}

Register a secret

If a script or action does work to create a secret at runtime, it can be registered with the runner to be masked in logs.

To mask a value in the logs, use ::add-mask:

sh
echo "::add-mask::mysecretvalue"

This is wrapped by the core setSecret method

javascript
function setSecret(secret: string): void {}

Now, future logs containing BAR will be masked. E.g. running echo "Hello FOO BAR World" will now print Hello FOO **** World.

WARNING The add-mask and setSecret commands only support single-line
secrets or multi-line secrets that have been escaped. @actions/core
setSecret will escape the string you provide by default. When an escaped
multi-line string is provided the whole string and each of its lines
individually will be masked. For example you can mask first\nsecond\r\nthird
using:

sh
echo "::add-mask::first%0Asecond%0D%0Athird"

This will mask first%0Asecond%0D%0Athird, first, second and third.

WARNING Do not mask short values if you can avoid it, it could render your output unreadable (and future steps' output as well).
For example, if you mask the letter l, running echo "Hello FOO BAR World" will now print He*********o FOO BAR Wor****d

Group and Ungroup Log Lines

Emitting a group with a title will instruct the logs to create a collapsible region up to the next endgroup command.

bash
echo "::group::my title"   
echo "::endgroup::"

This is wrapped by the core methods:

javascript
function startGroup(name: string): void {}
function endGroup(): void {}

Problem Matchers

Problems matchers can be used to scan a build's output to automatically surface lines to the user that matches the provided pattern. A file path to a .json Problem Matcher must be provided. See Problem Matchers for more information on how to define a Problem Matcher.

bash
echo "::add-matcher::eslint-compact-problem-matcher.json"   
echo "::remove-matcher owner=eslint-compact::"

add-matcher takes a path to a Problem Matcher file
remove-matcher removes a Problem Matcher by owner

Save State

Save a state to an environmental variable that can later be used in the main or post action.

bash
echo "::save-state name=FOO::foovalue"

Because save-state prepends the string STATE_ to the name, the environment variable STATE_FOO will be available to use in the post or main action. See Sending Values to the pre and post actions for more information.

Log Level

There are several commands to emit different levels of log output:

log level example usage
debug echo "::debug::My debug message"
notice echo "::notice::My notice message"
warning echo "::warning::My warning message"
error echo "::error::My error message"

Additional syntax options are described at the workflow command documentation.

Command Echoing

By default, the echoing of commands to stdout only occurs if Step Debugging is enabled

You can enable or disable this for the current step by using the echo command.

bash
echo "::echo::on"

You can also disable echoing.

bash
echo "::echo::off"

This is wrapped by the core method:

javascript
function setCommandEcho(enabled: boolean): void {}

The add-mask, debug, warning and error commands do not support echoing.

Command Prompt

CMD processes the " character differently from other shells when echoing. In CMD, the above snippets should have the " characters removed in order to correctly process. For example, the set output command would be:

cmd
echo ::set-output name=FOO::BAR

Environment files

During the execution of a workflow, the runner generates temporary files that can be used to perform certain actions. The path to these files are exposed via environment variables. You will need to use the utf-8 encoding when writing to these files to ensure proper processing of the commands. Multiple commands can be written to the same file, separated by newlines.

Set an environment variable

To set an environment variable for future out of process steps, write to the file located at GITHUB_ENV or use the equivalent actions/core function

sh
echo "FOO=BAR" >> $GITHUB_ENV

Running $FOO in a future step will now return BAR

For multiline strings, you may use a heredoc style syntax with your choice of delimeter. In the below example, we use EOF.

text
steps:
  - name: Set the value
    id: step_one
    run: |
        echo 'JSON_RESPONSE<<EOF' >> $GITHUB_ENV
        curl https://httpbin.org/json >> $GITHUB_ENV
        echo 'EOF' >> $GITHUB_ENV

This would set the value of the JSON_RESPONSE env variable to the value of the curl response.

The expected syntax for the heredoc style is:

text
{VARIABLE_NAME}<<{DELIMETER}
{VARIABLE_VALUE}
{DELIMETER}

This is wrapped by the core exportVariable method which sets for future steps but also updates the variable for this step.

javascript
export function exportVariable(name: string, val: string): void {}

PATH Manipulation

To prepend a string to PATH write to the file located at GITHUB_PATH or use the equivalent actions/core function

sh
echo "/Users/test/.nvm/versions/node/v12.18.3/bin" >> $GITHUB_PATH

Running $PATH in a future step will now return /Users/test/.nvm/versions/node/v12.18.3/bin:{Previous Path};

This is wrapped by the core addPath method:

javascript
export function addPath(inputPath: string): void {}

Powershell

Powershell does not use UTF8 by default. You will want to make sure you write in the correct encoding. For example, to set the path:

text
steps:
  - run: echo "mypath" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append

File: docs/container-action.md

Creating a Docker Action

The container-template repo contains the base files to create a Docker action.

Create a Repo from the Template

Navigate to https://github.com/actions/container-template

Click on Use this template to create the repo for your action.

Complete creating your repo and clone the repo.

NOTE: The location of the repo will be how users will reference your action in their workflow file with the using keyword.

e.g. To use https://github.com/actions/setup-node, users will author:

yaml
steps:
    using: actions/setup-node@v5

Define Metadata

Your action has a name and a description. Update the author.

Create inputs that your unit of work will need. These will be what workflow authors set with the with: keyword.

yaml
name: 'My Container Action'
author: 'GitHub'
inputs: 
  myInput:
    default: 'world'
runs:
  using: 'docker'
  image: 'Dockerfile'
  args:
    - ${{ inputs.myInput }}

It will be run with docker and the input is mapped into the args

Change Code

The entry point is in entrypoint.sh

bash
#!/bin/sh -l

echo "hello $1"

Publish

Simply push your action to publish.

bash
$ git push

The runner will download the action and build the docker container on the fly at runtime.

Consider versioning your actions with tags. See versioning