diff --git a/.github/workflows/cd.dev.yml b/.github/workflows/cd.dev.yml index fcb167d96..5623f3390 100644 --- a/.github/workflows/cd.dev.yml +++ b/.github/workflows/cd.dev.yml @@ -1,35 +1,40 @@ -name: Dev Branch Deployment - +name: Dev cd pipeline on: - workflow_run: - workflows: ["CI"] - types: - - completed + push: branches: [dev] jobs: - on-success: + build-and-deploy: runs-on: ubuntu-latest - if: ${{ github.event.workflow_run.conclusion == 'success' }} steps: - name: Checkout code uses: actions/checkout@v3 - - name: Use SSH Action + - name: Build image + run: docker build -t anchor-python-bp-dev:latest -f Dockerfile . + + - name: Save image + run: docker save anchor-python-bp-dev:latest | gzip > dev.tar.gz + + - name: Copy image to server + uses: appleboy/scp-action@v0.1.7 + with: + host: ${{ secrets.HOST }} + username: ${{ secrets.USERNAME }} + password: ${{ secrets.PASSWORD }} + source: "dev.tar.gz" + target: "/home/${{ secrets.USERNAME }}/boilerplate-python/dev_source_code" + + - name: Deploy image on server uses: appleboy/ssh-action@v0.1.8 with: host: ${{ secrets.HOST }} username: ${{ secrets.USERNAME }} password: ${{ secrets.PASSWORD }} script: | - cd python/dev_source_code/ + cd /home/${{ secrets.USERNAME }}/boilerplate-python/dev_source_code git pull origin dev - source .venv/bin/activate - pip install -r requirements.txt - alembic upgrade head + docker load --input dev.tar.gz + docker-compose -f docker-compose.yml up -d + rm -f dev.tar.gz - on-failure: - runs-on: ubuntu-latest - if: ${{ github.event.workflow_run.conclusion == 'failure' }} - steps: - - run: echo 'The triggering workflow failed' diff --git a/.github/workflows/cd.dev.yml.bac b/.github/workflows/cd.dev.yml.bac new file mode 100644 index 000000000..fcb167d96 --- /dev/null +++ b/.github/workflows/cd.dev.yml.bac @@ -0,0 +1,35 @@ +name: Dev Branch Deployment + +on: + workflow_run: + workflows: ["CI"] + types: + - completed + branches: [dev] + +jobs: + on-success: + runs-on: ubuntu-latest + if: ${{ github.event.workflow_run.conclusion == 'success' }} + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Use SSH Action + uses: appleboy/ssh-action@v0.1.8 + with: + host: ${{ secrets.HOST }} + username: ${{ secrets.USERNAME }} + password: ${{ secrets.PASSWORD }} + script: | + cd python/dev_source_code/ + git pull origin dev + source .venv/bin/activate + pip install -r requirements.txt + alembic upgrade head + + on-failure: + runs-on: ubuntu-latest + if: ${{ github.event.workflow_run.conclusion == 'failure' }} + steps: + - run: echo 'The triggering workflow failed' diff --git a/.github/workflows/cd.prod.yml b/.github/workflows/cd.prod.yml index 23217d2e5..9902cd940 100644 --- a/.github/workflows/cd.prod.yml +++ b/.github/workflows/cd.prod.yml @@ -1,35 +1,39 @@ -name: Prod Branch Deployment - +name: Prod cd pipeline on: - workflow_run: - workflows: ["CI"] - types: - - completed + push: branches: [main] jobs: - on-success: + build-and-deploy: runs-on: ubuntu-latest - if: ${{ github.event.workflow_run.conclusion == 'success' }} steps: - name: Checkout code uses: actions/checkout@v3 - - name: Use SSH Action + - name: Build image + run: docker build -t anchor-python-bp-prod:latest -f Dockerfile . + + - name: Save image + run: docker save anchor-python-bp-prod:latest | gzip > prod.tar.gz + + - name: Copy image to server + uses: appleboy/scp-action@v0.1.7 + with: + host: ${{ secrets.HOST }} + username: ${{ secrets.USERNAME }} + password: ${{ secrets.PASSWORD }} + source: "prod.tar.gz" + target: "/home/${{ secrets.USERNAME }}/boilerplate-python/prod_source_code" + + - name: Deploy image on server uses: appleboy/ssh-action@v0.1.8 with: host: ${{ secrets.HOST }} username: ${{ secrets.USERNAME }} password: ${{ secrets.PASSWORD }} script: | - cd python/prod_source_code/ + cd /home/${{ secrets.USERNAME }}/boilerplate-python/prod_source_code git pull origin main - source .venv/bin/activate - pip install -r requirements.txt - alembic upgrade head - - on-failure: - runs-on: ubuntu-latest - if: ${{ github.event.workflow_run.conclusion == 'failure' }} - steps: - - run: echo 'The triggering workflow failed' + docker load --input prod.tar.gz + docker-compose -f docker-compose.prod.yml up -d + rm -f prod.tar.gz diff --git a/.github/workflows/cd.prod.yml.bac b/.github/workflows/cd.prod.yml.bac new file mode 100644 index 000000000..23217d2e5 --- /dev/null +++ b/.github/workflows/cd.prod.yml.bac @@ -0,0 +1,35 @@ +name: Prod Branch Deployment + +on: + workflow_run: + workflows: ["CI"] + types: + - completed + branches: [main] + +jobs: + on-success: + runs-on: ubuntu-latest + if: ${{ github.event.workflow_run.conclusion == 'success' }} + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Use SSH Action + uses: appleboy/ssh-action@v0.1.8 + with: + host: ${{ secrets.HOST }} + username: ${{ secrets.USERNAME }} + password: ${{ secrets.PASSWORD }} + script: | + cd python/prod_source_code/ + git pull origin main + source .venv/bin/activate + pip install -r requirements.txt + alembic upgrade head + + on-failure: + runs-on: ubuntu-latest + if: ${{ github.event.workflow_run.conclusion == 'failure' }} + steps: + - run: echo 'The triggering workflow failed' diff --git a/.github/workflows/cd.staging.yml b/.github/workflows/cd.staging.yml index 7de13a1b6..81c6f8903 100644 --- a/.github/workflows/cd.staging.yml +++ b/.github/workflows/cd.staging.yml @@ -1,35 +1,39 @@ -name: Staging Branch Deployment - +name: Staging cd pipeline on: - workflow_run: - workflows: ["CI"] - types: - - completed + push: branches: [staging] jobs: - on-success: + build-and-deploy: runs-on: ubuntu-latest - if: ${{ github.event.workflow_run.conclusion == 'success' }} steps: - name: Checkout code uses: actions/checkout@v3 - - name: Use SSH Action + - name: Build image + run: docker build -t anchor-python-bp-staging:latest -f Dockerfile . + + - name: Save image + run: docker save anchor-python-bp-staging:latest | gzip > staging.tar.gz + + - name: Copy image to server + uses: appleboy/scp-action@v0.1.7 + with: + host: ${{ secrets.HOST }} + username: ${{ secrets.USERNAME }} + password: ${{ secrets.PASSWORD }} + source: "staging.tar.gz" + target: "/home/${{ secrets.USERNAME }}/boilerplate-python/staging_source_code" + + - name: Deploy image on server uses: appleboy/ssh-action@v0.1.8 with: host: ${{ secrets.HOST }} username: ${{ secrets.USERNAME }} password: ${{ secrets.PASSWORD }} script: | - cd python/staging_source_code/ + cd /home/${{ secrets.USERNAME }}/boilerplate-python/staging_source_code git pull origin staging - source .venv/bin/activate - pip install -r requirements.txt - alembic upgrade head - - on-failure: - runs-on: ubuntu-latest - if: ${{ github.event.workflow_run.conclusion == 'failure' }} - steps: - - run: echo 'The triggering workflow failed' + docker load --input staging.tar.gz + docker-compose -f docker-compose.staging.yml up -d + rm -f staging.tar.gz diff --git a/.github/workflows/cd.staging.yml.bac b/.github/workflows/cd.staging.yml.bac new file mode 100644 index 000000000..7de13a1b6 --- /dev/null +++ b/.github/workflows/cd.staging.yml.bac @@ -0,0 +1,35 @@ +name: Staging Branch Deployment + +on: + workflow_run: + workflows: ["CI"] + types: + - completed + branches: [staging] + +jobs: + on-success: + runs-on: ubuntu-latest + if: ${{ github.event.workflow_run.conclusion == 'success' }} + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Use SSH Action + uses: appleboy/ssh-action@v0.1.8 + with: + host: ${{ secrets.HOST }} + username: ${{ secrets.USERNAME }} + password: ${{ secrets.PASSWORD }} + script: | + cd python/staging_source_code/ + git pull origin staging + source .venv/bin/activate + pip install -r requirements.txt + alembic upgrade head + + on-failure: + runs-on: ubuntu-latest + if: ${{ github.event.workflow_run.conclusion == 'failure' }} + steps: + - run: echo 'The triggering workflow failed' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml.bac similarity index 100% rename from .github/workflows/ci.yml rename to .github/workflows/ci.yml.bac diff --git a/.github/workflows/pr-deploy.yml b/.github/workflows/pr-deploy.yml new file mode 100644 index 000000000..7eabeedc9 --- /dev/null +++ b/.github/workflows/pr-deploy.yml @@ -0,0 +1,34 @@ +name: PR Deploy +on: + pull_request: + types: [opened, synchronize, reopened, closed] + paths-ignore: + - "README.md" + - ".github/workflows/**" + +jobs: + deploy-pr: + environment: + name: preview + url: ${{ steps.deploy.outputs.preview-url }} + runs-on: ubuntu-latest + steps: + - name: Checkout to branch + uses: actions/checkout@v4 + - name: Copy .env.sample to .env + run: cp .env.sample .env + - id: deploy + name: Pull Request Deploy + uses: hngprojects/pr-deploy@dev + with: + server_host: ${{ secrets.HOST }} + server_username: ${{ secrets.USERNAME }} + server_password: ${{ secrets.PASSWORD }} + comment: true + context: '.' + dockerfile: 'Dockerfile' + exposed_port: '7001' + github_token: ${{ secrets.GITHUB_TOKEN }} + - name: Print Preview Url + run: | + echo "Preview Url: ${{ steps.deploy.outputs.preview-url }}" \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 000000000..7ea06da3b --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,53 @@ +name: Test +on: + pull_request: + types: [opened, synchronize, reopened, closed] + paths-ignore: + - "README.md" + - ".github/workflows/**" + +jobs: + test: + runs-on: ubuntu-latest + + services: + postgres: + image: postgres:latest + env: + POSTGRES_USER: "username" + POSTGRES_PASSWORD: "password" + POSTGRES_DB: "test" + ports: + - 5432:5432 + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: "3.12" + + - name: Cache dependencies + uses: actions/cache@v3 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} + restore-keys: | + ${{ runner.os }}-pip- + + - name: Install dependencies + run: | + pip install -r requirements.txt + + - name: Copy env file + run: cp .env.sample .env + + - name: Run migrations + run: | + alembic upgrade head + + - name: Run tests + run: | + PYTHONPATH=. pytest \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..0a84aa847 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,25 @@ +# Use an official Python runtime as the base image +FROM python:3.12-alpine + +# Set environment variables +ENV PYTHONDONTWRITEBYTECODE=1 +ENV PYTHONUNBUFFERED=1 +ENV PYTHONPATH=/app +# Set the working directory in the container +WORKDIR /app + +# Install system dependencies +RUN apk add --no-cache curl + +#copy the requirements.txt file and install with pip +COPY ./requirements.txt /app/requirements.txt +RUN pip install --no-cache-dir --upgrade -r requirements.txt + +# Copy the rest of the backend files +COPY . /app/ + +# Expose the port the app runs on +EXPOSE 7001 + +# Command to run the application +CMD ["/bin/sh", "-c", "uvicorn main:app --host 0.0.0.0 --port 7001 --reload"] \ No newline at end of file diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml new file mode 100644 index 000000000..cb8fbe15b --- /dev/null +++ b/docker-compose.prod.yml @@ -0,0 +1,36 @@ +services: + app_prod: + image: anchor-python-bp-prod:latest + command: ["sh", "-c", "alembic upgrade head && uvicorn main:app --host 0.0.0.0 --port 7001 --reload"] + container_name: app_prod + networks: + - hng-network + restart: unless-stopped + ports: + - 7007:7001 + working_dir: /app + volumes: + - .env:/app/.env + depends_on: + - prod_db + + prod_db: + image: postgres:14.12 + container_name: prod_db + restart: always + volumes: + - prod_db:/var/lib/postgresql/data + - .env:/app/.env + environment: + - POSTGRES_PASSWORD=${DB_PASSWORD?Variable not set} + - POSTGRES_USER=${DB_USER?Variable not set} + - POSTGRES_DB=${DB_NAME?Variable not set} + networks: + - hng-network + +volumes: + prod_db: + +networks: + hng-network: + driver: bridge \ No newline at end of file diff --git a/docker-compose.staging.yml b/docker-compose.staging.yml new file mode 100644 index 000000000..8c2a062a2 --- /dev/null +++ b/docker-compose.staging.yml @@ -0,0 +1,36 @@ +services: + app_staging: + image: anchor-python-bp-staging:latest + command: ["sh", "-c", "alembic upgrade head && uvicorn main:app --host 0.0.0.0 --port 7001 --reload"] + container_name: app_staging + networks: + - hng-network + restart: unless-stopped + ports: + - 7008:7001 + working_dir: /app + volumes: + - .env:/app/.env + depends_on: + - staging_db + + staging_db: + image: postgres:14.12 + container_name: staging_db + restart: always + volumes: + - staging_db:/var/lib/postgresql/data + - .env:/app/.env + environment: + - POSTGRES_PASSWORD=${DB_PASSWORD?Variable not set} + - POSTGRES_USER=${DB_USER?Variable not set} + - POSTGRES_DB=${DB_NAME?Variable not set} + networks: + - hng-network + +volumes: + staging_db: + +networks: + hng-network: + driver: bridge diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 000000000..57cf66b4e --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,36 @@ +services: + app_dev: + image: anchor-python-bp-dev:latest + command: ["sh", "-c", "alembic upgrade head && uvicorn main:app --host 0.0.0.0 --port 7001 --reload"] + container_name: app_dev + networks: + - hng-network + restart: unless-stopped + ports: + - 7006:7001 + working_dir: /app + volumes: + - .env:/app/.env + depends_on: + - dev_db + + dev_db: + image: postgres:14.12 + container_name: dev_db + restart: always + volumes: + - dev_db:/var/lib/postgresql/data + - .env:/app/.env + environment: + - POSTGRES_PASSWORD=${DB_PASSWORD?Variable not set} + - POSTGRES_USER=${DB_USER?Variable not set} + - POSTGRES_DB=${DB_NAME?Variable not set} + networks: + - hng-network + +volumes: + dev_db: + +networks: + hng-network: + driver: bridge \ No newline at end of file