add: github workflows
This commit is contained in:
parent
22d03a100a
commit
f0f29deef1
453
.github/workflows/deploy-linux.yml
vendored
453
.github/workflows/deploy-linux.yml
vendored
@ -1,4 +1,4 @@
|
|||||||
name: Deploy Linux Binary
|
name: Build and Deploy Linux Binary
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
@ -9,30 +9,26 @@ on:
|
|||||||
- '.github/workflows/deploy-linux.yml'
|
- '.github/workflows/deploy-linux.yml'
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
environment:
|
mode:
|
||||||
description: 'Deployment environment'
|
description: 'Operation mode'
|
||||||
required: true
|
required: true
|
||||||
default: 'production'
|
default: 'build'
|
||||||
type: choice
|
type: choice
|
||||||
options:
|
options:
|
||||||
- production
|
- build-only
|
||||||
- staging
|
- deploy-staging
|
||||||
- testing
|
- deploy-production
|
||||||
|
- build-and-deploy
|
||||||
version:
|
version:
|
||||||
description: 'Version to deploy (leave empty for latest)'
|
description: 'Version to build (leave empty for auto)'
|
||||||
required: false
|
required: false
|
||||||
type: string
|
type: string
|
||||||
force_deploy:
|
|
||||||
description: 'Force deployment even if same version'
|
|
||||||
required: false
|
|
||||||
default: false
|
|
||||||
type: boolean
|
|
||||||
release:
|
release:
|
||||||
types: [ published ]
|
types: [ published ]
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: write
|
||||||
packages: read
|
packages: write
|
||||||
|
|
||||||
env:
|
env:
|
||||||
BINARY_NAME: ppanel-server
|
BINARY_NAME: ppanel-server
|
||||||
@ -107,38 +103,148 @@ jobs:
|
|||||||
- name: Upload build artifacts
|
- name: Upload build artifacts
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: binary
|
name: linux-binary
|
||||||
path: |
|
path: |
|
||||||
${{ steps.build.outputs.binary_name }}
|
${{ steps.build.outputs.binary_name }}
|
||||||
checksum.txt
|
checksum.txt
|
||||||
retention-days: 7
|
retention-days: 30
|
||||||
|
|
||||||
|
- name: Create deployment package
|
||||||
|
run: |
|
||||||
|
mkdir -p deploy-package
|
||||||
|
cp "${{ steps.build.outputs.binary_name }}" deploy-package/ppanel-server
|
||||||
|
cp checksum.txt deploy-package/
|
||||||
|
cp -r etc deploy-package/ 2>/dev/null || echo "etc directory not found, skipping"
|
||||||
|
|
||||||
|
# Create deployment script
|
||||||
|
cat > deploy-package/deploy.sh << 'EOF'
|
||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
BINARY_NAME="ppanel-server"
|
||||||
|
DEPLOY_PATH="/opt/ppanel"
|
||||||
|
CONFIG_PATH="/app/etc/ppanel.yaml"
|
||||||
|
SERVICE_NAME="ppanel-server"
|
||||||
|
|
||||||
|
# Create directories
|
||||||
|
sudo mkdir -p $DEPLOY_PATH /app/etc /opt/ppanel/logs
|
||||||
|
|
||||||
|
# Install binary
|
||||||
|
sudo cp $BINARY_NAME $DEPLOY_PATH/ppanel-server
|
||||||
|
sudo chmod +x $DEPLOY_PATH/ppanel-server
|
||||||
|
|
||||||
|
# Copy config if needed
|
||||||
|
if [ -d "etc" ] && [ ! -f "/app/etc/ppanel.yaml" ]; then
|
||||||
|
sudo cp -r etc/* /app/etc/
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create systemd service
|
||||||
|
sudo tee /etc/systemd/system/$SERVICE_NAME.service > /dev/null << 'SERVICE_EOF'
|
||||||
|
[Unit]
|
||||||
|
Description=PPanel Server
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=root
|
||||||
|
WorkingDirectory=$DEPLOY_PATH
|
||||||
|
ExecStart=$DEPLOY_PATH/ppanel-server run --config $CONFIG_PATH
|
||||||
|
Restart=always
|
||||||
|
RestartSec=5
|
||||||
|
StandardOutput=journal
|
||||||
|
StandardError=journal
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
SERVICE_EOF
|
||||||
|
|
||||||
|
# Reload and start service
|
||||||
|
sudo systemctl daemon-reload
|
||||||
|
sudo systemctl enable $SERVICE_NAME
|
||||||
|
sudo systemctl restart $SERVICE_NAME
|
||||||
|
|
||||||
|
echo "✅ Deployment completed!"
|
||||||
|
echo "📊 Service status: sudo systemctl status $SERVICE_NAME"
|
||||||
|
echo "📝 Logs: sudo journalctl -u $SERVICE_NAME -f"
|
||||||
|
EOF
|
||||||
|
|
||||||
|
chmod +x deploy-package/deploy.sh
|
||||||
|
tar -czf "${{ steps.build.outputs.binary_name }}-deploy.tar.gz" deploy-package/
|
||||||
|
|
||||||
|
- name: Upload deployment package
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: deploy-package
|
||||||
|
path: "${{ steps.build.outputs.binary_name }}-deploy.tar.gz"
|
||||||
|
retention-days: 30
|
||||||
|
|
||||||
|
- name: Upload to GitHub Release (if release)
|
||||||
|
if: github.event_name == 'release'
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
gh release upload ${{ env.VERSION }} \
|
||||||
|
${{ steps.build.outputs.binary_name }} \
|
||||||
|
checksum.txt
|
||||||
|
|
||||||
|
- name: Create summary
|
||||||
|
run: |
|
||||||
|
echo "## 📦 Build Summary" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "**Binary:** ${{ steps.build.outputs.binary_name }}" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "**Version:** ${{ env.VERSION }}" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "**Checksum:** \`${{ steps.checksum.outputs.checksum }}\`" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "**Size:** $(du -h ${{ steps.build.outputs.binary_name }} | cut -f1)" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "### 📥 Download" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "- **Binary only**: Actions → Artifacts → \`linux-binary\`" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "- **Deploy package**: Actions → Artifacts → \`deploy-package\`" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "### 🚀 Next Steps" >> $GITHUB_STEP_SUMMARY
|
||||||
|
if [ "${{ github.event.inputs.mode }}" = "build-only" ] || [ "${{ github.event_name }}" = "push" ]; then
|
||||||
|
echo "1. Download the binary from Artifacts" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "2. Deploy manually to your server" >> $GITHUB_STEP_SUMMARY
|
||||||
|
else
|
||||||
|
echo "1. Configure server secrets in repository settings" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "2. The deployment will start automatically" >> $GITHUB_STEP_SUMMARY
|
||||||
|
fi
|
||||||
|
|
||||||
deploy-staging:
|
deploy-staging:
|
||||||
name: Deploy to Staging
|
name: Deploy to Staging
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: build
|
needs: build
|
||||||
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master' || github.event_name == 'workflow_dispatch'
|
if: (github.event.inputs.mode == 'deploy-staging' || github.event.inputs.mode == 'build-and-deploy') && env.STAGING_HOST != ''
|
||||||
environment:
|
|
||||||
name: staging
|
|
||||||
url: ${{ steps.deploy.outputs.url }}
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Download build artifacts
|
- name: Download build artifacts
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: binary
|
name: linux-binary
|
||||||
|
|
||||||
|
- name: Validate staging configuration
|
||||||
|
run: |
|
||||||
|
echo "✅ Checking staging configuration..."
|
||||||
|
if [ -z "${{ secrets.STAGING_HOST }}" ]; then
|
||||||
|
echo "❌ STAGING_HOST is not configured"
|
||||||
|
echo "Please set STAGING_HOST in repository secrets"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [ -z "${{ secrets.STAGING_USER }}" ]; then
|
||||||
|
echo "❌ STAGING_USER is not configured"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [ -z "${{ secrets.STAGING_SSH_KEY }}" ]; then
|
||||||
|
echo "❌ STAGING_SSH_KEY is not configured"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "✅ Staging configuration validated"
|
||||||
|
|
||||||
- name: Deploy to staging server
|
- name: Deploy to staging server
|
||||||
id: deploy
|
|
||||||
uses: appleboy/ssh-action@v1.0.3
|
uses: appleboy/ssh-action@v1.0.3
|
||||||
env:
|
env:
|
||||||
BINARY_NAME: ${{ needs.build.outputs.binary_name }}
|
BINARY_NAME: ${{ needs.build.outputs.binary_name }}
|
||||||
VERSION: ${{ needs.build.outputs.version }}
|
VERSION: ${{ needs.build.outputs.version }}
|
||||||
CHECKSUM: ${{ needs.build.outputs.checksum }}
|
CHECKSUM: ${{ needs.build.outputs.checksum }}
|
||||||
DEPLOY_PATH: ${{ env.DEPLOY_PATH }}
|
|
||||||
SERVICE_NAME: ${{ env.SERVICE_NAME }}
|
|
||||||
CONFIG_PATH: ${{ env.CONFIG_PATH }}
|
|
||||||
BACKUP_PATH: ${{ env.BACKUP_PATH }}
|
|
||||||
with:
|
with:
|
||||||
host: ${{ secrets.STAGING_HOST }}
|
host: ${{ secrets.STAGING_HOST }}
|
||||||
username: ${{ secrets.STAGING_USER }}
|
username: ${{ secrets.STAGING_USER }}
|
||||||
@ -147,32 +253,25 @@ jobs:
|
|||||||
timeout: 300s
|
timeout: 300s
|
||||||
script: |
|
script: |
|
||||||
set -e
|
set -e
|
||||||
|
BINARY="${BINARY_NAME}"
|
||||||
|
DEPLOY_PATH="/opt/ppanel"
|
||||||
|
SERVICE_NAME="ppanel-server"
|
||||||
|
|
||||||
# Create necessary directories
|
echo "🚀 Deploying $BINARY to staging..."
|
||||||
sudo mkdir -p $DEPLOY_PATH $BACKUP_PATH $LOG_PATH
|
|
||||||
sudo chown $USER:$USER $DEPLOY_PATH $BACKUP_PATH $LOG_PATH
|
|
||||||
|
|
||||||
# Move binary to server
|
# Create directories
|
||||||
chmod +x $BINARY_NAME
|
sudo mkdir -p $DEPLOY_PATH /app/etc /opt/ppanel/{backups,logs}
|
||||||
mv $BINARY_NAME $DEPLOY_PATH/
|
|
||||||
|
|
||||||
# Create symlink for easy access
|
# Backup current binary
|
||||||
ln -sf $DEPLOY_PATH/$BINARY_NAME $DEPLOY_PATH/ppanel-server
|
if [ -f "$DEPLOY_PATH/ppanel-server" ]; then
|
||||||
|
sudo cp "$DEPLOY_PATH/ppanel-server" "/opt/ppanel/backups/ppanel-server-$(date +%Y%m%d_%H%M%S)"
|
||||||
# Backup current running binary if exists
|
|
||||||
if [ -f "$DEPLOY_PATH/ppanel-server-running" ]; then
|
|
||||||
cp "$DEPLOY_PATH/ppanel-server-running" "$BACKUP_PATH/ppanel-server-$(date +%Y%m%d_%H%M%S)"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Update running binary
|
# Install new binary
|
||||||
ln -sf $DEPLOY_PATH/$BINARY_NAME $DEPLOY_PATH/ppanel-server-running
|
chmod +x $BINARY
|
||||||
|
sudo mv $BINARY $DEPLOY_PATH/ppanel-server
|
||||||
|
|
||||||
# Verify binary
|
# Create systemd service
|
||||||
echo "Verifying binary checksum..."
|
|
||||||
echo "$CHECKSUM $DEPLOY_PATH/$BINARY_NAME" | sha256sum -c -
|
|
||||||
$DEPLOY_PATH/ppanel-server --version
|
|
||||||
|
|
||||||
# Create/Update systemd service
|
|
||||||
sudo tee /etc/systemd/system/$SERVICE_NAME.service > /dev/null << 'EOF'
|
sudo tee /etc/systemd/system/$SERVICE_NAME.service > /dev/null << 'EOF'
|
||||||
[Unit]
|
[Unit]
|
||||||
Description=PPanel Server
|
Description=PPanel Server
|
||||||
@ -182,68 +281,67 @@ jobs:
|
|||||||
Type=simple
|
Type=simple
|
||||||
User=root
|
User=root
|
||||||
WorkingDirectory=$DEPLOY_PATH
|
WorkingDirectory=$DEPLOY_PATH
|
||||||
ExecStart=$DEPLOY_PATH/ppanel-server-running run --config $CONFIG_PATH
|
ExecStart=$DEPLOY_PATH/ppanel-server run --config /app/etc/ppanel.yaml
|
||||||
Restart=always
|
Restart=always
|
||||||
RestartSec=5
|
RestartSec=5
|
||||||
StandardOutput=journal
|
StandardOutput=journal
|
||||||
StandardError=journal
|
StandardError=journal
|
||||||
SyslogIdentifier=$SERVICE_NAME
|
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
WantedBy=multi-user.target
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
# Reload systemd and restart service
|
# Restart service
|
||||||
sudo systemctl daemon-reload
|
sudo systemctl daemon-reload
|
||||||
sudo systemctl enable $SERVICE_NAME
|
sudo systemctl enable $SERVICE_NAME
|
||||||
sudo systemctl restart $SERVICE_NAME
|
sudo systemctl restart $SERVICE_NAME
|
||||||
|
|
||||||
# Wait for service to be ready
|
# Check status
|
||||||
sleep 5
|
sleep 5
|
||||||
if sudo systemctl is-active --quiet $SERVICE_NAME; then
|
if sudo systemctl is-active --quiet $SERVICE_NAME; then
|
||||||
echo "Service started successfully"
|
echo "✅ Staging deployment successful!"
|
||||||
echo "Service status:"
|
|
||||||
sudo systemctl status $SERVICE_NAME --no-pager
|
|
||||||
else
|
else
|
||||||
echo "Service failed to start"
|
echo "❌ Service failed to start"
|
||||||
sudo journalctl -u $SERVICE_NAME --no-pager -n 20
|
sudo journalctl -u $SERVICE_NAME --no-pager -n 20
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Health check
|
|
||||||
run: |
|
|
||||||
sleep 10
|
|
||||||
curl -f ${{ secrets.STAGING_URL }}/health || {
|
|
||||||
echo "Health check failed"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
deploy-production:
|
deploy-production:
|
||||||
name: Deploy to Production
|
name: Deploy to Production
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [build, deploy-staging]
|
needs: [build, deploy-staging]
|
||||||
if: github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && github.event.inputs.environment == 'production')
|
if: (github.event.inputs.mode == 'deploy-production' || github.event.inputs.mode == 'build-and-deploy' || github.event_name == 'release') && env.PRODUCTION_HOST != ''
|
||||||
environment:
|
|
||||||
name: production
|
|
||||||
url: ${{ steps.deploy.outputs.url }}
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Download build artifacts
|
- name: Download build artifacts
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: binary
|
name: linux-binary
|
||||||
|
|
||||||
|
- name: Validate production configuration
|
||||||
|
run: |
|
||||||
|
echo "✅ Checking production configuration..."
|
||||||
|
if [ -z "${{ secrets.PRODUCTION_HOST }}" ]; then
|
||||||
|
echo "❌ PRODUCTION_HOST is not configured"
|
||||||
|
echo "Please set PRODUCTION_HOST in repository secrets"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [ -z "${{ secrets.PRODUCTION_USER }}" ]; then
|
||||||
|
echo "❌ PRODUCTION_USER is not configured"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [ -z "${{ secrets.PRODUCTION_SSH_KEY }}" ]; then
|
||||||
|
echo "❌ PRODUCTION_SSH_KEY is not configured"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "✅ Production configuration validated"
|
||||||
|
|
||||||
- name: Deploy to production server
|
- name: Deploy to production server
|
||||||
id: deploy
|
|
||||||
uses: appleboy/ssh-action@v1.0.3
|
uses: appleboy/ssh-action@v1.0.3
|
||||||
env:
|
env:
|
||||||
BINARY_NAME: ${{ needs.build.outputs.binary_name }}
|
BINARY_NAME: ${{ needs.build.outputs.binary_name }}
|
||||||
VERSION: ${{ needs.build.outputs.version }}
|
VERSION: ${{ needs.build.outputs.version }}
|
||||||
CHECKSUM: ${{ needs.build.outputs.checksum }}
|
CHECKSUM: ${{ needs.build.outputs.checksum }}
|
||||||
DEPLOY_PATH: ${{ env.DEPLOY_PATH }}
|
|
||||||
SERVICE_NAME: ${{ env.SERVICE_NAME }}
|
|
||||||
CONFIG_PATH: ${{ env.CONFIG_PATH }}
|
|
||||||
BACKUP_PATH: ${{ env.BACKUP_PATH }}
|
|
||||||
with:
|
with:
|
||||||
host: ${{ secrets.PRODUCTION_HOST }}
|
host: ${{ secrets.PRODUCTION_HOST }}
|
||||||
username: ${{ secrets.PRODUCTION_USER }}
|
username: ${{ secrets.PRODUCTION_USER }}
|
||||||
@ -252,54 +350,25 @@ jobs:
|
|||||||
timeout: 300s
|
timeout: 300s
|
||||||
script: |
|
script: |
|
||||||
set -e
|
set -e
|
||||||
|
BINARY="${BINARY_NAME}"
|
||||||
|
DEPLOY_PATH="/opt/ppanel"
|
||||||
|
SERVICE_NAME="ppanel-server"
|
||||||
|
|
||||||
# Check if service is currently healthy
|
echo "🚀 Deploying $BINARY to production..."
|
||||||
if systemctl is-active --quiet $SERVICE_NAME; then
|
|
||||||
echo "Current service is running, preparing for zero-downtime deployment..."
|
|
||||||
|
|
||||||
# Download new binary
|
# Create directories
|
||||||
BINARY_NEW="$BINARY_NAME.new"
|
sudo mkdir -p $DEPLOY_PATH /app/etc /opt/ppanel/{backups,logs}
|
||||||
mv $BINARY_NAME $BINARY_NEW
|
|
||||||
chmod +x $BINARY_NEW
|
|
||||||
|
|
||||||
# Verify new binary
|
# Backup current binary
|
||||||
echo "Verifying new binary checksum..."
|
if [ -f "$DEPLOY_PATH/ppanel-server" ]; then
|
||||||
echo "$CHECKSUM $BINARY_NEW" | sha256sum -c -
|
sudo cp "$DEPLOY_PATH/ppanel-server" "/opt/ppanel/backups/ppanel-server-$(date +%Y%m%d_%H%M%S)"
|
||||||
./$BINARY_NEW --version
|
|
||||||
|
|
||||||
# Test new binary configuration
|
|
||||||
./$BINARY_NEW run --config $CONFIG_PATH --check || {
|
|
||||||
echo "Configuration check failed"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# Create necessary directories
|
|
||||||
sudo mkdir -p $DEPLOY_PATH $BACKUP_PATH $LOG_PATH
|
|
||||||
sudo chown $USER:$USER $DEPLOY_PATH $BACKUP_PATH $LOG_PATH
|
|
||||||
|
|
||||||
# Move to deployment directory
|
|
||||||
mv $BINARY_NEW $DEPLOY_PATH/
|
|
||||||
|
|
||||||
# Create symlink for easy access
|
|
||||||
ln -sf $DEPLOY_PATH/$BINARY_NAME $DEPLOY_PATH/ppanel-server
|
|
||||||
|
|
||||||
# Backup current running binary
|
|
||||||
if [ -f "$DEPLOY_PATH/ppanel-server-running" ]; then
|
|
||||||
cp "$DEPLOY_PATH/ppanel-server-running" "$BACKUP_PATH/ppanel-server-$(date +%Y%m%d_%H%M%S)"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Atomic update of running binary
|
|
||||||
ln -sf $DEPLOY_PATH/$BINARY_NAME $DEPLOY_PATH/ppanel-server-running.new
|
|
||||||
mv -Tf $DEPLOY_PATH/ppanel-server-running.new $DEPLOY_PATH/ppanel-server-running
|
|
||||||
|
|
||||||
else
|
|
||||||
echo "Service is not running, performing direct deployment..."
|
|
||||||
mv $BINARY_NAME $DEPLOY_PATH/
|
|
||||||
chmod +x $DEPLOY_PATH/$BINARY_NAME
|
|
||||||
ln -sf $DEPLOY_PATH/$BINARY_NAME $DEPLOY_PATH/ppanel-server-running
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Create/Update systemd service
|
# Install new binary
|
||||||
|
chmod +x $BINARY
|
||||||
|
sudo mv $BINARY $DEPLOY_PATH/ppanel-server
|
||||||
|
|
||||||
|
# Create/update systemd service
|
||||||
sudo tee /etc/systemd/system/$SERVICE_NAME.service > /dev/null << 'EOF'
|
sudo tee /etc/systemd/system/$SERVICE_NAME.service > /dev/null << 'EOF'
|
||||||
[Unit]
|
[Unit]
|
||||||
Description=PPanel Server
|
Description=PPanel Server
|
||||||
@ -309,151 +378,69 @@ jobs:
|
|||||||
Type=simple
|
Type=simple
|
||||||
User=root
|
User=root
|
||||||
WorkingDirectory=$DEPLOY_PATH
|
WorkingDirectory=$DEPLOY_PATH
|
||||||
ExecStart=$DEPLOY_PATH/ppanel-server-running run --config $CONFIG_PATH
|
ExecStart=$DEPLOY_PATH/ppanel-server run --config /app/etc/ppanel.yaml
|
||||||
Restart=always
|
Restart=always
|
||||||
RestartSec=5
|
RestartSec=5
|
||||||
StandardOutput=journal
|
StandardOutput=journal
|
||||||
StandardError=journal
|
StandardError=journal
|
||||||
SyslogIdentifier=$SERVICE_NAME
|
|
||||||
# Limit resources
|
|
||||||
LimitNOFILE=65536
|
|
||||||
MemoryLimit=512M
|
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
WantedBy=multi-user.target
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
# Reload systemd and restart service
|
# Restart service
|
||||||
sudo systemctl daemon-reload
|
sudo systemctl daemon-reload
|
||||||
sudo systemctl enable $SERVICE_NAME
|
sudo systemctl enable $SERVICE_NAME
|
||||||
sudo systemctl restart $SERVICE_NAME
|
sudo systemctl restart $SERVICE_NAME
|
||||||
|
|
||||||
# Wait for service to be ready
|
# Wait for service to be ready
|
||||||
sleep 10
|
sleep 10
|
||||||
|
|
||||||
# Check service status
|
|
||||||
if sudo systemctl is-active --quiet $SERVICE_NAME; then
|
if sudo systemctl is-active --quiet $SERVICE_NAME; then
|
||||||
echo "✅ Service started successfully"
|
echo "✅ Production deployment successful!"
|
||||||
echo "📊 Service status:"
|
echo "📊 Service status:"
|
||||||
sudo systemctl status $SERVICE_NAME --no-pager -l
|
sudo systemctl status $SERVICE_NAME --no-pager
|
||||||
echo "📝 Recent logs:"
|
|
||||||
sudo journalctl -u $SERVICE_NAME --no-pager -n 10
|
|
||||||
else
|
else
|
||||||
echo "❌ Service failed to start"
|
echo "❌ Production deployment failed!"
|
||||||
echo "📝 Error logs:"
|
|
||||||
sudo journalctl -u $SERVICE_NAME --no-pager -n 50
|
|
||||||
|
|
||||||
# Attempt rollback
|
|
||||||
echo "🔄 Attempting rollback..."
|
echo "🔄 Attempting rollback..."
|
||||||
LATEST_BACKUP=$(ls -t $BACKUP_PATH/ppanel-server-* 2>/dev/null | head -1)
|
LATEST_BACKUP=$(ls -t /opt/ppanel/backups/ppanel-server-* 2>/dev/null | head -1)
|
||||||
if [ -n "$LATEST_BACKUP" ]; then
|
if [ -n "$LATEST_BACKUP" ]; then
|
||||||
cp "$LATEST_BACKUP" "$DEPLOY_PATH/ppanel-server-running"
|
sudo cp "$LATEST_BACKUP" "$DEPLOY_PATH/ppanel-server"
|
||||||
sudo systemctl restart $SERVICE_NAME
|
sudo systemctl restart $SERVICE_NAME
|
||||||
echo "Rollback completed"
|
echo "✅ Rollback completed"
|
||||||
fi
|
fi
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Health check
|
docker:
|
||||||
run: |
|
name: Build Docker Image
|
||||||
echo "Waiting for service to be fully ready..."
|
|
||||||
for i in {1..30}; do
|
|
||||||
if curl -f ${{ secrets.PRODUCTION_URL }}/health; then
|
|
||||||
echo "✅ Health check passed"
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
echo "Attempt $i: Service not ready, waiting 10 seconds..."
|
|
||||||
sleep 10
|
|
||||||
done
|
|
||||||
|
|
||||||
- name: Deployment notification
|
|
||||||
if: always()
|
|
||||||
run: |
|
|
||||||
if [ "${{ job.status }}" = "success" ]; then
|
|
||||||
echo "🎉 Production deployment completed successfully!"
|
|
||||||
echo "Version: ${{ needs.build.outputs.version }}"
|
|
||||||
echo "Deployed to: ${{ secrets.PRODUCTION_URL }}"
|
|
||||||
else
|
|
||||||
echo "❌ Production deployment failed!"
|
|
||||||
echo "Please check the logs and rollback if necessary"
|
|
||||||
fi
|
|
||||||
|
|
||||||
rollback:
|
|
||||||
name: Rollback Production
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: deploy-production
|
needs: build
|
||||||
if: failure() && github.event_name == 'workflow_dispatch'
|
if: (secrets.DOCKER_USERNAME && secrets.DOCKER_PASSWORD) && (github.event_name == 'release' || github.event.inputs.mode == 'build-and-deploy')
|
||||||
environment: production
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Rollback to previous version
|
- name: Checkout repository
|
||||||
uses: appleboy/ssh-action@v1.0.3
|
uses: actions/checkout@v4
|
||||||
env:
|
|
||||||
DEPLOY_PATH: ${{ env.DEPLOY_PATH }}
|
- name: Set up Docker Buildx
|
||||||
SERVICE_NAME: ${{ env.SERVICE_NAME }}
|
uses: docker/setup-buildx-action@v3
|
||||||
BACKUP_PATH: ${{ env.BACKUP_PATH }}
|
|
||||||
|
- name: Log in to Docker Hub
|
||||||
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
host: ${{ secrets.PRODUCTION_HOST }}
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
username: ${{ secrets.PRODUCTION_USER }}
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
key: ${{ secrets.PRODUCTION_SSH_KEY }}
|
|
||||||
port: ${{ secrets.PRODUCTION_PORT || 22 }}
|
|
||||||
script: |
|
|
||||||
set -e
|
|
||||||
|
|
||||||
echo "🔄 Rolling back to previous version..."
|
- name: Build and push Docker image
|
||||||
|
uses: docker/build-push-action@v6
|
||||||
# Find latest backup
|
|
||||||
LATEST_BACKUP=$(ls -t $BACKUP_PATH/ppanel-server-* 2>/dev/null | head -1)
|
|
||||||
|
|
||||||
if [ -z "$LATEST_BACKUP" ]; then
|
|
||||||
echo "❌ No backup found for rollback"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "📦 Rolling back to: $(basename $LATEST_BACKUP)"
|
|
||||||
|
|
||||||
# Restore from backup
|
|
||||||
cp "$LATEST_BACKUP" "$DEPLOY_PATH/ppanel-server-running"
|
|
||||||
|
|
||||||
# Restart service
|
|
||||||
sudo systemctl restart $SERVICE_NAME
|
|
||||||
|
|
||||||
# Wait and check
|
|
||||||
sleep 10
|
|
||||||
if sudo systemctl is-active --quiet $SERVICE_NAME; then
|
|
||||||
echo "✅ Rollback completed successfully"
|
|
||||||
sudo systemctl status $SERVICE_NAME --no-pager
|
|
||||||
else
|
|
||||||
echo "❌ Rollback failed"
|
|
||||||
sudo journalctl -u $SERVICE_NAME --no-pager -n 20
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
cleanup:
|
|
||||||
name: Cleanup Old Artifacts
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: [deploy-production]
|
|
||||||
if: always() && needs.deploy-production.result == 'success'
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Cleanup old binaries and backups
|
|
||||||
uses: appleboy/ssh-action@v1.0.3
|
|
||||||
env:
|
|
||||||
DEPLOY_PATH: ${{ env.DEPLOY_PATH }}
|
|
||||||
BACKUP_PATH: ${{ env.BACKUP_PATH }}
|
|
||||||
with:
|
with:
|
||||||
host: ${{ secrets.PRODUCTION_HOST }}
|
context: .
|
||||||
username: ${{ secrets.PRODUCTION_USER }}
|
platforms: linux/amd64
|
||||||
key: ${{ secrets.PRODUCTION_SSH_KEY }}
|
push: true
|
||||||
script: |
|
tags: |
|
||||||
echo "🧹 Cleaning up old artifacts..."
|
${{ secrets.DOCKER_USERNAME }}/ppanel-server:${{ needs.build.outputs.version }}
|
||||||
|
${{ secrets.DOCKER_USERNAME }}/ppanel-server:latest
|
||||||
# Keep only last 5 binaries in deployment directory
|
build-args: |
|
||||||
cd $DEPLOY_PATH
|
VERSION=${{ needs.build.outputs.version }}
|
||||||
ls -t ppanel-server-*-linux-amd64 | tail -n +6 | xargs -r rm
|
labels: |
|
||||||
|
org.opencontainers.image.version=${{ needs.build.outputs.version }}
|
||||||
# Keep only last 10 backups
|
org.opencontainers.image.revision=${{ github.sha }}
|
||||||
cd $BACKUP_PATH
|
|
||||||
ls -t ppanel-server-* | tail -n +11 | xargs -r rm
|
|
||||||
|
|
||||||
echo "✅ Cleanup completed"
|
|
||||||
Loading…
x
Reference in New Issue
Block a user