Skip to main content

devops-deploy-activate — Reference Material

Extracted reference tables, code templates, and patterns for the devops-deploy-activate skill. This file is loaded by devops-deploy-activate SKILL.md via inline pointers. Do not duplicate content back into SKILL.md.


GitHub Actions SHA Pinning

Resolution Process

Resolve mutable tags to immutable SHA digests for supply chain security:

Step 1: Parse action reference from workflow YAML:

# Before
- uses: actions/checkout@v4

Step 2: Resolve tag to SHA using GitHub API:

# Get the commit SHA for a tag
gh api repos/actions/checkout/git/ref/tags/v4 --jq '.object.sha'

# For tags pointing to tag objects (not commits), dereference:
gh api repos/actions/checkout/git/tags/{tag_sha} --jq '.object.sha'

Step 3: Replace in workflow:

# After
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4

Common Actions to Pin

ActionCurrent TagPurpose
actions/checkoutv4Repository checkout
actions/setup-nodev4Node.js environment
actions/cachev4Dependency caching
docker/setup-buildx-actionv3Docker Buildx
docker/build-push-actionv5Docker build + push
docker/login-actionv3Container registry auth
github/codeql-action/upload-sarifv3SARIF security upload
dorny/paths-filterv3Monorepo path filtering

Handling Organization Actions

For org-scoped actions (e.g., my-org/my-action@v1):

gh api repos/my-org/my-action/git/ref/tags/v1 --jq '.object.sha'

If the tag does not exist (uses branch reference):

gh api repos/my-org/my-action/git/ref/heads/main --jq '.object.sha'

Platform CLI Provisioning

Railway

Prerequisites: railway CLI installed, authenticated via railway login

Setup sequence:

# 1. Create project
railway init

# 2. Link to repository
railway link

# 3. Set environment variables
railway variables set DATABASE_URL="postgresql://..."
railway variables set JWT_SECRET="..."
railway variables set NODE_ENV="production"

# 4. Configure deployment
railway up

# 5. Verify
railway status
railway logs --tail 50

Error handling:

Exit CodeMeaningRecovery
0SuccessContinue
1Auth errorRe-run railway login
2Project not foundRe-run railway init

Vercel

Prerequisites: vercel CLI installed, authenticated via vercel login

Setup sequence:

# 1. Link to project
vercel link

# 2. Set environment variables (per environment)
vercel env add DATABASE_URL production
vercel env add NEXT_PUBLIC_API_URL production

# 3. Configure project settings
vercel project settings --build-command "pnpm build"
vercel project settings --output-directory ".next"

# 4. Deploy preview
vercel

# 5. Deploy production
vercel --prod

Preview deployments: Automatic on PR creation when GitHub integration is connected.

Fly.io

Prerequisites: fly CLI installed, authenticated via fly auth login

Setup sequence:

# 1. Launch app (creates fly.toml)
fly launch --no-deploy

# 2. Set secrets
fly secrets set DATABASE_URL="postgresql://..."
fly secrets set JWT_SECRET="..."

# 3. Create PostgreSQL (if needed)
fly postgres create --name myapp-db
fly postgres attach myapp-db

# 4. Deploy
fly deploy

# 5. Verify
fly status
fly logs

Secret Management Patterns

Per-Platform Secret Requirements

PlatformSecrets NeededWhere to Set
GitHub ActionsDOCKER_USERNAME, DOCKER_PASSWORD (or GHCR token)gh secret set
VercelVERCEL_TOKEN, VERCEL_ORG_ID, VERCEL_PROJECT_IDgh secret set
RailwayRAILWAY_TOKENgh secret set
Fly.ioFLY_API_TOKENgh secret set
TurborepoTURBO_TOKEN, TURBO_TEAMgh secret set

Secret Naming Conventions

PatternExampleUse
{SERVICE}_{TYPE}DATABASE_URLService connection strings
{PLATFORM}_TOKENVERCEL_TOKENPlatform API tokens
{TOOL}_{PURPOSE}TURBO_TOKENTool-specific credentials

GitHub Environments

For environment-scoped secrets:

# Set secret for specific environment
gh secret set DATABASE_URL --env production
gh secret set DATABASE_URL --env staging

Repository Variable Patterns

Secrets vs Variables

PropertyGitHub SecretsGitHub Variables
EncryptedYes — encrypted at restNo — stored as plaintext
CLI commandgh secret set {NAME}gh variable set {NAME} --body "{value}"
Workflow access${{ secrets.NAME }}${{ vars.NAME }}
Visible in logsMasked automaticallyVisible in logs
Use casesAPI keys, tokens, passwords, connection stringsService URLs, feature flags, environment names
Environment-scopedgh secret set --env productiongh variable set --env production

Rule of thumb: If the value appears in logs or public config, use a variable. If leaking it would be a security incident, use a secret.

Common Repository Variables

VariableDescriptionSource
BACKEND_URLBackend service production URLPlatform CLI output after provisioning
FRONTEND_URLFrontend production URLPlatform CLI output after provisioning
BACKEND_STAGING_URLBackend staging URLPlatform CLI output (staging environment)
FRONTEND_STAGING_URLFrontend staging URLPlatform CLI output (staging environment)
API_VERSIONCurrent API version prefixProject config (e.g., /api/v1)
HEALTH_CHECK_PATHHealth endpoint pathBackend scaffold (e.g., /health)

URL Capture Commands

After platform provisioning, capture service URLs for repository variables:

Railway:

# Get the public URL for the deployed service
railway status --json | jq -r '.url'
# Or from domain list
railway domain

Vercel:

# Get the production URL
vercel inspect --json | jq -r '.alias[0]'
# Or from project info
vercel project ls

Fly.io:

# Get the app URL
fly status --json | jq -r '.Hostname'
# Format: {app-name}.fly.dev

Workflow Usage Example

# In health-check workflow
- name: Health Check
run: |
curl --fail --max-time 10 "${{ vars.BACKEND_URL }}/health"
curl --fail --max-time 10 "${{ vars.FRONTEND_URL }}"

Environment-Scoped Variables

# Set different URLs per environment
gh variable set BACKEND_URL --env staging --body "https://api-staging.example.com"
gh variable set BACKEND_URL --env production --body "https://api.example.com"

Supply Chain Hardening

.npmrc Security Defaults

Verify these settings exist in .npmrc:

save-exact=true        # Pin exact versions
strict-ssl=true # Enforce SSL for registry
ignore-scripts=true # Prevent install scripts (enable per-package)
audit=true # Auto-audit on install

Node.js Runtime Security Flags

Verify Dockerfile CMD includes security flags:

CMD ["node", "--disable-proto=delete", "--no-experimental-fetch", "dist/server.js"]

SLSA Framework Compliance

LevelRequirementImplementation
SLSA 1Build process documentedGitHub Actions workflow
SLSA 2Build service + signed provenanceGitHub Actions + npm provenance
SLSA 3Hardened build platformSHA-pinned actions + OIDC

Enable npm provenance attestations:

npm publish --provenance
# or in package.json:
# "publishConfig": { "provenance": true }

SARIF CI Gate Configuration

Severity/Confidence Matrix

SeverityHigh ConfidenceMedium ConfidenceLow Confidence
CriticalBlockBlockWarn
HighBlockWarnInfo
MediumWarnInfoInfo
LowInfoInfo

Baseline Diffing

Only fail on NEW findings (not pre-existing):

# In CI workflow
- name: Upload SARIF
uses: github/codeql-action/upload-sarif@{sha}
with:
sarif_file: results.sarif
category: security

# Gate: compare against baseline
- name: Security Gate
run: |
# Compare current SARIF against baseline
# Fail only on new critical/high findings
# 14-day grace period for pre-existing findings

Caching Hierarchy

Ordered from most specific to most general:

LayerToolKey PatternRestore Keys
1pnpm storepnpm-store-{os}-{hash(pnpm-lock.yaml)}pnpm-store-{os}-
2Next.jsnextjs-{os}-{hash(pnpm-lock.yaml)}nextjs-{os}-
3Dockertype=gha,scope={branch}type=gha
4TurborepoRemote cache via TURBO_TOKENN/A (remote)

pnpm Store Cache (GitHub Actions)

- uses: actions/setup-node@{sha} # v4
with:
node-version: '22'
cache: 'pnpm'

Next.js Build Cache

- uses: actions/cache@{sha} # v4
with:
path: ${{ github.workspace }}/apps/web/.next/cache
key: nextjs-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: nextjs-${{ runner.os }}-

Docker Layer Cache

- uses: docker/build-push-action@{sha} # v5
with:
cache-from: type=gha
cache-to: type=gha,mode=max

OIDC Federation Setup

GitHub → AWS

# 1. Create OIDC provider in AWS
aws iam create-open-id-connect-provider \
--url https://token.actions.githubusercontent.com \
--thumbprint-list "6938fd4d98bab03faadb97b34396831e3780aea1" \
--client-id-list "sts.amazonaws.com"

# 2. Create IAM role with trust policy
# Trust policy allows GitHub Actions from specific repo

GitHub → GCP

# 1. Create Workload Identity Pool
gcloud iam workload-identity-pools create github-pool \
--location="global"

# 2. Create Provider
gcloud iam workload-identity-pools providers create-oidc github-provider \
--location="global" \
--workload-identity-pool="github-pool" \
--attribute-mapping="google.subject=assertion.sub" \
--issuer-uri="https://token.actions.githubusercontent.com"

GitHub → Azure

# 1. Create app registration with federated credential
az ad app create --display-name "GitHub Actions"
az ad app federated-credential create --id {app-id} \
--parameters '{"issuer":"https://token.actions.githubusercontent.com",...}'

Workflow usage (all providers):

permissions:
id-token: write # Required for OIDC
contents: read

steps:
- uses: aws-actions/configure-aws-credentials@{sha}
with:
role-to-assume: arn:aws:iam::123456789:role/github-actions
aws-region: us-east-1