Skill: Generate GitHub Workflow
Purpose
Produce GitHub Actions workflow files that satisfy this skill’s Appendix A: Workflow output contract for a wide range of software projects. Standardized structure, triggers, and security reduce CI/CD setup cost and improve maintainability and auditability while avoiding common security and permission issues. This skill only produces workflow YAML; it does not chain to documentation or rule skills. If the user later needs README or AGENTS.md updates, invoke those skills separately.
Use Cases
- New project setup: Add CI (build, test, lint) or PR-check workflows to a new repo.
- Unified standards: Align workflow style and naming across many repos for ops and audit.
- Fill gaps: Add missing CI/release/scheduled workflows to legacy projects, with minimal permissions and pinned versions.
- Scenario-based: Generate YAML for a given scenario (e.g. “run tests only on PR”, “build and publish on tag”).
When to use: When the user or project needs to “create or add GitHub workflows for the current or specified project.”
Scope: This skill’s output follows the
embedded Appendix A (narrow triggers, minimal permissions, pinned versions, auditable). Generic templates (e.g. skills.sh
) are more general; this skill emphasizes security and maintainability.
Behavior
Principles
- Appendix A is authoritative: Output YAML must satisfy Appendix A (structure, naming, security, maintainability).
- Narrow triggers: must specify branches/paths/tags; avoid triggering on every push. Common pattern: / with or . Release workflows must trigger only on version tags (e.g. ) and live in a separate file from CI.
- Minimal permissions: When the workflow needs repo write, PR, or Secrets, set at workflow or job level to the minimum required; e.g. CI , release , ; avoid .
- Pinned versions: Pin third-party actions (commit SHA or major-version tag); do not use or unpinned refs; prefer specific versions for security/scan actions (e.g. Trivy).
Tone and style
- Use objective, technical language; keep workflow and step short and readable for the Actions log.
- Match the project stack: choose runner, package manager, and build commands by project type (Node/Python/Go/Rust) and existing conventions; if the project already has workflows, align naming and style.
Input-driven
- Use the user’s scenario (e.g. “CI: run tests on PR”, “Release: build and upload on tag”) and stack (language, package manager, test/build commands) to generate the workflow; use sensible placeholders when information is missing and mark them for replacement; do not invent commands or paths.
Interaction
- Confirm before write: After generating YAML, list required notes (placeholders, branch names, Secret names the user must set), then ask for confirmation; do not write to or commit without user confirmation.
- Multiple files / release: If generating several workflows (e.g. CI + Release) or using write permissions (, ), list files to be created/overwritten and permission scope, then confirm before writing.
- Conflicts: If the target path already has a workflow with the same or overlapping purpose, warn and ask whether to overwrite or save elsewhere; do not overwrite silently.
Input & Output
Input
- Scenario: Purpose (CI, PR check, release, scheduled, matrix).
- Stack: Language and version (e.g. Node 20, Python 3.11, Go 1.21), package manager (npm/pnpm/yarn, pip, cargo), test/build/release commands.
- Triggers: Branches (e.g. , ), path filters, optional .
- Target path: Where to write the file(s), default under project root; for multiple workflows, specify each filename (e.g. , ).
Output
- Workflow YAML: Full file content conforming to Appendix A, ready to write to
.github/workflows/<name>.yml
.
- Notes: List placeholders (e.g. , branch ), Secret names, and any items the user must configure.
Restrictions
- Do not violate Appendix A: Output must have , , , and each job must have and ; do not use unpinned third-party actions or hardcoded secrets.
- Do not over-trigger: Do not use bare with no branch/path filter unless the user explicitly requests it.
- Do not invent commands: Use placeholders for unknown test/build/release commands and mark “replace with actual command”; do not invent scripts or paths.
- Do not ignore existing workflows: If the project already has , align naming and style and avoid duplication or conflict.
- Do not duplicate build logic: If the project uses GoReleaser, Dockerfile, etc. for build and image shape, the workflow only triggers, logs in, and passes args (e.g. , ); do not reimplement that logic.
Self-Check
Examples
Example 1: Node CI (test + lint on PR)
Input: Scenario: CI. Stack: Node 20, pnpm, test
, lint
. Trigger:
to
. File:
.
Expected: Single
with
e.g. “CI”;
on: pull_request: branches: [main]
; job on
with checkout, setup Node/pnpm, install, lint, test; use official
and
(or equivalent) pinned; no hardcoded secrets;
read-only if set.
Example 2: Go PR check with path filter
Input: Scenario: PR check. Stack: Go 1.21, test
. Trigger only when
or
change. File:
.
Expected:
with
paths: ['**.go', 'go.mod']
; job with
pinned, steps checkout, setup Go, go test;
omitted or
if no write needed.
Example 3: Go release (Docker + GHCR + GoReleaser)
Input: Scenario: CD/release. Stack: Go, Docker multi-arch (amd64/arm64), GoReleaser for image and GitHub Release. Trigger: only
tags
. File:
.
Expected:
;
include
,
. Steps: Checkout (
) → Set up Go (
, cache) → Set up QEMU (
,
) → Set up Docker Buildx (
, same platforms) → Login to GHCR (
,
) → GoReleaser (
goreleaser/goreleaser-action
pinned, pass
and
BUILDX_BUILDER: ${{ steps.buildx.outputs.name }}
). Do not reimplement logic defined in
/Dockerfile.
See Appendix B.
Example 4 (edge): Minimal info
Input: Project: legacy-api. No description. Language and commands unknown. User wants “at least one CI placeholder workflow”.
Expected: Produce one structurally complete, Appendix A–compliant YAML; use placeholders for runner and steps (e.g. “Specify runner and install/test commands”) and mark “to be replaced”; keep
narrow (e.g.
pull_request: branches: [main]
); do not invent test or build commands; keep
,
,
,
,
, and recommended fields (e.g.
) so the user can fill in later.
Appendix A: Workflow output contract
The following are mandatory for workflow files produced by this skill; use this appendix for self-check.
Scope: YAML workflow files produced by this skill for a project’s
.
A.1 File and path
- Location: Must live under the target project’s .
- Naming: , extension or ; name should reflect purpose (e.g. , , ).
- One file, one workflow: One file defines one workflow; split into multiple files for complex cases; avoid many unrelated jobs in one file.
A.2 Required structure
Each workflow YAML must contain (order recommended):
| Field | Required | Description |
|---|
| Yes | Display name in GitHub UI; short and readable (e.g. “CI”, “PR check”, “Release”). |
| Yes | Triggers: , , , etc.; must narrow branch/path/tag; avoid broad with no filter. |
| Yes | At least one job; each job must have and . |
| Yes | Runner (e.g. ). |
| Yes | List of steps; each step has (human-readable) and or . |
Optional but recommended:
,
,
.
A.3 Naming and readability
- Job id: , clear meaning (e.g. , , , ).
- Step name: Short, scannable description for the Actions log.
- Workflow name: Align with filename and other workflows in the repo.
A.4 Security and minimal permissions
- Permissions: If is not set, GitHub uses default permissions. For sensitive operations, set at workflow or job level to the minimum needed. By type: CI (build/test/scan only) → ; release (Release, GHCR push) → explicit , ; avoid default or .
- Secrets: Inject secrets via Secrets; never hardcode keys, tokens, or passwords in YAML.
- Third-party actions: Prefer official or widely used actions; pin version (commit SHA or major-version tag); do not use or unpinned; use specific versions for security/scan actions to reduce drift.
A.5 Maintainability
- CI vs CD (recommended): CI only builds, tests, and scans; no release. CD (image push, GitHub Release) runs only on version tags (e.g. ). Use separate files (e.g. , ); do not mix “run on every push” and “release only on tag” in one workflow.
- Reuse: Extract common logic into Composite Actions or reusable workflows.
- Comments: Briefly comment non-obvious triggers, matrix strategy, or env usage; keep comments short.
- Project alignment: Runner, language version, package manager, and commands must match the target project; if the project has existing workflows, align style and naming.
A.6 Self-check (producer)
After producing the workflow:
Appendix B: Go + Docker + GHCR + GoReleaser
Conventions and practices for Go + Docker + GHCR + GoReleaser workflows; follow together with the main skill and Appendix A when generating or editing such workflows.
B.1 Layout
- CI and CD separate: Two workflows.
- CI (e.g. ): / to main branch. Build, test, security scan only; no release.
- CD (e.g. ): Only on of version tags (e.g. ). Publish image and GitHub Release.
- Do not mix “run on every push” and “release only on tag” in one workflow.
B.2 Permissions
- Set explicitly. CI: . Release: , . Do not use .
B.3 Steps and order
Go
- Use with . Enable . For release, checkout with (needed for GoReleaser); CI can use the same for consistency.
CI (example order)
- Checkout ()
- Set up Go (go.mod + cache)
- govulncheck:
go install golang.org/x/vuln/cmd/govulncheck@latest
then
- Docker Buildx (setup only, single platform)
- Build image for scanning: single arch , , , tag e.g.
local/your-app:ci-${{ github.sha }}
- Trivy on that image: , , so CI fails on findings
Multi-arch in Release only; CI scans single arch for speed.
Release (example order)
- Checkout ()
- Set up Go (go.mod + cache)
- Set up QEMU: ,
platforms: linux/amd64,linux/arm64
- Set up Docker Buildx: , ,
platforms: linux/amd64,linux/arm64
- Login to GHCR: , registry , password
secrets.GHCR_TOKEN || secrets.GITHUB_TOKEN
,
- GoReleaser:
goreleaser/goreleaser-action@v6
, , env and BUILDX_BUILDER: ${{ steps.buildx.outputs.name }}
QEMU before Buildx; Buildx
must match QEMU. GoReleaser needs the Buildx builder name for multi-arch, so set
and pass
.
B.4 Relation to repo config
- Docker image: Shape is defined in and Dockerfile; workflow does not duplicate build logic.
- GHCR: Image path and tagging in GoReleaser config; workflow only logs in and passes and Buildx builder.
- Makefile: Local build/test can stay; CI steps can align with Make targets but need not depend on them.
B.5 When editing
- Full flow: Changing one job may affect the whole flow; verify checkout → Go → QEMU → Buildx → login → GoReleaser order and deps.
- Action versions: Use current major versions (e.g. , , , ); check changelog for breaking changes when upgrading.
- Trivy: Pin version (e.g. ) to avoid CI breakage from behavior changes.
- YAML: Check indent and no duplicate keys; validate with a tool after edits.
B.6 Lessons learned
| Issue | Approach |
|---|
| Single workflow too large | Split into CI + Release: CI for build/test/scan, Release only on tag via GoReleaser; clearer permissions and logic. |
| GHCR auth too complex | Use minimal login ( + token); avoid heavy auth-verify that can false-fail. |
| Multi-arch manifest validation fails | Pull and validate per platform instead of generic manifest pull. |
| Date/version format inconsistent | Use one format (e.g. ISO8601) in workflow and Dockerfile; add to if using GoReleaser output. |
| GoReleaser multi-arch build fails | GoReleaser needs Buildx builder: set id: buildx on Buildx step and pass BUILDX_BUILDER: ${{ steps.buildx.outputs.name }}. |
| Version drift | Use reasonable version constraints and check release notes when upgrading; validate on a branch first. |
Inspect workflow history:
git log --oneline -- .github/workflows/
References