Claude
Skill
github-actions
CI/CD workflow patterns, testing pipelines, deployment automation. Use when setting up GitHub Actions workflows for testing, building, linting, or deploying a fullstack app.
Virus-scanned
Reviewed automatically before listing.
Download
sabahattink-antigravity-fullstack-hq-skills_github-actions-1acbfa7.zip · 2 KB
Install
skills CLI
npx skills add https://github.com/sabahattink/antigravity-fullstack-hq/tree/main/skills/github-actions
Claude Code
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install sabahattink-antigravity-fullstack-hq@llmmart
Git
git clone https://github.com/sabahattink/antigravity-fullstack-hq.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole sabahattink/antigravity-fullstack-hq collection as a plugin from our marketplace. Git is the plain clone.
Skill manifest
GitHub Actions
Full CI Pipeline
# .github/workflows/ci.yml
name: CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true # Cancel stale runs on new push
jobs:
# ─── Lint & Type Check ─────────────────────────────────────
lint:
name: Lint & Type Check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with: { version: 9 }
- uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
- run: pnpm install --frozen-lockfile
- name: Type check (API)
run: pnpm --filter api run type-check
- name: Type check (Web)
run: pnpm --filter web run type-check
- name: Lint
run: pnpm run lint
# ─── Unit Tests ────────────────────────────────────────────
test-unit:
name: Unit Tests
runs-on: ubuntu-latest
needs: lint
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with: { version: 9 }
- uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
- run: pnpm install --frozen-lockfile
- name: Run unit tests
run: pnpm --filter api run test:cov
- name: Upload coverage
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./apps/api/coverage/lcov.info
# ─── Integration Tests ─────────────────────────────────────
test-integration:
name: Integration Tests
runs-on: ubuntu-latest
needs: lint
services:
postgres:
image: postgres:16-alpine
env:
POSTGRES_DB: test_db
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
ports: ["5432:5432"]
options: >-
--health-cmd pg_isready
--health-interval 5s
--health-timeout 5s
--health-retries 5
redis:
image: redis:7-alpine
ports: ["6379:6379"]
options: >-
--health-cmd "redis-cli ping"
--health-interval 5s
--health-retries 5
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with: { version: 9 }
- uses: actions/setup-node@v4
with: { node-version: 22, cache: pnpm }
- run: pnpm install --frozen-lockfile
- name: Run migrations
run: pnpm --filter api run migration:run
env:
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/test_db
- name: Run integration tests
run: pnpm --filter api run test:integration
env:
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/test_db
REDIS_URL: redis://localhost:6379
JWT_SECRET: test-secret-32-chars-minimum-length
# ─── E2E Tests ─────────────────────────────────────────────
test-e2e:
name: E2E Tests (Playwright)
runs-on: ubuntu-latest
needs: [test-unit, test-integration]
timeout-minutes: 20
services:
postgres:
image: postgres:16-alpine
env: { POSTGRES_DB: e2e_db, POSTGRES_USER: postgres, POSTGRES_PASSWORD: postgres }
ports: ["5432:5432"]
options: --health-cmd pg_isready --health-interval 5s --health-retries 5
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with: { version: 9 }
- uses: actions/setup-node@v4
with: { node-version: 22, cache: pnpm }
- run: pnpm install --frozen-lockfile
- name: Cache Playwright browsers
uses: actions/cache@v4
id: pw-cache
with:
path: ~/.cache/ms-playwright
key: playwright-${{ hashFiles('**/package.json') }}
- name: Install Playwright browsers
if: steps.pw-cache.outputs.cache-hit != 'true'
run: pnpm exec playwright install --with-deps chromium
- name: Build apps
env:
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/e2e_db
run: pnpm run build
- name: Run E2E tests
run: pnpm run test:e2e
env:
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/e2e_db
PLAYWRIGHT_BASE_URL: http://localhost:3000
- name: Upload Playwright report
if: failure()
uses: actions/upload-artifact@v4
with:
name: playwright-report
path: playwright-report/
retention-days: 7
Deploy Workflow
# .github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [main]
workflow_dispatch:
inputs:
environment:
description: Target environment
required: true
default: staging
type: choice
options: [staging, production]
jobs:
deploy-backend:
name: Deploy API to Railway
runs-on: ubuntu-latest
environment: ${{ github.event.inputs.environment || 'staging' }}
steps:
- uses: actions/checkout@v4
- name: Install Railway CLI
run: npm i -g @railway/cli
- name: Deploy to Railway
run: railway up --service api --detach
env:
RAILWAY_TOKEN: ${{ secrets.RAILWAY_TOKEN }}
- name: Wait for deployment health
run: |
for i in {1..12}; do
STATUS=$(curl -s -o /dev/null -w "%{http_code}" ${{ vars.API_URL }}/health/liveness)
if [ "$STATUS" == "200" ]; then
echo "API is healthy"
exit 0
fi
echo "Attempt $i: status=$STATUS, waiting..."
sleep 10
done
echo "Health check failed after 2 minutes"
exit 1
deploy-frontend:
name: Deploy Web to Vercel
runs-on: ubuntu-latest
needs: deploy-backend
environment: ${{ github.event.inputs.environment || 'staging' }}
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with: { version: 9 }
- uses: actions/setup-node@v4
with: { node-version: 22, cache: pnpm }
- run: pnpm install --frozen-lockfile
- name: Deploy to Vercel
run: |
pnpm dlx vercel \
--token=${{ secrets.VERCEL_TOKEN }} \
--prod \
${{ github.event.inputs.environment == 'production' && '--prod' || '' }}
env:
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
Reusable Workflow
# .github/workflows/_setup-node.yml
name: Setup Node (Reusable)
on:
workflow_call:
inputs:
node-version:
type: string
default: '22'
jobs:
setup:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with: { version: 9 }
- uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}
cache: pnpm
- run: pnpm install --frozen-lockfile
# Usage:
# jobs:
# setup:
# uses: ./.github/workflows/_setup-node.yml
PR Checks & Branch Protection
# .github/workflows/pr-checks.yml
name: PR Checks
on:
pull_request:
types: [opened, synchronize, reopened, edited]
jobs:
pr-title:
name: Validate PR Title
runs-on: ubuntu-latest
steps:
- uses: amannn/action-semantic-pull-request@v5
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
types: [feat, fix, refactor, docs, test, chore, perf, ci]
requireScope: false
check-diff:
name: Check Changed Files
runs-on: ubuntu-latest
outputs:
api-changed: ${{ steps.changes.outputs.api }}
web-changed: ${{ steps.changes.outputs.web }}
steps:
- uses: actions/checkout@v4
- uses: dorny/paths-filter@v3
id: changes
with:
filters: |
api:
- 'apps/api/**'
- 'packages/**'
web:
- 'apps/web/**'
- 'packages/**'
Secrets Required
# Repository → Settings → Secrets and variables → Actions
RAILWAY_TOKEN — Railway API token for backend deploys
VERCEL_TOKEN — Vercel CLI token
VERCEL_ORG_ID — Vercel organization ID
VERCEL_PROJECT_ID — Vercel project ID
CODECOV_TOKEN — Codecov upload token
# Variables (non-secret)
API_URL — https://api.example.com (for health checks)
Caching Strategy
# Cache node_modules (pnpm store)
- uses: actions/cache@v4
with:
path: ~/.local/share/pnpm/store
key: pnpm-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: pnpm-${{ runner.os }}-
# Cache Next.js build
- uses: actions/cache@v4
with:
path: ${{ github.workspace }}/apps/web/.next/cache
key: nextjs-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}-${{ hashFiles('apps/web/**/*.ts', 'apps/web/**/*.tsx') }}
restore-keys: nextjs-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}-
Forbidden Patterns
- Never hardcode secrets as plain text in workflow files
- Never use
continue-on-error: trueto hide test failures - Never deploy without a prior successful test run
- Never use
actions/checkout@v2or older — use v4 - Never skip
--frozen-lockfile— it ensures reproducible installs - Never give workflows more permissions than needed (use
permissions: read-allas default) - Never run E2E tests before unit/integration pass — it wastes expensive minutes
Files (antigravity-fullstack-hq)
-
SKILL.md 9.9 KB
--- name: github-actions description: CI/CD workflow patterns, testing pipelines, deployment automation. Use when setting up GitHub Actions workflows for testing, building, linting, or deploying a fullstack app. --- # GitHub Actions ## Full CI Pipeline ```yaml # .github/workflows/ci.yml name: CI on: push: branches: [main, develop] pull_request: branches: [main, develop] concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true # Cancel stale runs on new push jobs: # ─── Lint & Type Check ───────────────────────────────────── lint: name: Lint & Type Check runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: pnpm/action-setup@v4 with: { version: 9 } - uses: actions/setup-node@v4 with: node-version: 22 cache: pnpm - run: pnpm install --frozen-lockfile - name: Type check (API) run: pnpm --filter api run type-check - name: Type check (Web) run: pnpm --filter web run type-check - name: Lint run: pnpm run lint # ─── Unit Tests ──────────────────────────────────────────── test-unit: name: Unit Tests runs-on: ubuntu-latest needs: lint steps: - uses: actions/checkout@v4 - uses: pnpm/action-setup@v4 with: { version: 9 } - uses: actions/setup-node@v4 with: node-version: 22 cache: pnpm - run: pnpm install --frozen-lockfile - name: Run unit tests run: pnpm --filter api run test:cov - name: Upload coverage uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} files: ./apps/api/coverage/lcov.info # ─── Integration Tests ───────────────────────────────────── test-integration: name: Integration Tests runs-on: ubuntu-latest needs: lint services: postgres: image: postgres:16-alpine env: POSTGRES_DB: test_db POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres ports: ["5432:5432"] options: >- --health-cmd pg_isready --health-interval 5s --health-timeout 5s --health-retries 5 redis: image: redis:7-alpine ports: ["6379:6379"] options: >- --health-cmd "redis-cli ping" --health-interval 5s --health-retries 5 steps: - uses: actions/checkout@v4 - uses: pnpm/action-setup@v4 with: { version: 9 } - uses: actions/setup-node@v4 with: { node-version: 22, cache: pnpm } - run: pnpm install --frozen-lockfile - name: Run migrations run: pnpm --filter api run migration:run env: DATABASE_URL: postgresql://postgres:postgres@localhost:5432/test_db - name: Run integration tests run: pnpm --filter api run test:integration env: DATABASE_URL: postgresql://postgres:postgres@localhost:5432/test_db REDIS_URL: redis://localhost:6379 JWT_SECRET: test-secret-32-chars-minimum-length # ─── E2E Tests ───────────────────────────────────────────── test-e2e: name: E2E Tests (Playwright) runs-on: ubuntu-latest needs: [test-unit, test-integration] timeout-minutes: 20 services: postgres: image: postgres:16-alpine env: { POSTGRES_DB: e2e_db, POSTGRES_USER: postgres, POSTGRES_PASSWORD: postgres } ports: ["5432:5432"] options: --health-cmd pg_isready --health-interval 5s --health-retries 5 steps: - uses: actions/checkout@v4 - uses: pnpm/action-setup@v4 with: { version: 9 } - uses: actions/setup-node@v4 with: { node-version: 22, cache: pnpm } - run: pnpm install --frozen-lockfile - name: Cache Playwright browsers uses: actions/cache@v4 id: pw-cache with: path: ~/.cache/ms-playwright key: playwright-${{ hashFiles('**/package.json') }} - name: Install Playwright browsers if: steps.pw-cache.outputs.cache-hit != 'true' run: pnpm exec playwright install --with-deps chromium - name: Build apps env: DATABASE_URL: postgresql://postgres:postgres@localhost:5432/e2e_db run: pnpm run build - name: Run E2E tests run: pnpm run test:e2e env: DATABASE_URL: postgresql://postgres:postgres@localhost:5432/e2e_db PLAYWRIGHT_BASE_URL: http://localhost:3000 - name: Upload Playwright report if: failure() uses: actions/upload-artifact@v4 with: name: playwright-report path: playwright-report/ retention-days: 7 ``` ## Deploy Workflow ```yaml # .github/workflows/deploy.yml name: Deploy on: push: branches: [main] workflow_dispatch: inputs: environment: description: Target environment required: true default: staging type: choice options: [staging, production] jobs: deploy-backend: name: Deploy API to Railway runs-on: ubuntu-latest environment: ${{ github.event.inputs.environment || 'staging' }} steps: - uses: actions/checkout@v4 - name: Install Railway CLI run: npm i -g @railway/cli - name: Deploy to Railway run: railway up --service api --detach env: RAILWAY_TOKEN: ${{ secrets.RAILWAY_TOKEN }} - name: Wait for deployment health run: | for i in {1..12}; do STATUS=$(curl -s -o /dev/null -w "%{http_code}" ${{ vars.API_URL }}/health/liveness) if [ "$STATUS" == "200" ]; then echo "API is healthy" exit 0 fi echo "Attempt $i: status=$STATUS, waiting..." sleep 10 done echo "Health check failed after 2 minutes" exit 1 deploy-frontend: name: Deploy Web to Vercel runs-on: ubuntu-latest needs: deploy-backend environment: ${{ github.event.inputs.environment || 'staging' }} steps: - uses: actions/checkout@v4 - uses: pnpm/action-setup@v4 with: { version: 9 } - uses: actions/setup-node@v4 with: { node-version: 22, cache: pnpm } - run: pnpm install --frozen-lockfile - name: Deploy to Vercel run: | pnpm dlx vercel \ --token=${{ secrets.VERCEL_TOKEN }} \ --prod \ ${{ github.event.inputs.environment == 'production' && '--prod' || '' }} env: VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }} ``` ## Reusable Workflow ```yaml # .github/workflows/_setup-node.yml name: Setup Node (Reusable) on: workflow_call: inputs: node-version: type: string default: '22' jobs: setup: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: pnpm/action-setup@v4 with: { version: 9 } - uses: actions/setup-node@v4 with: node-version: ${{ inputs.node-version }} cache: pnpm - run: pnpm install --frozen-lockfile # Usage: # jobs: # setup: # uses: ./.github/workflows/_setup-node.yml ``` ## PR Checks & Branch Protection ```yaml # .github/workflows/pr-checks.yml name: PR Checks on: pull_request: types: [opened, synchronize, reopened, edited] jobs: pr-title: name: Validate PR Title runs-on: ubuntu-latest steps: - uses: amannn/action-semantic-pull-request@v5 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: types: [feat, fix, refactor, docs, test, chore, perf, ci] requireScope: false check-diff: name: Check Changed Files runs-on: ubuntu-latest outputs: api-changed: ${{ steps.changes.outputs.api }} web-changed: ${{ steps.changes.outputs.web }} steps: - uses: actions/checkout@v4 - uses: dorny/paths-filter@v3 id: changes with: filters: | api: - 'apps/api/**' - 'packages/**' web: - 'apps/web/**' - 'packages/**' ``` ## Secrets Required ``` # Repository → Settings → Secrets and variables → Actions RAILWAY_TOKEN — Railway API token for backend deploys VERCEL_TOKEN — Vercel CLI token VERCEL_ORG_ID — Vercel organization ID VERCEL_PROJECT_ID — Vercel project ID CODECOV_TOKEN — Codecov upload token # Variables (non-secret) API_URL — https://api.example.com (for health checks) ``` ## Caching Strategy ```yaml # Cache node_modules (pnpm store) - uses: actions/cache@v4 with: path: ~/.local/share/pnpm/store key: pnpm-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }} restore-keys: pnpm-${{ runner.os }}- # Cache Next.js build - uses: actions/cache@v4 with: path: ${{ github.workspace }}/apps/web/.next/cache key: nextjs-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}-${{ hashFiles('apps/web/**/*.ts', 'apps/web/**/*.tsx') }} restore-keys: nextjs-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}- ``` ## Forbidden Patterns - Never hardcode secrets as plain text in workflow files - Never use `continue-on-error: true` to hide test failures - Never deploy without a prior successful test run - Never use `actions/checkout@v2` or older — use v4 - Never skip `--frozen-lockfile` — it ensures reproducible installs - Never give workflows more permissions than needed (use `permissions: read-all` as default) - Never run E2E tests before unit/integration pass — it wastes expensive minutes
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.