diff --git a/.github/workflows/build_docker.yml b/.github/workflows/build_docker.yml new file mode 100644 index 0000000..d74f8a6 --- /dev/null +++ b/.github/workflows/build_docker.yml @@ -0,0 +1,102 @@ +name: Build Docker Image + +on: + workflow_call: + outputs: + server_image_tag: + description: "The tag of the server image that was built" + value: ${{ jobs.build.outputs.server_image_tag }} + client_image_tag: + description: "The tag of the client image that was built" + value: ${{ jobs.build.outputs.client_image_tag }} + +jobs: + build: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - dockerfile: .docker/client/Dockerfile + image: ghcr.io/ls1intum/thaii/client + context: ./client + path: client + - dockerfile: .docker/server/Dockerfile + image: ghcr.io/ls1intum/thaii/server + context: ./server + path: server + outputs: + server_image_tag: "${{ steps.output-tag-server.outputs.server_image_tag }}" + client_image_tag: "${{ steps.output-tag-client.outputs.client_image_tag }}" + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 1 + + - name: Get changed files in the client folder + id: changed-files-client-folder + uses: tj-actions/changed-files@v44 + with: + files: client/** + + - name: Get changed files in the server folder + id: changed-files-server-folder + uses: tj-actions/changed-files@v44 + with: + files: server/** + + - name: Log in to the Container registry + if: ${{ (steps.changed-files-client-folder.outputs.any_changed == 'true') || (steps.changed-files-server-folder.outputs.any_changed == 'true') }} + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Set up QEMU + if: ${{ (steps.changed-files-client-folder.outputs.any_changed == 'true') || (steps.changed-files-server-folder.outputs.any_changed == 'true') }} + uses: docker/setup-qemu-action@v3 + with: + platforms: all + + - name: Install Docker Buildx + if: ${{ (steps.changed-files-client-folder.outputs.any_changed == 'true') || (steps.changed-files-server-folder.outputs.any_changed == 'true') }} + id: buildx + uses: docker/setup-buildx-action@v3 + + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ matrix.image }} + tags: | + type=raw,value=latest,enable={{is_default_branch}} + type=ref,event=branch + type=ref,event=pr + + - name: Build and push Docker Image + uses: docker/build-push-action@v5 + if: ${{ (steps.changed-files-client-folder.outputs.any_changed == 'true' && matrix.path == 'client') || (steps.changed-files-server-folder.outputs.any_changed == 'true' && matrix.path == 'server') }} + with: + context: ${{ matrix.context }} + file: ${{ matrix.dockerfile }} + platforms: linux/amd64,linux/arm64 + push: true + tags: ${{ steps.meta.outputs.tags }} + + - id: output-tag-client + run: | + if [[ "${{ matrix.path }}" == "client" ]] && [[ "${{ steps.changed-files-client-folder.outputs.any_changed }}" == "true" ]]; then + echo "client_image_tag=${{ steps.meta.outputs.version }}" >> "$GITHUB_OUTPUT" + elif [[ "${{ matrix.path }}" == "client" ]]; then + echo "client_image_tag=latest" >> "$GITHUB_OUTPUT" + fi + + - id: output-tag-server + run: | + if [[ "${{ matrix.path }}" == "server" ]] && [[ "${{ steps.changed-files-server-folder.outputs.any_changed }}" == "true" ]]; then + echo "server_image_tag=${{ steps.meta.outputs.version }}" >> "$GITHUB_OUTPUT" + elif [[ "${{ matrix.path }}" == "server" ]]; then + echo "server_image_tag=latest" >> "$GITHUB_OUTPUT" + fi \ No newline at end of file diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 17a1014..15f0707 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -3,7 +3,7 @@ name: Deploy on: push: branches: - - develop # or the branch you want to deploy from + - main # or the branch you want to deploy from jobs: build: @@ -58,6 +58,7 @@ jobs: scp -o StrictHostKeyChecking=no -r ./letsencrypt ${{ secrets.SERVER_USER }}@${{ secrets.SERVER_DOMAIN }}:~/letsencrypt - name: Set Up Environment Variables + uses: appleboy/ssh-action@v1.0.3 with: host: ${{ secrets.SERVER_DOMAIN }} username: ${{ secrets.SERVER_USER }} diff --git a/.github/workflows/deploy_docker.yml b/.github/workflows/deploy_docker.yml new file mode 100644 index 0000000..a4c78fe --- /dev/null +++ b/.github/workflows/deploy_docker.yml @@ -0,0 +1,79 @@ +name: Deploy Docker Image + +on: + workflow_call: + inputs: + environment: + required: true + type: string + server_image_tag: + default: "latest" + type: string + client_image_tag: + default: "latest" + type: string + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - name: SSH to VM and Execute Docker-Compose Down + uses: appleboy/ssh-action@v1.0.3 + with: + host: ${{ secrets.SERVER_DOMAIN }} + username: ${{ secrets.SERVER_USER }} + key: ${{ secrets.SSH_KEY }} + script: | + docker compose -f compose.yml --env-file=.env down --remove-orphans --rmi all + + - name: Checkout Code + uses: actions/checkout@v3 + + - name: Copy Files to Server + uses: appleboy/ssh-action@v1.0.3 + with: + host: ${{ secrets.SERVER_DOMAIN }} + username: ${{ secrets.SERVER_USER }} + key: ${{ secrets.SSH_KEY }} + script: | + scp -o StrictHostKeyChecking=no ./compose.yml ${{ secrets.SERVER_USER }}@${{ secrets.SERVER_DOMAIN }}:~/compose.yml + scp -o StrictHostKeyChecking=no -r ./letsencrypt ${{ secrets.SERVER_USER }}@${{ secrets.SERVER_DOMAIN }}:~/letsencrypt + + - name: Set Up Environment Variables + uses: appleboy/ssh-action@v1.0.3 + with: + host: ${{ secrets.SERVER_DOMAIN }} + username: ${{ secrets.SERVER_USER }} + key: ${{ secrets.SSH_KEY }} + script: | + ssh -o StrictHostKeyChecking=no ${{ secrets.SERVER_USER }}@${{ secrets.SERVER_DOMAIN }} << 'EOF' + touch .env + echo "OPENAI_API_KEY=${{ secrets.OPENAI_API_KEY }}" >> .env + echo "DEBUG=${{ secrets.DEBUG }}" >> .env + echo "SECRET_KEY=${{ secrets.SECRET_KEY }}" >> .env + echo "POSTGRES_DB=${{ secrets.POSTGRES_DB }}" >> .env + echo "POSTGRES_USER=${{ secrets.POSTGRES_USER }}" >> .env + echo "POSTGRES_PASSWORD=${{ secrets.POSTGRES_PASSWORD }}" >> .env + echo "POSTGRES_HOST=${{ secrets.POSTGRES_HOST }}" >> .env + echo "EMAIL_USE_TLS=${{ secrets.EMAIL_USE_TLS }}" >> .env + echo "EMAIL_HOST=${{ secrets.EMAIL_HOST }}" >> .env + echo "EMAIL_HOST_USER=${{ secrets.EMAIL_HOST_USER }}" >> .env + echo "EMAIL_HOST_PASSWORD=${{ secrets.EMAIL_HOST_PASSWORD }}" >> .env + echo "DEFAULT_FROM_EMAIL=${{ secrets.DEFAULT_FROM_EMAIL }}" >> .env + echo "EMAIL_PORT=${{ secrets.EMAIL_PORT }}" >> .env + echo "DJANGO_SUPERUSER_USERNAME=${{ secrets.DJANGO_SUPERUSER_USERNAME }}" >> .env + echo "DJANGO_SUPERUSER_PASSWORD=${{ secrets.DJANGO_SUPERUSER_PASSWORD }}" >> .env + echo "DJANGO_SUPERUSER_EMAIL=${{ secrets.DJANGO_SUPERUSER_EMAIL }}" >> .env + EOF + + - name: SSH to VM and Execute Docker-Compose Up + uses: appleboy/ssh-action@v1.0.3 + with: + host: ${{ secrets.SERVER_DOMAIN }} + username: ${{ secrets.SERVER_USER }} + key: ${{ secrets.SSH_KEY }} + script: | + ssh -o StrictHostKeyChecking=no ${{ secrets.SERVER_USER }}@${{ secrets.SERVER_DOMAIN }} "mkdir -p ~/" + ssh -o StrictHostKeyChecking=no ${{ secrets.SERVER_USER }}@${{ secrets.SERVER_DOMAIN }} "touch ~/letsencrypt/acme.json && chmod 600 ~/letsencrypt/acme.json" + ssh -o StrictHostKeyChecking=no ${{ secrets.SERVER_USER }}@${{ secrets.SERVER_DOMAIN }} "docker login ghcr.io -u ${{ github.actor }} --password-stdin <<< ${{ secrets.GITHUB_TOKEN }}" + ssh -o StrictHostKeyChecking=no ${{ secrets.SERVER_USER }}@${{ secrets.SERVER_DOMAIN }} "docker compose pull && docker compose up -d && docker compose logs" diff --git a/.github/workflows/prod.yml b/.github/workflows/prod.yml new file mode 100644 index 0000000..b3e462c --- /dev/null +++ b/.github/workflows/prod.yml @@ -0,0 +1,19 @@ +name: Build and Deploy to Prod + +on: + push: + branches: [develop] + +jobs: + build-prod-container: + uses: ./.github/workflows/build_docker.yml + secrets: inherit + deploy-prod-container: + needs: build-prod-container + uses: ./.github/workflows/deploy_docker.yml + secrets: inherit + with: + environment: Production + server_image_tag: "latest" + client_image_tag: "latest" + \ No newline at end of file