Installs into .claude/skills of the current project.
Are you the author of Set Up Ci?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/cboone-set-up-ci)
---
name: set-up-ci
description: >-
Create GitHub Actions CI with test, lint, format, and vulnerability jobs and
matching Makefile targets. Use for "set up CI" or "add a CI workflow".
---
# Set-Up CI
Detect the project's language(s), create a GitHub Actions CI workflow with appropriate parallel jobs (test, lint, format, vulnerability check), and create matching Makefile targets for local development.
## Workflow
### 1. Detect Project Type
If the user specified a language in their request (e.g., `go-cli`, `go-library`, `javascript`, `python`, `rust`, `ruby`, `shell`, `zig`, `zsh`), use it directly instead of scanning for markers. Still perform sub-detection steps as needed (e.g., JS package manager detection for `javascript`, or verifying `main.go`/`cmd/` for `go-cli` vs `go-library`).
Scan for language and file-type markers using Glob. **Exclude `node_modules/`, `.yarn/`, `vendor/`, and other dependency directories from all searches** to avoid false positives from vendored code.
| Marker(s) | Language |
| ------------------------------------------------------------------------------------------------------------- | --------------------- |
| `go.mod` | Go |
| `package.json` + source files (`*.js`, `*.ts`, `*.jsx`, `*.tsx`, `*.mjs`, `*.mts`, excluding `node_modules/`) | JavaScript/TypeScript |
| `pyproject.toml`, `setup.py`, `requirements.txt` | Python |
| `Cargo.toml` | Rust |
| `build.zig`, `build.zig.zon` | Zig |
| `Gemfile`, `*.gemspec` | Ruby |
| `*.sh`, `bin/*`, `scripts/*` | Shell |
| `*.zsh`, `#!/usr/bin/env zsh` shebangs, `.zshrc`, `.zshenv` | Zsh |
**Go sub-detection**: If `main.go` exists at the root or a `cmd/` directory exists, classify as **Go CLI**. Otherwise classify as **Go Library**.
**JS/TS sub-detection**: Determine the package manager from lockfiles:
- `package-lock.json` = npm
- `yarn.lock` or `.yarnrc.yml` = yarn
- `pnpm-lock.yaml` = pnpm
- `bun.lock` = bun
- Default to npm if no lockfile found.
**Source file verification**: When `package.json` is detected, verify that actual JavaScript or TypeScript source files exist (`*.js`, `*.ts`, `*.jsx`, `*.tsx`, `*.mjs`, `*.mts`, excluding `node_modules/` and config files like `eslint.config.js`). A `package.json` used only for devDependencies (e.g., markdownlint tooling) does not make the project a JavaScript project. If no source files are found, skip JavaScript/TypeScript CI.
If multiple languages are detected, create a multi-language workflow with one job group per language. Present the detected languages to the user and confirm before proceeding.
If no language is detected, offer a generic workflow with `make test` and `make lint` targets.
### 2. Check for Existing CI
Look for existing CI files:
```bash
ls .github/workflows/ci.yml
ls .github/workflows/ci.yaml
ls .github/workflows/*.yml
```
If a CI workflow exists, present its contents and ask the user:
1. **Overwrite**: Replace the existing CI workflow entirely
1. **Merge**: Add missing jobs to the existing workflow (keep existing jobs intact)
1. **Abort**: Stop without changes
### 3. Check for Existing Makefile
If a `Makefile` exists, scan for existing CI-relevant targets:
```bash
grep -E '^(test|lint|fmt|vet|vuln|build|cover|coverage|tidy|tools|all|deny|audit|typos|changelog):' Makefile
```
Report which targets already exist and which will be added. Only add targets that do not already exist. Ask before modifying any existing target.
If no `Makefile` exists, offer to create one with the appropriate language-specific Makefile reference (see `./references/makefile-<language>.md`). The Makefile provides standard targets for local development (`test`, `lint`, `fmt`, `vuln`, etc.) and is required by the Go CI reusable workflow (`run-go-ci.yml@v4.1.0`), which calls Makefile targets (`make test`, `make vet`, `make fmt`, etc.) directly. If the user declines, note that CI will fail for Go templates because the reusable workflow requires Makefile targets.
### 4. Create CI Workflow
Read the appropriate language CI reference under `./references/ci-<language>.md` and generate `.github/workflows/ci.yml` from it. Write the file using the Write tool. The `.github/workflows/` directory will be created automatically if it does not exist.
Go, Rust, Zig, Shell, and secret scanning templates use `cboone/gh-actions` reusable workflows that handle tool installation, caching, and execution internally. Other language templates use inline jobs.
#### Ensure a Language Version File
The CI templates read language runtime versions from a project-owned version file rather than pinning inline:
| Language | Version source the workflow reads | Action if missing |
| -------- | ------------------------------------------------ | --------------------------------------------------------------------------------- |
| Node.js | `.tool-versions` (line `nodejs <version>`) | Create `.tool-versions` with `nodejs <latest LTS>` and warn the user to commit it |
| Ruby | `.tool-versions` (line `ruby <version>`) | Create `.tool-versions` (or append) with `ruby <latest stable>` |
| Go | `go.mod` (`go X.Y` directive) | Always present in a Go project; no action needed |
| Rust | `rust-toolchain.toml` (when present) | Create one with `[toolchain] channel = "stable"` if missing |
| Python | `pyproject.toml` `requires-python` (uv reads it) | Ensure the constraint is present in `pyproject.toml` |
| Zig | `build.zig.zon` `minimum_zig_version` | Always present in a Zig project; no action needed |
Before writing the CI workflow, check for the relevant version file and create or update it as needed. Latest LTS / stable lookups:
- Node.js LTS: `curl -s https://nodejs.org/dist/index.json | jq -r 'first(.[] | select(.lts != false)) | .version' | sed 's/^v//'`
- Latest stable Ruby: `gh api repos/ruby/ruby/releases --jq 'first(.[] | select(.prerelease == false)) | .tag_name' | sed 's/^v//; s/_/./g'` (Ruby tags use both `v3.4.0` and `v3_4_0` forms; the trailing `sed` normalizes them to the dotted format `.tool-versions` expects)
All templates share:
- Triggers: push to `main`, pull requests targeting `main`
- `paths-ignore` for documentation and agent configuration changes
- Concurrency groups to cancel in-progress runs on the same branch/PR
- `permissions: contents: read`
- `actions/checkout@v6` (in inline jobs) or handled by reusable workflows
#### Runner Usage Notes
The `paths-ignore` patterns skip CI for changes that do not affect build or test outcomes:
- `*.md` matches root-level Markdown only (README, CONTRIBUTING, etc.). Nested `.md` files such as Scrut CLI tests in `tests/scrut/` are NOT ignored, so CI still runs when test files change.
- `docs/**` skips documentation directory changes.
- `.claude/**`, `**/CLAUDE.md`, `**/AGENTS.md` skip AI agent configuration files.
- `LICENSE` and `.editorconfig` skip non-code metadata.
**When to adjust**: Remove `*.md` from `paths-ignore` if your project treats Markdown files as source code (e.g., documentation-focused projects where Markdown linting is a CI step). Remove `docs/**` if your docs directory contains generated API references that should trigger CI.
The concurrency group cancels in-progress CI runs when new commits are pushed to the same branch or PR. This prevents wasted minutes on superseded commits.
For multi-language projects, combine language-specific jobs into a single workflow file using the multi-language pattern in `./references/ci-multi-language.md`.
### 5. Create or Update Makefile Targets
Read the appropriate language Makefile reference under `./references/makefile-<language>.md` and add missing targets from it. Include both targets that the CI workflow references directly (e.g., `make test`, `make vet`) and standard local-development targets (`test`, `lint`, `fmt`, `vuln`, etc.) even when the CI workflow runs equivalent commands directly rather than via `make`.
Rules:
- Only add targets that do not already exist in the Makefile
- Ask before modifying existing targets
- If creating a new Makefile, include a `help` target
- Preserve any existing Makefile content (append new targets at the end)
### 6. Summary
Print a summary of what was created or modified:
- List every file created or modified
- Suggest complementary plugins:
- The set-up-secret-scanning skill for secret scanning
- The set-up-linters skill for linter configuration (if no linter configs detected)
- The add-scrut-cli-tests skill for CLI snapshot testing (if CLI project detected)
- The add-goreleaser-homebrew skill for release automation (if Go project detected)
## Error Handling
- **Not a git repo**: Warn the user, suggest `git init`, then continue (CI workflow files do not require a git repo to create, but will not trigger without one)
- **No language detected**: Offer a generic workflow with checkout + `make test` / `make lint` targets
- **Existing CI**: Ask before overwriting (covered in step 2)
- **Missing Makefile**: Offer to create one; if the user declines, note that CI may fail for language templates whose workflows reference `make` targets
## CI Workflow Templates
- `./references/ci-go-cli.md` -- Go CLI CI workflow
- `./references/ci-go-library.md` -- Go library CI workflow
- `./references/ci-javascript.md` -- JavaScript/TypeScript CI workflow
- `./references/ci-python.md` -- Python CI workflow
- `./references/ci-rust.md` -- Rust CI workflow
- `./references/ci-ruby.md` -- Ruby CI workflow
- `./references/ci-shell.md` -- Shell CI workflow
- `./references/ci-zig.md` -- Zig CI workflow
- `./references/ci-zsh.md` -- Zsh CI workflow
- `./references/ci-multi-language.md` -- Multi-language CI pattern
## Makefile Templates
- `./references/makefile-go-cli.md` -- Go CLI Makefile
- `./references/makefile-go-library.md` -- Go library Makefile
- `./references/makefile-javascript.md` -- JavaScript/TypeScript Makefile
- `./references/makefile-python.md` -- Python Makefile
- `./references/makefile-rust.md` -- Rust Makefile
- `./references/makefile-ruby.md` -- Ruby Makefile
- `./references/makefile-shell.md` -- Shell Makefile
- `./references/makefile-zig.md` -- Zig Makefile
- `./references/makefile-zsh.md` -- Zsh Makefile
## Refresh `cboone/gh-actions` SHAs before scaffolding
The `cboone/gh-actions` reusable-workflow refs in this skill's templates are SHA-pinned with a `# vX.Y.Z` comment that was current when the template was authored. New releases of `cboone/gh-actions` rot those SHAs. Before emitting a workflow into a user's repo, refresh both the SHA and the comment to current latest:
```bash
TAG="$(gh release view --repo cboone/gh-actions --json tagName --jq '.tagName')"
SHA="$(gh api "repos/cboone/gh-actions/commits/${TAG}" --jq '.sha')"
echo "${SHA} # ${TAG}"
```
Replace each `cboone/gh-actions/.../<workflow>.yml@<old-sha> # <old-tag>` in the emitted workflow with the new SHA and tag. Dependabot in the user's repo keeps them in sync afterwards.