-
Notifications
You must be signed in to change notification settings - Fork 183
GitHub Actions Design Guidelines
Mike Harder edited this page Jan 29, 2025
·
18 revisions
# Dev Dependencies for VS Code intellisense in github-script JS files
.github/package.json (no package-lock.json to minimize maintenance, OK since only dev deps)
# Composite Actions (preferred option for code reuse)
.github/actions/do-something/action.yaml
.github/actions/do-something/src/action.js (if using `github-script`, but prefer declarative YAML when possible)
.github/actions/do-something/test/action.test.js (if using `github-script`, but prefer declarative YAML when possible)
.github/actions/src/util.js (code shared between actions)
.github/actions/test/util.test.js (code shared between actions)
# Workflows
.github/workflows/test-something.yaml
.github/workflows/src/test-something.js (if using `github-script`, but prefer declarative YAML or composite actions)
.github/workflows/test/test-something.test.js (if using `github-script`, but prefer declarative YAML or composite actions)
.github/workflows/src/util.js (code shared between workflows)
.github/workflows/test/util.test.js (code shared between workflows)
# Shared JS Libraries
.github/src/util.js (code shared between action and workflows)
.github/test/util.test.js (code shared between action and workflows)
For VS Code intellisense and tools:
- cd
.github
npm ci
-
YAML syntax for Workflows - Files under
.github/workflows
-
YAML syntax for Actions - Files under
.github/actions
- Workflows and Actions should use the
.yaml
file extension (not.yml
) - Use pinned runner images like
ubuntu-24.04
rather than floated images likeubuntu-latest
- Updating all pinned images is a single search/replace, while floating causes distracting warnings during updates
- Less is more, but make things nice
- Don't set a property (like
id
orshell
) unless it's useful - Do set a property (like
name
) if (and only if) it improves the log UI
- Don't set a property (like
- Sort properties approximately in the order listed in the documents above (and/or align with examples in GitHub docs)
- Log as much as you can, but within reason
- Do log anything you think might be useful to debug a problem in the future
- Don't log anything verbose, unless it's useful
- Consider grouping log chunks to improve UI
- Use
debug
when appropriate for verbose logs, but remember it's only useful for repros (not the original failure)
- Before merging, run "Format Document" (will be enforced in the future)
- Goal is 100% branch coverage of all actions, workflows, and shared code
- Security hardening for GitHub Actions
- Keeping your GitHub Actions and workflows secure: Preventing pwn requests
- Trigger
pull_request
has a read-onlyGITHUB_TOKEN
- Should be used whenever possible, like to run a test and return pass/fail
- Triggers like
pull_request_target
andworkflow_completed
have read-writeGITHUB_TOKEN
- You must never do anything that could execute untrusted code from a fork PR
- Safest option is to never clone the PR source branch at all
- Perform most of the work in a safe
pull_request
trigger, then use aworkflow_completed
trigger to execute the privileged tasks (e.g. adding a label) based on artifacts published by the first workflow - If you must clone the PR source branch, it can only be treated as text, never executed
- Don't log anything that might leak secrets
- GitHub has some built-in protections (e.g. masking secrets), but treat them as defense-in-depth
To follow the principle of least-privilege, all workflows should use the following top-level permissions by default:
permissions:
content: read
This allows the workflow to clone the repo (and upload artifacts, which appears to be always allowed). Additional privileges should be granted at the job level as needed. For example:
permissions:
contents: read
jobs:
update-labels:
name: Update Labels
permissions:
contents: read
pull-requests: write
- Use
workflow_dispatch
wherever you can- Manually triggering a pipeline can make testing/debugging easier
- Beware the different context objects available to the pipeline and don't couple to a specific one
- Naming jobs in a matrix
- Matrix UI improvements
- Composite Actions behavior is different from DevOps templates
- Code is written in JavaScript instead of TypeScript
- Significantly improves startup time
- Recommended by
github-script
action - Encourages us to keep our code simple
- With type checking and intellisense, the editing experience is similar
- Before merging, run "Format Document" (will be enforced in the future)
- All JS files should start with header
// @ts-check
to enable type checking - Functions called from
github-script
should start with the following, to set the argument types:
/**
* @param {import('github-script').AsyncFunctionArguments} AsyncFunctionArguments
*/
module.exports = async ({ github, context, core }) => {
- Additional type declarations can be added via JSDoc comments
const payload =
/** @type {import("@octokit/webhooks-types").WorkflowRunCompletedEvent} */ (
context.payload
);
- Error logging in
pwsh
- To
$ErrorActionPreference
andWrite-Error
or not to$ErrorActionPreference
andWrite-Host
?
- To