Files
gh-emillindfors-claude-mark…/commands/lambda-github-actions.md
2025-11-29 18:25:52 +08:00

613 lines
14 KiB
Markdown

---
description: Set up GitHub Actions CI/CD pipeline for Rust Lambda deployment
---
You are helping the user set up a GitHub Actions workflow for automated Lambda deployment.
## Your Task
Create a complete GitHub Actions workflow for building, testing, and deploying Rust Lambda functions.
1. **Ask about deployment preferences**:
- Which AWS region(s)?
- Which architecture (x86_64, arm64, or both)?
- Deploy on every push to main, or only on tags/releases?
- AWS authentication method (OIDC or access keys)?
- Single function or multiple functions?
2. **Create workflow file**:
Create `.github/workflows/deploy-lambda.yml` with appropriate configuration
3. **Set up AWS authentication**:
- OIDC (recommended, more secure)
- Access keys (simpler setup)
4. **Configure required secrets** in GitHub repo settings
## Complete Workflow Examples
### Option 1: OIDC Authentication (Recommended)
```yaml
name: Deploy Lambda
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
env:
CARGO_TERM_COLOR: always
AWS_REGION: us-east-1
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Cache cargo dependencies
uses: actions/cache@v4
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
- name: Run tests
run: cargo test --verbose
- name: Run clippy
run: cargo clippy -- -D warnings
- name: Check formatting
run: cargo fmt -- --check
build-and-deploy:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
permissions:
id-token: write # Required for OIDC
contents: read
steps:
- uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Cache cargo dependencies
uses: actions/cache@v4
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
- name: Install Zig
uses: goto-bus-stop/setup-zig@v2
with:
version: 0.11.0
- name: Install cargo-lambda
run: pip install cargo-lambda
- name: Build Lambda (ARM64)
run: cargo lambda build --release --arm64
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: ${{ env.AWS_REGION }}
- name: Deploy to AWS Lambda
run: |
cargo lambda deploy \
--iam-role ${{ secrets.LAMBDA_EXECUTION_ROLE_ARN }} \
--region ${{ env.AWS_REGION }} \
--arch arm64
- name: Test deployed function
run: |
aws lambda invoke \
--function-name ${{ secrets.LAMBDA_FUNCTION_NAME }} \
--payload '{"test": true}' \
response.json
cat response.json
```
### Option 2: Access Keys Authentication
```yaml
name: Deploy Lambda
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
env:
CARGO_TERM_COLOR: always
AWS_REGION: us-east-1
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Cache cargo dependencies
uses: actions/cache@v4
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
- name: Run tests
run: cargo test --verbose
build-and-deploy:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Cache cargo dependencies
uses: actions/cache@v4
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
- name: Install Zig
uses: goto-bus-stop/setup-zig@v2
with:
version: 0.11.0
- name: Install cargo-lambda
run: pip install cargo-lambda
- name: Build Lambda (ARM64)
run: cargo lambda build --release --arm64
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ env.AWS_REGION }}
- name: Deploy to AWS Lambda
run: |
cargo lambda deploy \
--iam-role ${{ secrets.LAMBDA_EXECUTION_ROLE_ARN }} \
--region ${{ env.AWS_REGION }} \
--arch arm64
```
### Option 3: Multi-Architecture Build
```yaml
name: Deploy Lambda
on:
push:
branches: [ main ]
release:
types: [ published ]
env:
CARGO_TERM_COLOR: always
jobs:
build-matrix:
strategy:
matrix:
include:
- arch: x86_64
aws_arch: x86_64
build_flags: ""
- arch: arm64
aws_arch: arm64
build_flags: "--arm64"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Install Zig
uses: goto-bus-stop/setup-zig@v2
- name: Install cargo-lambda
run: pip install cargo-lambda
- name: Build for ${{ matrix.arch }}
run: cargo lambda build --release ${{ matrix.build_flags }} --output-format zip
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: lambda-${{ matrix.arch }}
path: target/lambda/**/*.zip
deploy:
needs: build-matrix
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
permissions:
id-token: write
contents: read
strategy:
matrix:
arch: [arm64, x86_64]
steps:
- uses: actions/checkout@v4
- name: Download artifact
uses: actions/download-artifact@v4
with:
name: lambda-${{ matrix.arch }}
path: target/lambda
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: us-east-1
- name: Install cargo-lambda
run: pip install cargo-lambda
- name: Deploy ${{ matrix.arch }}
run: |
cargo lambda deploy my-function-${{ matrix.arch }} \
--iam-role ${{ secrets.LAMBDA_EXECUTION_ROLE_ARN }} \
--arch ${{ matrix.arch }}
```
### Option 4: Multiple Functions
```yaml
name: Deploy Lambda Functions
on:
push:
branches: [ main ]
env:
CARGO_TERM_COLOR: always
AWS_REGION: us-east-1
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- run: cargo test --all
deploy:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
permissions:
id-token: write
contents: read
strategy:
matrix:
function:
- name: api-handler
memory: 512
timeout: 30
- name: data-processor
memory: 2048
timeout: 300
- name: event-consumer
memory: 1024
timeout: 60
steps:
- uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Install Zig
uses: goto-bus-stop/setup-zig@v2
- name: Install cargo-lambda
run: pip install cargo-lambda
- name: Build ${{ matrix.function.name }}
run: cargo lambda build --release --arm64 --bin ${{ matrix.function.name }}
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: ${{ env.AWS_REGION }}
- name: Deploy ${{ matrix.function.name }}
run: |
cargo lambda deploy ${{ matrix.function.name }} \
--iam-role ${{ secrets.LAMBDA_EXECUTION_ROLE_ARN }} \
--region ${{ env.AWS_REGION }} \
--memory ${{ matrix.function.memory }} \
--timeout ${{ matrix.function.timeout }} \
--arch arm64 \
--env-var RUST_LOG=info
```
## AWS OIDC Setup
For OIDC authentication (recommended), set up in AWS:
### 1. Create OIDC Provider in AWS IAM
```bash
aws iam create-open-id-connect-provider \
--url https://token.actions.githubusercontent.com \
--client-id-list sts.amazonaws.com \
--thumbprint-list 6938fd4d98bab03faadb97b34396831e3780aea1
```
### 2. Create IAM Role for GitHub Actions
```bash
# Create trust policy
cat > github-actions-trust-policy.json <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::YOUR_ACCOUNT_ID:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
},
"StringLike": {
"token.actions.githubusercontent.com:sub": "repo:YOUR_GITHUB_ORG/YOUR_REPO:*"
}
}
}
]
}
EOF
# Create role
aws iam create-role \
--role-name GitHubActionsLambdaDeployRole \
--assume-role-policy-document file://github-actions-trust-policy.json
# Attach policies
aws iam attach-role-policy \
--role-name GitHubActionsLambdaDeployRole \
--policy-arn arn:aws:iam::aws:policy/AWSLambda_FullAccess
# Get role ARN (save this for GitHub secrets)
aws iam get-role --role-name GitHubActionsLambdaDeployRole --query 'Role.Arn'
```
## GitHub Secrets Configuration
Configure these secrets in GitHub repository settings (Settings → Secrets and variables → Actions):
### For OIDC:
- `AWS_ROLE_ARN`: ARN of the GitHub Actions IAM role
- `LAMBDA_EXECUTION_ROLE_ARN`: ARN of the Lambda execution role
- `LAMBDA_FUNCTION_NAME` (optional): Function name if different from repo
### For Access Keys:
- `AWS_ACCESS_KEY_ID`: AWS access key
- `AWS_SECRET_ACCESS_KEY`: AWS secret key
- `LAMBDA_EXECUTION_ROLE_ARN`: ARN of the Lambda execution role
### Optional secrets:
- `AWS_REGION`: Override default region
- Environment-specific variables as needed
## Advanced Patterns
### Deploy on Git Tags
```yaml
on:
push:
tags:
- 'v*'
# In deploy step:
- name: Get tag version
id: tag
run: echo "version=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
- name: Deploy with version tag
run: |
cargo lambda deploy \
--tags Version=${{ steps.tag.outputs.version }}
```
### Environment-Specific Deployment
```yaml
on:
push:
branches:
- main
- develop
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Set environment
id: env
run: |
if [ "${{ github.ref }}" = "refs/heads/main" ]; then
echo "name=production" >> $GITHUB_OUTPUT
echo "suffix=" >> $GITHUB_OUTPUT
else
echo "name=development" >> $GITHUB_OUTPUT
echo "suffix=-dev" >> $GITHUB_OUTPUT
fi
- name: Deploy
run: |
cargo lambda deploy my-function${{ steps.env.outputs.suffix }} \
--env-var ENVIRONMENT=${{ steps.env.outputs.name }}
```
### Conditional Deployment
```yaml
- name: Check if Lambda code changed
id: lambda-changed
uses: dorny/paths-filter@v2
with:
filters: |
lambda:
- 'src/**'
- 'Cargo.toml'
- 'Cargo.lock'
- name: Deploy Lambda
if: steps.lambda-changed.outputs.lambda == 'true'
run: cargo lambda deploy
```
### Slack Notifications
```yaml
- name: Notify Slack on success
if: success()
uses: slackapi/slack-github-action@v1
with:
payload: |
{
"text": "Lambda deployed successfully: ${{ github.repository }}"
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
```
## Performance Optimizations
### Faster Builds with Caching
```yaml
- name: Cache Rust
uses: Swatinem/rust-cache@v2
with:
cache-on-failure: true
```
### Parallel Jobs
```yaml
jobs:
test:
# Testing job
build:
# Build job (independent of test for speed)
deploy:
needs: [test, build] # Only deploy if both succeed
```
## Troubleshooting CI/CD
### Issue: "cargo-lambda: command not found"
**Solution**: Ensure `pip install cargo-lambda` runs before use
### Issue: "Zig not found"
**Solution**: Add `goto-bus-stop/setup-zig@v2` step
### Issue: "AWS credentials not configured"
**Solution**: Verify secrets are set and aws-actions step is included
### Issue: Build caching not working
**Solution**: Use `Swatinem/rust-cache@v2` for better Rust caching
### Issue: Deployment fails intermittently
**Solution**: Add retry logic or use `aws lambda wait function-updated`
## Testing the Workflow
1. **Create workflow file** in `.github/workflows/deploy-lambda.yml`
2. **Configure secrets** in GitHub settings
3. **Push to trigger**:
```bash
git add .github/workflows/deploy-lambda.yml
git commit -m "Add Lambda deployment workflow"
git push
```
4. **Monitor** in GitHub Actions tab
5. **Check logs** for any issues
## Best Practices
1. **Always run tests** before deployment
2. **Use OIDC** instead of long-lived credentials
3. **Cache dependencies** for faster builds
4. **Deploy on main branch** only, test on PRs
5. **Use matrix builds** for multiple architectures
6. **Tag deployments** with version info
7. **Add notifications** for deployment status
8. **Set up monitoring** and alerts in AWS
9. **Use environments** for production deployments
10. **Document secrets** needed in README
After creating the workflow, guide the user through:
1. Setting up required secrets
2. Testing the workflow
3. Monitoring deployments
4. Handling failures