Files
你的用户名 237c7802e5
Some checks failed
Deploy / deploy (push) Has been cancelled
Initial commit: Telegram Management System
Full-stack web application for Telegram management
- Frontend: Vue 3 + Vben Admin
- Backend: NestJS
- Features: User management, group broadcast, statistics

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-04 15:37:50 +08:00

348 lines
13 KiB
YAML

name: CD Pipeline
on:
push:
branches: [ main ]
tags: [ 'v*.*.*' ]
workflow_dispatch:
inputs:
environment:
description: 'Deployment environment'
required: true
default: 'staging'
type: choice
options:
- staging
- production
version:
description: 'Version to deploy (leave empty for latest)'
required: false
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
HELM_VERSION: '3.13.0'
jobs:
# Prepare deployment
prepare-deployment:
name: Prepare Deployment
runs-on: ubuntu-latest
outputs:
environment: ${{ steps.determine-env.outputs.environment }}
version: ${{ steps.determine-version.outputs.version }}
should-deploy: ${{ steps.check-deploy.outputs.should-deploy }}
steps:
- uses: actions/checkout@v4
- name: Determine environment
id: determine-env
run: |
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
echo "environment=${{ github.event.inputs.environment }}" >> $GITHUB_OUTPUT
elif [[ "${{ github.ref }}" == "refs/heads/main" ]]; then
echo "environment=staging" >> $GITHUB_OUTPUT
elif [[ "${{ github.ref }}" =~ ^refs/tags/v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "environment=production" >> $GITHUB_OUTPUT
else
echo "environment=development" >> $GITHUB_OUTPUT
fi
- name: Determine version
id: determine-version
run: |
if [[ "${{ github.event_name }}" == "workflow_dispatch" && -n "${{ github.event.inputs.version }}" ]]; then
echo "version=${{ github.event.inputs.version }}" >> $GITHUB_OUTPUT
elif [[ "${{ github.ref }}" =~ ^refs/tags/v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "version=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
else
echo "version=${{ github.sha }}" >> $GITHUB_OUTPUT
fi
- name: Check deployment conditions
id: check-deploy
run: |
ENV="${{ steps.determine-env.outputs.environment }}"
if [[ "$ENV" == "production" && ! "${{ github.ref }}" =~ ^refs/tags/v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "should-deploy=false" >> $GITHUB_OUTPUT
echo "::warning::Production deployment requires a semantic version tag"
else
echo "should-deploy=true" >> $GITHUB_OUTPUT
fi
# Database Migration
database-migration:
name: Database Migration
runs-on: ubuntu-latest
needs: [prepare-deployment]
if: needs.prepare-deployment.outputs.should-deploy == 'true'
environment: ${{ needs.prepare-deployment.outputs.environment }}
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '18.x'
- 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: ${{ vars.AWS_REGION }}
- name: Get database connection string
id: get-db-connection
run: |
SECRET_ARN="${{ secrets.DB_SECRET_ARN }}"
DB_SECRET=$(aws secretsmanager get-secret-value --secret-id $SECRET_ARN --query SecretString --output text)
echo "::add-mask::$DB_SECRET"
echo "DB_CONNECTION=$DB_SECRET" >> $GITHUB_ENV
- name: Run migrations
run: |
cd migrations
npm ci
npm run migrate:up
env:
MONGODB_URI: ${{ env.DB_CONNECTION }}
MIGRATION_ENV: ${{ needs.prepare-deployment.outputs.environment }}
# Deploy to Kubernetes
deploy-kubernetes:
name: Deploy to Kubernetes
runs-on: ubuntu-latest
needs: [prepare-deployment, database-migration]
if: needs.prepare-deployment.outputs.should-deploy == 'true'
environment:
name: ${{ needs.prepare-deployment.outputs.environment }}
url: ${{ steps.deploy.outputs.app-url }}
strategy:
matrix:
region: [us-east-1, eu-west-1, ap-southeast-1]
steps:
- uses: actions/checkout@v4
- 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: ${{ matrix.region }}
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v2
- name: Setup kubectl
uses: azure/setup-kubectl@v3
with:
version: 'v1.28.0'
- name: Setup Helm
uses: azure/setup-helm@v3
with:
version: ${{ env.HELM_VERSION }}
- name: Update kubeconfig
run: |
aws eks update-kubeconfig --name marketing-agent-${{ needs.prepare-deployment.outputs.environment }}-${{ matrix.region }}
- name: Create namespace
run: |
kubectl create namespace marketing-agent-${{ needs.prepare-deployment.outputs.environment }} --dry-run=client -o yaml | kubectl apply -f -
- name: Deploy with Helm
id: deploy
run: |
NAMESPACE="marketing-agent-${{ needs.prepare-deployment.outputs.environment }}"
RELEASE_NAME="marketing-agent"
helm upgrade --install $RELEASE_NAME ./helm/marketing-agent \
--namespace $NAMESPACE \
--values ./helm/marketing-agent/values.yaml \
--values ./helm/marketing-agent/values.${{ needs.prepare-deployment.outputs.environment }}.yaml \
--set global.image.tag=${{ needs.prepare-deployment.outputs.version }} \
--set global.image.registry=${{ steps.login-ecr.outputs.registry }} \
--set global.region=${{ matrix.region }} \
--set-string global.environment=${{ needs.prepare-deployment.outputs.environment }} \
--wait \
--timeout 10m
# Get the application URL
APP_URL=$(kubectl get ingress -n $NAMESPACE -o jsonpath='{.items[0].spec.rules[0].host}')
echo "app-url=https://$APP_URL" >> $GITHUB_OUTPUT
- name: Verify deployment
run: |
NAMESPACE="marketing-agent-${{ needs.prepare-deployment.outputs.environment }}"
kubectl rollout status deployment -n $NAMESPACE --timeout=5m
kubectl get pods -n $NAMESPACE
- name: Run smoke tests
run: |
APP_URL="${{ steps.deploy.outputs.app-url }}"
./scripts/smoke-tests.sh $APP_URL
# Deploy Static Assets to CDN
deploy-cdn:
name: Deploy Static Assets to CDN
runs-on: ubuntu-latest
needs: [prepare-deployment, database-migration]
if: needs.prepare-deployment.outputs.should-deploy == 'true'
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '18.x'
cache: 'npm'
- name: Build frontend
working-directory: ./frontend
run: |
npm ci
npm run build
env:
VITE_API_URL: ${{ vars.API_URL }}
VITE_ENVIRONMENT: ${{ needs.prepare-deployment.outputs.environment }}
- 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: us-east-1
- name: Deploy to S3
run: |
BUCKET_NAME="marketing-agent-frontend-${{ needs.prepare-deployment.outputs.environment }}"
aws s3 sync ./frontend/dist s3://$BUCKET_NAME \
--delete \
--cache-control "public, max-age=31536000" \
--exclude "index.html" \
--exclude "*.map"
# Upload index.html with no-cache
aws s3 cp ./frontend/dist/index.html s3://$BUCKET_NAME/index.html \
--cache-control "no-cache, no-store, must-revalidate"
- name: Invalidate CloudFront
run: |
DISTRIBUTION_ID="${{ secrets.CLOUDFRONT_DISTRIBUTION_ID }}"
aws cloudfront create-invalidation \
--distribution-id $DISTRIBUTION_ID \
--paths "/*"
# Post-deployment tasks
post-deployment:
name: Post Deployment Tasks
runs-on: ubuntu-latest
needs: [prepare-deployment, deploy-kubernetes, deploy-cdn]
if: always() && needs.prepare-deployment.outputs.should-deploy == 'true'
steps:
- uses: actions/checkout@v4
- name: Update deployment status
uses: actions/github-script@v7
with:
script: |
const environment = '${{ needs.prepare-deployment.outputs.environment }}';
const version = '${{ needs.prepare-deployment.outputs.version }}';
const status = '${{ needs.deploy-kubernetes.result }}';
// Create deployment status
await github.rest.repos.createDeploymentStatus({
owner: context.repo.owner,
repo: context.repo.repo,
deployment_id: context.payload.deployment.id,
state: status === 'success' ? 'success' : 'failure',
environment_url: status === 'success' ? '${{ needs.deploy-kubernetes.outputs.app-url }}' : '',
description: `Deployed version ${version} to ${environment}`
});
- name: Send deployment notification
if: success()
uses: 8398a7/action-slack@v3
with:
status: custom
custom_payload: |
{
"text": "Deployment Successful! :rocket:",
"attachments": [{
"color": "good",
"fields": [
{ "title": "Environment", "value": "${{ needs.prepare-deployment.outputs.environment }}", "short": true },
{ "title": "Version", "value": "${{ needs.prepare-deployment.outputs.version }}", "short": true },
{ "title": "Deployed By", "value": "${{ github.actor }}", "short": true },
{ "title": "Repository", "value": "${{ github.repository }}", "short": true }
]
}]
}
webhook_url: ${{ secrets.SLACK_WEBHOOK }}
- name: Create release notes
if: startsWith(github.ref, 'refs/tags/')
uses: actions/github-script@v7
with:
script: |
const tag = context.ref.replace('refs/tags/', '');
const { data: commits } = await github.rest.repos.compareCommits({
owner: context.repo.owner,
repo: context.repo.repo,
base: 'v1.0.0', // Previous release tag
head: tag
});
const releaseNotes = commits.commits
.map(commit => `- ${commit.commit.message}`)
.join('\n');
await github.rest.repos.createRelease({
owner: context.repo.owner,
repo: context.repo.repo,
tag_name: tag,
name: `Release ${tag}`,
body: releaseNotes,
draft: false,
prerelease: false
});
# Rollback on failure
rollback:
name: Rollback Deployment
runs-on: ubuntu-latest
needs: [prepare-deployment, deploy-kubernetes]
if: failure() && needs.prepare-deployment.outputs.environment == 'production'
steps:
- 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: ${{ vars.AWS_REGION }}
- name: Rollback Kubernetes deployment
run: |
aws eks update-kubeconfig --name marketing-agent-production
NAMESPACE="marketing-agent-production"
helm rollback marketing-agent -n $NAMESPACE
- name: Send rollback notification
uses: 8398a7/action-slack@v3
with:
status: custom
custom_payload: |
{
"text": "Production Deployment Failed - Rollback Initiated! :warning:",
"attachments": [{
"color": "danger",
"fields": [
{ "title": "Environment", "value": "production", "short": true },
{ "title": "Failed Version", "value": "${{ needs.prepare-deployment.outputs.version }}", "short": true }
]
}]
}
webhook_url: ${{ secrets.SLACK_WEBHOOK }}