feat: add initial multi-environment CI/CD pipeline POC #3

Merged
nietzshn merged 24 commits from staging into main 2026-06-02 22:31:09 -06:00
2 changed files with 96 additions and 46 deletions
Showing only changes of commit 0a798cf3b0 - Show all commits
+33 -14
View File
@@ -66,34 +66,52 @@ jobs:
url: https://practicas.prod.kubistudio.cloud url: https://practicas.prod.kubistudio.cloud
steps: steps:
- name: Deploy via SSH - name: Deploy via SSH
uses: appleboy/ssh-action@v1.0.3 run: |
with: set -euo pipefail
host: ${{ secrets.DEPLOY_HOST }} IMAGE_TAG="${{ needs.build-and-push.outputs.image_tag }}"
username: ${{ secrets.DEPLOY_USERNAME }} APP_VERSION="${{ needs.build-and-push.outputs.app_version }}"
key: ${{ secrets.DEPLOY_SSH_KEY }}
script: | eval $(ssh-agent -s)
echo "${{ secrets.DEPLOY_SSH_KEY }}" | ssh-add -
mkdir -p ~/.ssh
ssh-keyscan -H ${{ secrets.DEPLOY_HOST }} >> ~/.ssh/known_hosts 2>/dev/null
# 1. Pasamos las variables como argumentos en el mismo orden
ssh ${{ secrets.DEPLOY_USERNAME }}@${{ secrets.DEPLOY_HOST }} bash -s \
"${{ env.REGISTRY_URL }}" \
"${{ env.IMAGE_NAME }}" \
"${IMAGE_TAG}" \
"${APP_VERSION}" \
"${{ gitea.actor }}" \
"${{ secrets.TOKEN }}" << 'EOF'
set -euo pipefail set -euo pipefail
# 2. Las recibimos dentro de la sesión remota
REGISTRY_URL=$1
IMAGE_NAME=$2
IMAGE_TAG=$3
APP_VERSION=$4
GITEA_ACTOR=$5
TOKEN=$6
echo "Saving current image tag for rollback..." echo "Saving current image tag for rollback..."
CURRENT_IMAGE=$(docker inspect cicd-prod --format '{{.Config.Image}}' 2>/dev/null || echo "") CURRENT_IMAGE=$(docker inspect cicd-prod --format '{{.Config.Image}}' 2>/dev/null || echo "")
echo "Pulling image..." echo "Pulling image..."
echo "${{ secrets.TOKEN }}" | docker login ${{ env.REGISTRY_URL }} -u ${{ gitea.actor }} --password-stdin echo "$TOKEN" | docker login $REGISTRY_URL -u $GITEA_ACTOR --password-stdin
docker pull ${{ env.REGISTRY_URL }}/${{ env.IMAGE_NAME }}:${{ needs.build-and-push.outputs.image_tag }} docker pull $REGISTRY_URL/$IMAGE_NAME:$IMAGE_TAG
echo "Stopping existing container..." echo "Stopping existing container..."
docker stop cicd-prod 2>/dev/null || true docker stop cicd-prod 2>/dev/null || true
docker rm cicd-prod 2>/dev/null || true docker rm cicd-prod 2>/dev/null || true
echo "Starting new container..." echo "Starting new container..."
docker run -d \ docker run -d --name cicd-prod --restart unless-stopped -p 8083:80 \
--name cicd-prod \
--restart unless-stopped \
-p 8083:80 \
-e APP_ENV=production \ -e APP_ENV=production \
-e APP_VERSION=${{ needs.build-and-push.outputs.app_version }} \ -e APP_VERSION=${APP_VERSION} \
-e DEPLOY_TIME=$(date -u +"%Y-%m-%dT%H:%M:%SZ") \ -e DEPLOY_TIME=$(date -u +"%Y-%m-%dT%H:%M:%SZ") \
${{ env.REGISTRY_URL }}/${{ env.IMAGE_NAME }}:${{ needs.build-and-push.outputs.image_tag }} $REGISTRY_URL/$IMAGE_NAME:$IMAGE_TAG
echo "Waiting for health check..." echo "Waiting for health check..."
HEALTHY=false HEALTHY=false
@@ -129,6 +147,7 @@ jobs:
exit 1 exit 1
fi fi
EOF
release-notes: release-notes:
name: Release Notes name: Release Notes
+63 -32
View File
@@ -55,47 +55,78 @@ jobs:
needs: build-and-push needs: build-and-push
steps: steps:
- name: Deploy via SSH - name: Deploy via SSH
uses: appleboy/ssh-action@v1.0.3 run: |
with: set -euo pipefail
host: ${{ secrets.DEPLOY_HOST }} IMAGE_TAG="${{ needs.build-and-push.outputs.image_tag }}"
username: ${{ secrets.DEPLOY_USERNAME }}
key: ${{ secrets.DEPLOY_SSH_KEY }} eval $(ssh-agent -s)
script: | echo "${{ secrets.DEPLOY_SSH_KEY }}" | ssh-add -
mkdir -p ~/.ssh
ssh-keyscan -H ${{ secrets.DEPLOY_HOST }} >> ~/.ssh/known_hosts 2>/dev/null
# 1. Pasamos las variables como argumentos en el mismo orden
ssh ${{ secrets.DEPLOY_USERNAME }}@${{ secrets.DEPLOY_HOST }} bash -s \
"${{ env.REGISTRY_URL }}" \
"${{ env.IMAGE_NAME }}" \
"${IMAGE_TAG}" \
"${{ gitea.sha }}" \
"${{ gitea.actor }}" \
"${{ gitea.run_id }}" \
"${{ secrets.TOKEN }}" << 'EOF'
set -euo pipefail set -euo pipefail
# 2. Las recibimos dentro de la sesión remota
REGISTRY_URL=$1
IMAGE_NAME=$2
IMAGE_TAG=$3
GIT_SHA=$4
GITEA_ACTOR=$5
BUILD_NUMBER=$6
TOKEN=$7
# Variables locales del script
GIT_BRANCH="staging"
BUILD_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
echo "Pulling image..." echo "Pulling image..."
echo "${{ secrets.TOKEN }}" | docker login ${{ env.REGISTRY_URL }} -u ${{ gitea.actor }} --password-stdin echo "$TOKEN" | docker login $REGISTRY_URL -u $GITEA_ACTOR --password-stdin
docker pull ${{ env.REGISTRY_URL }}/${{ env.IMAGE_NAME }}:${{ needs.build-and-push.outputs.image_tag }} docker pull $REGISTRY_URL/$IMAGE_NAME:$IMAGE_TAG
echo "Stopping existing container..." echo "Stopping existing container..."
docker stop cicd-staging 2>/dev/null || true docker stop cicd-staging 2>/dev/null || true
docker rm cicd-staging 2>/dev/null || true docker rm cicd-staging 2>/dev/null || true
echo "Starting new container..." echo "Starting new container..."
docker run -d \ docker run -d --name cicd-staging --restart unless-stopped -p 8082:80 \
--name cicd-staging \
--restart unless-stopped \
-p 8082:80 \
-e APP_ENV=staging \ -e APP_ENV=staging \
-e APP_VERSION=staging-${{ gitea.sha }} \ -e APP_VERSION=staging-${GIT_SHA} \
-e GIT_COMMIT=${{ gitea.sha }} \ -e GIT_COMMIT=${GIT_SHA} \
-e GIT_BRANCH=staging \ -e GIT_BRANCH=${GIT_BRANCH} \
-e BUILD_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ") \ -e BUILD_DATE=${BUILD_DATE} \
-e DEPLOY_TIME=$(date -u +"%Y-%m-%dT%H:%M:%SZ") \ -e DEPLOY_TIME=${BUILD_DATE} \
-e BUILD_NUMBER=${{ gitea.run_id }} \ -e BUILD_NUMBER=${BUILD_NUMBER} \
${{ env.REGISTRY_URL }}/${{ env.IMAGE_NAME }}:${{ needs.build-and-push.outputs.image_tag }} $REGISTRY_URL/$IMAGE_NAME:$IMAGE_TAG
echo "Running smoke tests..." echo "Waiting for health check..."
RESPONSE=$(curl -sf http://localhost:8082/health) HEALTHY=false
echo "Health response: $RESPONSE" for i in $(seq 1 12); do
RESPONSE=$(curl -sf http://localhost:8082/health || echo "")
ENV_VALUE=$(echo "$RESPONSE" | python3 -c "import sys,json; print(json.load(sys.stdin)['env'])" 2>/dev/null || echo "unknown") if [ -n "$RESPONSE" ]; then
if [ "$ENV_VALUE" != "staging" ]; then ENV_VALUE=$(echo "$RESPONSE" | python3 -c "import sys,json; print(json.load(sys.stdin)['env'])" 2>/dev/null || echo "unknown")
echo "::error::Smoke test failed: expected env=staging, got env=$ENV_VALUE" if [ "$ENV_VALUE" = "staging" ]; then
echo "::notice::Staging smoke tests passed"
HEALTHY=true
break
fi
fi
sleep 5
done
if [ "$HEALTHY" = false ]; then
echo "::error::Staging smoke tests/health check failed"
exit 1 exit 1
fi fi
EOF
echo "::notice::Staging smoke tests passed"
notify: notify:
name: Notification name: Notification