Copilot Instructions for CI/CD and Shell Scripts
DevOps conventions for Copilot: strict-mode bash with quoted expansions, pipelines that cache correctly and pin action versions, and secrets that never touch logs or env dumps.
Use when: Use in repos with GitHub Actions workflows and shell scripts when you want Copilot to write safe bash and CI config instead of unquoted variables and floating action tags.
The skill file
GitHub Copilot · .github/copilot-instructions.md.github/copilot-instructions.md3355 chars
# CI/CD and Shell Instructions
## Bash safety
- Every script starts with `#!/usr/bin/env bash` and `set -euo pipefail`. Scripts that intentionally tolerate failures disable the flag for one command (`cmd || true`), never globally.
- Quote every expansion: `"$var"`, `"${arr[@]}"`, `"$(cmd)"`. An unquoted variable is a bug even if it "works".
- Use `[[ ... ]]` for conditionals, never `[ ... ]`. Compare with `==`, check emptiness with `-z`/`-n`.
- Iterate files with globs or `find -print0 | while IFS= read -r -d ''`, never by parsing `ls` output.
- `mktemp` for all temp files, with a `trap 'rm -rf "$tmpdir"' EXIT` cleanup. Never write to `/tmp/fixed-name`.
- All scripts must pass `shellcheck` with zero warnings; add a `# shellcheck disable=SCxxxx` with a reason comment only when genuinely unavoidable.
- Scripts that take arguments validate them up front and print usage to stderr with exit code 2 when wrong.
## Pipeline structure and caching
- Pin third-party GitHub Actions to a full commit SHA with a version comment: `uses: actions/checkout@<sha> # v4.2.2`. Floating tags (`@v4`, `@main`) are forbidden for non-GitHub-owned actions.
- Every job sets `timeout-minutes`; nothing relies on the 6-hour default.
- Cache keys hash the lockfile, not the manifest: `key: deps-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }}` plus a `restore-keys` prefix fallback. Never cache `node_modules` directly when the package manager has a cache dir — cache that instead.
- Workflows triggered by `pull_request` get `concurrency` with `cancel-in-progress: true` keyed on the ref, so stale runs stop.
- Set least-privilege `permissions:` at workflow level (start from `contents: read`) and widen per-job only where needed.
- Build artifacts pass between jobs via `actions/upload-artifact`/`download-artifact`; jobs never rebuild what a previous job already produced.
## Secret handling
- Secrets are read only from `${{ secrets.* }}` or the deployment platform's secret store — never from committed files, and never with default values in shell (`: "${API_KEY:=...}"` with a real key is an incident).
- Never `echo`, `printf`, or log a variable that holds a secret; never run `env`, `printenv`, or `set -x` in a step that has secrets in scope. If tracing is needed, mask first with `echo "::add-mask::$value"`.
- Pass secrets to processes via environment or stdin, never as command-line arguments (argv is visible in process lists and CI logs).
- `.env` files are gitignored; a committed `.env.example` lists every required variable with placeholder values only.
- Prefer OIDC federation for cloud auth over long-lived keys stored as repo secrets; when a static key is unavoidable, note its rotation owner in a comment next to its first use.
## Workflow hygiene
- Validate pipeline changes with `actionlint` and scripts with `shellcheck`/`shfmt -d` before suggesting a commit.
- One workflow file per concern (ci, release, deploy) — do not grow a single yaml past ~200 lines; extract reusable workflows or composite actions.
- Steps that run shell logic longer than ~10 lines move into a script under `scripts/` so they can be linted and run locally; workflow `run:` blocks stay thin.
- Deploy jobs gate on `environment:` with required reviewers for production; never deploy to prod from a workflow that also runs on pull requests from forks.
Install
drop into your repoSave this to your project or home directory so GitHub Copilot can load it.
path./.github/copilot-instructions.md
Discussion
What people are saying
Forks
0 forks of this skillLoading forks…