Update Deploy.yml #11
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: Deploy | ||
on: | ||
push: | ||
branches: [ master ] | ||
workflow_dispatch: | ||
env: | ||
DOCKER_REGISTRY: ghcr.io | ||
IMAGE_NAME: ${{ github.repository | lower }} | ||
jobs: | ||
deploy: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- name: Checkout code with submodules | ||
uses: actions/checkout@v4 | ||
with: | ||
submodules: recursive | ||
token: ${{ secrets.GITHUB_TOKEN }} | ||
- name: Force initialize submodule | ||
run: | | ||
echo "=== Force initializing FileChecker submodule ===" | ||
git submodule add --force https://github.com/Safeturned/FileChecker.git FileChecker || true | ||
git submodule update --init --recursive | ||
echo "=== Checking submodule status ===" | ||
git submodule status | ||
echo "=== FileChecker directory contents ===" | ||
ls -la FileChecker/ | ||
echo "=== FileChecker/src directory contents ===" | ||
ls -la FileChecker/src/ | ||
- name: Setup .NET | ||
uses: actions/setup-dotnet@v4 | ||
env: | ||
DOTNET_NOLOGO: true | ||
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true | ||
DOTNET_CLI_TELEMETRY_OPTOUT: true | ||
with: | ||
dotnet-version: 9.* | ||
- name: Setup Docker Buildx | ||
uses: docker/setup-buildx-action@v3 | ||
- name: Install Aspire CLI | ||
run: dotnet tool install --global Aspire.Cli | ||
- name: Publish with Aspire | ||
run: | | ||
aspire publish -o ./publish/aspire | ||
- name: Debug Aspire output | ||
run: | | ||
echo "=== Aspire publish output ===" | ||
ls -la ./publish/aspire/ | ||
echo "=== Docker compose content ===" | ||
cat ./publish/aspire/docker-compose.yaml | ||
- name: Tag and push Docker image | ||
run: | | ||
# Find the latest built image for safeturned-api from aspire publish | ||
BUILT_IMAGE=$(docker images --format "{{.Repository}}:{{.Tag}}" | grep safeturned-api | head -n 1) | ||
if [ -n "$BUILT_IMAGE" ]; then | ||
echo "Found built image: $BUILT_IMAGE" | ||
# Login to registry | ||
echo "${{ secrets.DOCKER_PASSWORD }}" | docker login ${{ env.DOCKER_REGISTRY }} -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin | ||
# Tag with registry path | ||
docker tag "$BUILT_IMAGE" "${{ env.DOCKER_REGISTRY }}/${{ env.IMAGE_NAME }}:latest" | ||
# Push to registry | ||
docker push "${{ env.DOCKER_REGISTRY }}/${{ env.IMAGE_NAME }}:latest" | ||
echo "✅ Image pushed successfully" | ||
else | ||
echo "❌ No built image found, skipping push" | ||
exit 1 | ||
fi | ||
- name: Create deployment package | ||
run: | | ||
mkdir -p ./deploy | ||
# Copy Aspire generated files | ||
cp ./publish/aspire/docker-compose.yaml ./deploy/ | ||
cp ./publish/aspire/*.env ./deploy/ 2>/dev/null || true | ||
# No tar files from Aspire - it builds locally but doesn't export | ||
# Create override to use registry image and expose port | ||
cat > ./deploy/docker-compose.override.yaml << 'EOF' | ||
services: | ||
safeturned-api: | ||
image: ${{ env.DOCKER_REGISTRY }}/${{ env.IMAGE_NAME }}:latest | ||
ports: | ||
- "8080:${SAFETURNED_API_PORT:-80}" # Expose for Cloudflare Tunnel | ||
EOF | ||
# Create deployment script | ||
cat > ./deploy/deploy.sh << 'EOF' | ||
#!/bin/bash | ||
set -e | ||
echo "Starting deployment..." | ||
# Set default environment variables | ||
export DATABASE_PASSWORD=${DATABASE_PASSWORD:-"safeturned_password"} | ||
export SAFETURNED_API_PORT=${SAFETURNED_API_PORT:-"8080"} | ||
export CLOUDFLARE_TUNNEL_TOKEN=${CLOUDFLARE_TUNNEL_TOKEN:-""} | ||
echo "Environment variables set:" | ||
echo "DATABASE_PASSWORD: $DATABASE_PASSWORD" | ||
echo "SAFETURNED_API_PORT: $SAFETURNED_API_PORT" | ||
echo "CLOUDFLARE_TUNNEL_TOKEN: ${CLOUDFLARE_TUNNEL_TOKEN:0:10}..." | ||
# Stop existing containers | ||
docker compose down || true | ||
# Pull the latest image from registry | ||
echo "Pulling latest image from registry..." | ||
docker pull ${{ env.DOCKER_REGISTRY }}/${{ env.IMAGE_NAME }}:latest || echo "Image pull failed, will build locally" | ||
# Start with Aspire's docker-compose | ||
docker compose up -d | ||
# Wait for services to be healthy | ||
echo "Waiting for services to be healthy..." | ||
timeout 300 bash -c 'until docker compose ps | grep -q "healthy"; do sleep 5; done' || echo "Warning: Some services may not be healthy" | ||
echo "Deployment completed successfully!" | ||
echo "API is available on port 8080 for Cloudflare Tunnel" | ||
EOF | ||
chmod +x ./deploy/deploy.sh | ||
echo "Deployment package created in ./deploy/" | ||
- name: Debug deployment files | ||
run: | | ||
echo "=== Debugging deployment files ===" | ||
echo "Current directory: $(pwd)" | ||
echo "Deploy directory contents:" | ||
ls -la ./deploy/ | ||
echo "Deploy.sh contents:" | ||
cat ./deploy/deploy.sh | ||
- name: Copy files to server | ||
uses: appleboy/scp-action@v1 | ||
with: | ||
host: ${{ secrets.SERVER_HOST }} | ||
port: ${{ secrets.SSH_PORT }} | ||
username: ${{ secrets.SERVER_USER }} | ||
key: ${{ secrets.SSH_PRIVATE_KEY }} | ||
source: "./deploy/" | ||
target: "/opt/safeturned" | ||
strip_components: 0 | ||
- name: Debug server files | ||
uses: appleboy/ssh-action@v1 | ||
with: | ||
host: ${{ secrets.SERVER_HOST }} | ||
port: ${{ secrets.SSH_PORT }} | ||
username: ${{ secrets.SERVER_USER }} | ||
key: ${{ secrets.SSH_PRIVATE_KEY }} | ||
script: | | ||
echo "=== Debugging server files ===" | ||
echo "Current directory: $(pwd)" | ||
echo "Opt directory contents:" | ||
ls -la /opt/ | ||
echo "Safeturned directory contents:" | ||
ls -la /opt/safeturned/ || echo "Directory does not exist" | ||
- name: Copy Aspire build artifacts | ||
run: | | ||
# Aspire creates the container image, we just need to copy the compose files | ||
echo "Aspire build artifacts are ready in ./publish/aspire/" | ||
echo "Deployment package created in ./deploy/" | ||
- name: Deploy to server | ||
uses: appleboy/ssh-action@v1 | ||
with: | ||
host: ${{ secrets.SERVER_HOST }} | ||
port: ${{ secrets.SSH_PORT }} | ||
username: ${{ secrets.SERVER_USER }} | ||
key: ${{ secrets.SSH_PRIVATE_KEY }} | ||
script: | | ||
cd /opt/safeturned/deploy | ||
./deploy.sh | ||
- name: Create Release | ||
if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main' | ||
uses: actions/create-release@v1 | ||
env: | ||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
with: | ||
tag_name: v${{ github.run_number }} | ||
release_name: Release v${{ github.run_number }} | ||
body: | | ||
## Changes | ||
- Built from commit: ${{ github.sha }} | ||
- Deployed to server: ${{ secrets.SERVER_HOST }} | ||
## Deployment Status | ||
Application has been deployed to your server and is running with docker-compose. | ||
## Cloudflare Tunnel Configuration | ||
Your API is now available on port 8080 and ready for Cloudflare Tunnel configuration. | ||
### Cloudflare Tunnel Setup: | ||
1. Install cloudflared on your server | ||
2. Create a tunnel in Cloudflare dashboard | ||
3. Configure the tunnel to point to `localhost:8080` | ||
4. Set up your custom domain in Cloudflare | ||
draft: false | ||
prerelease: false |