Package Exports
- scf-deploy
- scf-deploy/dist/index.js
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (scf-deploy) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
scf-deploy - S3 + CloudFront Deployment CLI
Automate static website deployment to AWS S3 and CloudFront with a simple CLI tool.
Features
- Easy Setup: Interactive
initcommand with helpful post-install message - Simple Deployment: Deploy with a single command
npx scf-deploy deploy - TypeScript Configuration: Type-safe config files with
scf.config.ts - Auto Build Detection: Automatically finds your build directory (dist, build, out, etc.)
- Build Validation: Ensures deployable files exist before creating AWS resources
- Multiple Templates: Pre-configured templates for React, Vue, Next.js
- Incremental Deployment: Only upload changed files (SHA-256 hash comparison)
- CloudFront Integration: Automatic cache invalidation after deployment
- Multi-Environment Support: Manage dev, staging, and prod environments
- AWS Credentials Integration: Supports AWS profiles, environment variables, and IAM roles
- State Management: Track deployed resources locally with automatic .gitignore handling
- State Recovery: Recover lost state files from AWS resource tags
- Progress Tracking: Real-time upload progress with visual feedback
- SSR Detection: Prevents accidental deployment of SSR builds (.next, .nuxt)
Installation
Global Installation
npm install -g scf-deployDirect Execution with npx (Recommended)
npx scf-deploy deployQuick Start
1. Initialize Configuration
Run the init command to create scf.config.ts:
npx scf-deploy initThis will guide you through an interactive setup or you can use defaults:
npx scf-deploy init --yesOr manually create scf.config.ts in your project root:
import type { SCFConfig } from 'scf-deploy';
const config: SCFConfig = {
app: 'my-static-site',
region: 'ap-northeast-2',
s3: {
bucketName: 'my-site-bucket',
// buildDir is auto-detected (dist, build, out, etc.)
// You can override: buildDir: './custom-dir',
indexDocument: 'index.html',
errorDocument: '404.html',
},
cloudfront: {
enabled: true,
// priceClass: 'PriceClass_100', // Optional, defaults to PriceClass_100
},
};
export default config;Benefits of type annotation:
- ✅ IDE auto-completion: Get suggestions for all available properties
- ✅ Type checking: Catch errors before deployment
- ✅ Documentation: Hover tooltips show property descriptions
- ✅ Validation: Required properties are enforced at compile time
2. Build Your Site
# For React, Vue, etc.
npm run build
# For plain HTML
# Just make sure your files are in the buildDir3. Deploy
npx scf-deploy deployThat's it! Your site is now live on S3 and CloudFront.
Configuration
Basic Configuration
import type { SCFConfig } from 'scf-deploy';
const config: SCFConfig = {
app: 'my-app', // Application name
region: 'us-east-1', // AWS region
s3: {
bucketName: 'my-bucket',
// buildDir is optional - auto-detected from: dist, build, out, .output/public, _site
indexDocument: 'index.html',
errorDocument: '404.html',
},
cloudfront: {
enabled: true,
// priceClass: 'PriceClass_100', // Optional: PriceClass_100, PriceClass_200, PriceClass_All
},
};
export default config;Build Directory Auto-Detection
scf-deploy automatically detects your build directory by searching for:
dist- Vite, Rollup, Vue, etc.build- Create React App, Next.js, etc.out- Next.js static export.output/public- Nuxt 3_site- Jekyll, 11tyoutput- Some SSGs
Requirements:
- Directory must contain
index.htmlas the entry point - Must have deployable web files (.html, .js, .css, etc.)
SSR Build Detection: scf-deploy will reject SSR build directories that require a server:
.next- Next.js SSR build.nuxt- Nuxt SSR build
For Next.js, use next export to generate static files in ./out:
# next.config.js
module.exports = {
output: 'export',
};
# Then build
npm run build
# Creates ./out directory with static filesEnvironment-Specific Configuration
import type { SCFConfig } from 'scf-deploy';
const config: SCFConfig = {
app: 'my-app',
region: 'ap-northeast-2',
s3: {
bucketName: 'my-site-prod',
},
cloudfront: {
enabled: true,
},
// Environment overrides
environments: {
dev: {
s3: { bucketName: 'my-site-dev' },
cloudfront: { enabled: false }, // Skip CloudFront in dev
},
staging: {
s3: { bucketName: 'my-site-staging' },
},
prod: {
cloudfront: { priceClass: 'PriceClass_All' }, // Use all edge locations in prod
},
},
};
export default config;Custom Domain Configuration
import type { SCFConfig } from 'scf-deploy';
const config: SCFConfig = {
app: 'my-app',
region: 'us-east-1',
s3: {
bucketName: 'my-site',
},
cloudfront: {
enabled: true,
customDomain: {
domainName: 'example.com',
certificateArn: 'arn:aws:acm:us-east-1:123456789012:certificate/abc-def',
},
},
};
export default config;Commands
init
Initialize scf.config.ts configuration file.
# Interactive mode (asks questions)
scf-deploy init
# Non-interactive mode (use defaults)
scf-deploy init --yes
# Use a template (react, vue, next, custom)
scf-deploy init --template react
# Overwrite existing config
scf-deploy init --forceOptions:
-f, --force- Overwrite existing config file-y, --yes- Skip prompts and use default values-t, --template <template>- Use template (custom, react, vue, next)
Templates:
custom- Custom configuration (default build dir:./dist)react- React/CRA configuration (build dir:./build)vue- Vue.js configuration (build dir:./dist)next- Next.js static export (build dir:./out)
deploy
Deploy your static site to S3 and CloudFront.
# Basic deployment
scf-deploy deploy
# Deploy to specific environment
scf-deploy deploy --env prod
# Use specific AWS profile
scf-deploy deploy --profile my-aws-profile
# Preview without uploading
scf-deploy deploy --dry-run
# Skip CloudFront (S3 only)
scf-deploy deploy --no-cloudfront
# Force full deployment (ignore cached state)
scf-deploy deploy --forceOptions:
-e, --env <environment>- Environment name (default: "default")-c, --config <path>- Config file path (default: "scf.config.ts")-p, --profile <profile>- AWS profile name--dry-run- Preview deployment without uploading--no-cloudfront- Skip CloudFront deployment--force- Force full deployment (ignore state)--skip-cache- Skip CloudFront cache invalidation
remove
Remove deployed AWS resources.
# Remove resources (with confirmation prompt)
scf-deploy remove
# Force remove without confirmation
scf-deploy remove --force
# Remove specific environment
scf-deploy remove --env dev
# Keep S3 bucket (only delete files)
scf-deploy remove --keep-bucket
# Keep CloudFront distribution
scf-deploy remove --keep-distributionOptions:
-e, --env <environment>- Environment name (default: "default")-c, --config <path>- Config file path (default: "scf.config.ts")-p, --profile <profile>- AWS profile name-f, --force- Skip confirmation prompt--keep-bucket- Keep S3 bucket (only delete files)--keep-distribution- Keep CloudFront distribution
status
Check current deployment status.
# Basic status
scf-deploy status
# Specific environment
scf-deploy status --env prod
# Detailed information
scf-deploy status --detailed
# JSON output
scf-deploy status --jsonOptions:
-e, --env <environment>- Environment name (default: "default")-d, --detailed- Show detailed information--json- Output as JSON
recover
Recover lost deployment state from AWS resources.
If you accidentally delete the .deploy/state.json file, you can recover it from AWS resource tags.
# Recover state for default environment
scf-deploy recover
# Recover specific environment
scf-deploy recover --env prod
# Overwrite existing state file
scf-deploy recover --forceHow it works:
- Searches for S3 buckets with
scf:managed=truetag - Finds associated CloudFront distributions
- Filters by app name and environment
- Reconstructs the state file from AWS resources
Options:
-e, --env <environment>- Environment name to recover-c, --config <path>- Config file path (default: "scf.config.ts")-p, --profile <profile>- AWS profile name-f, --force- Overwrite existing state file
Note: All AWS resources created by scf-deploy are automatically tagged for recovery:
scf:managed=true- Indicates resource is managed by scf-deployscf:app=<app-name>- Application name from configscf:environment=<env>- Environment name
AWS Credentials
scf-deploy looks for AWS credentials in the following order:
- Command-line option:
--profileflag - Environment variables:
AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY - AWS CLI profiles:
~/.aws/credentials - IAM Role: When running on EC2, ECS, etc.
Using AWS Profile
scf-deploy deploy --profile my-company-profileUsing Environment Variables
export AWS_ACCESS_KEY_ID=your_access_key
export AWS_SECRET_ACCESS_KEY=your_secret_key
scf-deploy deployFeatures in Detail
Build Validation
Before creating any AWS resources, scf-deploy validates your build:
- Auto-detection: Searches for common build directories (dist, build, out, etc.)
- index.html check: Ensures an entry point exists
- Deployable files: Verifies web files (.html, .js, .css, etc.) are present
- SSR rejection: Prevents deployment of SSR builds that require a server
This prevents wasted time and costs by catching issues before AWS resources are created.
Automatic .gitignore Management
scf-deploy automatically manages your .gitignore file:
- Auto-detection: Checks if your project is a Git repository
- Safe addition: Adds
.deploy/if not already present - Non-intrusive: Creates
.gitignoreif it doesn't exist - One-time: Only modifies once, won't duplicate entries
This happens automatically during:
scf-deploy init- When initializing configurationscf-deploy deploy- After first successful deployment
State Recovery
If you accidentally delete .deploy/state.json, you can recover it:
scf-deploy recover --env prodHow it works:
- All AWS resources are tagged with
scf:managed,scf:app,scf:environment recovercommand searches for these tagged resources- State file is reconstructed from AWS metadata
- You can continue deploying without recreating resources
What can be recovered:
- S3 bucket information
- CloudFront distribution ID and domain
- Resource creation timestamps
- Environment configuration
Note: File hashes are not recoverable, so the next deployment will re-upload all files.
Incremental Deployment
scf-deploy uses SHA-256 hashing to detect file changes:
- First deployment: All files are uploaded
- Subsequent deployments: Only changed files are uploaded
- Time savings: 80-95% faster deployment times
State is stored in .deploy/state.{env}.json (automatically added to .gitignore).
CloudFront Cache Invalidation
After deployment, scf-deploy automatically:
- Creates or updates CloudFront distribution
- Invalidates cache for changed files
- Waits for distribution to be fully deployed
- Shows real-time progress
CloudFront Cache Warming
Reduce cold start latency by pre-warming CloudFront edge locations after deployment:
cloudfront: {
enabled: true,
cacheWarming: {
enabled: true,
paths: ['/', '/index.html', '/app.js'], // Critical paths only
concurrency: 3, // Concurrent requests (max: 10)
delay: 500, // Delay between requests (ms)
},
}How it works:
- After CloudFront deployment completes, scf-deploy makes HTTP requests to specified paths
- Files are downloaded and cached at edge locations
- First users get cached responses immediately (no cold start)
Cost considerations:
- ⚠️ Data transfer costs: Downloads files, incurs CloudFront outbound traffic charges
- Example: 10 files × 100KB each × $0.085/GB = ~$0.00009 per deployment
- Best practice: Only warm essential files (HTML, critical JS/CSS)
- Avoid: Large images, videos, or non-critical assets
When to use:
- ✅ Production deployments where first-load performance is critical
- ✅ After major releases to ensure global availability
- ❌ Development/staging environments (disable to save costs)
- ❌ High-frequency deployments (costs accumulate)
Configuration tips:
environments: {
dev: {
cloudfront: {
enabled: false, // No CloudFront = no warming needed
},
},
staging: {
cloudfront: {
enabled: true,
cacheWarming: { enabled: false }, // Skip warming in staging
},
},
prod: {
cloudfront: {
cacheWarming: {
enabled: true,
paths: ['/', '/index.html'], // Minimal paths
concurrency: 3,
delay: 500,
},
},
},
}Multi-Environment Support
Manage multiple environments with ease:
scf-deploy deploy --env dev
scf-deploy deploy --env staging
scf-deploy deploy --env prodEach environment:
- Has its own state file
- Can override configuration
- Is completely isolated
Progress Tracking
Visual feedback during deployment:
- File scanning progress
- Upload progress bar
- Real-time status updates
- Detailed error messages
Examples
React Application
# Build your React app
npm run build
# Deploy to production
scf-deploy deploy --env prodConfiguration:
import type { SCFConfig } from 'scf-deploy';
const config: SCFConfig = {
app: 'my-react-app',
region: 'us-east-1',
s3: {
bucketName: 'my-react-app',
// buildDir auto-detected (React uses ./build by default)
indexDocument: 'index.html',
},
cloudfront: {
enabled: true,
},
};
export default config;Vue Application
# Build your Vue app
npm run build
# Deploy
scf-deploy deployConfiguration:
import type { SCFConfig } from 'scf-deploy';
const config: SCFConfig = {
app: 'my-vue-app',
region: 'eu-west-1',
s3: {
bucketName: 'my-vue-app',
// buildDir auto-detected (Vue uses ./dist by default)
indexDocument: 'index.html',
},
cloudfront: {
enabled: true,
},
};
export default config;Static HTML Site
import type { SCFConfig } from 'scf-deploy';
const config: SCFConfig = {
app: 'my-website',
region: 'ap-northeast-2',
s3: {
bucketName: 'my-website',
// For custom build directory (not auto-detected)
buildDir: './public',
indexDocument: 'index.html',
errorDocument: '404.html',
},
cloudfront: {
enabled: true,
},
};
export default config;Troubleshooting
Command not found
# Check if installed
which scf-deploy
# Reinstall globally
npm uninstall -g scf-deploy
npm install -g scf-deploy
# Or use npx (recommended)
npx scf-deploy deployAWS Credentials Error
# Verify AWS credentials
aws sts get-caller-identity
# Use specific profile
scf-deploy deploy --profile my-profileConfig file not found
# Check if scf.config.ts exists
ls -la scf.config.ts
# Specify custom path
scf-deploy deploy --config ./config/scf.config.tsState file conflicts
# Check state files
ls -la .deploy/
# Force full redeployment
scf-deploy deploy --forceBuild directory not found
# Error: Could not find build directory
# Solution: Ensure you've built your project first
npm run build
# Or specify a custom build directory
# In scf.config.ts:
s3: {
bucketName: 'my-bucket',
buildDir: './my-custom-output',
}SSR build detected error
# Error: Cannot deploy SSR build directory (.next, .nuxt)
# For Next.js: Enable static export
# next.config.js:
module.exports = {
output: 'export', // Generates static files in ./out
};
# For Nuxt: Use static generation
# nuxt.config.js:
export default {
ssr: false, // SPA mode
target: 'static',
};Lost state file recovery
# If you accidentally deleted .deploy/state.json
scf-deploy recover --env prod
# Then continue deploying as normal
scf-deploy deploy --env prodRequirements
- Node.js: >= 18.0.0
- AWS Account: With appropriate permissions
- AWS Credentials: Configured via CLI, environment, or IAM role
Required AWS Permissions
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:CreateBucket",
"s3:DeleteBucket",
"s3:ListBucket",
"s3:PutObject",
"s3:GetObject",
"s3:DeleteObject",
"s3:PutBucketWebsite",
"s3:PutBucketTagging",
"s3:GetBucketTagging",
"s3:ListAllMyBuckets",
"cloudfront:CreateDistribution",
"cloudfront:GetDistribution",
"cloudfront:UpdateDistribution",
"cloudfront:DeleteDistribution",
"cloudfront:CreateInvalidation",
"cloudfront:ListDistributions",
"cloudfront:TagResource",
"cloudfront:ListTagsForResource"
],
"Resource": "*"
}
]
}Note: Tagging permissions are required for the state recovery feature.
Best Practices
Build before deploying: Always run your build command before deployment
npm run build && npx scf-deploy deploy
State file management:
.deploy/is automatically added to.gitignore- Never commit state files to Git
- Use
scf-deploy recoverif state is lost
Use environment-specific configs: Separate dev/staging/prod
scf-deploy deploy --env dev # For development scf-deploy deploy --env prod # For production
Test with
--dry-runfirst: Preview changes before deployingscf-deploy deploy --dry-run
Use IAM roles in CI/CD: Don't hardcode credentials
- Prefer IAM roles over access keys
- Use AWS profiles locally
- Let EC2/ECS IAM roles work automatically
Enable CloudFront in production: Better performance and HTTPS
- Disable CloudFront in dev to save costs
- Use
PriceClass_100for cost optimization - Upgrade to
PriceClass_Allfor global coverage
Set up custom domain with ACM certificate: Professional appearance
- Request ACM certificate in
us-east-1for CloudFront - Verify domain ownership
- Add domain to CloudFront config
- Request ACM certificate in
Static export for Next.js: Use
output: 'export'// next.config.js module.exports = { output: 'export', };
Monitor AWS costs:
- Check S3 storage and transfer costs
- Monitor CloudFront data transfer
- Use CloudWatch for usage metrics
Keep your CLI updated:
npm update -g scf-deploy # Or with npx (always uses latest) npx scf-deploy@latest deploy
Testing
SCF uses Jest as the testing framework with comprehensive unit tests for core functionality.
Running Tests
# Run all tests
npm test
# Run only unit tests
npm run test:unit
# Run tests in watch mode
npm run test:watch
# Run tests with coverage report
npm run test:coverageTest Structure
src/__tests__/
├── unit/ # Unit tests for core modules
│ ├── config/ # Config parsing, validation, merging
│ ├── deployer/ # File scanning, hashing
│ └── state/ # State management
├── integration/ # Integration tests (future)
├── e2e/ # End-to-end tests (future)
└── fixtures/ # Test data and config filesTest Coverage
Current test coverage for core modules:
| Module | Coverage |
|---|---|
| Config Schema | 100% |
| Config Merger | 100% |
| Config Loader | 91.66% |
| File Scanner | 100% |
| State Manager | 93.1% |
Total Unit Tests: 130 tests
Writing Tests
When contributing, please ensure:
- Add tests for new features: All new functionality should include tests
- Maintain coverage: Keep coverage above 90% for core modules
- Use fixtures: Add test data to
src/__tests__/fixtures/ - Follow patterns: Match existing test structure and naming
Example test:
import { describe, it, expect } from '@jest/globals';
import { validateConfig } from '../../../core/config/schema.js';
describe('Config Validation', () => {
it('should validate a minimal config', () => {
const config = {
app: 'test-app',
region: 'us-east-1',
s3: { bucketName: 'test-bucket', buildDir: './dist' },
};
expect(() => validateConfig(config)).not.toThrow();
});
});Test Scripts
test- Run all teststest:unit- Run only unit teststest:watch- Run tests in watch modetest:coverage- Generate coverage report (saved tocoverage/)
Coverage reports are generated in:
- HTML:
coverage/index.html(open in browser) - LCOV:
coverage/lcov-report/(for CI/CD tools)
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
License
MIT License - see LICENSE file for details
Links
- Homepage: https://github.com/SCF-org
- Issues: https://github.com/SCF-org/scf/issues
- NPM: https://www.npmjs.com/package/scf-deploy
Author
jeonghodong fire13764@gmail.com