Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,8 @@ updates:
directory: /
schedule:
interval: weekly

- package-ecosystem: npm
directory: /workflow-inputs
schedule:
interval: weekly
48 changes: 48 additions & 0 deletions .github/workflows/workflow-inputs-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name: Workflow Inputs CI

on:
push:
branches: [main]
paths:
- "workflow-inputs/**"
- ".github/workflows/workflow-inputs-ci.yml"
pull_request:
paths:
- "workflow-inputs/**"
- ".github/workflows/workflow-inputs-ci.yml"

permissions:
contents: read

defaults:
run:
working-directory: workflow-inputs

jobs:
build:
name: Build & test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6

- name: Set up Vite+
uses: voidzero-dev/setup-vp@v1

- name: Install dependencies
run: vp install

- name: Format, lint, type-check
run: vp check

- name: Run tests
run: vp test run

- name: Build action bundle
run: vp pack

- name: Verify dist/ is up to date
run: |
if ! git diff --exit-code dist/; then
echo "::error::The committed workflow-inputs/dist/ is out of date. Run 'vp pack' inside workflow-inputs/ and commit the result."
exit 1
fi
95 changes: 95 additions & 0 deletions .github/workflows/workflow-inputs-e2e.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
name: Workflow Inputs E2E

# End-to-end smoke test for the workflow-inputs action.
#
# Declares its own workflow_dispatch inputs purely as test fixtures — the
# action runs against this very workflow file and renders a summary, which
# the assertion step then verifies.
#
# Triggers on push and pull_request (scoped to workflow-inputs/ changes) so
# every code change gets an end-to-end smoke test, and on workflow_dispatch
# so maintainers can rerun manually.

on:
push:
branches: [main]
paths:
- "workflow-inputs/**"
- ".github/workflows/workflow-inputs-e2e.yml"
pull_request:
paths:
- "workflow-inputs/**"
- ".github/workflows/workflow-inputs-e2e.yml"
workflow_dispatch:
inputs:
environment:
description: Where the e2e step pretends to deploy.
type: choice
options:
- staging
- production
default: staging
dry-run:
description: Skip the (pretend) deploy.
type: boolean
default: true
version:
description: Version label.
type: string
default: 0.0.0-e2e
github_token:
description: Should be redacted by the default mask pattern.
type: string
default: ghp_thisisnotreal

permissions:
contents: read

jobs:
e2e:
name: End-to-end smoke test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6

- name: Set up Vite+
uses: voidzero-dev/setup-vp@v1

- name: Install dependencies
working-directory: workflow-inputs
run: vp install

# Rebuild the bundle from source rather than trusting the committed
# dist/. Decouples this workflow from workflow-inputs-ci.yml: even if
# dist/ is stale on main, this still tests the current source code.
- name: Build action bundle
working-directory: workflow-inputs
run: vp pack

- name: Run the action against this workflow
id: action
uses: ./workflow-inputs

- name: Verify the rendered summary
env:
SUMMARY: ${{ steps.action.outputs.summary }}
run: |
set -euo pipefail
# Each declared input should appear as a row.
for name in environment dry-run version github_token; do
if ! printf '%s' "$SUMMARY" | grep -qE "^\| ${name} \|"; then
echo "::error::Expected the summary to contain a row for '${name}'."
printf '%s' "$SUMMARY"
exit 1
fi
done
# The fake token must be masked.
if printf '%s' "$SUMMARY" | grep -q 'ghp_thisisnotreal'; then
echo "::error::Expected the github_token value to be masked."
exit 1
fi
if ! printf '%s' "$SUMMARY" | grep -q '\*\*\*'; then
echo "::error::Expected the masked placeholder (***) to appear."
exit 1
fi
echo "Summary verified."
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ GitHub actions and workflows for Solana projects.

## Actions

| Name | Description |
|------------------|-------------|
| `install-solana` | Install Solana CLI tool with optional caching and verify the installed version |
| `setup-ubuntu` | Setup Ubuntu runner for program repo CI jobs |
| Name | Description |
|-------------------|-------------|
| `install-solana` | Install Solana CLI tool with optional caching and verify the installed version |
| `setup-ubuntu` | Setup Ubuntu runner for program repo CI jobs |
| `workflow-inputs` | Render the inputs of the current workflow run as a markdown table in the GitHub Actions job summary |

## Workflows

Expand Down
8 changes: 8 additions & 0 deletions workflow-inputs/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
node_modules/
.vite-plus/
*.tsbuildinfo

# Override the global gitignore which excludes dist/. JS GitHub Actions
# require the bundled output to be committed so the runner can execute it.
!dist/
!dist/**
120 changes: 120 additions & 0 deletions workflow-inputs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# workflow-inputs

A GitHub Action that renders the inputs of the current workflow run as a markdown table in the [job summary](https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/adding-a-job-summary). Useful for `workflow_dispatch` runs so anyone reviewing the run later can see exactly what inputs triggered it — something the GitHub Actions UI doesn't show out of the box.

## Why?

When you dispatch a workflow manually, GitHub remembers the input values it triggered with, but it doesn't surface them in the run UI. If you come back three months later and want to know what the deploy was, you can't tell without re-running it. This action solves that by writing the inputs to the job summary, where they're easy to find later.

## Usage

```yaml
name: Deploy

on:
workflow_dispatch:
inputs:
environment:
description: Where to deploy
type: choice
options: [staging, production]
default: staging
dry-run:
description: Skip the actual deploy
type: boolean
default: false

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: solana-program/actions/workflow-inputs@v1
# … rest of the deploy steps
```

That's it. After the action runs, the job summary will contain a table with each input's name, value, type, and description.

> [!NOTE]
> `actions/checkout` should run before this action. The action reads the calling workflow's YAML file from the workspace to extract input descriptions, types, and defaults. Without checkout, the summary still works but only shows the values that were passed (no descriptions).

## Inputs

| Input | Default | Description |
| --------------- | ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `title` | `Workflow inputs` | Heading for the summary section. |
| `inputs-json` | _(none)_ | Optional explicit JSON object of inputs. Pass `${{ toJSON(inputs) }}` to override the default behavior of reading from `github.event.inputs`. |
| `mask-patterns` | <code>(?i)(secret\|token\|password\|api[_-]?key)</code> | Comma-separated regex patterns. Input keys matching any pattern have their values redacted. Prefix a pattern with `(?i)` for case-insensitive matching. |
| `show-defaults` | `true` | Tag inputs that fell back to their declared default with `(default)` in the summary. |

## Outputs

| Output | Description |
| --------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `summary` | The rendered markdown summary, also written to `$GITHUB_STEP_SUMMARY`. Useful for piping into a Slack/Discord notification step. |

## Example: piping the summary to Slack

```yaml
- uses: solana-program/actions/workflow-inputs@v1
id: inputs
- uses: slackapi/slack-github-action@v2
with:
payload: |
{
"text": ${{ toJSON(steps.inputs.outputs.summary) }}
}
```

## Example: explicit `inputs-json`

If you'd rather not rely on the action reading the workflow file (e.g. you don't run `actions/checkout`), pass the inputs explicitly:

```yaml
- uses: solana-program/actions/workflow-inputs@v1
with:
inputs-json: ${{ toJSON(inputs) }}
```

## Triggers supported

- `workflow_dispatch` — the primary use case.
- `workflow_call` — reusable workflows. Same input schema shape.
- Any other trigger — values from `github.event.inputs` will still render if present, but no schema will be discovered.

## Secret masking

By default, input keys matching `(?i)(secret|token|password|api[_-]?key)` have their values replaced with `***`. The runner's own `::add-mask::` mechanism is independent and is honored by GitHub when it renders summaries — this layer is an extra belt to go with the runner's braces.

To extend the patterns:

```yaml
- uses: solana-program/actions/workflow-inputs@v1
with:
mask-patterns: "(?i)(secret|token|password|api[_-]?key),(?i)credential"
```

To disable masking entirely:

```yaml
- uses: solana-program/actions/workflow-inputs@v1
with:
mask-patterns: "$^" # Matches nothing.
```

## Development

This project uses [Vite+](https://viteplus.dev) as its toolchain.

```bash
vp install # install dependencies
vp check # format, lint, type-check
vp test # run the test suite
vp pack # build the action bundle into dist/index.js
```

After changes to anything in `src/`, run `vp pack` and commit the updated `dist/index.js` — it's the file the runner actually executes.

## Acknowledgments

Inspired by a Slack thread where Jon pointed out that GitHub Actions doesn't show the parameters used in a manual workflow run, so figuring out what happened in a deploy after the fact is harder than it should be. This action puts those parameters where you can find them later.
42 changes: 42 additions & 0 deletions workflow-inputs/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: "Workflow Inputs Summary"
description: >-
Render the inputs of the current workflow run as a markdown table in the
GitHub Actions job summary. Useful for `workflow_dispatch` runs so anyone
reviewing the run later can see what inputs triggered it.
author: "Loris Leiva"
branding:
icon: "list"
color: "blue"

inputs:
title:
description: "Heading for the summary section."
required: false
default: "Workflow inputs"
inputs-json:
description: >-
Optional JSON object of inputs. When provided, this is used in place of
github.event.inputs. See the README for usage with toJSON(inputs).
required: false
default: ""
mask-patterns:
description: >-
Comma-separated regex patterns. Input keys matching any pattern have
their values redacted in the summary. Prefix a pattern with `(?i)` for
case-insensitive matching.
required: false
default: "(?i)(secret|token|password|api[_-]?key)"
show-defaults:
description: >-
When true, inputs falling back to their declared default are tagged with
`(default)` in the summary.
required: false
default: "true"

outputs:
summary:
description: "The rendered markdown summary, also written to $GITHUB_STEP_SUMMARY."

runs:
using: "node24"
main: "dist/index.js"
Loading