diff --git a/.github/workflows/build_deploy.yml b/.github/workflows/build_deploy.yml index e446606..d9cddf8 100644 --- a/.github/workflows/build_deploy.yml +++ b/.github/workflows/build_deploy.yml @@ -17,7 +17,7 @@ jobs: 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 }} + prod: ${{ steps.env.outputs.prod }} steps: - name: Checkout the repo uses: actions/checkout@v4 @@ -25,20 +25,26 @@ jobs: - name: Ensure scripts are executable run: chmod +x scripts/*.sh - - name: Setup build metadata and environment + - name: Get full and partial repository name 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 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Set PROD environment variable + id: env + run: | + echo "โœ… Exit script on any error" + set -eu -o pipefail + 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') }}" @@ -47,18 +53,12 @@ jobs: 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" + echo "prod=${prod_value}" >> $GITHUB_OUTPUT - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - name: Generate tags id: generated_docker_tags run: | @@ -126,7 +126,7 @@ jobs: - name: Upload container as artifact uses: actions/upload-artifact@v4 with: - name: ${{ env.IMAGE_TAR_NAME }} + name: docker-image path: ${{ runner.temp }}/${{ steps.meta.outputs.REPO_NAME_ONLY }}-${{ github.ref_name }}_${{ github.sha }}.tar if-no-files-found: error retention-days: 1 @@ -136,8 +136,7 @@ jobs: 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' }} + environment: ${{ needs.build.outputs.prod == 'true' && 'production' || 'staging' }} steps: - name: Checkout the repo uses: actions/checkout@v4 @@ -145,45 +144,12 @@ jobs: - 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 }} + name: docker-image path: ${{ runner.temp }} - github-token: ${{ secrets.GITHUB_TOKEN }} - fail-on-missing: true - + - name: Get Deploy Secrets uses: bitwarden/sm-action@v2 with: @@ -218,10 +184,10 @@ jobs: - 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 }} + REPO_PROJECT_PATH: ${{ needs.build.outputs.repo-path }} + REPO_NAME_ONLY: ${{ needs.build.outputs.repo-name }} + IMAGE_TAR: ${{ runner.temp }}/${{ needs.build.outputs.image-tar }} + IS_PROD: ${{ needs.build.outputs.prod }} run: | echo "โœ… Exit script on any error" set -eu -o pipefail @@ -230,7 +196,7 @@ jobs: - name: Health Check and Rollback run: | # Determine the correct URL based on environment - if [ "${{ env.PROD }}" = "true" ]; then + if [ "${{ needs.build.outputs.prod }}" = "true" ]; then # Ensure PRODUCTION_DOMAIN is set if [ -z "${{ vars.PRODUCTION_DOMAIN }}" ]; then echo "Error: PRODUCTION_DOMAIN is not set" @@ -249,5 +215,5 @@ jobs: # 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 "/tmp/health-check-and-rollback.sh '${{ needs.build.outputs.repo-path }}' '${{ needs.build.outputs.prod }}' '$HEALTH_CHECK_URL' 30" ssh deploy "rm -f /tmp/health-check-and-rollback.sh" \ No newline at end of file diff --git a/scripts/deploy-to-server.sh b/scripts/deploy-to-server.sh index 1a375ac..15fd44b 100644 --- a/scripts/deploy-to-server.sh +++ b/scripts/deploy-to-server.sh @@ -16,7 +16,7 @@ source "${SCRIPT_DIR}/retry.sh" : "${ENV_FILE_BASE64:?Error: ENV_FILE_BASE64 not set}" : "${CF_PEM_CERT:?Error: CF_PEM_CERT not set}" : "${CF_PEM_CA:?Error: CF_PEM_CA not set}" -: "${PROD:?Error: PROD not set}" +: "${IS_PROD:?Error: IS_PROD not set}" echo "โš™๏ธ Docker host: $DOCKER_HOST" @@ -46,7 +46,7 @@ echo "๐Ÿ’พ Copy new files to server" if [ -d "./server" ]; then retry scp -pr ./server/* "deploy:${NEW_RELEASE_PATH}/" else - echo "โš ๏ธ No server directory found, error" + echo "โš ๏ธ No server directory found, erroring out" exit 1 fi @@ -61,12 +61,12 @@ printf "%s" "$CF_PEM_CA" | ssh deploy "cat > '${NEW_RELEASE_PATH}/certs/ca.pem' echo "๐Ÿ”„ Prepare deployment (stop current containers)" # Copy script to remote and execute with parameters scp "${SCRIPT_DIR}/prepare-deployment.sh" deploy:/tmp/ -ssh deploy "chmod +x /tmp/prepare-deployment.sh && /tmp/prepare-deployment.sh '${REPO_PROJECT_PATH}' '${PROD}' '${CURRENT_LINK_PATH}'" +ssh deploy "chmod +x /tmp/prepare-deployment.sh && /tmp/prepare-deployment.sh '${REPO_PROJECT_PATH}' '${IS_PROD}' '${CURRENT_LINK_PATH}'" ssh deploy "rm -f /tmp/prepare-deployment.sh" echo "๐Ÿ“ Save deployment metadata" ssh deploy "echo '${DEPLOYMENT_TIMESTAMP}' > '${NEW_RELEASE_PATH}/.deployment_version'" -ssh deploy "echo '${PROD}' > '${NEW_RELEASE_PATH}/.deployment_env'" +ssh deploy "echo '${IS_PROD}' > '${NEW_RELEASE_PATH}/.deployment_env'" # Save previous version info for potential rollback ssh deploy "if [ -L '${CURRENT_LINK_PATH}' ]; then readlink -f '${CURRENT_LINK_PATH}' > '${NEW_RELEASE_PATH}/.previous_version'; fi" @@ -109,10 +109,10 @@ ssh deploy "ln -sfn '${NEW_RELEASE_PATH}' '${CURRENT_LINK_PATH}'" # fi echo "๐Ÿš€ Start the new containers" -if [ "$PROD" = "true" ]; then - retry ssh deploy "cd '${CURRENT_LINK_PATH}' && docker compose -f docker-compose_core.yml -f docker-compose_web.yml -p pkmntrade-club up -d --no-build" +if [ "$IS_PROD" = "true" ]; then + retry ssh deploy "cd '${CURRENT_LINK_PATH}' && docker compose -f docker-compose_core.yml -f docker-compose_web.yml up -d --no-build" else - retry ssh deploy "cd '${CURRENT_LINK_PATH}' && docker compose -f docker-compose_core.yml -f docker-compose_web.yml -f docker-compose_staging.yml -p pkmntrade-club up -d --no-build" + retry ssh deploy "cd '${CURRENT_LINK_PATH}' && docker compose -f docker-compose_core.yml -f docker-compose_web.yml -f docker-compose_staging.yml up -d --no-build" fi echo "๐Ÿงน Prune unused Docker resources" diff --git a/scripts/generate-docker-tags.sh b/scripts/generate-docker-tags.sh index 70fa8b1..b22dc5b 100644 --- a/scripts/generate-docker-tags.sh +++ b/scripts/generate-docker-tags.sh @@ -2,22 +2,22 @@ set -euo pipefail # Generate Docker tags based on git ref and environment -# Usage: ./generate-docker-tags.sh IMAGE_BASE GIT_SHA GIT_REF PROD +# Usage: ./generate-docker-tags.sh IMAGE_BASE GIT_SHA GIT_REF IS_PROD if [ $# -ne 4 ]; then - echo "Error: Invalid number of arguments" > /dev/stderr - echo "Usage: $0 IMAGE_BASE GIT_SHA GIT_REF PROD" > /dev/stderr + echo "Error: Invalid number of arguments" + echo "Usage: $0 IMAGE_BASE GIT_SHA GIT_REF IS_PROD" exit 1 fi IMAGE_BASE="$1" GIT_SHA="$2" GIT_REF="$3" -PROD="$4" +IS_PROD="$4" # Validate inputs if [ -z "$IMAGE_BASE" ] || [ -z "$GIT_SHA" ]; then - echo "Error: IMAGE_BASE and GIT_SHA cannot be empty" > /dev/stderr + echo "Error: IMAGE_BASE and GIT_SHA cannot be empty" exit 1 fi @@ -32,7 +32,7 @@ if [[ "$GIT_REF" =~ ^refs/tags/v([0-9]+)\.([0-9]+)\.([0-9]+)(-.*)?$ ]]; then PATCH="${BASH_REMATCH[3]}" PRERELEASE="${BASH_REMATCH[4]}" - if [[ -z "$PRERELEASE" ]] && [[ "$PROD" == "true" ]]; then + if [[ -z "$PRERELEASE" ]] && [[ "$IS_PROD" == "true" ]]; then echo "${IMAGE_BASE}:latest" echo "${IMAGE_BASE}:stable" [[ "$MAJOR" -gt 0 ]] && echo "${IMAGE_BASE}:v${MAJOR}" @@ -43,7 +43,7 @@ if [[ "$GIT_REF" =~ ^refs/tags/v([0-9]+)\.([0-9]+)\.([0-9]+)(-.*)?$ ]]; then echo "${IMAGE_BASE}:staging" echo "${IMAGE_BASE}:v${MAJOR}.${MINOR}.${PATCH}-prerelease" fi -elif [[ "$PROD" == "false" ]]; then +elif [[ "$IS_PROD" == "false" ]]; then echo "${IMAGE_BASE}:latest-staging" echo "${IMAGE_BASE}:staging" fi \ No newline at end of file diff --git a/scripts/health-check-and-rollback.sh b/scripts/health-check-and-rollback.sh index b07607b..446f1f5 100644 --- a/scripts/health-check-and-rollback.sh +++ b/scripts/health-check-and-rollback.sh @@ -2,16 +2,16 @@ set -euo pipefail # Perform health check and rollback if necessary -# Usage: ./health-check-and-rollback.sh REPO_PROJECT_PATH PROD HEALTH_CHECK_URL [MAX_ATTEMPTS] +# Usage: ./health-check-and-rollback.sh REPO_PROJECT_PATH IS_PROD HEALTH_CHECK_URL [MAX_ATTEMPTS] if [ $# -lt 3 ]; then echo "Error: Invalid number of arguments" - echo "Usage: $0 REPO_PROJECT_PATH PROD HEALTH_CHECK_URL [MAX_ATTEMPTS]" + echo "Usage: $0 REPO_PROJECT_PATH IS_PROD HEALTH_CHECK_URL [MAX_ATTEMPTS]" exit 1 fi REPO_PROJECT_PATH="$1" -PROD="$2" +IS_PROD="$2" HEALTH_CHECK_URL="$3" MAX_ATTEMPTS="${4:-30}" @@ -60,22 +60,22 @@ while [ "$ATTEMPT" -lt "$MAX_ATTEMPTS" ]; do # Stop failed deployment containers cd "$CURRENT_LINK_PATH" echo "Stopping failed deployment containers..." - docker compose -f docker-compose_web.yml -p pkmntrade-club down || true - if [ "$PROD" = "false" ]; then - docker compose -f docker-compose_staging.yml -p pkmntrade-club down || true + docker compose -f docker-compose_web.yml down || true + if [ "$IS_PROD" = "false" ]; then + docker compose -f docker-compose_staging.yml down || true fi - docker compose -f docker-compose_core.yml -p pkmntrade-club down || true + docker compose -f docker-compose_core.yml down || true # Switch symlink back to previous version ln -sfn "$PREVIOUS_VERSION_PATH" "$CURRENT_LINK_PATH" # Start previous version containers cd "$CURRENT_LINK_PATH" - docker compose -f docker-compose_core.yml -p pkmntrade-club up -d --no-build - if [ "$PROD" = "true" ]; then - docker compose -f docker-compose_web.yml -p pkmntrade-club up -d --no-build + docker compose -f docker-compose_core.yml up -d --no-build + if [ "$IS_PROD" = "true" ]; then + docker compose -f docker-compose_web.yml up -d --no-build else - docker compose -f docker-compose_web.yml -f docker-compose_staging.yml -p pkmntrade-club up -d --no-build + docker compose -f docker-compose_web.yml -f docker-compose_staging.yml up -d --no-build fi echo "โœ… Rollback completed to version: ${PREVIOUS_VERSION}" diff --git a/scripts/manage-releases.sh b/scripts/manage-releases.sh index f7e16b7..255c35c 100644 --- a/scripts/manage-releases.sh +++ b/scripts/manage-releases.sh @@ -71,17 +71,17 @@ case "$COMMAND" in # Read environment from target version if [ -f "${TARGET_PATH}/.deployment_env" ]; then - PROD=$(cat "${TARGET_PATH}/.deployment_env") + IS_PROD=$(cat "${TARGET_PATH}/.deployment_env") else echo "Warning: Could not determine environment, assuming staging" - PROD="false" + IS_PROD="false" fi # Stop current containers if [ -L "$CURRENT_LINK_PATH" ] && [ -d "$CURRENT_LINK_PATH" ]; then cd "$CURRENT_LINK_PATH" docker compose -f docker-compose_web.yml down || true - [ "$PROD" = "false" ] && docker compose -f docker-compose_staging.yml down || true + [ "$IS_PROD" = "false" ] && docker compose -f docker-compose_staging.yml down || true docker compose -f docker-compose_core.yml down || true fi @@ -91,7 +91,7 @@ case "$COMMAND" in # Start containers cd "$CURRENT_LINK_PATH" docker compose -f docker-compose_core.yml up -d --no-build - if [ "$PROD" = "true" ]; then + if [ "$IS_PROD" = "true" ]; then docker compose -f docker-compose_web.yml up -d --no-build else docker compose -f docker-compose_web.yml -f docker-compose_staging.yml up -d --no-build diff --git a/scripts/parse-repository-name.sh b/scripts/parse-repository-name.sh index dc1343f..2e3aa80 100644 --- a/scripts/parse-repository-name.sh +++ b/scripts/parse-repository-name.sh @@ -5,25 +5,25 @@ set -euo pipefail # Usage: ./parse-repository-name.sh GITHUB_REPOSITORY if [ $# -eq 0 ]; then - echo "Error: No repository name provided" > /dev/stderr - echo "Usage: $0 GITHUB_REPOSITORY" > /dev/stderr + echo "Error: No repository name provided" + echo "Usage: $0 GITHUB_REPOSITORY" exit 1 fi GITHUB_REPOSITORY="$1" -echo "GITHUB_REPOSITORY: $GITHUB_REPOSITORY" > /dev/stderr +echo "GITHUB_REPOSITORY: $GITHUB_REPOSITORY" if [[ "$GITHUB_REPOSITORY" == *".git" ]]; then if [[ "$GITHUB_REPOSITORY" == "https://"* ]]; then - echo "GITHUB_REPOSITORY ends in .git and is a URL" > /dev/stderr + echo "GITHUB_REPOSITORY ends in .git and is a URL" REPO=$(echo "$GITHUB_REPOSITORY" | sed 's/\.git$//' | cut -d'/' -f4-5 | sed 's/[^a-zA-Z0-9\/-]/-/g') else - echo "GITHUB_REPOSITORY ends in .git and is not a URL" > /dev/stderr + echo "GITHUB_REPOSITORY ends in .git and is not a URL" REPO=$(echo "$GITHUB_REPOSITORY" | sed 's/\.git$//' | sed 's/[^a-zA-Z0-9\/-]/-/g') fi else - echo "GITHUB_REPOSITORY is not a URL" > /dev/stderr + echo "GITHUB_REPOSITORY is not a URL" REPO=$(echo "$GITHUB_REPOSITORY" | sed 's/[^a-zA-Z0-9\/-]/-/g') fi diff --git a/scripts/prepare-deployment.sh b/scripts/prepare-deployment.sh index 15a41c4..ae6ac29 100644 --- a/scripts/prepare-deployment.sh +++ b/scripts/prepare-deployment.sh @@ -2,16 +2,16 @@ set -euo pipefail # Prepare deployment by stopping containers -# Usage: ./prepare-deployment.sh REPO_PROJECT_PATH PROD CURRENT_LINK_PATH +# Usage: ./prepare-deployment.sh REPO_PROJECT_PATH IS_PROD CURRENT_LINK_PATH if [ $# -ne 3 ]; then echo "Error: Invalid number of arguments" - echo "Usage: $0 REPO_PROJECT_PATH PROD CURRENT_LINK_PATH" + echo "Usage: $0 REPO_PROJECT_PATH IS_PROD CURRENT_LINK_PATH" exit 1 fi REPO_PROJECT_PATH="$1" -PROD="$2" +IS_PROD="$2" CURRENT_LINK_PATH="$3" # Ensure base directory exists @@ -27,15 +27,15 @@ if [ -L "$CURRENT_LINK_PATH" ] && [ -d "$CURRENT_LINK_PATH" ]; then # Stop containers if [ -f "docker-compose_web.yml" ]; then - docker compose -f docker-compose_web.yml -p pkmntrade-club down || true + docker compose -f docker-compose_web.yml down || true fi - if [ "$PROD" = "false" ] && [ -f "docker-compose_staging.yml" ]; then - docker compose -f docker-compose_staging.yml -p pkmntrade-club down || true + if [ "$IS_PROD" = "false" ] && [ -f "docker-compose_staging.yml" ]; then + docker compose -f docker-compose_staging.yml down || true fi if [ -f "docker-compose_core.yml" ]; then - docker compose -f docker-compose_core.yml -p pkmntrade-club down || true + docker compose -f docker-compose_core.yml down || true fi echo "โœ… Containers stopped" diff --git a/server/docker-compose_core.yml b/server/docker-compose_core.yml index c49b1d2..b07e5de 100644 --- a/server/docker-compose_core.yml +++ b/server/docker-compose_core.yml @@ -120,8 +120,8 @@ services: set -eu -o pipefail apk add --no-cache curl - COMPOSE_FILE_PATH="/srv/pkmntrade-club/current/gatekeepers.yml" - PROJECT_DIR_PATH="/srv/pkmntrade-club/current" + COMPOSE_FILE_PATH="/srv/pkmntrade-club/gatekeepers.yml" + PROJECT_DIR_PATH="/srv/pkmntrade-club" PROJECT_NAME_TAG="gatekeepers" TERMINATING="false" RESTARTING="false" diff --git a/uv.lock b/uv.lock index f73771b..2c2eefa 100644 --- a/uv.lock +++ b/uv.lock @@ -1,5 +1,4 @@ version = 1 -revision = 1 requires-python = ">=3.12" [[package]] @@ -107,7 +106,7 @@ name = "click" version = "8.2.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "colorama", marker = "platform_system == 'Windows'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/60/6c/8ca2efa64cf75a977a0d7fac081354553ebe483345c734fb6b6515d96bbc/click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202", size = 286342 } wheels = [ @@ -596,6 +595,7 @@ wheels = [ [[package]] name = "pkmntrade-club" +version = "0.1.0" source = { editable = "." } dependencies = [ { name = "asgiref" },