Skip to content

GitHub Actions Design Guidelines

Mike Harder edited this page Jan 29, 2025 · 18 revisions

Folder Structure

# 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)

VS Code

Extensions

Install dev dependencies

For VS Code intellisense and tools:

  1. cd .github
  2. npm ci

YAML

Docs

Guidelines

  • Workflows and Actions should use the .yaml file extension (not .yml)
  • Use pinned runner images like ubuntu-24.04 rather than floated images like ubuntu-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 or shell) unless it's useful
    • Do set a property (like name) if (and only if) it improves the log UI
  • 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

Default Permissions

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

Suggestions

  • 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

JS

Guidelines

  • 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)

Type Checking

  • 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
  );

PowerShell

  • Error logging in pwsh
    • To $ErrorActionPreference and Write-Error or not to $ErrorActionPreference and Write-Host?

Disabling Actions in Forks

Disable all Actions

image

Disable individual workflows

image