diff options
| author | IwanIDev <iwan@iwani.dev> | 2026-03-21 12:59:11 +0000 |
|---|---|---|
| committer | IwanIDev <iwan@iwani.dev> | 2026-03-21 12:59:11 +0000 |
| commit | 34626af40755e585a94612229c0640b7795b1641 (patch) | |
| tree | a777150ae8015c8bece8f9b46d390386a27e6a9c | |
| parent | 72f129472044a97ffa06136da0a473581914c7e9 (diff) | |
Started setting up my CI/CD
| -rw-r--r-- | .github/workflows/docker_build.yml | 132 | ||||
| -rw-r--r-- | Dockerfile | 23 | ||||
| -rw-r--r-- | docker-stack.yml | 20 |
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} |
