| name | smaqit.infrastructure-cicd-generate |
| description | Use when manually generating or updating the canonical 3-workflow GitHub Actions CI/CD set (deploy.yml, provision.yml, post-merge-deploy.yml) for a Node.js + React application deployed to a VM via Terraform and Docker Compose. Also use when an operator invokes /cicd.generate, or when updating existing workflows to match project conventions. In `smaqit.new-greenfield-project`, the deployment agent generates workflows during Phase 5 using these patterns as reference — invoke this skill directly only when generating CI/CD outside the zero-to-prod flow. Note: `copilot-setup-steps.yml` is provided by the smaqit framework and is not generated by this skill. |
CI/CD Workflow Generator
Steps
-
Read existing workflows from .github/workflows/. If workflows exist and are substantially complete, offer to diff rather than overwrite.
-
Resolve configuration values from Infrastructure specs and copilot-instructions.md. Use these defaults if not found:
| Reference | Default |
|---|
| VM host | ${{ secrets.VM_HOST }} |
| SSH key secret | VM_SSH_KEY |
| Terraform backend | TF_BACKEND_ACCESS_KEY, TF_BACKEND_SECRET_KEY |
| Cloud credentials | TF_VAR_APP_CREDENTIAL_ID, TF_VAR_APP_CREDENTIAL_SECRET |
| GitHub token (Terraform) | GH_TERRAFORM_TOKEN |
| Terraform path filter | deployment/terraform/** |
-
Generate deploy.yml with two sequential jobs:
provision job: runs terraform apply. Env MUST include TF_VAR_github_token: ${{ secrets.GH_TERRAFORM_TOKEN }} — NOT GITHUB_TOKEN (reserved; runner overwrites it before steps execute).
deploy job: needs: [provision]. Steps in order: checkout → setup Node.js → install deps → build backend → build frontend (with VITE_DEMO_MODE: ${{ vars.VITE_DEMO_MODE }} on the build step) → rsync backend dist → rsync frontend dist → install node_modules on VM via Docker → write deploy stamps → docker compose restart → nginx reload.
-
Generate provision.yml with:
- Trigger:
pull_request, paths filter deployment/terraform/**
- Single
plan job: terraform plan only — NO terraform apply
- Posts plan output as a PR comment
- Same env vars as the
provision job in deploy.yml, including TF_VAR_github_token
-
Generate post-merge-deploy.yml with:
- Trigger:
pull_request, types: [closed]
- Condition:
if: github.event.pull_request.merged == true && contains(github.event.pull_request.body, 'smaqit:deploy')
- Action: trigger
deploy.yml via workflow_dispatch
- Do NOT use a label trigger — Copilot Coding Agent tokens lack
issues:write and cannot apply labels.
-
Write all 3 files to .github/workflows/.
-
Report the list of files written with a brief description of each.
Output
.github/workflows/deploy.yml — provision + deploy sequential jobs
.github/workflows/provision.yml — PR-only terraform plan with PR comment
.github/workflows/post-merge-deploy.yml — body sentinel trigger for deploy.yml
Scope
- Does NOT set up GitHub Secrets or Variables — use
smaqit.infrastructure-repo-config for that.
- Does NOT provision infrastructure — Terraform runs inside the generated workflow.
- Does NOT handle matrix builds, monorepos, or container registry pushes.
Examples
Input: New project with no CI/CD workflows. Operator invokes /cicd.generate.
Output: 4 files written to .github/workflows/. deploy.yml has sequential provision→deploy jobs; provision.yml is PR-only plan with PR comment; post-merge-deploy.yml uses body sentinel; copilot-setup-steps.yml has checkout.
Gotchas
GITHUB_TOKEN is reserved — GitHub Actions runner injects its own short-lived installation token under GITHUB_TOKEN before any step executes, overwriting any env.GITHUB_TOKEN set by the workflow. Use TF_VAR_github_token; Terraform auto-maps TF_VAR_* → var.*.
- PR body sentinel, not label — Copilot Coding Agent tokens lack
issues:write. post-merge-deploy.yml MUST use contains(github.event.pull_request.body, 'smaqit:deploy'), not a label check.
provision.yml also needs TF_VAR_github_token — omitting it causes interactive prompt hangs in PR preview plans.
copilot-setup-steps.yml must include actions/checkout@v4 — without checkout, the Coding Agent cannot read the codebase of a private repository.
VITE_* vars are build-time — pass VITE_DEMO_MODE and any other VITE_* vars to the npm run build step, not the deploy step. Vite bakes them at compile time; they are not available at runtime.
.terraform.lock.hcl must be committed — do NOT add it to .gitignore. If CI fails with provider version errors, check that this file is tracked in git.
Completion
Failure Handling
| Situation | Action |
|---|
| Required input not provided | Request the missing information before proceeding |
| Gathered input is ambiguous | Flag the ambiguity and ask for clarification |
| Subagent invocation fails | Report the failure with context; do not silently retry |
| Output artifact already exists | Confirm with user before overwriting |
| Workflows already exist and differ | Show a diff summary; ask whether to overwrite or merge |
| Infrastructure spec not available | Use defaults for all VM and Terraform references; note which values need to be updated post-generation |
| Configuration value ambiguous | Apply the documented default; annotate the generated file with a # TODO: verify comment |