diff --git a/.github/workflows/build_deploy.yml b/.github/workflows/build_deploy.yml index d9cddf8..e446606 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.env.outputs.prod }} + prod: ${{ steps.meta.outputs.prod }} steps: - name: Checkout the repo uses: actions/checkout@v4 @@ -25,26 +25,20 @@ jobs: - name: Ensure scripts are executable run: chmod +x scripts/*.sh - - name: Get full and partial repository name + - 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 - - 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 - + # 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') }}" @@ -53,12 +47,18 @@ 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 "prod=${prod_value}" >> $GITHUB_OUTPUT + 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: | @@ -126,7 +126,7 @@ jobs: - name: Upload container as artifact uses: actions/upload-artifact@v4 with: - name: docker-image + 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 @@ -136,7 +136,8 @@ jobs: needs: build runs-on: ubuntu-latest if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/')) - environment: ${{ needs.build.outputs.prod == 'true' && 'production' || 'staging' }} + # 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 @@ -144,12 +145,45 @@ 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: docker-image + name: ${{ env.IMAGE_TAR_NAME }} path: ${{ runner.temp }} - + github-token: ${{ secrets.GITHUB_TOKEN }} + fail-on-missing: true + - name: Get Deploy Secrets uses: bitwarden/sm-action@v2 with: @@ -184,10 +218,10 @@ jobs: - name: Deploy to Server env: DOCKER_HOST: ssh://deploy - 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 }} + 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 @@ -196,7 +230,7 @@ jobs: - name: Health Check and Rollback run: | # Determine the correct URL based on environment - if [ "${{ needs.build.outputs.prod }}" = "true" ]; then + if [ "${{ env.PROD }}" = "true" ]; then # Ensure PRODUCTION_DOMAIN is set if [ -z "${{ vars.PRODUCTION_DOMAIN }}" ]; then echo "Error: PRODUCTION_DOMAIN is not set" @@ -215,5 +249,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 '${{ needs.build.outputs.repo-path }}' '${{ needs.build.outputs.prod }}' '$HEALTH_CHECK_URL' 30" + 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" \ No newline at end of file diff --git a/scripts/deploy-to-server.sh b/scripts/deploy-to-server.sh index 15fd44b..1a375ac 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}" -: "${IS_PROD:?Error: IS_PROD not set}" +: "${PROD:?Error: 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, erroring out" + echo "โš ๏ธ No server directory found, error" 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}' '${IS_PROD}' '${CURRENT_LINK_PATH}'" +ssh deploy "chmod +x /tmp/prepare-deployment.sh && /tmp/prepare-deployment.sh '${REPO_PROJECT_PATH}' '${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 '${IS_PROD}' > '${NEW_RELEASE_PATH}/.deployment_env'" +ssh deploy "echo '${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 [ "$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" +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" 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 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 -p pkmntrade-club 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 b22dc5b..70fa8b1 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 IS_PROD +# Usage: ./generate-docker-tags.sh IMAGE_BASE GIT_SHA GIT_REF PROD if [ $# -ne 4 ]; then - echo "Error: Invalid number of arguments" - echo "Usage: $0 IMAGE_BASE GIT_SHA GIT_REF IS_PROD" + echo "Error: Invalid number of arguments" > /dev/stderr + echo "Usage: $0 IMAGE_BASE GIT_SHA GIT_REF PROD" > /dev/stderr exit 1 fi IMAGE_BASE="$1" GIT_SHA="$2" GIT_REF="$3" -IS_PROD="$4" +PROD="$4" # Validate inputs if [ -z "$IMAGE_BASE" ] || [ -z "$GIT_SHA" ]; then - echo "Error: IMAGE_BASE and GIT_SHA cannot be empty" + echo "Error: IMAGE_BASE and GIT_SHA cannot be empty" > /dev/stderr 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" ]] && [[ "$IS_PROD" == "true" ]]; then + if [[ -z "$PRERELEASE" ]] && [[ "$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 [[ "$IS_PROD" == "false" ]]; then +elif [[ "$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 446f1f5..b07607b 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 IS_PROD HEALTH_CHECK_URL [MAX_ATTEMPTS] +# Usage: ./health-check-and-rollback.sh REPO_PROJECT_PATH PROD HEALTH_CHECK_URL [MAX_ATTEMPTS] if [ $# -lt 3 ]; then echo "Error: Invalid number of arguments" - echo "Usage: $0 REPO_PROJECT_PATH IS_PROD HEALTH_CHECK_URL [MAX_ATTEMPTS]" + echo "Usage: $0 REPO_PROJECT_PATH PROD HEALTH_CHECK_URL [MAX_ATTEMPTS]" exit 1 fi REPO_PROJECT_PATH="$1" -IS_PROD="$2" +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 down || true - if [ "$IS_PROD" = "false" ]; then - docker compose -f docker-compose_staging.yml down || true + 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 fi - docker compose -f docker-compose_core.yml down || true + docker compose -f docker-compose_core.yml -p pkmntrade-club 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 up -d --no-build - if [ "$IS_PROD" = "true" ]; then - docker compose -f docker-compose_web.yml up -d --no-build + 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 else - docker compose -f docker-compose_web.yml -f docker-compose_staging.yml up -d --no-build + docker compose -f docker-compose_web.yml -f docker-compose_staging.yml -p pkmntrade-club 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 255c35c..f7e16b7 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 - IS_PROD=$(cat "${TARGET_PATH}/.deployment_env") + PROD=$(cat "${TARGET_PATH}/.deployment_env") else echo "Warning: Could not determine environment, assuming staging" - IS_PROD="false" + 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 - [ "$IS_PROD" = "false" ] && docker compose -f docker-compose_staging.yml down || true + [ "$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 [ "$IS_PROD" = "true" ]; then + if [ "$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 2e3aa80..dc1343f 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" - echo "Usage: $0 GITHUB_REPOSITORY" + echo "Error: No repository name provided" > /dev/stderr + echo "Usage: $0 GITHUB_REPOSITORY" > /dev/stderr exit 1 fi GITHUB_REPOSITORY="$1" -echo "GITHUB_REPOSITORY: $GITHUB_REPOSITORY" +echo "GITHUB_REPOSITORY: $GITHUB_REPOSITORY" > /dev/stderr if [[ "$GITHUB_REPOSITORY" == *".git" ]]; then if [[ "$GITHUB_REPOSITORY" == "https://"* ]]; then - echo "GITHUB_REPOSITORY ends in .git and is a URL" + echo "GITHUB_REPOSITORY ends in .git and is a URL" > /dev/stderr 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" + echo "GITHUB_REPOSITORY ends in .git and is not a URL" > /dev/stderr REPO=$(echo "$GITHUB_REPOSITORY" | sed 's/\.git$//' | sed 's/[^a-zA-Z0-9\/-]/-/g') fi else - echo "GITHUB_REPOSITORY is not a URL" + echo "GITHUB_REPOSITORY is not a URL" > /dev/stderr 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 ae6ac29..15a41c4 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 IS_PROD CURRENT_LINK_PATH +# Usage: ./prepare-deployment.sh REPO_PROJECT_PATH PROD CURRENT_LINK_PATH if [ $# -ne 3 ]; then echo "Error: Invalid number of arguments" - echo "Usage: $0 REPO_PROJECT_PATH IS_PROD CURRENT_LINK_PATH" + echo "Usage: $0 REPO_PROJECT_PATH PROD CURRENT_LINK_PATH" exit 1 fi REPO_PROJECT_PATH="$1" -IS_PROD="$2" +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 down || true + docker compose -f docker-compose_web.yml -p pkmntrade-club down || true fi - if [ "$IS_PROD" = "false" ] && [ -f "docker-compose_staging.yml" ]; then - docker compose -f docker-compose_staging.yml down || true + if [ "$PROD" = "false" ] && [ -f "docker-compose_staging.yml" ]; then + docker compose -f docker-compose_staging.yml -p pkmntrade-club down || true fi if [ -f "docker-compose_core.yml" ]; then - docker compose -f docker-compose_core.yml down || true + docker compose -f docker-compose_core.yml -p pkmntrade-club down || true fi echo "โœ… Containers stopped" diff --git a/server/docker-compose_core.yml b/server/docker-compose_core.yml index b07e5de..c49b1d2 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/gatekeepers.yml" - PROJECT_DIR_PATH="/srv/pkmntrade-club" + COMPOSE_FILE_PATH="/srv/pkmntrade-club/current/gatekeepers.yml" + PROJECT_DIR_PATH="/srv/pkmntrade-club/current" PROJECT_NAME_TAG="gatekeepers" TERMINATING="false" RESTARTING="false" diff --git a/uv.lock b/uv.lock index 2c2eefa..f73771b 100644 --- a/uv.lock +++ b/uv.lock @@ -1,4 +1,5 @@ version = 1 +revision = 1 requires-python = ">=3.12" [[package]] @@ -106,7 +107,7 @@ name = "click" version = "8.2.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "colorama", marker = "platform_system == 'Windows'" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/60/6c/8ca2efa64cf75a977a0d7fac081354553ebe483345c734fb6b6515d96bbc/click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202", size = 286342 } wheels = [ @@ -595,7 +596,6 @@ wheels = [ [[package]] name = "pkmntrade-club" -version = "0.1.0" source = { editable = "." } dependencies = [ { name = "asgiref" },