Skip to content

Commit

Permalink
Add support for summary output (#35)
Browse files Browse the repository at this point in the history
This PR adds support for [job
summary](https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#adding-a-job-summary)
output. If enabled via the `GITHUB_STEP_SUMMARY` env var, it will write
any summary output to the designated file path.

A few other housecleaning items were included:

- Add the expanded list of available GitHub environment variables to
reference in `.env.example`
- Move feature information to separate files in `docs/`
- Add more tests :)
- Add support for `toWin32Path`, `toPlatformPath`, and `toPosixPath`
- Add docs on supported `actions/toolkit` packages/functions
-
  • Loading branch information
ncalteen authored Feb 16, 2024
2 parents c23f7c2 + b2a7a2d commit c244633
Show file tree
Hide file tree
Showing 24 changed files with 1,228 additions and 60 deletions.
55 changes: 52 additions & 3 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,9 +1,58 @@
# Do not commit your actual .env file to Git! This may contain secrets or other
# private information.

# Enable/disable step debug logging (default: `false`). For local debugging, it
# may be useful to set it to `true`.
ACTIONS_STEP_DEBUG=true

# GitHub Actions inputs should follow `INPUT_<name>` format (case-insensitive).
INPUT_milliseconds=2400

# Enable/disable step debug logging. Normally this is false by default, but for
# the purpose of debugging, it is set to true here.
ACTIONS_STEP_DEBUG=true
# GitHub Actions default environment variables. These are set for every run of a
# workflow and can be used in your actions. Setting the value here will override
# any value set by the local-action tool.
# https://docs.github.com/en/actions/learn-github-actions/variables#default-environment-variables

# CI="true"
# GITHUB_ACTION=""
# GITHUB_ACTION_PATH=""
# GITHUB_ACTION_REPOSITORY=""
# GITHUB_ACTIONS=""
# GITHUB_ACTOR="mona"
# GITHUB_ACTOR_ID="123456789"
# GITHUB_API_URL=""
# GITHUB_BASE_REF=""
# GITHUB_ENV=""
# GITHUB_EVENT_NAME=""
# GITHUB_EVENT_PATH=""
# GITHUB_GRAPHQL_URL=""
# GITHUB_HEAD_REF=""
# GITHUB_JOB=""
# GITHUB_OUTPUT=""
# GITHUB_PATH=""
# GITHUB_REF=""
# GITHUB_REF_NAME=""
# GITHUB_REF_PROTECTED=""
# GITHUB_REF_TYPE=""
# GITHUB_REPOSITORY=""
# GITHUB_REPOSITORY_ID=""
# GITHUB_REPOSITORY_OWNER=""
# GITHUB_REPOSITORY_OWNER_ID=""
# GITHUB_RETENTION_DAYS=""
# GITHUB_RUN_ATTEMPT=""
# GITHUB_RUN_ID=""
# GITHUB_RUN_NUMBER=""
# GITHUB_SERVER_URL=""
# GITHUB_SHA=""
# GITHUB_STEP_SUMMARY=""
# GITHUB_TRIGGERING_ACTOR=""
# GITHUB_WORKFLOW=""
# GITHUB_WORKFLOW_REF=""
# GITHUB_WORKFLOW_SHA=""
# GITHUB_WORKSPACE=""
# RUNNER_ARCH=""
# RUNNER_DEBUG=""
# RUNNER_NAME=""
# RUNNER_OS=""
# RUNNER_TEMP=""
# RUNNER_TOOL_CACHE=""
35 changes: 8 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ actions can be run directly on your workstation.
>
> This tool currently only supports JavaScript and TypeScript actions!
## v1.0.0 Changes
## v1 Changes

With the release of `v1.0.0`, there was a need to switch from
[`ts-node`](https://www.npmjs.com/package/ts-node) to
Expand Down Expand Up @@ -209,30 +209,11 @@ $ local-action run /path/to/typescript-action src/index.ts .env
================================================================================
```
## Debugging in VS Code
This package can also be used with VS Code's built-in debugging tools. You just
need to add a `launch.json` to the project containing your local action. The
following can be used as an example.
```json
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug Local Action",
"type": "node",
"request": "launch",
"runtimeExecutable": "local-action",
"cwd": "${workspaceRoot}",
"args": [".", "src/index.ts", ".env"],
"console": "integratedTerminal",
"skipFiles": ["<node_internals>/**", "node_modules/**"]
}
]
}
```
## Features
The following list links to documentation on how to use various features of the
`local-action` tool.
In the `args` section, make sure to specify the appropriate path, entrypoint,
and dotenv file path. After that, you can add breakpoints to your action code
and start debugging!
- [Supported Functionality](./docs//supported-functionality.md)
- [Debugging in VS Code](./docs/debugging-in-vscode.md)
- [Create a Job Summary](./docs/create-a-step-summary.md)
4 changes: 3 additions & 1 deletion __fixtures__/javascript/failure/src/main.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
/* eslint-disable @typescript-eslint/no-var-requires */
/* eslint-disable @typescript-eslint/require-await */
/* eslint-disable import/no-commonjs */

const core = require('@actions/core')

async function run() {
core.summary.addRaw('JavaScript Action Failed!')
await core.summary.write()

core.setFailed('JavaScript Action Failed!')
}

Expand Down
3 changes: 1 addition & 2 deletions __fixtures__/javascript/no-import/src/main.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
/* eslint-disable @typescript-eslint/require-await */
/* eslint-disable import/no-commonjs */

async function run() {
return
return Promise.resolve()
}

module.exports = {
Expand Down
5 changes: 4 additions & 1 deletion __fixtures__/javascript/success/.env.fixture
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,7 @@
INPUT_milliseconds=2400

# Enable/disable step debug logs
ACTIONS_STEP_DEBUG=false
ACTIONS_STEP_DEBUG=false

# Step summary output location
GITHUB_STEP_SUMMARY='summary.md'
6 changes: 5 additions & 1 deletion __fixtures__/javascript/success/src/main.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
/* eslint-disable @typescript-eslint/no-var-requires */
/* eslint-disable @typescript-eslint/require-await */
/* eslint-disable import/no-commonjs */

const core = require('@actions/core')

async function run() {
const myInput = core.getInput('myInput')

core.setOutput('myOutput', myInput)

core.summary.addRaw('JavaScript Action Succeeded!')
await core.summary.write()

core.info('JavaScript Action Succeeded!')
}

Expand Down
7 changes: 4 additions & 3 deletions __fixtures__/typescript/failure/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
/* eslint-disable @typescript-eslint/require-await */

import { setFailed } from '@actions/core'
import { setFailed, summary } from '@actions/core'

export async function run(): Promise<void> {
summary.addRaw('TypeScript Action Failed!')
await summary.write()

setFailed('TypeScript Action Failed!')
}
4 changes: 1 addition & 3 deletions __fixtures__/typescript/no-import/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
/* eslint-disable @typescript-eslint/require-await */

export async function run(): Promise<void> {
return
return Promise.resolve()
}
5 changes: 4 additions & 1 deletion __fixtures__/typescript/success/.env.fixture
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,7 @@
INPUT_milliseconds=2400

# Enable/disable step debug logs
ACTIONS_STEP_DEBUG=false
ACTIONS_STEP_DEBUG=false

# Step summary output location
GITHUB_STEP_SUMMARY='summary.md'
9 changes: 6 additions & 3 deletions __fixtures__/typescript/success/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
/* eslint-disable @typescript-eslint/require-await */

import { getInput, info, setOutput } from '@actions/core'
import { getInput, info, setOutput, summary } from '@actions/core'

export async function run(): Promise<void> {
const myInput: string = getInput('myInput')

setOutput('myOutput', myInput)

summary.addRaw('TypeScript Action Succeeded!')
await summary.write()

info('TypeScript Action Succeeded!')
}
48 changes: 48 additions & 0 deletions __mocks__/@actions/core.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,67 @@
const addPath = jest.fn()
const debug = jest.fn()
const endGroup = jest.fn()
const error = jest.fn()
const exportVariable = jest.fn()
const getBooleanInput = jest.fn()
const getIDToken = jest.fn()
const getInput = jest.fn()
const getMultilineInput = jest.fn()
const getState = jest.fn()
const group = jest.fn()
const info = jest.fn()
const isDebug = jest.fn()
const notice = jest.fn()
const saveState = jest.fn()
const setCommandEcho = jest.fn()
const setFailed = jest.fn()
const setOutput = jest.fn()
const setSecret = jest.fn()
const startGroup = jest.fn()
const warning = jest.fn()

const summary = {}
summary['filePath'] = jest.fn().mockReturnValue(summary)
summary['wrap'] = jest.fn().mockReturnValue(summary)
summary['write'] = jest.fn().mockReturnValue(summary)
summary['clear'] = jest.fn().mockReturnValue(summary)
summary['stringify'] = jest.fn().mockReturnValue(summary)
summary['isEmptyBuffer'] = jest.fn().mockReturnValue(summary)
summary['emptyBuffer'] = jest.fn().mockReturnValue(summary)
summary['addRaw'] = jest.fn().mockReturnValue(summary)
summary['addEOL'] = jest.fn().mockReturnValue(summary)
summary['addCodeBlock'] = jest.fn().mockReturnValue(summary)
summary['addList'] = jest.fn().mockReturnValue(summary)
summary['addTable'] = jest.fn().mockReturnValue(summary)
summary['addDetails'] = jest.fn().mockReturnValue(summary)
summary['addImage'] = jest.fn().mockReturnValue(summary)
summary['addHeading'] = jest.fn().mockReturnValue(summary)
summary['addSeparator'] = jest.fn().mockReturnValue(summary)
summary['addBreak'] = jest.fn().mockReturnValue(summary)
summary['addQuote'] = jest.fn().mockReturnValue(summary)
summary['addLink'] = jest.fn().mockReturnValue(summary)

export {
addPath,
debug,
endGroup,
error,
exportVariable,
getBooleanInput,
getIDToken,
getInput,
getMultilineInput,
getState,
group,
info,
isDebug,
notice,
saveState,
setCommandEcho,
setFailed,
setOutput,
setSecret,
startGroup,
summary,
warning
}
26 changes: 25 additions & 1 deletion __tests__/commands/run.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
/* eslint-disable import/no-namespace */

import { setFailed } from '@actions/core'
import { setFailed, summary } from '@actions/core'
import { action } from '../../src/commands/run'
import { ResetCoreMetadata } from '../../src/stubs/core-stubs'
import { EnvMeta, ResetEnvMetadata } from '../../src/stubs/env-stubs'
import * as output from '../../src/utils/output'

const summary_writeSpy: jest.SpyInstance = jest
.spyOn(summary, 'write')
.mockImplementation()

let envBackup: { [key: string]: string | undefined } = process.env

describe('Command: run', () => {
Expand Down Expand Up @@ -35,22 +39,30 @@ describe('Command: run', () => {

describe('TypeScript', () => {
it('Action: success', async () => {
process.env.GITHUB_STEP_SUMMARY = 'summary.md'

EnvMeta.actionFile = `./__fixtures__/typescript/success/action.yml`
EnvMeta.actionPath = `./__fixtures__/typescript/success`
EnvMeta.dotenvFile = `./__fixtures__/typescript/success/.env.fixture`
EnvMeta.entrypoint = `./__fixtures__/typescript/success/src/index.ts`

await expect(action()).resolves.toBeUndefined()

expect(summary_writeSpy).toHaveBeenCalled()
expect(setFailed).not.toHaveBeenCalled()
})

it('Action: failure', async () => {
delete process.env.GITHUB_STEP_SUMMARY

EnvMeta.actionFile = `./__fixtures__/typescript/failure/action.yml`
EnvMeta.actionPath = `./__fixtures__/typescript/failure`
EnvMeta.dotenvFile = `./__fixtures__/typescript/failure/.env.fixture`
EnvMeta.entrypoint = `./__fixtures__/typescript/failure/src/index.ts`

await expect(action()).resolves.toBeUndefined()

expect(summary_writeSpy).toHaveBeenCalled()
expect(setFailed).toHaveBeenCalledWith('TypeScript Action Failed!')
})

Expand All @@ -61,28 +73,38 @@ describe('Command: run', () => {
EnvMeta.entrypoint = `./__fixtures__/typescript/no-import/src/index.ts`

await expect(action()).resolves.toBeUndefined()

expect(summary_writeSpy).not.toHaveBeenCalled()
expect(setFailed).not.toHaveBeenCalled()
})
})

describe('JavaScript', () => {
it('Action: success', async () => {
process.env.GITHUB_STEP_SUMMARY = 'summary.md'

EnvMeta.actionFile = `./__fixtures__/javascript/success/action.yml`
EnvMeta.actionPath = `./__fixtures__/javascript/success`
EnvMeta.dotenvFile = `./__fixtures__/javascript/success/.env.fixture`
EnvMeta.entrypoint = `./__fixtures__/javascript/success/src/index.js`

await expect(action()).resolves.toBeUndefined()

expect(summary_writeSpy).toHaveBeenCalled()
expect(setFailed).not.toHaveBeenCalled()
})

it('Action: failure', async () => {
delete process.env.GITHUB_STEP_SUMMARY

EnvMeta.actionFile = `./__fixtures__/javascript/failure/action.yml`
EnvMeta.actionPath = `./__fixtures__/javascript/failure`
EnvMeta.dotenvFile = `./__fixtures__/javascript/failure/.env.fixture`
EnvMeta.entrypoint = `./__fixtures__/javascript/failure/src/index.js`

await expect(action()).resolves.toBeUndefined()

expect(summary_writeSpy).toHaveBeenCalled()
expect(setFailed).toHaveBeenCalled()
})

Expand All @@ -93,6 +115,8 @@ describe('Command: run', () => {
EnvMeta.entrypoint = `./__fixtures__/javascript/no-import/src/index.js`

await expect(action()).resolves.toBeUndefined()

expect(summary_writeSpy).not.toHaveBeenCalled()
expect(setFailed).not.toHaveBeenCalled()
})
})
Expand Down
Loading

0 comments on commit c244633

Please sign in to comment.