[ PROMPT_NODE_24823 ]
Workflow Templates
[ SKILL_DOCUMENTATION ]
# Workflow Templates
Complete, production-ready templates for the most common GitHub Actions use cases. Use these as starting points and adapt to the user's project.
---
## Node.js CI
```yaml
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
lint:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- run: npm ci
- run: npm run lint
- run: npm run typecheck
if: hashFiles('tsconfig.json') != ''
test:
runs-on: ubuntu-latest
timeout-minutes: 15
strategy:
matrix:
node-version: [18, 20, 22]
fail-fast: false
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- run: npm ci
- run: npm test
- uses: codecov/codecov-action@v4
if: matrix.node-version == 20
with:
token: ${{ secrets.CODECOV_TOKEN }}
```
---
## Python CI
```yaml
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
lint:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
cache: 'pip'
- run: pip install ruff mypy
- run: ruff check .
- run: ruff format --check .
test:
runs-on: ubuntu-latest
timeout-minutes: 15
strategy:
matrix:
python-version: ['3.10', '3.11', '3.12']
fail-fast: false
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'
- run: pip install -e ".[dev]"
- run: pytest --cov --cov-report=xml
- uses: codecov/codecov-action@v4
if: matrix.python-version == '3.12'
with:
token: ${{ secrets.CODECOV_TOKEN }}
```
---
## Go CI
```yaml
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
lint:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '1.22'
- uses: golangci/golangci-lint-action@v6
with:
version: latest
test:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '1.22'
- run: go test -race -coverprofile=coverage.out ./...
- uses: codecov/codecov-action@v4
with:
files: coverage.out
token: ${{ secrets.CODECOV_TOKEN }}
build:
runs-on: ubuntu-latest
timeout-minutes: 10
needs: [lint, test]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '1.22'
- run: go build ./...
```
---
## Rust CI
```yaml
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
check:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy
- uses: actions/cache@v4
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
target/
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
- run: cargo fmt --all -- --check
- run: cargo clippy --all-targets -- -D warnings
test:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: actions/cache@v4
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
target/
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
- run: cargo test --all
```
---
## Docker Build & Push
```yaml
name: Docker
on:
push:
branches: [main]
tags: ['v*']
pull_request:
branches: [main]
permissions:
contents: read
packages: write
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- uses: docker/setup-qemu-action@v3
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
if: github.event_name != 'pull_request'
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/metadata-action@v5
id: meta
with:
images: ghcr.io/${{ github.repository }}
tags: |
type=ref,event=branch
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=sha
- uses: docker/build-push-action@v6
with:
context: .
platforms: linux/amd64,linux/arm64
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
```
---
## Deploy to Vercel
```yaml
name: Deploy
on:
push:
branches: [main]
pull_request:
branches: [main]
permissions:
contents: read
pull-requests: write
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
deploy:
runs-on: ubuntu-latest
timeout-minutes: 15
environment:
name: ${{ github.ref == 'refs/heads/main' && 'production' || 'preview' }}
url: ${{ steps.deploy.outputs.url }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- run: npm ci
- run: npm test
- name: Deploy to Vercel
id: deploy
run: |
npm i -g vercel
if [ "${{ github.ref }}" = "refs/heads/main" ]; then
url=$(vercel --prod --token=${{ secrets.VERCEL_TOKEN }} 2>&1 | tail -1)
else
url=$(vercel --token=${{ secrets.VERCEL_TOKEN }} 2>&1 | tail -1)
fi
echo "url=$url" >> $GITHUB_OUTPUT
env:
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
- name: Comment PR with preview URL
if: github.event_name == 'pull_request'
uses: actions/github-script@v7
with:
script: |
github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: `Preview deployed: ${{ steps.deploy.outputs.url }}`
})
```
---
## Deploy to AWS (ECS)
```yaml
name: Deploy to AWS
on:
push:
branches: [main]
permissions:
id-token: write
contents: read
concurrency:
group: deploy-production
cancel-in-progress: false
jobs:
deploy:
runs-on: ubuntu-latest
timeout-minutes: 20
environment: production
steps:
- uses: actions/checkout@v4
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: us-east-1
- uses: aws-actions/amazon-ecr-login@v2
id: ecr
- name: Build and push image
run: |
IMAGE=${{ steps.ecr.outputs.registry }}/${{ github.repository }}:${{ github.sha }}
docker build -t $IMAGE .
docker push $IMAGE
echo "image=$IMAGE" >> $GITHUB_OUTPUT
id: build
- name: Deploy to ECS
run: |
aws ecs update-service
--cluster production
--service app
--force-new-deployment
```
---
## npm Publish on Release
```yaml
name: Publish
on:
push:
tags: ['v*']
permissions:
contents: write
id-token: write
jobs:
publish:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
registry-url: 'https://registry.npmjs.org'
- run: npm ci
- run: npm test
- run: npm publish --provenance --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- uses: softprops/action-gh-release@v2
with:
generate_release_notes: true
```
---
## PyPI Publish
```yaml
name: Publish to PyPI
on:
push:
tags: ['v*']
permissions:
contents: write
id-token: write
jobs:
publish:
runs-on: ubuntu-latest
timeout-minutes: 15
environment: pypi
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- run: pip install build
- run: python -m build
- uses: pypa/gh-action-pypi-publish@release/v1
- uses: softprops/action-gh-release@v2
with:
generate_release_notes: true
```
---
## Scheduled Dependency Updates Check
```yaml
name: Dependency Review
on:
pull_request:
branches: [main]
schedule:
- cron: '0 9 * * 1' # Monday 9am UTC
workflow_dispatch:
permissions:
contents: read
pull-requests: write
jobs:
audit:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: actions/dependency-review-action@v4
if: github.event_name == 'pull_request'
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- run: npm ci
- run: npm audit --audit-level=high
continue-on-error: ${{ github.event_name == 'schedule' }}
```
---
## Monorepo CI (with path filters)
```yaml
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
changes:
runs-on: ubuntu-latest
timeout-minutes: 5
outputs:
frontend: ${{ steps.filter.outputs.frontend }}
backend: ${{ steps.filter.outputs.backend }}
infra: ${{ steps.filter.outputs.infra }}
steps:
- uses: actions/checkout@v4
- uses: dorny/paths-filter@v3
id: filter
with:
filters: |
frontend:
- 'packages/frontend/**'
backend:
- 'packages/backend/**'
infra:
- 'terraform/**'
- 'docker/**'
frontend:
needs: changes
if: needs.changes.outputs.frontend == 'true'
runs-on: ubuntu-latest
timeout-minutes: 15
defaults:
run:
working-directory: packages/frontend
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
cache-dependency-path: packages/frontend/package-lock.json
- run: npm ci
- run: npm run lint
- run: npm test
- run: npm run build
backend:
needs: changes
if: needs.changes.outputs.backend == 'true'
runs-on: ubuntu-latest
timeout-minutes: 15
defaults:
run:
working-directory: packages/backend
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
cache-dependency-path: packages/backend/package-lock.json
- run: npm ci
- run: npm test
```
---
## Release Please (Automated Versioning)
```yaml
name: Release
on:
push:
branches: [main]
permissions:
contents: write
pull-requests: write
jobs:
release:
runs-on: ubuntu-latest
timeout-minutes: 10
outputs:
release_created: ${{ steps.release.outputs.release_created }}
tag_name: ${{ steps.release.outputs.tag_name }}
steps:
- uses: googleapis/release-please-action@v4
id: release
with:
release-type: node # or python, go, etc.
publish:
needs: release
if: needs.release.outputs.release_created
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
registry-url: 'https://registry.npmjs.org'
- run: npm ci
- run: npm publish --provenance --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
```
---
## Cloudflare Workers Deploy
```yaml
name: Deploy Worker
on:
push:
branches: [main]
pull_request:
branches: [main]
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
deploy:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- run: npm ci
- name: Deploy
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
command: ${{ github.ref == 'refs/heads/main' && 'deploy' || 'deploy --dry-run' }}
```
---
## E2E Tests with Playwright
```yaml
name: E2E Tests
on:
push:
branches: [main]
pull_request:
branches: [main]
schedule:
- cron: '0 6 * * *' # Daily at 6am UTC
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
e2e:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- run: npm ci
- run: npx playwright install --with-deps chromium
- run: npm run build
- run: npx playwright test
env:
CI: true
- uses: actions/upload-artifact@v4
if: failure()
with:
name: playwright-report
path: playwright-report/
retention-days: 7
```
---
## Security Scanning (CodeQL + Trivy)
```yaml
name: Security
on:
push:
branches: [main]
pull_request:
branches: [main]
schedule:
- cron: '0 0 * * 1' # Weekly on Monday
permissions:
contents: read
security-events: write
jobs:
codeql:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- uses: github/codeql-action/init@v3
with:
languages: javascript-typescript # or python, go, java, etc.
- uses: github/codeql-action/analyze@v3
trivy:
runs-on: ubuntu-latest
timeout-minutes: 10
if: hashFiles('Dockerfile') != ''
steps:
- uses: actions/checkout@v4
- uses: aquasecurity/trivy-action@master
with:
scan-type: 'fs'
format: 'sarif'
output: 'trivy-results.sarif'
severity: 'CRITICAL,HIGH'
- uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: 'trivy-results.sarif'
```
---
## Terraform Plan & Apply
```yaml
name: Infrastructure
on:
push:
branches: [main]
paths: ['terraform/**']
pull_request:
branches: [main]
paths: ['terraform/**']
permissions:
contents: read
pull-requests: write
id-token: write
concurrency:
group: terraform-${{ github.ref }}
cancel-in-progress: false
jobs:
plan:
runs-on: ubuntu-latest
timeout-minutes: 15
defaults:
run:
working-directory: terraform
steps:
- uses: actions/checkout@v4
- uses: hashicorp/setup-terraform@v3
with:
terraform_version: '1.7'
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: us-east-1
- run: terraform init
- run: terraform validate
- run: terraform plan -out=tfplan
id: plan
- uses: actions/github-script@v7
if: github.event_name == 'pull_request'
with:
script: |
const plan = `${{ steps.plan.outputs.stdout }}`;
github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: `### Terraform Plann```n${plan}n````
})
apply:
needs: plan
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
timeout-minutes: 30
environment: production
defaults:
run:
working-directory: terraform
steps:
- uses: actions/checkout@v4
- uses: hashicorp/setup-terraform@v3
with:
terraform_version: '1.7'
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: us-east-1
- run: terraform init
- run: terraform apply -auto-approve
```
Source: claude-code-templates (MIT). See About Us for full credits.