| name | ci-workflow |
| description | Generate GitHub Actions CI workflows that call Makefile targets for consistent local and CI behavior. Use when a user asks about CI setup, GitHub Actions, continuous integration, or CI/CD pipelines. |
| autouse | false |
CI Workflow
Generate GitHub Actions workflows that mirror local Makefile targets so local and CI behavior stay identical. Supports Go, Node.js, and combined stacks.
Instructions
Step 1 — Detect project context
Before generating anything, check the repository:
- Makefile: Does one exist? Which targets does it expose (
check, test, build, lint, format)?
- Existing workflows: Is there already a
.github/workflows/ directory? If so, read existing files to avoid conflicts.
- Language stack: Look for
go.mod (Go), package.json (Node.js), or both.
- Go version: Check
go.mod for the go directive; use that version in the matrix.
- Go linter config: Check for
.golangci.yml or .golangci.yaml to confirm golangci-lint usage.
- Node version: Check
.nvmrc, .node-version, or engines in package.json.
Step 2 — Choose strategy
| Makefile? | Language | Strategy |
|---|
| Yes | Go | Makefile-first: make check, make test, make build |
| Yes | Node.js | Makefile-first: make check, make test, make build |
| Yes | Go + Node.js | Makefile-first: single make check covers both |
| No | Go | Direct commands: golangci-lint run, go test, go build |
| No | Node.js | Direct commands: npm ci, npm run lint, npm test, npm run build |
| No | Go + Node.js | Direct commands for each language in separate steps |
Makefile-first is preferred because it keeps CI and local behavior identical. If no Makefile exists, suggest creating one with makefile-workflow first.
Step 3 — Generate workflow
Create .github/workflows/ci.yml using the patterns below.
Go + Makefile (recommended)
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with:
go-version-file: go.mod
- run: make check
- run: make build
Go without Makefile
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with:
go-version-file: go.mod
- uses: golangci/golangci-lint-action@v9
- run: go test ./...
- run: go build ./...
Node.js + Makefile
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version-file: .nvmrc
cache: npm
- run: npm ci
- run: make check
- run: make build
Node.js without Makefile
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version-file: .nvmrc
cache: npm
- run: npm ci
- run: npm run lint
- run: npm test
- run: npm run build
Combined Go + Node.js
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with:
go-version-file: go.mod
- uses: actions/setup-node@v6
with:
node-version-file: .nvmrc
cache: npm
- run: npm ci
- run: make check
- run: make build
Step 4 — Validate
After generating the workflow file:
- YAML syntax: Confirm the file is valid YAML (no tab indentation, correct nesting).
- Referenced targets: If using Makefile-first strategy, verify the Makefile actually exposes the targets called in the workflow (e.g.,
check, build).
- Action versions: Use pinned major versions (
actions/checkout@v6, actions/setup-go@v6, actions/setup-node@v6).
- Version sources: Confirm
go-version-file / node-version-file point to files that exist.
- Runner compatibility: If the repository uses self-hosted runners, confirm the runner version meets the minimum required by the selected action majors before generating the workflow.
Step 5 — Explain
After generating, summarize:
- Which strategy was chosen and why
- What each workflow step does
- How to trigger the workflow (push to main, open a PR)
- How to customize further (see table below)
Customization
| Need | How |
|---|
| Matrix builds | Add strategy.matrix with Go or Node version arrays |
| Separate jobs per language | Split into go: and node: jobs under jobs: |
| Release on tag | Add on: push: tags: ['v*'] trigger with a release job |
| Coverage upload | Add a step after tests: uses: codecov/codecov-action@v5 |
| Caching Go modules | actions/setup-go@v6 caches by default; no extra step needed |
| Caching Node modules | Use cache: npm (or pnpm) in actions/setup-node@v6 |
| Branch protection | Recommend requiring the ci job to pass before merging |
Best practices
- Concurrency control: Always include the
concurrency block to cancel redundant runs on the same branch.
- Pin action versions: Use
@v6 not @main for stability; dependabot can update these.
- Self-hosted runners: Newer GitHub-maintained action majors can require newer runner versions; call that out when a repo uses self-hosted runners.
- Version from file: Prefer
go-version-file: go.mod and node-version-file: .nvmrc over hardcoded version strings so CI tracks the same version developers use locally.
- Minimal permissions: Set
permissions: contents: read at the workflow level; escalate per-job only when needed.
- Branch protection: After the workflow is running, suggest enabling required status checks on the
ci job.
Cross-plugin synergy
If the project has no Makefile, suggest using the makefile-workflow plugin to create one first. This ensures both local (make check) and CI (make check) use the same commands, reducing drift between environments.