A unified, production-ready GitHub Action for SST (Serverless Stack) operations: deploy, diff, remove, and stage. This action consolidates functionality from multiple composite actions into a single, maintainable, and distributable solution.
- π Multi-Operation Support - Deploy, diff, remove, and stage operations in one action
- π€ Automatic Stage Inference - Automatically compute stage names from Git context (branches, PRs)
- π Automated PR Comments - Rich markdown comments with deployment status and changes
- π Infrastructure Diff - See planned changes before deployment
- π§Ή Resource Cleanup - Automated removal of staging environments
- π― Stage Calculation - Manual stage name computation from Git branches (when needed)
- βοΈ Configurable Runtime - Support for Bun, npm, pnpm, Yarn, or direct SST CLI
- π GitHub Integration - Workflow summaries, artifacts, and status reporting
name: Deploy
on:
push:
pull_request:
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: kodehort/sst-ops-action@v1
with:
operation: deploy
# Stage automatically computed from branch/PR name
token: ${{ secrets.GITHUB_TOKEN }}name: Deploy to Staging
on:
push:
branches: [develop]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: kodehort/sst-ops-action@v1
with:
operation: deploy
stage: staging
token: ${{ secrets.GITHUB_TOKEN }}name: Infrastructure Diff
on:
pull_request:
types: [opened, synchronize]
jobs:
diff:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: kodehort/sst-ops-action@v1
with:
operation: diff
# Stage automatically computed from PR branch name
token: ${{ secrets.GITHUB_TOKEN }}
comment-mode: alwaysname: Cleanup Resources
on:
pull_request:
types: [closed]
jobs:
cleanup:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: kodehort/sst-ops-action@v1
with:
operation: remove
stage: pr-${{ github.event.number }}
token: ${{ secrets.GITHUB_TOKEN }}name: Auto-Deploy
on:
push:
pull_request:
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# Stage is automatically computed from Git context
- name: Deploy
uses: kodehort/sst-ops-action@v1
with:
operation: deploy
# No stage input - automatically computed from branch/PR
token: ${{ secrets.GITHUB_TOKEN }}name: Smart Deploy
on:
push:
pull_request:
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# Calculate stage name from current branch/PR (alternative approach)
- name: Calculate Stage
id: stage
uses: kodehort/sst-ops-action@v1
with:
operation: stage
token: ${{ secrets.GITHUB_TOKEN }}
# Use calculated stage for deployment
- name: Deploy
uses: kodehort/sst-ops-action@v1
with:
operation: deploy
stage: ${{ steps.stage.outputs.computed_stage }}
token: ${{ secrets.GITHUB_TOKEN }}Each operation type has different input requirements and behavior patterns. Choose the right operation for your use case:
Purpose: Deploy infrastructure to AWS
Stage: Optional - auto-computed from Git context if not provided
Token: Required for infrastructure access and PR comments
# Auto-compute stage from branch/PR name
- name: Deploy (Auto Stage)
uses: kodehort/sst-ops-action@v1
with:
operation: deploy
token: ${{ secrets.GITHUB_TOKEN }}
# Explicit stage name
- name: Deploy to Production
uses: kodehort/sst-ops-action@v1
with:
operation: deploy
stage: production
token: ${{ secrets.GITHUB_TOKEN }}
comment-mode: always
runner: npmPurpose: Preview infrastructure changes without deploying
Stage: Required - need target stage to compare against
Token: Required for PR comments
# Compare against existing stage
- name: Preview Infrastructure Changes
uses: kodehort/sst-ops-action@v1
with:
operation: diff
stage: production # Required - what to compare against
token: ${{ secrets.GITHUB_TOKEN }}
comment-mode: always
# Compare branch changes against main
- name: PR Infrastructure Diff
uses: kodehort/sst-ops-action@v1
with:
operation: diff
stage: ${{ github.base_ref || 'main' }}
token: ${{ secrets.GITHUB_TOKEN }}Purpose: Delete deployed resources for cleanup
Stage: Required - explicit stage name for safety
Token: Required for authentication and confirmation
# Clean up PR resources
- name: Remove PR Resources
uses: kodehort/sst-ops-action@v1
with:
operation: remove
stage: pr-${{ github.event.number }} # Required for safety
token: ${{ secrets.GITHUB_TOKEN }}
comment-mode: on-success
# Production removal (requires extra confirmation)
- name: Remove Production (Dangerous!)
uses: kodehort/sst-ops-action@v1
with:
operation: remove
stage: production
token: ${{ secrets.GITHUB_TOKEN }}
env:
CONFIRM_PRODUCTION_REMOVE: truePurpose: Compute stage names from Git context (utility only)
Stage: Not applicable - computes stage as output
Token: Not required - no infrastructure access
# Basic stage computation
- name: Compute Stage Name
id: stage
uses: kodehort/sst-ops-action@v1
with:
operation: stage
# Custom stage name parameters
- name: Compute Stage with Custom Rules
id: stage
uses: kodehort/sst-ops-action@v1
with:
operation: stage
truncation-length: 20
prefix: feat-
# Use computed stage in later step
- name: Deploy with Computed Stage
uses: kodehort/sst-ops-action@v1
with:
operation: deploy
stage: ${{ steps.stage.outputs.computed_stage }}
token: ${{ secrets.GITHUB_TOKEN }}| Input | Description | Required | Default | Example |
|---|---|---|---|---|
operation |
SST operation to perform | No | deploy |
deploy, diff, remove, stage |
stage |
SST stage to operate on (auto-computed from Git context if not provided) | No | - | production, staging, pr-123 |
token |
GitHub token for authentication | Yes | - | ${{ secrets.GITHUB_TOKEN }} |
runner |
Package manager/runtime for SST commands | No | bun |
bun, npm, pnpm, yarn, sst |
comment-mode |
When to create PR comments | No | on-success |
always, on-success, on-failure, never |
fail-on-error |
Whether to fail the workflow on errors | No | true |
true, false |
max-output-size |
Maximum output size in bytes | No | 50000 |
100000 |
truncation-length |
Maximum length for computed stage names (stage operation only) | No | 26 |
15, 50 |
prefix |
Prefix for stage names starting with numbers (stage operation only) | No | pr- |
fix-, issue- |
| Output | Description | Type | Example |
|---|---|---|---|
success |
Whether the operation completed successfully | String | "true", "false" |
operation |
The operation that was performed | String | "deploy", "diff", "remove", "stage" |
stage |
The stage that was operated on | String | "production", "staging" |
app |
The SST app name | String | "my-app" |
resource_changes |
Number of resource changes | String | "5" |
urls |
Deployed URLs from outputs (deploy only) | JSON String | ["https://api.example.com"] |
diff_summary |
Diff summary (diff only) | String | "3 resources to create, 1 to update" |
computed_stage |
Computed stage name (stage only) | String | "feature-branch" |
ref |
Git reference (stage only) | String | "refs/heads/main" |
event_name |
GitHub event type (stage only) | String | "push", "pull_request" |
is_pull_request |
Whether from pull request (stage only) | String | "true", "false" |
completion_status |
Final operation status | String | "success", "failed", "partial" |
permalink |
SST Console permalink | String | "https://console.sst.dev/..." |
truncated |
Whether output was truncated | String | "false" |
Deploys your SST application to the specified stage.
- uses: kodehort/sst-ops-action@v1
with:
operation: deploy
stage: production
token: ${{ secrets.GITHUB_TOKEN }}
comment-mode: on-successFeatures:
- β Deploys all stack resources
- β Extracts URLs and outputs from deployment
- β Tracks resource changes (created, updated, unchanged)
- β Creates PR comments with deployment status
- β Generates workflow summaries
- β Uploads deployment artifacts
Use Cases:
- Production deployments on main branch
- Staging deployments on develop branch
- Preview environments for pull requests
- Scheduled deployments
Shows planned infrastructure changes without deploying.
- uses: kodehort/sst-ops-action@v1
with:
operation: diff
stage: staging
token: ${{ secrets.GITHUB_TOKEN }}
comment-mode: alwaysFeatures:
- β Shows planned resource changes
- β Categorizes changes by impact
- β Generates human-readable diff summary
- β Creates detailed PR comments
- β No actual infrastructure changes
Use Cases:
- Pull request reviews
- Pre-deployment validation
- Change impact assessment
- Infrastructure planning
When changes are detected, the action creates a structured PR comment showing the infrastructure diff:
π Example: Diff with Changes
Stage: staging
App: my-sst-app
Status: complete
| Property | Value |
|---|---|
| App | my-sst-app |
| Stage | staging |
| Total Changes | 5 |
| Summary | 5 changes planned |
| Console Link | View Diff |
+ AuthHandler (Function)
+ UserApi (Api)
* ProcessorFunction (Function)
* Database (Aurora)
- LegacyWebsite (StaticSite)View in SST Console to see detailed resource information and logs.
β Example: No Changes Detected
Stage: production
App: my-sst-app
Status: complete
| Property | Value |
|---|---|
| App | my-sst-app |
| Stage | production |
| Total Changes | 0 |
| Summary | No changes |
| Console Link | View Diff |
No infrastructure changes detected for this operation.
View in SST Console to see detailed resource information and logs.
The GitHub Actions summary provides a concise overview of the diff operation:
π Example: Action Summary with Changes
| Property | Value |
|---|---|
| App | my-sst-app |
| Stage | staging |
| Total Changes | 5 |
| Added Resources | 2 |
| Modified Resources | 2 |
| Removed Resources | 1 |
| Status | |
| Console Link | View Diff |
+ AuthHandler (Function)
+ UserApi (Api)
* ProcessorFunction (Function)
* Database (Aurora)
- LegacyWebsite (StaticSite)β Example: Action Summary with No Changes
| Property | Value |
|---|---|
| App | my-sst-app |
| Stage | production |
| Total Changes | 0 |
| Added Resources | 0 |
| Modified Resources | 0 |
| Removed Resources | 0 |
| Status | |
| Console Link | View Diff |
No infrastructure changes detected for this operation.
Removes all resources for the specified stage.
- uses: kodehort/sst-ops-action@v1
with:
operation: remove
stage: pr-${{ github.event.number }}
token: ${{ secrets.GITHUB_TOKEN }}Features:
- β Removes all stack resources
- β Tracks cleanup status
- β Handles partial cleanup scenarios
- β Auto-confirms removal in CI
Use Cases:
- PR environment cleanup
- Staging environment reset
- Cost optimization
- Resource cleanup
Calculates SST stage name from Git branch or pull request information.
- uses: kodehort/sst-ops-action@v1
with:
operation: stage
token: ${{ secrets.GITHUB_TOKEN }}
# With custom configuration
- uses: kodehort/sst-ops-action@v1
with:
operation: stage
token: ${{ secrets.GITHUB_TOKEN }}
truncation-length: 15
prefix: fix-Features:
- β Automatic stage name computation from Git references
- β Configurable truncation length (default: 26 characters)
- β
Configurable prefix (default:
pr-) - β Branch name sanitization and normalization
- β Pull request and push event support
- β Fallback stage handling for edge cases
- β No SST CLI dependency required
Use Cases:
- Dynamic stage name generation
- Consistent branch-to-stage mapping
- Multi-environment workflows
- Pull request preview environments
- Branch-based deployments
Automatic Stage Inference:
- When
stageinput is not provided, the action automatically computes it from Git context - Works with both push events (branch names) and pull request events (PR branch names)
- Uses the same computation rules as the manual stage operation
- Fallback to 'main' if no usable Git context is available
Stage Computation Rules:
- Removes path prefixes (
refs/heads/,feature/) - Converts to lowercase
- Replaces non-alphanumeric characters with hyphens
- Truncates to configurable length (default: 26 characters)
- Prefixes numeric branches with configurable prefix (default:
pr-) - Applies truncation including the prefix
- Removes leading/trailing hyphens after truncation
Examples:
| Branch | Event | Computed Stage | Auto/Manual | Notes |
|---|---|---|---|---|
main |
push | main |
Auto | Automatic inference |
feature/user-auth |
push | user-auth |
Auto | Prefix removed |
feature/new-api |
pull_request | new-api |
Auto | Same for PR events |
123-hotfix |
push | pr-123-hotfix |
Auto | Default prefix pr- |
123-hotfix |
push | fix-123-hotfix |
Manual | Custom prefix fix- |
refs/heads/develop |
push | develop |
Auto | Git prefix stripped |
very-long-branch-name |
push | very-long-branch-name-that |
Auto | Default truncation (26) |
very-long-branch-name |
push | very-long-branc |
Manual | Custom truncation (15) |
| N/A | N/A | production |
Manual | Explicit stage override |
name: Production Deploy
on:
push:
branches: [main]
workflow_dispatch:
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
jobs:
deploy:
runs-on: ubuntu-latest
environment: production
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install Dependencies
run: npm ci
- name: Run Tests
run: npm test
- name: Deploy to Production
id: deploy
uses: kodehort/sst-ops-action@v1
with:
operation: deploy
stage: production # Explicit stage for production
token: ${{ secrets.GITHUB_TOKEN }}
comment-mode: never
fail-on-error: true
- name: Notify Slack
if: steps.deploy.outputs.success == 'true'
uses: 8398a7/action-slack@v3
with:
status: success
text: 'Production deployment successful! π'
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}name: PR Review
on:
pull_request:
types: [opened, synchronize, reopened]
jobs:
review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# Show infrastructure changes (stage auto-computed from PR branch)
- name: Show Infrastructure Changes
id: diff
uses: kodehort/sst-ops-action@v1
with:
operation: diff
# Stage automatically computed from PR branch name
token: ${{ secrets.GITHUB_TOKEN }}
comment-mode: always
fail-on-error: false
# Deploy preview environment (stage auto-computed from PR branch)
- name: Deploy Preview Environment
if: contains(github.event.pull_request.labels.*.name, 'deploy-preview')
uses: kodehort/sst-ops-action@v1
with:
operation: deploy
# Stage automatically computed from PR branch name
token: ${{ secrets.GITHUB_TOKEN }}
comment-mode: on-successname: Multi-Environment Deploy
on:
push:
branches: [main]
jobs:
deploy-staging:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Deploy to Staging
id: staging
uses: kodehort/sst-ops-action@v1
with:
operation: deploy
stage: staging
token: ${{ secrets.GITHUB_TOKEN }}
deploy-production:
needs: deploy-staging
if: needs.deploy-staging.outputs.success == 'true'
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
- name: Deploy to Production
uses: kodehort/sst-ops-action@v1
with:
operation: deploy
stage: production
token: ${{ secrets.GITHUB_TOKEN }}Choose your preferred package manager or runtime for executing SST commands:
# Use Bun (default)
- uses: kodehort/sst-ops-action@v1
with:
operation: deploy
stage: production
token: ${{ secrets.GITHUB_TOKEN }}
runner: bun # Executes: bun sst deploy --stage production
# Use npm with package scripts
- uses: kodehort/sst-ops-action@v1
with:
operation: deploy
stage: production
token: ${{ secrets.GITHUB_TOKEN }}
runner: npm # Executes: npm run sst -- deploy --stage production
# Use pnpm
- uses: kodehort/sst-ops-action@v1
with:
operation: deploy
stage: production
token: ${{ secrets.GITHUB_TOKEN }}
runner: pnpm # Executes: pnpm sst deploy --stage production
# Use Yarn
- uses: kodehort/sst-ops-action@v1
with:
operation: deploy
stage: production
token: ${{ secrets.GITHUB_TOKEN }}
runner: yarn # Executes: yarn sst deploy --stage production
# Use SST CLI directly (requires global installation)
- uses: kodehort/sst-ops-action@v1
with:
operation: deploy
stage: production
token: ${{ secrets.GITHUB_TOKEN }}
runner: sst # Executes: sst deploy --stage productionRunner Selection Guide:
| Runner | Best For | Command Format | Requirements |
|---|---|---|---|
bun |
Modern projects, fastest execution | bun sst <operation> |
SST installed as dependency |
npm |
Traditional npm projects | npm run sst -- <operation> |
SST script in package.json |
pnpm |
Efficient package management | pnpm sst <operation> |
SST installed as dependency |
yarn |
Yarn-based projects | yarn sst <operation> |
SST installed as dependency |
sst |
Direct CLI usage | sst <operation> |
SST CLI globally installed |
- name: Deploy with Custom Error Handling
id: deploy
uses: kodehort/sst-ops-action@v1
with:
operation: deploy
stage: staging
token: ${{ secrets.GITHUB_TOKEN }}
fail-on-error: false # Don't fail workflow
max-output-size: 100000 # Increased output limit
- name: Handle Deployment Failure
if: steps.deploy.outputs.success == 'false'
run: |
echo "Deployment failed with status: ${{ steps.deploy.outputs.completion_status }}"
echo "Check the workflow summary for detailed error information"
# Custom failure handling logic here- name: Conditional Remove
uses: kodehort/sst-ops-action@v1
with:
operation: remove
stage: pr-${{ github.event.number }}
token: ${{ secrets.GITHUB_TOKEN }}
# Only run on PR close, and only for certain file changes
if: |
github.event_name == 'pull_request' &&
github.event.action == 'closed' &&
contains(github.event.pull_request.head.ref, 'feature/')- name: Process Deploy Outputs
id: deploy
uses: kodehort/sst-ops-action@v1
with:
operation: deploy
stage: production
token: ${{ secrets.GITHUB_TOKEN }}
- name: Extract Outputs
run: |
URLS='${{ steps.deploy.outputs.urls }}'
echo "Deployed URLs: $URLS"
# Parse JSON output (URLs extracted from generic outputs)
echo "API_URL=$(echo '$URLS' | jq -r '.[0]')" >> $GITHUB_ENV
echo "WEB_URL=$(echo '$URLS' | jq -r '.[1]')" >> $GITHUB_ENV
- name: Run Integration Tests
run: |
curl -f $API_URL/health || exit 1
echo "Integration tests passed!"This action requires minimal GitHub token permissions:
permissions:
contents: read # Read repository contents
issues: write # Create/update issue comments
pull-requests: write # Create/update PR commentsConfigure AWS credentials securely:
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_REGION: us-east-1Best Practices:
- Use IAM roles with minimal required permissions
- Rotate credentials regularly
- Use environment-specific AWS accounts
- Enable CloudTrail for audit logging
# Recommended secrets configuration
secrets:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Automatic
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
# Optional: Custom SST secrets
SST_TELEMETRY_DISABLED: "1"Solution: Ensure SST CLI is installed in your project
- name: Install SST CLI
run: npm install @serverless-stack/cliSolution: Verify stage exists in your SST configuration
- name: Debug Stage
run: |
echo "Available stages:"
npx sst list-stages || echo "No stages found"Solution: Set up AWS credentials in repository secrets
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}For more detailed troubleshooting, see TROUBLESHOOTING.md.
- π API Reference - Complete input/output documentation
- π οΈ Troubleshooting - Common issues and solutions
Real-world workflow examples are available in the examples/ directory:
- Basic Deploy - Simple deployment workflow
- PR Workflow - Pull request diff and deploy
- Multi-Environment - Staging β Production pipeline
- Cleanup - Resource cleanup strategies
- Error Handling - Advanced error handling patterns
# Clone and setup
git clone https://github.com/kodehort/sst-ops-action.git
cd sst-ops-action
bun install
# Run tests
bun test
# Build and validate
bun run build
bun run validateThe project uses TypeScript path aliases for cleaner imports:
@/*β./src/*(source files)@tests/*β./__tests__/*(test files)
Path aliases are configured in both tsconfig.json and vitest.config.ts for consistent IDE support.
This project is licensed under the MIT License - see the LICENSE file for details.
- Built for SST (Serverless Stack) framework
- Powered by GitHub Actions platform
- Uses Bun runtime and TypeScript
- Inspired by the GitHub Actions community and SST ecosystem
Need Help?
- π Report Issues
- π§ Contact Maintainers
Made with β€οΈ by the Kodehort team