diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml new file mode 100644 index 0000000..d637216 --- /dev/null +++ b/.github/workflows/deploy.yaml @@ -0,0 +1,87 @@ +name: Deploy to Server + +on: + push: + branches: + - deploytest + +permissions: + packages: write + +jobs: + commit-hash: + runs-on: ubuntu-latest + outputs: + commit_hash: ${{ steps.get_commit.outputs.commit_hash }} + steps: + - uses: actions/checkout@v4 + - name: Get commit hash + id: get_commit + run: echo "::set-output name=commit_hash::$(git rev-parse HEAD)" + + build-and-test: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: '1.22.x' + - name: Install dependencies + run: go get . + - name: Build + run: go build -v ./... + - name: Test with the Go CLI + run: go test + + build-and-push-image: + needs: + - build-and-test + - commit-hash + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Set short git commit SHA + id: vars + run: | + calculatedSha=$(git rev-parse --short ${{ github.sha }}) + echo "COMMIT_SHORT_SHA=$calculatedSha" >> $GITHUB_ENV + + - name: Log in to the Container registry + uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9 + with: + registry: https://ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push Docker image + uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc + with: + context: . + push: true + tags: ghcr.io/dreamsofcode-io/guestbook:${{ needs.commit-hash.outputs.commit_hash }} + + deploy: + runs-on: ubuntu-latest + needs: + - build-and-push-image + - commit-hash + + steps: + - name: Checkout code + uses: actions/checkout@v2 + - name: create env file + run: | + echo "GIT_COMMIT_HASH=${{ github.sha }}" >> env + - name: 'Docker Stack Deploy' + uses: cssnr/stack-deploy-action@v1 + with: + name: 'guestbook' + file: 'docker-stack.yaml' + host: zenful.cloud + user: deploytest + ssh_key: ${{ secrets.DEPLOY_SSH_KEY }} + env_file: './env' diff --git a/docker-stack.yaml b/docker-stack.yaml new file mode 100644 index 0000000..08fbf73 --- /dev/null +++ b/docker-stack.yaml @@ -0,0 +1,85 @@ +services: + reverse-proxy: + image: traefik:v3.1 + command: + - "--providers.docker" + - "--providers.docker.exposedbydefault=false" + - "--entryPoints.websecure.address=:443" + - "--certificatesresolvers.myresolver.acme.tlschallenge=true" + - "--certificatesresolvers.myresolver.acme.email=elliott@zenful.cloud" + - "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json" + - "--entrypoints.web.address=:80" + - "--entrypoints.web.http.redirections.entrypoint.to=websecure" + - "--entrypoints.web.http.redirections.entrypoint.scheme=https" + ports: + - mode: host + protocol: tcp + published: 80 + target: 80 + - mode: host + protocol: tcp + published: 443 + target: 443 + volumes: + - letsencrypt:/letsencrypt + - /var/run/docker.sock:/var/run/docker.sock + guestbook: + image: ghcr.io/dreamsofcode-io/guestbook:${GIT_COMMIT_HASH:-prod} + labels: + - "traefik.enable=true" + - "traefik.http.middlewares.guestbook-ratelimit.ratelimit.average=20" + - "traefik.http.routers.guestbook.rule=Host(`zenful.cloud`) && !Method(`POST`)" + - "traefik.http.services.guestbook.loadbalancer.server.port=8080" + - "traefik.http.routers.guestbook.entrypoints=websecure" + - "traefik.http.routers.guestbook.tls.certresolver=myresolver" + - "traefik.http.routers.guestbook.middlewares=guestbook-ratelimit" + # Define separate router for POST methods + - "traefik.http.middlewares.guestbook-ratelimit-post.ratelimit.average=1" + - "traefik.http.middlewares.guestbook-ratelimit-post.ratelimit.period=1m" + - "traefik.http.routers.guestbook-post.rule=Host(`zenful.cloud`) && Method(`POST`)" + - "traefik.http.routers.guestbook-post.middlewares=guestbook-ratelimit-post" + - "traefik.http.routers.guestbook-post.entrypoints=websecure" + - "traefik.http.routers.guestbook-post.tls.certresolver=myresolver" + # Proxy + - "traefik.http.routers.proxy.rule=Host(`proxy.dreamsofcode.io`)" + - "traefik.http.routers.proxy.entrypoints=websecure" + - "traefik.http.routers.proxy.tls.certresolver=myresolver" + secrets: + - db-password + environment: + - POSTGRES_HOST=db + - POSTGRES_PASSWORD_FILE=/run/secrets/db-password + - POSTGRES_USER=postgres + - POSTGRES_DB=guestbook + - POSTGRES_PORT=5432 + - POSTGRES_SSLMODE=disable + deploy: + mode: replicated + replicas: 3 + restart: always + depends_on: + - db + db: + image: postgres:16 + restart: always + user: postgres + volumes: + - db-data:/var/lib/postgresql/data + secrets: + - db-password + environment: + - POSTGRES_DB=guestbook + - POSTGRES_PASSWORD_FILE=/run/secrets/db-password + expose: + - 5432 + healthcheck: + test: [ "CMD", "pg_isready" ] + interval: 10s + timeout: 5s + retries: 5 +volumes: + db-data: + letsencrypt: +secrets: + db-password: + external: true