Installs into .claude/skills of the current project.
Are you the author of Gen Gitlab Ci?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/sgaunet-gen-gitlab-ci)
---
name: gen-gitlab-ci
description: Generate GitLab CI/CD pipeline configuration for Go projects with testing, coverage, and automated releases
argument-hint: "[--force] [--dry-run]"
user-invocable: true
disable-model-invocation: true
allowed-tools: Read, Write, Bash(git:*), Bash(test:*), AskUserQuestion
---
# Generate GitLab CI Configuration
Instantly create a `.gitlab-ci.yml` file for Go projects with a production-ready, **mise-based** GitLab CI/CD pipeline including unit tests, linting, optional coverage reporting, snapshot builds, and automated releases.
## Why This Skill Exists
**Problem**: Setting up GitLab CI/CD for Go projects requires manually creating pipeline configuration, understanding GitLab CI syntax, and keeping tool versions in sync between local development and CI - a 30-45 minute process.
**Solution**: One-command generation that:
- Creates a complete `.gitlab-ci.yml` configuration (mise-based)
- Runs every job in the `jdx/mise:latest` image, installs tools via `mise install`, then runs `task …` — so CI uses the exact versions pinned in `mise.toml`
- Ensures a `mise.toml` exists at the repo root (creates one with detected versions if missing) — the single source of truth shared with local dev
- Includes a pipeline: unit tests, linter, snapshot builds, releases (+ optional coverage)
- Keeps Docker-in-Docker image publishing as a clearly-marked optional block
- Preserves all customization comments for user control
## Content Generation Rules
**CRITICAL**: When generating any content (pipeline files, commit messages, or any other output):
- **NEVER** add Claude Code attribution or co-authorship
- **NEVER** include phrases like "Generated by Claude", "Created with Claude Code", or similar
- **NEVER** add "Co-Authored-By: Claude" in commit messages or documentation
- All generated content should appear as if written by a human developer
- Focus on technical accuracy and providing working templates
## Process (6 Phases)
### Phase 1: Prerequisites Validation
**Critical checks before proceeding:**
1. **Validate Git repository:**
```bash
git rev-parse --git-dir
```
- If fails: Exit with error "Not a git repository. Run 'git init' first."
2. **Validate Go project:**
```bash
test -f go.mod
```
- If fails: Exit with error "go.mod not found. This skill is for Go projects only."
3. **Check existing .gitlab-ci.yml:**
```bash
test -f .gitlab-ci.yml
```
- If exists: Store `existingGitlabCi = true`
- If not exists: Store `existingGitlabCi = false`
4. **Verify skill assets directory:**
- Use Read tool to verify `${CLAUDE_PLUGIN_ROOT}/assets/gitlab-ci/.gitlab-ci.yml` exists
- Use Read tool to verify `${CLAUDE_PLUGIN_ROOT}/assets/mise/mise.toml` exists
- If either fails: Exit with error "Skill assets directory not found at ${CLAUDE_PLUGIN_ROOT}/assets/. Plugin may be corrupted. Reinstall the go-specialist plugin."
5. **Check for .goreleaser.yml (non-blocking):**
```bash
test -f .goreleaser.yml
```
- If missing: Store `goreleaserMissing = true` (warn in Phase 6, don't block creation)
- If exists: Store `goreleaserMissing = false`
6. **Check existing mise.toml:**
```bash
test -f mise.toml
```
- Store `existingMise = true|false`. The pipeline depends on `mise.toml`; if it
is missing this skill creates one (Phase 3.5). An existing one is preserved.
### Phase 2: Argument Parsing
Parse skill arguments from `$argument` string:
**Supported flags:**
- `--force`: Skip confirmation for existing .gitlab-ci.yml
- `--dry-run`: Show preview without writing file
**Parsing logic:**
```
arguments = split($argument by space)
for each arg in arguments:
if arg == "--force": set forceMode = true
if arg == "--dry-run": set dryRunMode = true
```
**No minimal mode:** The template is already compact with comprehensive coverage.
### Phase 3: Template Reading
**Read template file** from skill assets directory:
**Files to read:**
1. **.gitlab-ci.yml**
- Path: `${CLAUDE_PLUGIN_ROOT}/assets/gitlab-ci/.gitlab-ci.yml`
- Target: `.gitlab-ci.yml` (project root)
2. **mise.toml** [Only if `existingMise == false`]
- Path: `${CLAUDE_PLUGIN_ROOT}/assets/mise/mise.toml`
- Target: `mise.toml` (repo root)
**Template handling:**
- Store content in memory for Phase 3.5 substitution
- The `.gitlab-ci.yml` template is **static** apart from the runner `tags:` —
tool versioning lives in `mise.toml`.
- Preserve all `# CUSTOMIZE:` comments
**Error handling:**
- If Read fails: Exit with error "Failed to read template file. Plugin may be corrupted."
### Phase 3.5: Auto-Detection & Substitution
**Detection Function Library:** Use the shared detection functions from `${CLAUDE_PLUGIN_ROOT}/assets/detection-functions.md`. Read that file to get the bash detection functions (`detect_go_version`, `detect_golangci_lint_version`, `detect_goreleaser_version`, `strip_leading_v`, `substitute_version_placeholders`).
**Detection Workflow:**
1. **The `.gitlab-ci.yml` template needs no version substitution.** Every job runs
in the `jdx/mise:latest` image and installs tools via `mise install`, so tool
versions come from `mise.toml`, not the pipeline YAML. The only thing to review
is the runner `tags:` (see customizations).
2. **Ensure `mise.toml` exists (create if missing):**
```bash
if [[ "$existingMise" != "true" ]]; then
GO_VERSION=$(detect_go_version)
GOLANGCI_LINT_VERSION=$(detect_golangci_lint_version)
GORELEASER_VERSION=$(detect_goreleaser_version)
GOLANGCI_LINT_VERSION_BARE=$(strip_leading_v "${GOLANGCI_LINT_VERSION:-v2.2.2}")
mise_template=$(substitute_version_placeholders "$mise_template" \
"GO_VERSION=${GO_VERSION:-1.25}" \
"GOLANGCI_LINT_VERSION_BARE=${GOLANGCI_LINT_VERSION_BARE:-2.2.2}" \
"GORELEASER_VERSION=${GORELEASER_VERSION:-v2.12.0}")
# Written in Phase 5.
fi
```
- If `existingMise == true`: do NOT modify the existing `mise.toml`; note it in the report.
### Phase 4: User Confirmation
**Build preview summary:**
```
File to be created:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✅ .gitlab-ci.yml
Pipeline Structure (mise-based):
- 2 Stages: test, release
- Jobs (each runs in jdx/mise:latest, `mise install`, then `task ...`):
• unit-tests: `task test` on every push
• linter: `task lint` on every push
• build-snapshot: `task snapshot` (test release, no publish)
• build-release: `task release` on Git tags
• coverage: Optional (currently commented out)
[If existingMise == false:]
✅ mise.toml (created — pins go/task/golangci-lint/goreleaser for dev + CI)
[If existingMise == true:]
ℹ️ mise.toml already exists — preserved (pipeline will use it as-is)
[If existingGitlabCi == true:]
⚠️ WARNING: .gitlab-ci.yml already exists and will be overwritten
[If goreleaserMissing == true:]
⚠️ NOTE: .goreleaser.yml not found
The snapshot/release jobs require this file to function.
Generate with: /gen-goreleaser
```
**Dry run mode:**
If `dryRunMode == true`:
- Show preview summary above
- Display first 30 lines of .gitlab-ci.yml
- Show customization requirements
- Display next steps guide
- Exit without creating file
**Confirmation logic:**
1. **If `--force` flag provided:** Skip confirmation, proceed to Phase 5
2. **If existing .gitlab-ci.yml without `--force`:**
```
Use AskUserQuestion:
Question: ".gitlab-ci.yml already exists. Overwrite existing pipeline configuration?"
Options:
1. "Yes, overwrite file" [Recommended if upgrading pipeline]
2. "Show full preview first"
3. "Cancel"
If option 2: Display full content preview, then re-ask confirmation
If option 3: Exit gracefully
```
3. **If no .gitlab-ci.yml exists:**
```
Use AskUserQuestion:
Question: "Create .gitlab-ci.yml at project root?"
Options:
1. "Yes, create file"
2. "Show preview first"
3. "Cancel"
```
### Phase 5: File Creation
**File writing:**
1. `mise.toml` [Only if `existingMise == false`]
- Use Write tool with file path: `mise.toml`
- Write the version-substituted template from Phase 3.5
- If `existingMise == true`: skip (preserve the project's existing tool pins)
2. `.gitlab-ci.yml`
- Use Write tool with file path: `.gitlab-ci.yml`
- Write content from Phase 3 (static apart from runner tags)
- Track success/failure status
**Error handling:**
- If a write fails: Report the error (continue if at least `.gitlab-ci.yml` succeeds)
- If successful: Proceed to Phase 6 validation
### Phase 6: Validation & Success Report
**Post-creation validation:** confirm `.gitlab-ci.yml` exists and is non-zero; on failure
report the error and exit.
**Report to the user** — cover these facts; no fixed layout required:
- **Files written:** `.gitlab-ci.yml`, and `mise.toml` marked as created vs. preserved.
- **Pipeline structure:** 2 stages (test, release). Every job runs `jdx/mise:latest` →
`mise install` → `task ...`: `unit-tests` (`task test`) and `linter` (`task lint`) on every
push, `build-snapshot` (`task snapshot`, no publish), `build-release` (`task release`) on
git tags, and `coverage` (commented out by default).
- **Detected values:** if `mise.toml` was created, the pinned Go version (from go.mod),
golangci-lint version and goreleaser version.
- **Required customization — PRIORITY 1, the pipeline will not run without it:** replace
`tags: [gitlab-org-docker]` on every job with the project's own runner tags, found under
GitLab Settings → CI/CD → Runners (commonly `docker`, `linux`, `kubernetes`).
- **Other customizations:** tool versions live in `mise.toml`, not the pipeline YAML —
editing it changes both local (`mise install`) and CI versions. The `coverage:` job can be
uncommented (with coverage exclusions configured if needed). To publish Docker images,
uncomment the docker tools (buildx, docker-cli) in `mise.toml` and the `.docker-dind` block
in `.gitlab-ci.yml`, then `extends:` it from the release jobs — it authenticates with
`CI_REGISTRY` / `CI_JOB_TOKEN`.
- **GoReleaser status:** if `.goreleaser.yml` exists, say the release jobs are ready. If it
is missing, warn that `build-snapshot` and `build-release` will both fail, and point at
`/gen-goreleaser` or https://goreleaser.com/quick-start/.
- **Troubleshooting:** "No such runner tags: gitlab-org-docker" → update the runner tags
(check Settings → CI/CD → Runners). "goreleaser: config file not found" → generate
`.goreleaser.yml` with `/gen-goreleaser`. "task/mise: command not found" → jobs run in
`jdx/mise:latest` and call `mise install` in `before_script`, so `mise.toml` must exist at
the repo root (this skill creates it). "Docker login failed" (only with the optional
docker block) → enable the Container Registry under Settings → General → Visibility and
confirm `.docker-dind` is uncommented and extended by the release jobs.
- **Next steps:** review `.gitlab-ci.yml` (runner tags) and `mise.toml` (versions), ensure
`.goreleaser.yml` exists, then `git add .gitlab-ci.yml mise.toml` and
`git commit -m "ci: add mise-based GitLab CI/CD pipeline"`, `git push origin main`, and
confirm the pipeline runs under CI/CD → Pipelines.
- **Docs:** inline comments in `.gitlab-ci.yml` · `/gen-goreleaser` for `.goreleaser.yml` ·
runner configuration under GitLab Settings → CI/CD → Runners.
**On failure:** report which files were written, which failed and why, and that `--force`
retries. Partial success is a warning, total failure an error — for a total failure also
name the likely causes (permissions, disk space, filesystem restrictions).
## Arguments
Arguments are parsed from the `argument` string parameter:
- **`--force`**: Skip confirmation prompt and overwrite existing file
- Use when: Upgrading existing pipeline to latest template
- Behavior: Overwrites .gitlab-ci.yml directly without confirmation
- **`--dry-run`**: Preview file without writing to disk
- Shows: File preview, pipeline structure, customization requirements
- Writes: Nothing
- Use when: Reviewing what will be created before committing
**Parsing example:**
```
argument = "--force"
Split by space, check for each flag:
- forceMode = true
- dryRunMode = false
```
## Error Handling
### Critical Errors (Exit Immediately)
**Not a Git repository:**
```
❌ Error: Not a git repository
This skill requires a Git repository. Initialize one:
git init
git add .
git commit -m "Initial commit"
```
**Not a Go project:**
```
❌ Error: go.mod not found
This skill is for Go projects only. Initialize a Go module:
go mod init gitlab.com/username/project
```
**Skill assets missing:**
```
❌ Error: Skill assets directory not found
The go-specialist plugin may be corrupted. Try:
1. Reinstalling the plugin
2. Updating Claude Code to latest version
3. Reporting issue at: https://github.com/sgaunet/claude-plugins/issues
```
### Warning Errors (Continue with Warnings)
**Existing .gitlab-ci.yml:**
- Without `--force`: Request user confirmation
- With `--force`: Overwrite silently
**Missing .goreleaser.yml:**
- Don't block creation
- Warn in success report
- Suggest using the `/gen-goreleaser` skill
### Recovery Actions
**If file write fails:**
```
❌ Error: Failed to write .gitlab-ci.yml
Check:
1. Current directory is writable
2. Sufficient disk space
3. No file system restrictions
Current permissions: [show with ls -ld .]
```
## File Mapping
| Source Path | Target (Relative to CWD) | Mode |
|-------------|--------------------------|------|
| `${CLAUDE_PLUGIN_ROOT}/assets/gitlab-ci/.gitlab-ci.yml` | `.gitlab-ci.yml` | Always |
| `${CLAUDE_PLUGIN_ROOT}/assets/mise/mise.toml` | `mise.toml` | Create-if-missing |
## Integration with Other Skills
**Works with:**
- `/commit`: Commit generated .gitlab-ci.yml
- `/gen-goreleaser`: Generate .goreleaser.yml config (required for release/snapshot jobs)
- `/create-issue`: Create issue to track customization tasks
**Typical Workflow:**
```bash
# 1. Generate dev tooling first (Taskfile.yml + mise.toml)
/gen-taskfiles
# 2. Generate GitLab CI configuration (reuses mise.toml; creates it if missing)
/gen-gitlab-ci
# 3. Generate GoReleaser config (required)
/gen-goreleaser
# 4. Customize files
# Edit .gitlab-ci.yml: Update runner tags
# Edit mise.toml: Adjust tool versions if needed
# 5. Commit everything
git add .gitlab-ci.yml mise.toml Taskfile*.yml .goreleaser.yml
git commit -m "ci: add mise-based GitLab CI/CD pipeline"
# 5. Push and verify
git push origin main
# Check: GitLab → CI/CD → Pipelines
```
## Prerequisites for Pipeline
**Required for pipeline to function:**
1. **mise.toml** (auto-created by this skill if missing):
- Pins the tool versions installed via `mise install` in every job
- Managed canonically by `/gen-taskfiles`
2. **Taskfile.yml** with `test`, `lint`, `snapshot`, `release` tasks:
- Generate with `/gen-taskfiles` (also emits mise.toml)
3. **.goreleaser.yml** configuration:
- Generate with `/gen-goreleaser` (required for build-snapshot and build-release)
4. **GitLab Runner** that can run container images:
- Update runner tags in .gitlab-ci.yml (one per job)
- Check available runners: Settings → CI/CD → Runners
- Docker-in-Docker is only needed if you enable the optional image-publishing block
**Optional enhancements:**
1. **Coverage reporting:**
- Uncomment the `coverage:` job in .gitlab-ci.yml
- Configure coverage badge in README.md
2. **Docker image publishing:**
- Uncomment the docker tools in mise.toml (buildx, docker-cli)
- Uncomment the `.docker-dind` block and `extends:` it from the release jobs
3. **Custom tag patterns:**
- Edit build-release job in .gitlab-ci.yml
- Adjust `rules: - if: $CI_COMMIT_TAG` to filter specific tag patterns
## Template Customization Points
All template files include `# CUSTOMIZE:` comments marking required changes:
**mise.toml:**
- Tool versions (`go`, `golangci-lint`, `goreleaser`) → adjust as needed (used by dev + CI)
- Docker tools (`buildx`, `docker-cli`) → uncomment if publishing images
**.gitlab-ci.yml:**
- `tags: [gitlab-org-docker]` (one per job) → Replace with your runner tags
- `coverage:` job → Uncomment to enable (configure exclusions if needed)
- `.docker-dind` block → Uncomment + `extends:` from release jobs to publish images
- Tag filtering → Adjust the `rules:` in the build-release job
## Best Practices
**Before running this skill:**
1. Ensure git repository initialized
2. Have go.mod configured
3. Review pipeline configuration inline comments in template
4. Check available GitLab Runners and their tags
**After generation:**
1. Review all `# CUSTOMIZE:` comments in .gitlab-ci.yml
2. Generate .goreleaser.yml with the `/gen-goreleaser` skill
3. Update runner tags (most critical customization)
4. Update Go version to match your project
5. Commit with meaningful message
6. Monitor first pipeline run for errors
**Ongoing maintenance:**
1. Keep GoReleaser version updated
2. Review pipeline efficiency and optimize as needed
3. Adjust coverage thresholds as codebase matures
4. Add custom pipeline jobs as project needs evolve
## Limitations
**This skill does NOT:**
- Generate .goreleaser.yml (use `/gen-goreleaser` skill)
- Generate Taskfile.yml (use `/gen-taskfiles`, which also manages mise.toml)
- Modify an existing mise.toml (it is preserved; only created when missing)
- Modify existing .gitlab-ci.yml intelligently (use `--force` to overwrite)
- Validate that pipeline will work (requires proper runner setup)
- Configure GitLab repository settings (must be done via UI)
- Create GitLab Runners (requires infrastructure setup)
**Scope boundaries:**
- Handles `.gitlab-ci.yml` (and creates mise.toml when missing)
- Template is opinionated (aligned with Go best practices)
- Assumes standard Go project structure
- Requires manual customization post-generation (runner tags)
- Docker-in-Docker only required if you enable the optional image-publishing block
## Examples
```bash
# Standard generation
/gen-gitlab-ci
# Preview before creating
/gen-gitlab-ci --dry-run
# Overwrite existing pipeline
/gen-gitlab-ci --force
# Force with dry run (preview what would be overwritten)
/gen-gitlab-ci --force --dry-run
```
## Output Summary
**Standard mode (default):**
- .gitlab-ci.yml + mise.toml (if missing)
- Comprehensive, mise-based CI/CD pipeline (test + release stages)
**Dry run mode (`--dry-run`):**
- 0 files created
- Preview shown
- Safe exploration