pkmntrade.club/.github/workflows/build_deploy.yml

251 lines
No EOL
9.7 KiB
YAML

name: Build & Deploy
on:
workflow_dispatch:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
# Job 1: Build the Docker image
build:
runs-on: ubuntu-latest
outputs:
repo: ${{ steps.meta.outputs.REPO }}
repo-name: ${{ steps.meta.outputs.REPO_NAME_ONLY }}
repo-path: ${{ steps.meta.outputs.REPO_PROJECT_PATH }}
image-tar: ${{ steps.meta.outputs.REPO_NAME_ONLY }}-${{ github.ref_name }}_${{ github.sha }}.tar
tags: ${{ steps.generated_docker_tags.outputs.tag }}
prod: ${{ steps.meta.outputs.prod }}
steps:
- name: Checkout the repo
uses: actions/checkout@v4
- name: Ensure scripts are executable
run: chmod +x scripts/*.sh
- name: Setup build metadata and environment
id: meta
run: |
echo "✅ Exit script on any error"
set -eu -o pipefail
# Parse repository name and set outputs
eval "$(./scripts/parse-repository-name.sh '${{ github.repository }}')"
echo "REPO=$REPO" >> $GITHUB_OUTPUT
echo "REPO_NAME_ONLY=$REPO_NAME_ONLY" >> $GITHUB_OUTPUT
echo "REPO_PROJECT_PATH=$REPO_PROJECT_PATH" >> $GITHUB_OUTPUT
# Determine PROD environment
prod_value=""
echo "🔍 Check if PROD is set via vars; if not, determine from github.ref"
if [ -z "${{ vars.PROD }}" ]; then
prod_value="${{ startsWith(github.ref, 'refs/tags/v') && !endsWith(github.ref, '-prerelease') }}"
echo "📦 PROD mode unset, determined from github.ref (starts with v and does not end with -prerelease?): ${prod_value}"
else
prod_value="${{ vars.PROD }}"
echo "📦 PROD mode already set to: ${prod_value}"
fi
echo "prod=${prod_value}" >> $GITHUB_OUTPUT
# Set environment variables for subsequent steps
echo "🖊️ Writing determined values to GITHUB_ENV:"
echo "PROD=${prod_value}" >> $GITHUB_ENV
echo "PROD=${prod_value} -> GITHUB_ENV"
echo "IMAGE_TAR_NAME=${REPO_NAME_ONLY}-${{ github.ref_name }}_${{ github.sha }}.tar" >> $GITHUB_ENV
echo "IMAGE_TAR_NAME=${REPO_NAME_ONLY}-${{ github.ref_name }}_${{ github.sha }}.tar -> GITHUB_ENV"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Generate tags
id: generated_docker_tags
run: |
echo "✅ Exit script on any error"
set -eu -o pipefail
# Use the script to generate tags
TAG_LIST=$(./scripts/generate-docker-tags.sh \
"${{ steps.meta.outputs.REPO }}" \
"${{ github.sha }}" \
"${{ github.ref }}" \
"$PROD")
echo "Final list of generated tags:"
echo "$TAG_LIST"
TAG_COUNT=$(echo "$TAG_LIST" | wc -l)
if [[ -z "${{ github.sha }}" || $TAG_COUNT -lt 4 ]]; then
echo "⚠️ No tags (or too few) were generated based on the logic. Need at least 4 tags. Generated: $TAG_COUNT"
exit 1
fi
# Output the tags for the docker build action
{
echo "tag<<EOF"
echo "$TAG_LIST"
echo "EOF"
} >> "$GITHUB_OUTPUT"
- name: Run prebuild tasks
run: ./scripts/prebuild.sh
- name: Cache Docker layers
uses: actions/cache@v4
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ github.sha }}
restore-keys: |
${{ runner.os }}-buildx-
- name: Extract version for Docker build
id: extract_version
run: |
pip install setuptools-scm
VERSION=$(python -c "from setuptools_scm import get_version; print(get_version())")
echo "VERSION=${VERSION}" >> $GITHUB_ENV
- name: Build container
uses: docker/build-push-action@v6
with:
outputs: type=docker,dest=${{ runner.temp }}/${{ steps.meta.outputs.REPO_NAME_ONLY }}-${{ github.ref_name }}_${{ github.sha }}.tar
tags: ${{ steps.generated_docker_tags.outputs.tag }}
build-args: |
VERSION=${{ env.VERSION }}
context: .
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max
- name: Rotate cache
run: |
rm -rf /tmp/.buildx-cache
mv /tmp/.buildx-cache-new /tmp/.buildx-cache
- name: Upload container as artifact
uses: actions/upload-artifact@v4
with:
name: ${{ env.IMAGE_TAR_NAME }}
path: ${{ runner.temp }}/${{ steps.meta.outputs.REPO_NAME_ONLY }}-${{ github.ref_name }}_${{ github.sha }}.tar
if-no-files-found: error
retention-days: 1
# Job 2: Deploy (only runs on main branch or tags)
deploy:
needs: build
runs-on: ubuntu-latest
if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/'))
# Determine environment based on ref
environment: ${{ (startsWith(github.ref, 'refs/tags/v') && !endsWith(github.ref, '-prerelease')) && 'production' || 'staging' }}
steps:
- name: Checkout the repo
uses: actions/checkout@v4
- name: Ensure scripts are executable
run: chmod +x scripts/*.sh
- name: Setup deployment metadata and environment
id: meta
run: |
echo "✅ Exit script on any error"
set -eu -o pipefail
# Parse repository name and set outputs
eval "$(./scripts/parse-repository-name.sh '${{ github.repository }}')"
echo "REPO=$REPO" >> $GITHUB_OUTPUT
echo "REPO_NAME_ONLY=$REPO_NAME_ONLY" >> $GITHUB_OUTPUT
echo "REPO_PROJECT_PATH=$REPO_PROJECT_PATH" >> $GITHUB_OUTPUT
# Determine PROD environment
prod_value=""
echo "🔍 Check if PROD is set via vars; if not, determine from github.ref"
if [ -z "${{ vars.PROD }}" ]; then
prod_value="${{ startsWith(github.ref, 'refs/tags/v') && !endsWith(github.ref, '-prerelease') }}"
echo "📦 PROD mode unset, determined from github.ref (starts with v and does not end with -prerelease?): ${prod_value}"
else
prod_value="${{ vars.PROD }}"
echo "📦 PROD mode already set to: ${prod_value}"
fi
echo "prod=${prod_value}" >> $GITHUB_OUTPUT
# Set all deployment environment variables
echo "📝 Setting deployment environment variables"
echo "REPO_PROJECT_PATH=${REPO_PROJECT_PATH}" >> $GITHUB_ENV
echo "REPO_NAME_ONLY=${REPO_NAME_ONLY}" >> $GITHUB_ENV
echo "IMAGE_TAR_NAME=${REPO_NAME_ONLY}-${{ github.ref_name }}_${{ github.sha }}.tar" >> $GITHUB_ENV
echo "PROD=${prod_value}" >> $GITHUB_ENV
- name: Download container artifact
uses: actions/download-artifact@v4
with:
name: ${{ env.IMAGE_TAR_NAME }}
path: ${{ runner.temp }}
- name: Get Deploy Secrets
uses: bitwarden/sm-action@v2
with:
access_token: ${{ secrets.BW_ACCESS_TOKEN }}
secrets: |
27a8da8d-5fb4-4f58-baaf-b2dd0032eca8 > DEPLOY_HOST
4cf9ab8d-1772-4ab0-9219-b2dd003315d4 > DEPLOY_KEY
9aefe34e-c2cf-442e-973c-b2dd0032b6cf > ENV_FILE_BASE64
d3bb47f8-bfc0-4a61-9cee-b2df0147a02a > CF_PEM_CERT
5f658ddf-aadd-4464-b501-b2df0147c338 > CF_PEM_CA
- name: Set up SSH
run: |
mkdir -p $HOME/.ssh
echo -e "${DEPLOY_KEY}" > $HOME/.ssh/deploy.key
chmod 700 $HOME/.ssh
chmod 600 $HOME/.ssh/deploy.key
cat >>$HOME/.ssh/config <<END
Host deploy
HostName ${DEPLOY_HOST}
Port ${{ vars.DEPLOY_PORT }}
User ${{ vars.DEPLOY_USER }}
IdentityFile $HOME/.ssh/deploy.key
UserKnownHostsFile /dev/null
StrictHostKeyChecking no
ControlMaster auto
ControlPath $HOME/.ssh/control-%C
ControlPersist yes
END
- name: Deploy to Server
env:
DOCKER_HOST: ssh://deploy
REPO_PROJECT_PATH: ${{ env.REPO_PROJECT_PATH }}
REPO_NAME_ONLY: ${{ env.REPO_NAME_ONLY }}
IMAGE_TAR: ${{ runner.temp }}/${{ env.IMAGE_TAR_NAME }}
PROD: ${{ env.PROD }}
run: |
echo "✅ Exit script on any error"
set -eu -o pipefail
./scripts/deploy-to-server.sh
- name: Health Check and Rollback
run: |
# Determine the correct URL based on environment
if [ "${{ env.PROD }}" = "true" ]; then
# Ensure PRODUCTION_DOMAIN is set
if [ -z "${{ vars.PRODUCTION_DOMAIN }}" ]; then
echo "Error: PRODUCTION_DOMAIN is not set"
exit 1
fi
HEALTH_CHECK_URL="https://${{ vars.PRODUCTION_DOMAIN }}/health/"
else
# Ensure STAGING_DOMAIN is set
if [ -z "${{ vars.STAGING_DOMAIN }}" ]; then
echo "Error: STAGING_DOMAIN is not set"
exit 1
fi
HEALTH_CHECK_URL="https://${{ vars.STAGING_DOMAIN }}/health/"
fi
# Copy script to remote and execute
scp scripts/health-check-and-rollback.sh deploy:/tmp/
ssh deploy "chmod +x /tmp/health-check-and-rollback.sh"
ssh deploy "/tmp/health-check-and-rollback.sh '${{ env.REPO_PROJECT_PATH }}' '${{ env.PROD }}' '$HEALTH_CHECK_URL' 30"
ssh deploy "rm -f /tmp/health-check-and-rollback.sh"