From d1292aaae4115f5d4abfb2c7f473313b16408529 Mon Sep 17 00:00:00 2001 From: doria <93405247+dr-frmr@users.noreply.github.com> Date: Fri, 9 May 2025 00:21:54 +0900 Subject: [PATCH] Continuous deployment of Honcho images to SaaS platform (#102) * feat: add CD for honcho images to saas test and prod environments * fix: use github tag in image label * feat: split up test and prod deployment flows, push to service after * fix: action parsing properly hopefully * fix: proper url, version * remove excessive fly.toml * fix: specify prod-image in prod workflow * Potential fix for code scanning alert no. 12: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Potential fix for code scanning alert no. 11: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * fix: Change App name in command and make steps sequential * fix: Address Code Rabbit nitpicks * Use IMAGE Label environment variable * fix: collisions between github action groups * feat: add migrate_db script * fix: correct image label on prod deploy --------- Co-authored-by: Vineeth Voruganti <13438633+VVoruganti@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- .github/workflows/fly-deploy-prod.yml | 61 +++++++++++++++++++++++++++ .github/workflows/fly-deploy.yml | 59 ++++++++++++++++++++++---- Dockerfile | 2 +- scripts/migrate_db.py | 19 +++++++++ scripts/provision_db.py | 6 +-- 5 files changed, 133 insertions(+), 14 deletions(-) create mode 100644 .github/workflows/fly-deploy-prod.yml create mode 100644 scripts/migrate_db.py diff --git a/.github/workflows/fly-deploy-prod.yml b/.github/workflows/fly-deploy-prod.yml new file mode 100644 index 00000000..b13da23e --- /dev/null +++ b/.github/workflows/fly-deploy-prod.yml @@ -0,0 +1,61 @@ +# See https://fly.io/docs/app-guides/continuous-deployment-with-github-actions/ + +name: Fly Deploy (Production Environment) +permissions: + contents: read +on: + push: + tags: + - v* + workflow_dispatch: + inputs: + version: + description: "Version to deploy (without v prefix)" + required: true + type: string + default: 'manual' + +jobs: + deploy-honcho-prod-image: + name: Deploy Honcho Image (Production Environment) + runs-on: ubuntu-latest + concurrency: + group: deploy-prod-group + cancel-in-progress: true + steps: + - uses: actions/checkout@v4 + - uses: superfly/flyctl-actions/setup-flyctl@1.5 + - run: | + # Determine the image label based on trigger type + if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then + IMAGE_LABEL="deployment-${{ github.event.inputs.version }}" + else + IMAGE_LABEL="deployment-${{ github.ref_name }}" + fi + flyctl deploy -a honcho-prod-image --remote-only --build-only --push --no-cache --image-label "$IMAGE_LABEL" + env: + FLY_API_TOKEN: ${{ secrets.FLY_PROD_API_TOKEN }} + + prompt-service: + name: Push to Service (Production Environment) + needs: deploy-honcho-prod-image + runs-on: ubuntu-latest + steps: + - name: Send POST request + env: + GITHUB_REF_NAME: ${{ github.ref_name }} + run: | + # Determine version and image label based on trigger type + if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then + TAG="${{ github.event.inputs.version }}" + IMAGE_LABEL="honcho-prod-image:deployment-${{ github.event.inputs.version }}" + else + TAG=${GITHUB_REF_NAME#v} + IMAGE_LABEL="honcho-prod-image:deployment-${GITHUB_REF_NAME}" + fi + + curl --fail -X POST \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer ${{ secrets.PROD_ENV_WEBHOOK_SECRET }}" \ + -d "{\"version\":\"$TAG\",\"image_label\":\"$IMAGE_LABEL\"}" \ + "${{ secrets.PROD_ENV_URL }}/webhooks/v1/add_honcho_version" diff --git a/.github/workflows/fly-deploy.yml b/.github/workflows/fly-deploy.yml index b0c246ed..8ba4d5e0 100644 --- a/.github/workflows/fly-deploy.yml +++ b/.github/workflows/fly-deploy.yml @@ -1,18 +1,61 @@ # See https://fly.io/docs/app-guides/continuous-deployment-with-github-actions/ -name: Fly Deploy +name: Fly Deploy (Test Environment) +permissions: + contents: read on: push: - branches: - - main + tags: + - v* + workflow_dispatch: + inputs: + version: + description: "Version to deploy (without v prefix)" + required: true + type: string + default: 'manual' + jobs: - deploy: - name: Deploy app + deploy-honcho-image: + name: Deploy Honcho Image (Test Environment) runs-on: ubuntu-latest - concurrency: deploy-group # optional: ensure only one action runs at a time + concurrency: + group: deploy-test-group + cancel-in-progress: true steps: - uses: actions/checkout@v4 - - uses: superfly/flyctl-actions/setup-flyctl@master - - run: flyctl deploy --remote-only + - uses: superfly/flyctl-actions/setup-flyctl@1.5 + - run: | + # Determine the image label based on trigger type + if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then + IMAGE_LABEL="deployment-${{ github.event.inputs.version }}" + else + IMAGE_LABEL="deployment-${{ github.ref_name }}" + fi + flyctl deploy -a honcho-image --remote-only --build-only --push --no-cache --image-label "$IMAGE_LABEL" env: FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }} + + prompt-service: + name: Push to Service (Test Environment) + runs-on: ubuntu-latest + needs: deploy-honcho-image + steps: + - name: Send POST request + env: + GITHUB_REF_NAME: ${{ github.ref_name }} + run: | + # Determine version and image label based on trigger type + if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then + TAG="${{ github.event.inputs.version }}" + IMAGE_LABEL="honcho-image:deployment-${{ github.event.inputs.version }}" + else + TAG=${GITHUB_REF_NAME#v} + IMAGE_LABEL="honcho-image:deployment-${GITHUB_REF_NAME}" + fi + + curl --fail -X POST \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer ${{ secrets.TEST_ENV_WEBHOOK_SECRET }}" \ + -d "{\"version\":\"$TAG\",\"image_label\":\"$IMAGE_LABEL\"}" \ + "${{ secrets.TEST_ENV_URL }}/webhooks/v1/add_honcho_version" diff --git a/Dockerfile b/Dockerfile index 2a4f0532..f15bb194 100644 --- a/Dockerfile +++ b/Dockerfile @@ -41,5 +41,5 @@ COPY --chown=app:app alembic.ini /app/alembic.ini EXPOSE 8000 # https://stackoverflow.com/questions/29663459/python-app-does-not-print-anything-when-running-detached-in-docker -CMD ["fastapi", "dev", "--host", "0.0.0.0", "src/main.py"] +CMD ["fastapi", "run", "--host", "0.0.0.0", "src/main.py"] diff --git a/scripts/migrate_db.py b/scripts/migrate_db.py new file mode 100644 index 00000000..35cca61a --- /dev/null +++ b/scripts/migrate_db.py @@ -0,0 +1,19 @@ +# honcho/scripts/migrate_db.py +import os +import sys + +# Add the project root to the path +# This assumes the script is run from the scripts directory +project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) +sys.path.insert(0, project_root) + +from alembic import command # noqa: E402 +from alembic.config import Config # noqa: E402 + +from src.db import scaffold_db # noqa: E402 + +if __name__ == "__main__": + scaffold_db() + # run alembic migrations + alembic_cfg = Config("alembic.ini") + command.upgrade(alembic_cfg, "head") diff --git a/scripts/provision_db.py b/scripts/provision_db.py index 3530e5a5..1ec8bc0b 100644 --- a/scripts/provision_db.py +++ b/scripts/provision_db.py @@ -7,11 +7,7 @@ import sys project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) sys.path.insert(0, project_root) -## First import the models to register them with Base -from src import ( - models, -) # This registers all models with Base Now you can import from src -from src.db import scaffold_db +from src.db import scaffold_db # noqa: E402 if __name__ == "__main__": scaffold_db()