Skip to content

Understanding GitHub Actions Reusable Workflows

FieldValue
TypeSkill Resource
Source~/.copilot/skills/technical-writing/references/docs-that-teach/page-template.mdx
DescriptionNot specified

Source Content

import {
Callout,
CodeBlock,
ComparisonTable,
InteractiveMermaid,
} from "@dmwd-io/design-system";
export const ciFlow = `flowchart LR
A[Open PR] --> B[CI runs checks]
B --> C{Checks pass?}
C -- Yes --> D[Review and merge]
C -- No --> E[Fix and push]
E --> B`;
export const ciYaml = `jobs:
call-shared-workflow:
uses: org/platform/.github/workflows/node-ci.yml@main
with:
node-version: 20`;
{/*
TEMPLATE NOTES — delete this block before shipping.
- Frontmatter defaults (required): title, created, last_updated, tags, tldr, contributors.
Recommended (mirror docs/docs-index.json): status, appliesWhen, last_verified.
Validate with scripts/lint_frontmatter.py before shipping.
- Top level of an MDX file accepts ONLY import/export + content. Data is `export const`.
- Storybook derives the page title and sidebar slot from the file path. Put the page
in the right folder and let folder placement own navigation. Do not add authored
title or layout overrides as boilerplate.
- Every component below was looked up via the Storybook MCP. Do not invent props.
- Replace each section's content; keep the spine. Run the `docs-that-teach` review rubric.
*/}
# Understanding GitHub Actions Reusable Workflows
Reusable workflows let many repositories share one CI definition instead of copy-pasting pipeline YAML. You maintain the logic in one place and each repo passes its own inputs. Reach for them the moment the same CI behavior appears in a second repo.
## Mental model
Think of a reusable workflow like a shared function: each repo passes inputs, but the implementation lives in one place. Change the function once and every caller gets the new behavior.
## How a PR flows through shared CI
<InteractiveMermaid chart={ciFlow} fileName="reusable-workflow-flow.mmd" height="380px" />
Notice that the consuming repo never owns the checks — it only triggers them. Fixing a failing check loops back through the *same* shared definition, so every repo fails and passes the same way.
## Calling a shared workflow
This is the entire CI file in a consuming repo. The implementation it points to lives elsewhere.
<CodeBlock code={ciYaml} language="yaml" filename=".github/workflows/ci.yml" />
The lines that matter:
- `uses:` points to the shared workflow and pins a ref (`@main` or a tag).
- `with:` passes repo-specific configuration into the shared logic.
- the repository no longer owns the CI implementation — only its inputs.
A common mistake is omitting the ref after the path. Without `@main` or `@v1`, the workflow will not resolve.
## When to centralize and when not to
Shared CI earns its keep by saving you from silent drift. Say a team copies its pipeline into twelve repos. They patch a security fix in three and forget the rest. The gap surfaces in an audit a year later — long after anyone remembers which repos were missed. Define the workflow once, so a fix reaches every repo and not just the ones you happen to remember.
<ComparisonTable
title="Where CI logic should live"
columns={[
{ id: "local", title: "Local config" },
{ id: "shared", title: "Shared workflow", highlighted: true },
{ id: "manual", title: "Manual process" },
]}
rows={[
{ id: "consistency", label: "Consistent across repos", values: { local: false, shared: true, manual: false } },
{ id: "ownership", label: "Central ownership", values: { local: false, shared: true, manual: false } },
{ id: "judgment", label: "Allows human judgment", values: { local: true, shared: false, manual: true } },
{ id: "churn", label: "Good when logic changes constantly", values: { local: true, shared: false, manual: false } },
]}
/>
<Callout tone="warning" title="Do not copy a shared workflow into each repo">
Copying the workflow back into every repository defeats the purpose — it makes the system harder
to patch, audit, and improve. If a repo needs a tweak, pass an input or add a variant, do not fork.
</Callout>
## Key takeaway
The best workflow is not the one with the most automation. It is the one that makes the right path obvious, repeatable, and safe — so define CI behavior once and let every repo call it.