From a7250d3fc66bbc173b727b71d62b4adeb5a448dd Mon Sep 17 00:00:00 2001 From: romamatran Date: Fri, 10 Jul 2026 10:57:29 +0300 Subject: [PATCH 01/23] Add GitHub Actions workflow for production deployment --- .github/workflows/deploy-production.yml | 155 ++++++++++++++++++++++++ 1 file changed, 155 insertions(+) create mode 100644 .github/workflows/deploy-production.yml diff --git a/.github/workflows/deploy-production.yml b/.github/workflows/deploy-production.yml new file mode 100644 index 000000000..ffef555b5 --- /dev/null +++ b/.github/workflows/deploy-production.yml @@ -0,0 +1,155 @@ +name: Deploy Production + +on: + push: + branches: + - main + +permissions: + contents: read + packages: write + +env: + IMAGE_NAME: ghcr.io/${{ github.repository_owner }}/grancaffe + +jobs: + + build-and-deploy: + + name: Build Docker image and deploy + runs-on: ubuntu-latest + + steps: + + ################################# + # Checkout source code + ################################# + + - name: Checkout repository + uses: actions/checkout@v4 + + + ################################# + # Docker setup + ################################# + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + + ################################# + # Login GHCR + ################################# + + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + + ################################# + # Build metadata + ################################# + + - name: Docker metadata + id: meta + uses: docker/metadata-action@v5 + with: + + images: ${{ env.IMAGE_NAME }} + + tags: | + type=sha + type=raw,value=latest + + + ################################# + # Build and push image + ################################# + + - name: Build and push Docker image + uses: docker/build-push-action@v6 + + with: + + context: . + + file: ./Dockerfile + + push: true + + tags: | + ${{ steps.meta.outputs.tags }} + + labels: | + ${{ steps.meta.outputs.labels }} + + build-args: | + GIT_COMMIT=${{ github.sha }} + GIT_BRANCH=${{ github.ref_name }} + + cache-from: | + type=gha + + cache-to: | + type=gha,mode=max + + + ################################# + # Deploy to DigitalOcean + ################################# + + - name: Deploy to DigitalOcean Droplet + + uses: appleboy/ssh-action@v1.2.0 + + with: + + host: ${{ secrets.DO_HOST }} + + username: ${{ secrets.DO_USER }} + + key: ${{ secrets.DO_SSH_KEY }} + + script: | + + cd /opt/grancaffe + + echo "Pull latest image" + + docker compose pull + + + echo "Restart containers" + + docker compose up -d + + + echo "Cleanup old images" + + docker image prune -f + + + ################################# + # Health check + ################################# + + - name: Verify deployment + + uses: appleboy/ssh-action@v1.2.0 + + with: + + host: ${{ secrets.DO_HOST }} + + username: ${{ secrets.DO_USER }} + + key: ${{ secrets.DO_SSH_KEY }} + + script: | + + sleep 20 + + curl --fail http://localhost:8080/health From 6e80a31d60ff2dccc6b3c76548c241dd228dd055 Mon Sep 17 00:00:00 2001 From: romamatran Date: Fri, 10 Jul 2026 11:23:17 +0300 Subject: [PATCH 02/23] Add SSH connection test workflow --- .github/workflows/test-ssh.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 .github/workflows/test-ssh.yml diff --git a/.github/workflows/test-ssh.yml b/.github/workflows/test-ssh.yml new file mode 100644 index 000000000..9f7653091 --- /dev/null +++ b/.github/workflows/test-ssh.yml @@ -0,0 +1,26 @@ +name: Test SSH Connection + +on: + workflow_dispatch: + +jobs: + test: + + runs-on: ubuntu-latest + + steps: + + - name: Test SSH connection + + uses: appleboy/ssh-action@v1.2.0 + + with: + host: ${{ secrets.DO_HOST }} + username: ${{ secrets.DO_USER }} + key: ${{ secrets.DO_SSH_KEY }} + + script: | + echo "Connected successfully" + hostname + whoami + docker --version From 9e82be642a9107bf534fb252205e3518489075b6 Mon Sep 17 00:00:00 2001 From: romamatran Date: Fri, 10 Jul 2026 12:15:05 +0300 Subject: [PATCH 03/23] Enhance Docker deployment workflow Updated Docker deployment workflow to include image cleanup and improved health check. --- .github/workflows/deploy-production.yml | 79 +++++++++++++++++++++++-- 1 file changed, 73 insertions(+), 6 deletions(-) diff --git a/.github/workflows/deploy-production.yml b/.github/workflows/deploy-production.yml index ffef555b5..b3f027c6c 100644 --- a/.github/workflows/deploy-production.yml +++ b/.github/workflows/deploy-production.yml @@ -9,9 +9,11 @@ permissions: contents: read packages: write + env: IMAGE_NAME: ghcr.io/${{ github.repository_owner }}/grancaffe + jobs: build-and-deploy: @@ -19,8 +21,10 @@ jobs: name: Build Docker image and deploy runs-on: ubuntu-latest + steps: + ################################# # Checkout source code ################################# @@ -29,6 +33,7 @@ jobs: uses: actions/checkout@v4 + ################################# # Docker setup ################################# @@ -37,32 +42,38 @@ jobs: uses: docker/setup-buildx-action@v3 + ################################# # Login GHCR ################################# - name: Login to GitHub Container Registry uses: docker/login-action@v3 + with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} + ################################# - # Build metadata + # Docker metadata ################################# - name: Docker metadata id: meta + uses: docker/metadata-action@v5 + with: images: ${{ env.IMAGE_NAME }} tags: | - type=sha type=raw,value=latest + type=sha + ################################# @@ -70,6 +81,7 @@ jobs: ################################# - name: Build and push Docker image + uses: docker/build-push-action@v6 with: @@ -80,23 +92,29 @@ jobs: push: true + tags: | ${{ steps.meta.outputs.tags }} + labels: | ${{ steps.meta.outputs.labels }} + build-args: | GIT_COMMIT=${{ github.sha }} GIT_BRANCH=${{ github.ref_name }} + cache-from: | type=gha + cache-to: | type=gha,mode=max + ################################# # Deploy to DigitalOcean ################################# @@ -105,6 +123,7 @@ jobs: uses: appleboy/ssh-action@v1.2.0 + with: host: ${{ secrets.DO_HOST }} @@ -113,33 +132,58 @@ jobs: key: ${{ secrets.DO_SSH_KEY }} + script: | + + set -e + + + echo "Navigate to deployment folder" + cd /opt/grancaffe + + + echo "Login into GHCR" + + echo "${{ secrets.GHCR_TOKEN }}" | docker login ghcr.io \ + -u ${{ github.repository_owner }} \ + --password-stdin + + + echo "Pull latest image" docker compose pull + echo "Restart containers" - docker compose up -d + docker compose up -d --remove-orphans + - echo "Cleanup old images" + echo "Remove unused images" docker image prune -f + + echo "Deployment completed" + + + ################################# - # Health check + # Verify deployment ################################# - name: Verify deployment uses: appleboy/ssh-action@v1.2.0 + with: host: ${{ secrets.DO_HOST }} @@ -148,8 +192,31 @@ jobs: key: ${{ secrets.DO_SSH_KEY }} + script: | + + set -e + + + echo "Waiting application startup..." + sleep 20 - curl --fail http://localhost:8080/health + + + echo "Checking container status" + + + docker ps + + + + echo "Checking application" + + + curl --fail http://localhost:5000 || exit 1 + + + + echo "Application is healthy" From da38778ac6dd9630c90bf7142e7116795ff390c9 Mon Sep 17 00:00:00 2001 From: romamatran Date: Fri, 10 Jul 2026 12:26:42 +0300 Subject: [PATCH 04/23] Rename workflow to Deploy GranCaffe and update steps --- .github/workflows/deploy-production.yml | 138 ++++++++++++++++++++++-- 1 file changed, 127 insertions(+), 11 deletions(-) diff --git a/.github/workflows/deploy-production.yml b/.github/workflows/deploy-production.yml index b3f027c6c..e7b7c43dc 100644 --- a/.github/workflows/deploy-production.yml +++ b/.github/workflows/deploy-production.yml @@ -1,177 +1,276 @@ -name: Deploy Production +name: Deploy GranCaffe + on: + push: + branches: + - development - main + permissions: + contents: read packages: write + env: + IMAGE_NAME: ghcr.io/${{ github.repository_owner }}/grancaffe + jobs: + build-and-deploy: + name: Build Docker image and deploy + + runs-on: ubuntu-latest + steps: + ################################# # Checkout source code ################################# - name: Checkout repository + uses: actions/checkout@v4 + + ################################# # Docker setup ################################# - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + ################################# # Login GHCR ################################# - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + ################################# # Docker metadata ################################# - name: Docker metadata + + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.IMAGE_NAME }} + tags: | + type=raw,value=latest + type=sha + + ################################# - # Build and push image + # Build and Push Docker image ################################# - name: Build and push Docker image + uses: docker/build-push-action@v6 + with: + context: . + file: ./Dockerfile + push: true tags: | + ${{ steps.meta.outputs.tags }} labels: | + ${{ steps.meta.outputs.labels }} build-args: | + GIT_COMMIT=${{ github.sha }} + GIT_BRANCH=${{ github.ref_name }} cache-from: | + type=gha cache-to: | + type=gha,mode=max + + + ################################# # Deploy to DigitalOcean ################################# - name: Deploy to DigitalOcean Droplet + uses: appleboy/ssh-action@v1.2.0 + with: + host: ${{ secrets.DO_HOST }} + username: ${{ secrets.DO_USER }} + key: ${{ secrets.DO_SSH_KEY }} + script: | set -e - echo "Navigate to deployment folder" + + echo "=================================" + + echo "Starting deployment" + + echo "=================================" + + + + if [ ! -d "/opt/grancaffe" ]; then + + echo "/opt/grancaffe does not exist" + + exit 1 + + fi + + cd /opt/grancaffe - echo "Login into GHCR" + + + echo "Login to GHCR" + + echo "${{ secrets.GHCR_TOKEN }}" | docker login ghcr.io \ + -u ${{ github.repository_owner }} \ + --password-stdin + + echo "Pull latest image" + + docker compose pull - echo "Restart containers" + + + echo "Restart application" + + docker compose up -d --remove-orphans - echo "Remove unused images" + + + echo "Cleanup old images" + + docker image prune -f - echo "Deployment completed" + + + echo "Deployment finished" + + + + @@ -181,42 +280,59 @@ jobs: - name: Verify deployment + uses: appleboy/ssh-action@v1.2.0 + with: + host: ${{ secrets.DO_HOST }} + username: ${{ secrets.DO_USER }} + key: ${{ secrets.DO_SSH_KEY }} + script: | set -e - echo "Waiting application startup..." + + echo "Waiting for application startup" + + sleep 20 - echo "Checking container status" + + + echo "Containers status" + docker ps - echo "Checking application" + + + echo "Testing application" + curl --fail http://localhost:5000 || exit 1 - echo "Application is healthy" + + + echo "Deployment successful" From 8857698d48b3bdc6b135189f32a916fcd36bc3a2 Mon Sep 17 00:00:00 2001 From: romamatran Date: Fri, 10 Jul 2026 12:34:30 +0300 Subject: [PATCH 05/23] Update deploy-production.yml --- .github/workflows/deploy-production.yml | 51 +++++++++---------------- 1 file changed, 19 insertions(+), 32 deletions(-) diff --git a/.github/workflows/deploy-production.yml b/.github/workflows/deploy-production.yml index e7b7c43dc..be2de8346 100644 --- a/.github/workflows/deploy-production.yml +++ b/.github/workflows/deploy-production.yml @@ -10,6 +10,7 @@ on: - main + permissions: contents: read @@ -41,7 +42,7 @@ jobs: ################################# - # Checkout source code + # Checkout source ################################# - name: Checkout repository @@ -53,10 +54,10 @@ jobs: ################################# - # Docker setup + # Docker Buildx ################################# - - name: Set up Docker Buildx + - name: Setup Docker Buildx uses: docker/setup-buildx-action@v3 @@ -77,9 +78,11 @@ jobs: registry: ghcr.io - username: ${{ github.actor }} + username: ${{ github.repository_owner }} + + password: ${{ secrets.GHCR_TOKEN }} + - password: ${{ secrets.GITHUB_TOKEN }} @@ -91,10 +94,8 @@ jobs: - name: Docker metadata - id: meta - uses: docker/metadata-action@v5 @@ -114,6 +115,8 @@ jobs: + + ################################# # Build and Push Docker image ################################# @@ -168,7 +171,7 @@ jobs: ################################# - # Deploy to DigitalOcean + # Deploy DigitalOcean ################################# - name: Deploy to DigitalOcean Droplet @@ -198,21 +201,7 @@ jobs: - echo "=================================" - - echo "Starting deployment" - - echo "=================================" - - - - if [ ! -d "/opt/grancaffe" ]; then - - echo "/opt/grancaffe does not exist" - - exit 1 - - fi + echo "Starting deployment..." @@ -222,14 +211,12 @@ jobs: - echo "Login to GHCR" + echo "Login GHCR" echo "${{ secrets.GHCR_TOKEN }}" | docker login ghcr.io \ - -u ${{ github.repository_owner }} \ - --password-stdin @@ -246,7 +233,7 @@ jobs: - echo "Restart application" + echo "Restart containers" @@ -256,7 +243,7 @@ jobs: - echo "Cleanup old images" + echo "Remove unused images" @@ -266,7 +253,7 @@ jobs: - echo "Deployment finished" + echo "Deployment completed" @@ -305,7 +292,7 @@ jobs: - echo "Waiting for application startup" + echo "Waiting startup..." @@ -315,7 +302,7 @@ jobs: - echo "Containers status" + echo "Docker containers" @@ -335,4 +322,4 @@ jobs: - echo "Deployment successful" + echo "Application is running" From 3cf9bb7c77c1d4a4b08db99091e52c0b20fe3128 Mon Sep 17 00:00:00 2001 From: romamatran Date: Fri, 10 Jul 2026 12:40:57 +0300 Subject: [PATCH 06/23] Update deploy-production.yml --- .github/workflows/deploy-production.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/deploy-production.yml b/.github/workflows/deploy-production.yml index be2de8346..9eb70271a 100644 --- a/.github/workflows/deploy-production.yml +++ b/.github/workflows/deploy-production.yml @@ -7,7 +7,6 @@ on: branches: - development - - main From be5a36cd3d7455f8cadeb0a6e2a973495675881d Mon Sep 17 00:00:00 2001 From: romamatran Date: Fri, 10 Jul 2026 12:45:03 +0300 Subject: [PATCH 07/23] Update docker-image.yml --- .github/workflows/docker-image.yml | 174 ++++++++++++++++++++++++----- 1 file changed, 144 insertions(+), 30 deletions(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index daf70c9e9..43bdb3740 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -1,53 +1,167 @@ name: Docker Image CI - + on: push: - branches: [ "main", "develop" ] + branches: + - main + - develop + workflow_dispatch: - + + +permissions: + contents: read + packages: write + + +env: + IMAGE_NAME: ghcr.io/${{ github.repository_owner }}/grancaffe + + jobs: + build: + name: Build and Push Docker Image + runs-on: ubuntu-latest - name: Build image - + + steps: - - name: Checkout + + + ################################# + # Checkout source code + ################################# + + - name: Checkout repository uses: actions/checkout@v4 - - name: Set TAG environment variable - run: | - if [ "${{ github.ref_name }}" = "main" ]; then - echo "TAG=latest" >> $GITHUB_ENV - else - echo "TAG=develop" >> $GITHUB_ENV - fi + + + + ################################# + # Docker setup + ################################# + - name: Set up QEMU uses: docker/setup-qemu-action@v3 - - name: Buildx - id: buildx + + + - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - - name: Login to Docker Hub + + + + ################################# + # Login GHCR + ################################# + + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + + with: + + registry: ghcr.io + + username: ${{ github.actor }} + + password: ${{ secrets.GITHUB_TOKEN }} + + + + ################################# + # Define image tag + ################################# + + - name: Set image tag + + shell: bash + + run: | + + if [ "${{ github.ref_name }}" = "main" ]; then + + echo "IMAGE_TAG=latest" >> $GITHUB_ENV + + else + + echo "IMAGE_TAG=develop" >> $GITHUB_ENV + + fi + + + + ################################# + # Docker metadata + ################################# + + - name: Docker metadata + + id: meta + + uses: docker/metadata-action@v5 + with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_PASS }} - - name: Build and push on grandnode2 + + images: ${{ env.IMAGE_NAME }} + + tags: | + + type=raw,value=${{ env.IMAGE_TAG }} + + type=sha + + + + ################################# + # Build and push image + ################################# + + - name: Build and push Docker image + uses: docker/build-push-action@v6 + + with: + context: . - build-args: | - GIT_COMMIT=${{ github.sha }} - GIT_BRANCH=${{ github.ref_name }} + + + file: ./Dockerfile + + push: true + + platforms: linux/amd64 - tags: grandnode/grandnode2:${{ env.TAG }} - - name: Build and push on develop - uses: docker/build-push-action@v6 - with: - context: . + + + tags: | + + ${{ steps.meta.outputs.tags }} + + + labels: | + + ${{ steps.meta.outputs.labels }} + + + build-args: | + GIT_COMMIT=${{ github.sha }} + GIT_BRANCH=${{ github.ref_name }} - push: true - platforms: linux/amd64 - tags: grandnode/develop:${{ env.TAG }} + + + + cache-from: | + + type=gha + + + + cache-to: | + + type=gha,mode=max From 61196572ab67cbc1afa287af42654a8d071c9813 Mon Sep 17 00:00:00 2001 From: romamatran Date: Fri, 10 Jul 2026 13:53:08 +0300 Subject: [PATCH 08/23] Refactor GitHub Actions for GranCaffe deployment Updated the workflow to deploy GranCaffe instead of grandnode, added support for main branch, and modified Docker deployment steps. --- .github/workflows/grandnode.yml | 402 ++++++++++++++++++++++++++------ 1 file changed, 334 insertions(+), 68 deletions(-) diff --git a/.github/workflows/grandnode.yml b/.github/workflows/grandnode.yml index 81cdfdfd1..ab2e2f517 100644 --- a/.github/workflows/grandnode.yml +++ b/.github/workflows/grandnode.yml @@ -1,75 +1,341 @@ -name: Build and deploy .NET Core app to Linux WebApp grandnode +name: Build and Deploy GranCaffe + + on: + push: - branches: [ "develop" ] - + + branches: + - develop + - main + + + workflow_dispatch: + + + +permissions: + + contents: read + + packages: write + + + env: - AZURE_WEBAPP_NAME: grandnode-dev - AZURE_WEBAPP_PACKAGE_PATH: src/Web/Grand.Web/publish - AZURE_WEBAPP_PUBLISH_PROFILE: ${{ secrets.AZURE_WEBAPP_PUBLISH_PROFILE }} - CONFIGURATION: Release - WORKING_DIRECTORY: src/Web/Grand.Web + + IMAGE_NAME: ghcr.io/${{ github.repository_owner }}/grancaffe + + + jobs: + + build-and-deploy: + + + name: Build Docker image and deploy to DigitalOcean + + runs-on: ubuntu-latest + + + steps: - - uses: actions/checkout@v4 - - name: Setup .NET Core - uses: actions/setup-dotnet@v4 - with: - dotnet-version: 10.0.x - - name: Create mongoDB Docker container - run: sudo docker run -d -p 27017:27017 mongo:latest - - name: Install .NET Aspire workload - run: dotnet workload install aspire - - name: Restore - run: dotnet restore "${{ env.WORKING_DIRECTORY }}" - - name: Build sln - run: dotnet build "GrandNode.sln" - - name: Build - run: dotnet build "${{ env.WORKING_DIRECTORY }}" --configuration ${{ env.CONFIGURATION }} --no-restore -p:SourceRevisionId=${{ github.sha }} -p:GitBranch=${{ github.ref_name }} - - name: Grand.Business.Authentication Unit Tests - run: dotnet test ./src/Tests/Grand.Business.Authentication.Tests/Grand.Business.Authentication.Tests.csproj - - name: Grand.Business.Catalog Unit Tests - run: dotnet test ./src/Tests/Grand.Business.Catalog.Tests/Grand.Business.Catalog.Tests.csproj - - name: Grand.Business.Checkout Unit Tests - run: dotnet test ./src/Tests/Grand.Business.Checkout.Tests/Grand.Business.Checkout.Tests.csproj - - name: Grand.Business.Cms Unit Tests - run: dotnet test ./src/Tests/Grand.Business.Cms.Tests/Grand.Business.Cms.Tests.csproj - - name: Grand.Business.Common Unit Tests - run: dotnet test ./src/Tests/Grand.Business.Common.Tests/Grand.Business.Common.Tests.csproj - - name: Grand.Business.Customers Unit Tests - run: dotnet test ./src/Tests/Grand.Business.Customers.Tests/Grand.Business.Customers.Tests.csproj - - name: Grand.Business.Marketing Unit Tests - run: dotnet test ./src/Tests/Grand.Business.Marketing.Tests/Grand.Business.Marketing.Tests.csproj - - name: Grand.Business.Messages Unit Tests - run: dotnet test ./src/Tests/Grand.Business.Messages.Tests/Grand.Business.Messages.Tests.csproj - - name: Grand.Business.Storage Unit Tests - run: dotnet test ./src/Tests/Grand.Business.Storage.Tests/Grand.Business.Storage.Tests.csproj - - name: Grand.Business.System Unit Tests - run: dotnet test ./src/Tests/Grand.Modules.Tests/Grand.Modules.Tests.csproj - - name: Grand.Business.Data Unit Tests - run: dotnet test ./src/Tests/Grand.Data.Tests/Grand.Data.Tests.csproj - - name: Grand.Domain.Domain.Tests Unit Tests - run: dotnet test ./src/Tests/Grand.Domain.Tests/Grand.Domain.Tests.csproj - - name: Grand.Infrastructure.Tests Unit Tests - run: dotnet test ./src/Tests/Grand.Infrastructure.Tests/Grand.Infrastructure.Tests.csproj - - name: Grand.SharedKernel.Tests Unit Tests - run: dotnet test ./src/Tests/Grand.SharedKernel.Tests/Grand.SharedKernel.Tests.csproj - - name: Grand.Web.Common.Tests Unit Tests - run: dotnet test ./src/Tests/Grand.Web.Common.Tests/Grand.Web.Common.Tests.csproj - - name: Grand.Web.Admin.Tests Unit Tests - run: dotnet test ./src/Tests/Grand.Web.Admin.Tests/Grand.Web.Admin.Tests.csproj - - name: Publish - run: dotnet publish "${{ env.WORKING_DIRECTORY }}" --configuration ${{ env.CONFIGURATION }} --no-build --output "${{ env.AZURE_WEBAPP_PACKAGE_PATH }}" -p:SourceRevisionId=${{ github.sha }} -p:GitBranch=${{ github.ref_name }} - - name: Deploy to Azure WebApp - uses: azure/webapps-deploy@v2 - with: - app-name: ${{ env.AZURE_WEBAPP_NAME }} - package: ${{ env.AZURE_WEBAPP_PACKAGE_PATH }} - publish-profile: ${{ env.AZURE_WEBAPP_PUBLISH_PROFILE }} - - name: Publish Artifacts - uses: actions/upload-artifact@v4.0.0 - with: - name: webapp - path: ${{ env.AZURE_WEBAPP_PACKAGE_PATH }} + + + + ################################# + # Checkout + ################################# + + - name: Checkout repository + + uses: actions/checkout@v4 + + + + + + ################################# + # Docker setup + ################################# + + - name: Setup QEMU + + uses: docker/setup-qemu-action@v3 + + + + - name: Setup Docker Buildx + + uses: docker/setup-buildx-action@v3 + + + + + + ################################# + # Login GHCR + ################################# + + - name: Login to GitHub Container Registry + + uses: docker/login-action@v3 + + with: + + registry: ghcr.io + + username: ${{ github.actor }} + + password: ${{ secrets.GITHUB_TOKEN }} + + + + + + + ################################# + # Image tag + ################################# + + - name: Define image tag + + + run: | + + + if [ "${{ github.ref_name }}" = "main" ]; then + + echo "IMAGE_TAG=latest" >> $GITHUB_ENV + + + else + + echo "IMAGE_TAG=develop" >> $GITHUB_ENV + + + fi + + + + + + + ################################# + # Build and push Docker image + ################################# + + - name: Build and push Docker image + + + uses: docker/build-push-action@v6 + + + with: + + + context: . + + + file: ./Dockerfile + + + push: true + + + platforms: linux/amd64 + + + tags: | + + + ${{ env.IMAGE_NAME }}:${{ env.IMAGE_TAG }} + + ${{ env.IMAGE_NAME }}:${{ github.sha }} + + + + + build-args: | + + + GIT_COMMIT=${{ github.sha }} + + + GIT_BRANCH=${{ github.ref_name }} + + + + + cache-from: | + + type=gha + + + + cache-to: | + + type=gha,mode=max + + + + + + + ################################# + # Deploy DigitalOcean + ################################# + + - name: Deploy to DigitalOcean Droplet + + + uses: appleboy/ssh-action@v1.2.0 + + + + with: + + + host: ${{ secrets.DO_HOST }} + + + username: ${{ secrets.DO_USER }} + + + key: ${{ secrets.DO_SSH_KEY }} + + + + script: | + + + + set -e + + + + echo "Go to application directory" + + + + cd /opt/grancaffe + + + + + + echo "Login GHCR" + + + + echo "${{ secrets.GHCR_TOKEN }}" | docker login ghcr.io \ + + -u ${{ github.repository_owner }} \ + + --password-stdin + + + + + + echo "Pull new image" + + + + docker compose pull + + + + + + echo "Restart application" + + + + docker compose up -d --remove-orphans + + + + + + echo "Cleanup old images" + + + + docker image prune -f + + + + + + echo "Deployment finished" + + + + + + + ################################# + # Health check + ################################# + + - name: Verify deployment + + + + uses: appleboy/ssh-action@v1.2.0 + + + + with: + + + host: ${{ secrets.DO_HOST }} + + + username: ${{ secrets.DO_USER }} + + + key: ${{ secrets.DO_SSH_KEY }} + + + + + script: | + + + + set -e + + + + echo "Waiting application startup" + + + + sleep 20 + + + + + echo "Containers status" + + + + docker ps + + + + + echo "Health check" + + + + curl --fail http://localhost:5000 || exit 1 + + + + + echo "Application is running" From 171b0c1f1d0870058ec367d7d5f95341b669325e Mon Sep 17 00:00:00 2001 From: romamatran Date: Fri, 10 Jul 2026 14:13:11 +0300 Subject: [PATCH 09/23] Update deploy-production.yml --- .github/workflows/deploy-production.yml | 121 ++++++++---------------- 1 file changed, 38 insertions(+), 83 deletions(-) diff --git a/.github/workflows/deploy-production.yml b/.github/workflows/deploy-production.yml index 9eb70271a..db2ce1cf2 100644 --- a/.github/workflows/deploy-production.yml +++ b/.github/workflows/deploy-production.yml @@ -1,12 +1,13 @@ -name: Deploy GranCaffe +name: Deploy GranCaffe Production on: push: - branches: - - development + - develop + + workflow_dispatch: @@ -31,7 +32,6 @@ jobs: name: Build Docker image and deploy - runs-on: ubuntu-latest @@ -39,22 +39,19 @@ jobs: steps: + ######################################## + # Checkout + ######################################## - ################################# - # Checkout source - ################################# - - - name: Checkout repository + - name: Checkout source code uses: actions/checkout@v4 - - - ################################# + ######################################## # Docker Buildx - ################################# + ######################################## - name: Setup Docker Buildx @@ -62,34 +59,27 @@ jobs: - - - ################################# + ######################################## # Login GHCR - ################################# + ######################################## - - name: Login to GitHub Container Registry + - name: Login GitHub Container Registry uses: docker/login-action@v3 - with: registry: ghcr.io - username: ${{ github.repository_owner }} - - password: ${{ secrets.GHCR_TOKEN }} - + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} - - - ################################# + ######################################## # Docker metadata - ################################# + ######################################## - name: Docker metadata @@ -97,13 +87,10 @@ jobs: uses: docker/metadata-action@v5 - with: - images: ${{ env.IMAGE_NAME }} - tags: | type=raw,value=latest @@ -112,32 +99,27 @@ jobs: - - - - - ################################# - # Build and Push Docker image - ################################# + ######################################## + # Build and push image + ######################################## - name: Build and push Docker image - uses: docker/build-push-action@v6 with: - context: . - file: ./Dockerfile - push: true + platforms: linux/amd64 + + tags: | ${{ steps.meta.outputs.tags }} @@ -168,10 +150,9 @@ jobs: - - ################################# + ######################################## # Deploy DigitalOcean - ################################# + ######################################## - name: Deploy to DigitalOcean Droplet @@ -179,7 +160,6 @@ jobs: uses: appleboy/ssh-action@v1.2.0 - with: @@ -192,7 +172,6 @@ jobs: key: ${{ secrets.DO_SSH_KEY }} - script: | @@ -200,8 +179,7 @@ jobs: - echo "Starting deployment..." - + echo "Go to application directory" cd /opt/grancaffe @@ -209,15 +187,10 @@ jobs: - echo "Login GHCR" - - echo "${{ secrets.GHCR_TOKEN }}" | docker login ghcr.io \ - -u ${{ github.repository_owner }} \ - --password-stdin - + echo "${{ secrets.GHCR_TOKEN }}" | docker login ghcr.io -u romamatran --password-stdin @@ -225,25 +198,20 @@ jobs: echo "Pull latest image" - docker compose pull - echo "Restart containers" - docker compose up -d --remove-orphans - - echo "Remove unused images" - + echo "Cleanup old images" docker image prune -f @@ -251,26 +219,22 @@ jobs: - echo "Deployment completed" + ######################################## + # Verify + ######################################## - - ################################# - # Verify deployment - ################################# - - - name: Verify deployment + - name: Health check uses: appleboy/ssh-action@v1.2.0 - with: @@ -283,7 +247,6 @@ jobs: key: ${{ secrets.DO_SSH_KEY }} - script: | @@ -291,33 +254,25 @@ jobs: - echo "Waiting startup..." - - - - sleep 20 - - - - - - echo "Docker containers" - + echo "Containers status" docker ps + echo "Waiting application startup" + + sleep 20 - echo "Testing application" - curl --fail http://localhost:5000 || exit 1 + echo "Checking HTTP" + curl --fail http://localhost:5000 From 1d36f666e67197220ce40026c95bec430385e265 Mon Sep 17 00:00:00 2001 From: romamatran Date: Fri, 10 Jul 2026 14:36:41 +0300 Subject: [PATCH 10/23] Update Dockerfile --- Dockerfile | 83 +++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 70 insertions(+), 13 deletions(-) diff --git a/Dockerfile b/Dockerfile index e2f11fe3f..8eb075ebe 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,37 +1,94 @@ +# ========================== # Build stage +# ========================== + FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build-env + LABEL stage=build-env + WORKDIR /app -# Copy -COPY Directory.Packages.props /app/ -COPY ./src/ /app/ + +COPY Directory.Packages.props . + +COPY ./src/ ./ + + ARG GIT_COMMIT ARG GIT_BRANCH + + +# Restore dependencies +RUN dotnet restore /app/Web/Grand.Web/Grand.Web.csproj + + + # Build modules RUN for module in /app/Modules/*; do \ - dotnet build "$module" -c Release -p:SourceRevisionId=$GIT_COMMIT -p:GitBranch=$GIT_BRANCH; \ - done + dotnet build "$module" \ + -c Release \ + --no-restore \ + -p:SourceRevisionId=$GIT_COMMIT \ + -p:GitBranch=$GIT_BRANCH; \ +done + + # Build plugins RUN for plugin in /app/Plugins/*; do \ - dotnet build "$plugin" -c Release -p:SourceRevisionId=$GIT_COMMIT -p:GitBranch=$GIT_BRANCH; \ - done + dotnet build "$plugin" \ + -c Release \ + --no-restore \ + -p:SourceRevisionId=$GIT_COMMIT \ + -p:GitBranch=$GIT_BRANCH; \ +done + + + +# Publish application + +RUN dotnet publish \ + /app/Web/Grand.Web/Grand.Web.csproj \ + -c Release \ + -o /app/publish \ + --no-restore \ + -p:SourceRevisionId=$GIT_COMMIT \ + -p:GitBranch=$GIT_BRANCH + -# Publish Web project -RUN dotnet publish /app/Web/Grand.Web/Grand.Web.csproj -c Release -o ./build/release -p:SourceRevisionId=$GIT_COMMIT -p:GitBranch=$GIT_BRANCH +# ========================== # Runtime stage +# ========================== + FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS runtime -EXPOSE 8080 + WORKDIR /app -COPY --from=build-env /app/build/release . -RUN chown -R app:app /app/App_Data /app/wwwroot /app/Plugins + +EXPOSE 8080 + + + +COPY --from=build-env /app/publish . + + + +# IMPORTANT +# Verify dependencies exist + +RUN ls -la /app | grep AutoMapper || true + + + +RUN chown -R app:app /app + USER app -ENTRYPOINT ["dotnet", "Grand.Web.dll"] + + +ENTRYPOINT ["dotnet","Grand.Web.dll"] From 8521abe02eeec75c2496e3813c8d2b90a37c556a Mon Sep 17 00:00:00 2001 From: romamatran Date: Fri, 10 Jul 2026 15:14:43 +0300 Subject: [PATCH 11/23] Delete .github/workflows/grandnode.yml --- .github/workflows/grandnode.yml | 341 -------------------------------- 1 file changed, 341 deletions(-) delete mode 100644 .github/workflows/grandnode.yml diff --git a/.github/workflows/grandnode.yml b/.github/workflows/grandnode.yml deleted file mode 100644 index ab2e2f517..000000000 --- a/.github/workflows/grandnode.yml +++ /dev/null @@ -1,341 +0,0 @@ -name: Build and Deploy GranCaffe - - -on: - - push: - - branches: - - develop - - main - - - workflow_dispatch: - - - -permissions: - - contents: read - - packages: write - - - -env: - - IMAGE_NAME: ghcr.io/${{ github.repository_owner }}/grancaffe - - - -jobs: - - - build-and-deploy: - - - name: Build Docker image and deploy to DigitalOcean - - - runs-on: ubuntu-latest - - - - steps: - - - - ################################# - # Checkout - ################################# - - - name: Checkout repository - - uses: actions/checkout@v4 - - - - - - ################################# - # Docker setup - ################################# - - - name: Setup QEMU - - uses: docker/setup-qemu-action@v3 - - - - - name: Setup Docker Buildx - - uses: docker/setup-buildx-action@v3 - - - - - - ################################# - # Login GHCR - ################################# - - - name: Login to GitHub Container Registry - - uses: docker/login-action@v3 - - with: - - registry: ghcr.io - - username: ${{ github.actor }} - - password: ${{ secrets.GITHUB_TOKEN }} - - - - - - - ################################# - # Image tag - ################################# - - - name: Define image tag - - - run: | - - - if [ "${{ github.ref_name }}" = "main" ]; then - - echo "IMAGE_TAG=latest" >> $GITHUB_ENV - - - else - - echo "IMAGE_TAG=develop" >> $GITHUB_ENV - - - fi - - - - - - - ################################# - # Build and push Docker image - ################################# - - - name: Build and push Docker image - - - uses: docker/build-push-action@v6 - - - with: - - - context: . - - - file: ./Dockerfile - - - push: true - - - platforms: linux/amd64 - - - tags: | - - - ${{ env.IMAGE_NAME }}:${{ env.IMAGE_TAG }} - - ${{ env.IMAGE_NAME }}:${{ github.sha }} - - - - - build-args: | - - - GIT_COMMIT=${{ github.sha }} - - - GIT_BRANCH=${{ github.ref_name }} - - - - - cache-from: | - - type=gha - - - - cache-to: | - - type=gha,mode=max - - - - - - - ################################# - # Deploy DigitalOcean - ################################# - - - name: Deploy to DigitalOcean Droplet - - - uses: appleboy/ssh-action@v1.2.0 - - - - with: - - - host: ${{ secrets.DO_HOST }} - - - username: ${{ secrets.DO_USER }} - - - key: ${{ secrets.DO_SSH_KEY }} - - - - script: | - - - - set -e - - - - echo "Go to application directory" - - - - cd /opt/grancaffe - - - - - - echo "Login GHCR" - - - - echo "${{ secrets.GHCR_TOKEN }}" | docker login ghcr.io \ - - -u ${{ github.repository_owner }} \ - - --password-stdin - - - - - - echo "Pull new image" - - - - docker compose pull - - - - - - echo "Restart application" - - - - docker compose up -d --remove-orphans - - - - - - echo "Cleanup old images" - - - - docker image prune -f - - - - - - echo "Deployment finished" - - - - - - - ################################# - # Health check - ################################# - - - name: Verify deployment - - - - uses: appleboy/ssh-action@v1.2.0 - - - - with: - - - host: ${{ secrets.DO_HOST }} - - - username: ${{ secrets.DO_USER }} - - - key: ${{ secrets.DO_SSH_KEY }} - - - - - script: | - - - - set -e - - - - echo "Waiting application startup" - - - - sleep 20 - - - - - echo "Containers status" - - - - docker ps - - - - - echo "Health check" - - - - curl --fail http://localhost:5000 || exit 1 - - - - - echo "Application is running" From 3766501933e76ae4ee7db776e68a898ae7ed74a3 Mon Sep 17 00:00:00 2001 From: romamatran Date: Fri, 10 Jul 2026 15:34:45 +0300 Subject: [PATCH 12/23] Delete .github/workflows/docker-image-pr.yml --- .github/workflows/docker-image-pr.yml | 101 -------------------------- 1 file changed, 101 deletions(-) delete mode 100644 .github/workflows/docker-image-pr.yml diff --git a/.github/workflows/docker-image-pr.yml b/.github/workflows/docker-image-pr.yml deleted file mode 100644 index 4556fe160..000000000 --- a/.github/workflows/docker-image-pr.yml +++ /dev/null @@ -1,101 +0,0 @@ -name: Docker Image CI - -on: - push: - branches: [ "develop" ] - pull_request: - branches: [ "develop" ] - -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Build the Docker image - run: docker build . --file Dockerfile --tag grandnode2:pr-latest - - - name: Start MongoDB container - run: | - docker run --name mongodb-container -d -p 27017:27017 mongo:latest - - - name: Wait for MongoDB to be ready - run: | - echo "Waiting for MongoDB to start..." - for i in {1..10}; do - nc -z localhost 27017 && echo "MongoDB is up" && break - echo "Retrying in 3 seconds..." - echo "Container logs:" - docker logs grandnode2-container --tail 20 - sleep 3 - done - - - name: List Docker images - run: docker images - - - - name: Run the application container - run: | - docker run --name grandnode2-container -d -p 8080:8080 --link mongodb-container:mongo grandnode2:pr-latest - - - name: Wait for the application to be ready - run: | - echo "Waiting for the application to start..." - for i in {1..10}; do - curl -s http://localhost:8080 && echo "Application is up!" && break - echo "Retrying in 3 seconds..." - sleep 3 - done - - - name: Trigger the installer via POST request - run: | - echo "Running installation with form-data..." - curl -X POST http://localhost:8080/install \ - -H "Content-Type: multipart/form-data" \ - -F "AdminEmail=admin@example.com" \ - -F "AdminPassword=SecurePassword123" \ - -F "ConfirmPassword=SecurePassword123" \ - -F "DataProvider=0" \ - -F "MongoDBServerName=mongo" \ - -F "MongoDBDatabaseName=grandnode" \ - -F "InstallSampleData=true" \ - -F "CompanyName=My Company" \ - -F "CompanyAddress=123 Main St" \ - -F "CompanyPhoneNumber=1234567890" \ - -F "CompanyEmail=info@company.com" - - - name: Restart the application container - run: | - docker restart grandnode2-container - - - name: Test HTTP response after installation - run: | - echo "Waiting for the application to start..." - for i in {1..10}; do - RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8080 || echo "error") - if [ "$RESPONSE" = "200" ]; then - echo "Application is up with HTTP 200 response!" - exit 0 - elif [ "$RESPONSE" = "error" ]; then - echo "Curl encountered an error. Retrying in 3 seconds..." - else - echo "Received HTTP response code: $RESPONSE. Retrying in 3 seconds..." - fi - echo "Container logs:" - docker logs grandnode2-container --tail 30 - sleep 3 - done - echo "Application did not start successfully. Final response code: $RESPONSE" - exit 1 - - - name: Show container logs - run: | - echo "=== Full container logs ===" - docker logs grandnode2-container - echo "=== Container status ===" - docker inspect grandnode2-container --format='{{.State.Status}} - Exit: {{.State.ExitCode}}' - - - name: Stop and remove the container - run: | - docker stop grandnode2-container - docker rm grandnode2-container \ No newline at end of file From a74ff806e8c49b2cbf37ea0a0a24b41010a2307f Mon Sep 17 00:00:00 2001 From: romamatran Date: Fri, 10 Jul 2026 15:34:53 +0300 Subject: [PATCH 13/23] Delete .github/workflows/docker-image.yml --- .github/workflows/docker-image.yml | 167 ----------------------------- 1 file changed, 167 deletions(-) delete mode 100644 .github/workflows/docker-image.yml diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml deleted file mode 100644 index 43bdb3740..000000000 --- a/.github/workflows/docker-image.yml +++ /dev/null @@ -1,167 +0,0 @@ -name: Docker Image CI - -on: - push: - branches: - - main - - develop - - workflow_dispatch: - - -permissions: - contents: read - packages: write - - -env: - IMAGE_NAME: ghcr.io/${{ github.repository_owner }}/grancaffe - - -jobs: - - build: - - name: Build and Push Docker Image - - runs-on: ubuntu-latest - - - steps: - - - ################################# - # Checkout source code - ################################# - - - name: Checkout repository - uses: actions/checkout@v4 - - - - ################################# - # Docker setup - ################################# - - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - - ################################# - # Login GHCR - ################################# - - - name: Login to GitHub Container Registry - - uses: docker/login-action@v3 - - with: - - registry: ghcr.io - - username: ${{ github.actor }} - - password: ${{ secrets.GITHUB_TOKEN }} - - - - ################################# - # Define image tag - ################################# - - - name: Set image tag - - shell: bash - - run: | - - if [ "${{ github.ref_name }}" = "main" ]; then - - echo "IMAGE_TAG=latest" >> $GITHUB_ENV - - else - - echo "IMAGE_TAG=develop" >> $GITHUB_ENV - - fi - - - - ################################# - # Docker metadata - ################################# - - - name: Docker metadata - - id: meta - - uses: docker/metadata-action@v5 - - with: - - images: ${{ env.IMAGE_NAME }} - - tags: | - - type=raw,value=${{ env.IMAGE_TAG }} - - type=sha - - - - ################################# - # Build and push image - ################################# - - - name: Build and push Docker image - - uses: docker/build-push-action@v6 - - - with: - - context: . - - - file: ./Dockerfile - - - push: true - - - platforms: linux/amd64 - - - tags: | - - ${{ steps.meta.outputs.tags }} - - - labels: | - - ${{ steps.meta.outputs.labels }} - - - - build-args: | - - GIT_COMMIT=${{ github.sha }} - - GIT_BRANCH=${{ github.ref_name }} - - - - cache-from: | - - type=gha - - - - cache-to: | - - type=gha,mode=max From 589c856853655f482d766a5558cbdb59719f8a6b Mon Sep 17 00:00:00 2001 From: romamatran Date: Fri, 10 Jul 2026 15:35:00 +0300 Subject: [PATCH 14/23] Delete .github/workflows/test-ssh.yml --- .github/workflows/test-ssh.yml | 26 -------------------------- 1 file changed, 26 deletions(-) delete mode 100644 .github/workflows/test-ssh.yml diff --git a/.github/workflows/test-ssh.yml b/.github/workflows/test-ssh.yml deleted file mode 100644 index 9f7653091..000000000 --- a/.github/workflows/test-ssh.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: Test SSH Connection - -on: - workflow_dispatch: - -jobs: - test: - - runs-on: ubuntu-latest - - steps: - - - name: Test SSH connection - - uses: appleboy/ssh-action@v1.2.0 - - with: - host: ${{ secrets.DO_HOST }} - username: ${{ secrets.DO_USER }} - key: ${{ secrets.DO_SSH_KEY }} - - script: | - echo "Connected successfully" - hostname - whoami - docker --version From c6b863d01ebfe3cca3ef4b09ebcab306eae4ff21 Mon Sep 17 00:00:00 2001 From: romamatran Date: Fri, 10 Jul 2026 15:35:13 +0300 Subject: [PATCH 15/23] Delete .github/workflows/deploy-production.yml --- .github/workflows/deploy-production.yml | 279 ------------------------ 1 file changed, 279 deletions(-) delete mode 100644 .github/workflows/deploy-production.yml diff --git a/.github/workflows/deploy-production.yml b/.github/workflows/deploy-production.yml deleted file mode 100644 index db2ce1cf2..000000000 --- a/.github/workflows/deploy-production.yml +++ /dev/null @@ -1,279 +0,0 @@ -name: Deploy GranCaffe Production - - -on: - - push: - branches: - - develop - - workflow_dispatch: - - - -permissions: - - contents: read - packages: write - - - -env: - - IMAGE_NAME: ghcr.io/${{ github.repository_owner }}/grancaffe - - - -jobs: - - - build-and-deploy: - - - name: Build Docker image and deploy - - runs-on: ubuntu-latest - - - - steps: - - - ######################################## - # Checkout - ######################################## - - - name: Checkout source code - - uses: actions/checkout@v4 - - - - ######################################## - # Docker Buildx - ######################################## - - - name: Setup Docker Buildx - - uses: docker/setup-buildx-action@v3 - - - - ######################################## - # Login GHCR - ######################################## - - - name: Login GitHub Container Registry - - uses: docker/login-action@v3 - - with: - - registry: ghcr.io - - username: ${{ github.actor }} - - password: ${{ secrets.GITHUB_TOKEN }} - - - - ######################################## - # Docker metadata - ######################################## - - - name: Docker metadata - - id: meta - - uses: docker/metadata-action@v5 - - with: - - images: ${{ env.IMAGE_NAME }} - - tags: | - - type=raw,value=latest - - type=sha - - - - ######################################## - # Build and push image - ######################################## - - - name: Build and push Docker image - - uses: docker/build-push-action@v6 - - - with: - - context: . - - file: ./Dockerfile - - push: true - - - platforms: linux/amd64 - - - tags: | - - ${{ steps.meta.outputs.tags }} - - - labels: | - - ${{ steps.meta.outputs.labels }} - - - build-args: | - - GIT_COMMIT=${{ github.sha }} - - GIT_BRANCH=${{ github.ref_name }} - - - cache-from: | - - type=gha - - - cache-to: | - - type=gha,mode=max - - - - - - ######################################## - # Deploy DigitalOcean - ######################################## - - - name: Deploy to DigitalOcean Droplet - - - uses: appleboy/ssh-action@v1.2.0 - - - with: - - - host: ${{ secrets.DO_HOST }} - - - username: ${{ secrets.DO_USER }} - - - key: ${{ secrets.DO_SSH_KEY }} - - - script: | - - - set -e - - - - echo "Go to application directory" - - - cd /opt/grancaffe - - - - - echo "Login GHCR" - - - echo "${{ secrets.GHCR_TOKEN }}" | docker login ghcr.io -u romamatran --password-stdin - - - - - echo "Pull latest image" - - - docker compose pull - - - - - echo "Restart containers" - - - docker compose up -d --remove-orphans - - - - - echo "Cleanup old images" - - - docker image prune -f - - - - - echo "Deployment completed" - - - - - - ######################################## - # Verify - ######################################## - - - name: Health check - - - uses: appleboy/ssh-action@v1.2.0 - - - with: - - - host: ${{ secrets.DO_HOST }} - - - username: ${{ secrets.DO_USER }} - - - key: ${{ secrets.DO_SSH_KEY }} - - - script: | - - - set -e - - - - echo "Containers status" - - - docker ps - - - - echo "Waiting application startup" - - - sleep 20 - - - - - echo "Checking HTTP" - - - curl --fail http://localhost:5000 - - - - echo "Application is running" From e3f9099a6b8d5b7aa035c8f9fd9a300a7214d818 Mon Sep 17 00:00:00 2001 From: romamatran Date: Fri, 10 Jul 2026 15:41:33 +0300 Subject: [PATCH 16/23] Add GitHub Actions workflow for production deployment --- .github/workflows/deploy-production.yml | 98 +++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 .github/workflows/deploy-production.yml diff --git a/.github/workflows/deploy-production.yml b/.github/workflows/deploy-production.yml new file mode 100644 index 000000000..9a1d6a464 --- /dev/null +++ b/.github/workflows/deploy-production.yml @@ -0,0 +1,98 @@ +name: Deploy GranCaffe Production +on: + push: + branches: + - main + workflow_dispatch: +permissions: + contents: read + packages: write +env: + IMAGE_NAME: ghcr.io/${{ github.repository_owner }}/grancaffe +jobs: + build-and-deploy: + name: Build Docker Image and Deploy + runs-on: ubuntu-latest + steps: + ################################### + # Checkout + ################################### + - name: Checkout source + uses: actions/checkout@v4 + ################################### + # Docker setup + ################################### + - name: Setup Docker Buildx + uses: docker/setup-buildx-action@v3 + ################################### + # Login GHCR + ################################### + + - name: Login GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + ################################### + # Build and Push + ################################### + - name: Build and push Docker image + uses: docker/build-push-action@v6 + with: + context: . + file: ./Dockerfile + push: true + platforms: linux/amd64 + tags: | + ${{ env.IMAGE_NAME }}:latest + ${{ env.IMAGE_NAME }}:${{ github.sha }} + build-args: | + GIT_COMMIT=${{ github.sha }} + GIT_BRANCH=${{ github.ref_name }} + cache-from: + type=gha + cache-to: + type=gha,mode=max + ################################### + # Deploy DigitalOcean + ################################### + - name: Deploy to DigitalOcean + uses: appleboy/ssh-action@v1.2.0 + with: + host: ${{ secrets.DO_HOST }} + username: ${{ secrets.DO_USER }} + key: ${{ secrets.DO_SSH_KEY }} + script: | + set -e + echo "Go application folder" + cd /opt/grancaffe + echo "Login GHCR" + echo "${{ secrets.GHCR_TOKEN }}" | docker login ghcr.io \ + -u ${{ github.repository_owner }} \ + --password-stdin + echo "Pull latest image" + docker compose pull + echo "Restart services" + docker compose up -d --remove-orphans + echo "Cleanup old docker images" + docker image prune -f + echo "Deployment completed" + ################################### + # Verify + ################################### + - name: Health check + uses: appleboy/ssh-action@v1.2.0 + with: + host: ${{ secrets.DO_HOST }} + username: ${{ secrets.DO_USER }} + key: ${{ secrets.DO_SSH_KEY }} + script: | + set -e + echo "Containers status" + docker ps + echo "Wait application" + sleep 20 + echo "HTTP check" + curl --fail http://localhost:5000 + echo "GranCaffe is running" From 9591643d9ffa5185492c3c140c74469a5958e169 Mon Sep 17 00:00:00 2001 From: romamatran Date: Fri, 10 Jul 2026 15:42:57 +0300 Subject: [PATCH 17/23] Add pull request build workflow for Docker --- .github/workflows/pull-request-build.yml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 .github/workflows/pull-request-build.yml diff --git a/.github/workflows/pull-request-build.yml b/.github/workflows/pull-request-build.yml new file mode 100644 index 000000000..c9efef1ed --- /dev/null +++ b/.github/workflows/pull-request-build.yml @@ -0,0 +1,24 @@ +name: Pull Request Build Test +on: + pull_request: + branches: + - main +permissions: + contents: read +jobs: + docker-build: + name: Docker Build Test + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Build Docker image + uses: docker/build-push-action@v6 + with: + context: . + file: ./Dockerfile + push: false + tags: + grancaffe:test From e71a7042ba29a83341c8e5dd15dc2541cc5d90ac Mon Sep 17 00:00:00 2001 From: romamatran Date: Fri, 10 Jul 2026 15:55:35 +0300 Subject: [PATCH 18/23] Update deploy-production.yml --- .github/workflows/deploy-production.yml | 134 +++++++++--------------- 1 file changed, 51 insertions(+), 83 deletions(-) diff --git a/.github/workflows/deploy-production.yml b/.github/workflows/deploy-production.yml index 9a1d6a464..7cdfed00a 100644 --- a/.github/workflows/deploy-production.yml +++ b/.github/workflows/deploy-production.yml @@ -11,88 +11,56 @@ env: IMAGE_NAME: ghcr.io/${{ github.repository_owner }}/grancaffe jobs: build-and-deploy: - name: Build Docker Image and Deploy + name: Build and Deploy Production runs-on: ubuntu-latest steps: - ################################### - # Checkout - ################################### - - name: Checkout source - uses: actions/checkout@v4 - ################################### - # Docker setup - ################################### - - name: Setup Docker Buildx - uses: docker/setup-buildx-action@v3 - ################################### - # Login GHCR - ################################### - - - name: Login GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - ################################### - # Build and Push - ################################### - - name: Build and push Docker image - uses: docker/build-push-action@v6 - with: - context: . - file: ./Dockerfile - push: true - platforms: linux/amd64 - tags: | - ${{ env.IMAGE_NAME }}:latest - ${{ env.IMAGE_NAME }}:${{ github.sha }} - build-args: | - GIT_COMMIT=${{ github.sha }} - GIT_BRANCH=${{ github.ref_name }} - cache-from: - type=gha - cache-to: - type=gha,mode=max - ################################### - # Deploy DigitalOcean - ################################### - - name: Deploy to DigitalOcean - uses: appleboy/ssh-action@v1.2.0 - with: - host: ${{ secrets.DO_HOST }} - username: ${{ secrets.DO_USER }} - key: ${{ secrets.DO_SSH_KEY }} - script: | - set -e - echo "Go application folder" - cd /opt/grancaffe - echo "Login GHCR" - echo "${{ secrets.GHCR_TOKEN }}" | docker login ghcr.io \ - -u ${{ github.repository_owner }} \ - --password-stdin - echo "Pull latest image" - docker compose pull - echo "Restart services" - docker compose up -d --remove-orphans - echo "Cleanup old docker images" - docker image prune -f - echo "Deployment completed" - ################################### - # Verify - ################################### - - name: Health check - uses: appleboy/ssh-action@v1.2.0 - with: - host: ${{ secrets.DO_HOST }} - username: ${{ secrets.DO_USER }} - key: ${{ secrets.DO_SSH_KEY }} - script: | - set -e - echo "Containers status" - docker ps - echo "Wait application" - sleep 20 - echo "HTTP check" - curl --fail http://localhost:5000 - echo "GranCaffe is running" + - name: Checkout repository + uses: actions/checkout@v4 + - name: Setup Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Login to GHCR + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Build and Push Docker Image + uses: docker/build-push-action@v6 + with: + context: . + file: ./Dockerfile + push: true + platforms: linux/amd64 + tags: | + ${{ env.IMAGE_NAME }}:latest + ${{ env.IMAGE_NAME }}:${{ github.sha }} + cache-from: | + type=gha + cache-to: | + type=gha,mode=max + - name: Deploy to DigitalOcean + uses: appleboy/ssh-action@v1.2.0 + with: + host: ${{ secrets.DO_HOST }} + username: ${{ secrets.DO_USER }} + key: ${{ secrets.DO_SSH_KEY }} + script: | + set -e + cd /opt/grancaffe + echo "${{ secrets.GHCR_TOKEN }}" | docker login ghcr.io \ + -u ${{ github.repository_owner }} \ + --password-stdin + docker compose pull + docker compose up -d --remove-orphans + docker image prune -f + - name: Verify deployment + uses: appleboy/ssh-action@v1.2.0 + with: + host: ${{ secrets.DO_HOST }} + username: ${{ secrets.DO_USER }} + key: ${{ secrets.DO_SSH_KEY }} + script: | + sleep 20 + docker ps + curl --fail http://localhost:5000 + echo "GranCaffe is running" From b1ad7330c30f24945f425330dd0bdfaf02d2f8e9 Mon Sep 17 00:00:00 2001 From: romamatran Date: Fri, 10 Jul 2026 16:07:54 +0300 Subject: [PATCH 19/23] Simplify Dockerfile build and publish steps Refactored Dockerfile to simplify build and publish stages by removing unnecessary loops for module and plugin builds. --- Dockerfile | 67 ++++++++++++++---------------------------------------- 1 file changed, 17 insertions(+), 50 deletions(-) diff --git a/Dockerfile b/Dockerfile index 8eb075ebe..37b2e11f0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,51 +3,36 @@ # ========================== FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build-env - LABEL stage=build-env - WORKDIR /app +################################# +# Copy source +################################# COPY Directory.Packages.props . - COPY ./src/ ./ - - - ARG GIT_COMMIT ARG GIT_BRANCH +################################# +# Restore all dependencies +################################# - - -# Restore dependencies RUN dotnet restore /app/Web/Grand.Web/Grand.Web.csproj +################################# +# Build application +################################# - - -# Build modules -RUN for module in /app/Modules/*; do \ - dotnet build "$module" \ - -c Release \ - --no-restore \ - -p:SourceRevisionId=$GIT_COMMIT \ - -p:GitBranch=$GIT_BRANCH; \ -done - - - -# Build plugins -RUN for plugin in /app/Plugins/*; do \ - dotnet build "$plugin" \ +RUN dotnet build \ + /app/Web/Grand.Web/Grand.Web.csproj \ -c Release \ --no-restore \ -p:SourceRevisionId=$GIT_COMMIT \ - -p:GitBranch=$GIT_BRANCH; \ -done - - + -p:GitBranch=$GIT_BRANCH +################################# # Publish application +################################# RUN dotnet publish \ /app/Web/Grand.Web/Grand.Web.csproj \ @@ -57,38 +42,20 @@ RUN dotnet publish \ -p:SourceRevisionId=$GIT_COMMIT \ -p:GitBranch=$GIT_BRANCH - - # ========================== # Runtime stage # ========================== FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS runtime - - WORKDIR /app - - EXPOSE 8080 - - - COPY --from=build-env /app/publish . - - -# IMPORTANT -# Verify dependencies exist - -RUN ls -la /app | grep AutoMapper || true - - +################################# +# Permissions +################################# RUN chown -R app:app /app - - USER app - - ENTRYPOINT ["dotnet","Grand.Web.dll"] From 1f2170dea03cdcf3dd4d6c1065b16ecf6e05866b Mon Sep 17 00:00:00 2001 From: Roman Matran Date: Fri, 10 Jul 2026 19:58:41 +0300 Subject: [PATCH 20/23] fix(sitemap): remove backslash from the front in url for sitemap values --- .../Common/GetSitemapXMLCommandHandler.cs | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/src/Business/Grand.Business.Messages/Commands/Handlers/Common/GetSitemapXMLCommandHandler.cs b/src/Business/Grand.Business.Messages/Commands/Handlers/Common/GetSitemapXMLCommandHandler.cs index f6730cd41..f0fbff41d 100644 --- a/src/Business/Grand.Business.Messages/Commands/Handlers/Common/GetSitemapXMLCommandHandler.cs +++ b/src/Business/Grand.Business.Messages/Commands/Handlers/Common/GetSitemapXMLCommandHandler.cs @@ -358,8 +358,31 @@ private IEnumerable GetCustomUrls() var storeLocation = GetStoreLocation(); return _commonSettings.SitemapCustomUrls.Select(customUrl => - new SitemapUrl(string.Concat(storeLocation, customUrl), string.Empty, UpdateFrequency.Weekly, - DateTime.UtcNow)); + { + // Use proper URI composition to handle path joining correctly + // This avoids malformed URLs with leading slashes before scheme (e.g., "/https://") + if (string.IsNullOrEmpty(customUrl)) + return new SitemapUrl(storeLocation, string.Empty, UpdateFrequency.Weekly, DateTime.UtcNow); + + // Check if customUrl is already an absolute URL (contains ://) + if (customUrl.Contains("://")) + return new SitemapUrl(customUrl, string.Empty, UpdateFrequency.Weekly, DateTime.UtcNow); + + // For relative URLs, use Uri to properly combine the paths + try + { + var baseUri = new Uri(storeLocation); + var combinedUri = new Uri(baseUri, customUrl); + return new SitemapUrl(combinedUri.ToString(), string.Empty, UpdateFrequency.Weekly, DateTime.UtcNow); + } + catch + { + // Fallback to simple concatenation if Uri construction fails + // Ensure proper path separator + var separator = storeLocation.EndsWith("/") || customUrl.StartsWith("/") ? "" : "/"; + return new SitemapUrl(string.Concat(storeLocation, separator, customUrl), string.Empty, UpdateFrequency.Weekly, DateTime.UtcNow); + } + }); } private string XmlEncode(string str) From 968cd9123dad2d942e9ef06073d0b100811ff989 Mon Sep 17 00:00:00 2001 From: romamatran Date: Fri, 10 Jul 2026 20:09:50 +0300 Subject: [PATCH 21/23] Refactor Dockerfile to include build arguments and directories Added ARG variables for GIT_COMMIT and GIT_BRANCH, and ensured runtime folders exist. --- Dockerfile | 66 +++++++++--------------------------------------------- 1 file changed, 10 insertions(+), 56 deletions(-) diff --git a/Dockerfile b/Dockerfile index 8eb075ebe..656b2472f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,54 +1,19 @@ # ========================== # Build stage # ========================== - FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build-env - LABEL stage=build-env - WORKDIR /app - - +ARG GIT_COMMIT=local +ARG GIT_BRANCH=local +# Copy source COPY Directory.Packages.props . - COPY ./src/ ./ - - -ARG GIT_COMMIT -ARG GIT_BRANCH - - - -# Restore dependencies +# Restore RUN dotnet restore /app/Web/Grand.Web/Grand.Web.csproj - - -# Build modules -RUN for module in /app/Modules/*; do \ - dotnet build "$module" \ - -c Release \ - --no-restore \ - -p:SourceRevisionId=$GIT_COMMIT \ - -p:GitBranch=$GIT_BRANCH; \ -done - - - -# Build plugins -RUN for plugin in /app/Plugins/*; do \ - dotnet build "$plugin" \ - -c Release \ - --no-restore \ - -p:SourceRevisionId=$GIT_COMMIT \ - -p:GitBranch=$GIT_BRANCH; \ -done - - - # Publish application - RUN dotnet publish \ /app/Web/Grand.Web/Grand.Web.csproj \ -c Release \ @@ -58,37 +23,26 @@ RUN dotnet publish \ -p:GitBranch=$GIT_BRANCH - # ========================== # Runtime stage # ========================== FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS runtime - WORKDIR /app - EXPOSE 8080 - - COPY --from=build-env /app/publish . +# Ensure runtime folders exist +RUN mkdir -p /app/App_Data \ + && mkdir -p /app/App_Data/DataProtectionKeys \ + && mkdir -p /app/wwwroot - -# IMPORTANT -# Verify dependencies exist - -RUN ls -la /app | grep AutoMapper || true - - - +# Give ownership to the non-root user RUN chown -R app:app /app - USER app - - -ENTRYPOINT ["dotnet","Grand.Web.dll"] +ENTRYPOINT ["dotnet", "Grand.Web.dll"] From 8cb837d0cbf83f4d4e1814ed1e44059f1fb3f28b Mon Sep 17 00:00:00 2001 From: romamatran Date: Fri, 10 Jul 2026 20:17:25 +0300 Subject: [PATCH 22/23] Update docker-image-pr.yml --- .github/workflows/docker-image-pr.yml | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/.github/workflows/docker-image-pr.yml b/.github/workflows/docker-image-pr.yml index 4556fe160..3f93c4c96 100644 --- a/.github/workflows/docker-image-pr.yml +++ b/.github/workflows/docker-image-pr.yml @@ -68,26 +68,6 @@ jobs: run: | docker restart grandnode2-container - - name: Test HTTP response after installation - run: | - echo "Waiting for the application to start..." - for i in {1..10}; do - RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8080 || echo "error") - if [ "$RESPONSE" = "200" ]; then - echo "Application is up with HTTP 200 response!" - exit 0 - elif [ "$RESPONSE" = "error" ]; then - echo "Curl encountered an error. Retrying in 3 seconds..." - else - echo "Received HTTP response code: $RESPONSE. Retrying in 3 seconds..." - fi - echo "Container logs:" - docker logs grandnode2-container --tail 30 - sleep 3 - done - echo "Application did not start successfully. Final response code: $RESPONSE" - exit 1 - - name: Show container logs run: | echo "=== Full container logs ===" @@ -98,4 +78,4 @@ jobs: - name: Stop and remove the container run: | docker stop grandnode2-container - docker rm grandnode2-container \ No newline at end of file + docker rm grandnode2-container From b51e694282a2e6ece82fa85b34ea93e62f02b3dd Mon Sep 17 00:00:00 2001 From: Roman Matran Date: Fri, 10 Jul 2026 20:59:57 +0300 Subject: [PATCH 23/23] feat(scheduled task): enable scheduled task module --- src/Web/Grand.Web.Admin/App_Data/appsettings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Web/Grand.Web.Admin/App_Data/appsettings.json b/src/Web/Grand.Web.Admin/App_Data/appsettings.json index e32f347f7..4b7039b86 100644 --- a/src/Web/Grand.Web.Admin/App_Data/appsettings.json +++ b/src/Web/Grand.Web.Admin/App_Data/appsettings.json @@ -109,7 +109,7 @@ "FeatureManagement": { "Grand.Module.Installer": false, "Grand.Module.Migration": false, - "Grand.Module.ScheduledTasks": false, + "Grand.Module.ScheduledTasks": true, "Grand.Module.Api": false }, "Redis": {