summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIwanIDev <iwan@iwani.dev>2026-03-21 12:59:11 +0000
committerIwanIDev <iwan@iwani.dev>2026-03-21 12:59:11 +0000
commit34626af40755e585a94612229c0640b7795b1641 (patch)
treea777150ae8015c8bece8f9b46d390386a27e6a9c
parent72f129472044a97ffa06136da0a473581914c7e9 (diff)
Started setting up my CI/CD
-rw-r--r--.github/workflows/docker_build.yml132
-rw-r--r--Dockerfile23
-rw-r--r--docker-stack.yml20
3 files changed, 175 insertions, 0 deletions
diff --git a/.github/workflows/docker_build.yml b/.github/workflows/docker_build.yml
new file mode 100644
index 0000000..047a318
--- /dev/null
+++ b/.github/workflows/docker_build.yml
@@ -0,0 +1,132 @@
+name: Build Docker Image
+
+on:
+ push:
+ branches: [main]
+ pull_request:
+ branches: [main]
+
+env:
+ REGISTRY: ghcr.io
+ IMAGE_NAME: ${{ github.repository_owner != '' && format('{0}', github.repository) || github.repository }}
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+
+ permissions:
+ contents: read
+ packages: write
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Lowercase image name
+ run: echo "IMAGE_NAME=${IMAGE_NAME,,}" >> $GITHUB_ENV
+
+ - name: Set up QEMU
+ uses: docker/setup-qemu-action@v3
+
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v3
+
+ - name: Log in to GitHub Container Registry
+ if: github.event_name != 'pull_request'
+ uses: docker/login-action@v3
+ with:
+ registry: ${{ env.REGISTRY }}
+ username: ${{ github.actor }}
+ password: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Extract metadata
+ id: meta
+ uses: docker/metadata-action@v5
+ with:
+ images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
+ tags: |
+ type=sha
+ type=raw,value=latest,enable={{is_default_branch}}
+
+ - name: Build and push
+ uses: docker/build-push-action@v6
+ with:
+ context: .
+ push: ${{ github.event_name != 'pull_request' }}
+ tags: ${{ steps.meta.outputs.tags }}
+ labels: ${{ steps.meta.outputs.labels }}
+ platforms: linux/arm64
+ provenance: false
+ build-args: |
+ VITE_DRUPAL_BASE_URL=${{ vars.VITE_DRUPAL_BASE_URL }}
+ VITE_DRUPAL_API_PREFIX=${{ vars.VITE_DRUPAL_API_PREFIX }}
+ cache-from: type=gha
+ cache-to: type=gha,mode=max
+
+ deploy:
+ needs: build
+ runs-on: ubuntu-latest
+ if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main'
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Lowercase image name
+ run: echo "IMAGE_NAME=${IMAGE_NAME,,}" >> $GITHUB_ENV
+
+ - name: Setup SSH key for Docker context
+ env:
+ DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
+ DEPLOY_USER: ${{ secrets.DEPLOY_USER }}
+ DEPLOY_PORT: ${{ secrets.DEPLOY_PORT || 22 }}
+ run: |
+ mkdir -p ~/.ssh
+ echo "${{ secrets.DEPLOY_SSH_KEY }}" > ~/.ssh/deploy_key
+ chmod 600 ~/.ssh/deploy_key
+ ssh-keyscan -H $DEPLOY_HOST >> ~/.ssh/known_hosts
+
+ # Create SSH config to use only the specific key and prevent trying multiple auth methods
+ cat > ~/.ssh/config << EOF
+ Host deploy-target
+ HostName $DEPLOY_HOST
+ User $DEPLOY_USER
+ Port $DEPLOY_PORT
+ IdentityFile ~/.ssh/deploy_key
+ IdentitiesOnly yes
+ PubkeyAuthentication yes
+ PasswordAuthentication no
+ EOF
+ chmod 600 ~/.ssh/config
+
+ # Test the connection
+ ssh -o BatchMode=yes deploy-target "echo 'SSH connection successful'"
+
+ - name: Create Docker context
+ run: |
+ docker context create remote --docker "host=ssh://deploy-target"
+ docker context use remote
+ docker context ls
+
+ - name: Log in to GitHub Container Registry on remote
+ run: |
+ echo "${{ secrets.GITHUB_TOKEN }}" | docker --context remote login ${{ env.REGISTRY }} -u ${{ github.actor }} --password-stdin
+
+ - name: Deploy stack to remote server
+ env:
+ IMAGE_NAME: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
+ STACK_NAME: vite-portfolio
+ PORTFOLIO_HOST: ${{ vars.PORTFOLIO_HOST }}
+ TRAEFIK_NETWORK: ${{ vars.TRAEFIK_NETWORK }}
+ TRAEFIK_ENTRYPOINTS: ${{ vars.TRAEFIK_ENTRYPOINTS }}
+ DRUPAL_DB_PASSWORD: ${{ secrets.DRUPAL_DB_PASSWORD }}
+ MARIADB_ROOT_PASSWORD: ${{ secrets.MARIADB_ROOT_PASSWORD }}
+ run: |
+ # Deploy the stack using the remote context
+ docker --context remote stack deploy -c docker-stack.yml --with-registry-auth $STACK_NAME
+
+ # Wait for services to be ready
+ sleep 10
+
+ # Display stack services status
+ docker --context remote stack services $STACK_NAME \ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..e44e0e9
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,23 @@
+FROM oven/bun:1.2.22-alpine AS builder
+
+WORKDIR /app
+
+ARG VITE_DRUPAL_BASE_URL
+ARG VITE_DRUPAL_API_PREFIX
+ENV VITE_DRUPAL_BASE_URL=${VITE_DRUPAL_BASE_URL}
+ENV VITE_DRUPAL_API_PREFIX=${VITE_DRUPAL_API_PREFIX}
+
+COPY package.json bun.lock ./
+RUN bun install --frozen-lockfile
+
+COPY . .
+RUN bun run build
+
+FROM nginx:1.27-alpine AS runtime
+
+WORKDIR /usr/share/nginx/html
+COPY --from=builder /app/dist ./
+
+EXPOSE 80
+
+CMD ["nginx", "-g", "daemon off;"]
diff --git a/docker-stack.yml b/docker-stack.yml
new file mode 100644
index 0000000..2912348
--- /dev/null
+++ b/docker-stack.yml
@@ -0,0 +1,20 @@
+services:
+ web:
+ image: ${IMAGE_NAME}
+ networks:
+ - traefik
+ deploy:
+ replicas: 1
+ restart_policy:
+ condition: on-failure
+ labels:
+ - traefik.enable=true
+ - traefik.docker.network=${TRAEFIK_NETWORK}
+ - traefik.http.routers.vite-portfolio.rule=Host(`${PORTFOLIO_HOST}`)
+ - traefik.http.routers.vite-portfolio.entrypoints=${TRAEFIK_ENTRYPOINTS}
+ - traefik.http.services.vite-portfolio.loadbalancer.server.port=80
+
+networks:
+ traefik:
+ external: true
+ name: ${TRAEFIK_NETWORK}