diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..a2d3019 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,7 @@ +node_modules +dist +dist-ssr +.git +.vscode +.idea +npm-debug.log* diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml new file mode 100644 index 0000000..85fbaf9 --- /dev/null +++ b/.gitea/workflows/deploy.yml @@ -0,0 +1,113 @@ +name: build-and-deploy + +# Прод — единственное окружение деплоя, поэтому катим при каждом пуше в main. +# Раннер должен быть self-hosted во внутренней сети: Nexus (nexus.tnsu.kz) снаружи +# не достаётся. workflow_dispatch — на случай ручного передеплоя (например, старым тегом). +on: + push: + branches: [main] + workflow_dispatch: + inputs: + image_tag: + description: "Тег образа (по умолчанию — короткий sha коммита)" + required: false + default: "" + +env: + REGISTRY: nexus.tnsu.kz:8082 + IMAGE: hr-appeals-web + +jobs: + # Единый источник тега для образа и для деплоя + prepare: + runs-on: ubuntu-22.04 + outputs: + tag: ${{ steps.vars.outputs.tag }} + steps: + - name: Resolve image tag + id: vars + run: | + TAG="${{ github.event.inputs.image_tag }}" + if [ -z "$TAG" ]; then TAG="${GITHUB_SHA::8}"; fi + echo "tag=$TAG" >> "$GITHUB_OUTPUT" + + build-and-push: + needs: prepare + runs-on: ubuntu-22.04 + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Ensure docker CLI is installed + run: | + set -e + SUDO="" + if [ "$(id -u)" -ne 0 ]; then SUDO="sudo"; fi + if ! command -v docker >/dev/null 2>&1; then + $SUDO apt-get update + $SUDO env DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends docker.io + fi + + - name: Docker login to Nexus + run: echo "${{ secrets.NEXUS_PASSWORD }}" | docker login "$REGISTRY" -u "${{ secrets.NEXUS_USER }}" --password-stdin + + - name: Build image + run: | + docker build \ + -t "$REGISTRY/$IMAGE:${{ needs.prepare.outputs.tag }}" \ + -t "$REGISTRY/$IMAGE:latest" \ + . + + - name: Push image + run: | + docker push "$REGISTRY/$IMAGE:${{ needs.prepare.outputs.tag }}" + docker push "$REGISTRY/$IMAGE:latest" + + - name: Docker logout + if: always() + run: docker logout "$REGISTRY" + + deploy: + needs: [prepare, build-and-push] + runs-on: ubuntu-22.04 + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Ensure ssh client is installed + run: | + set -e + SUDO="" + if [ "$(id -u)" -ne 0 ]; then SUDO="sudo"; fi + if ! command -v ssh >/dev/null 2>&1 || ! command -v scp >/dev/null 2>&1 || ! command -v ssh-keyscan >/dev/null 2>&1; then + $SUDO apt-get update + $SUDO env DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends openssh-client + fi + + - name: Configure SSH + run: | + mkdir -p ~/.ssh + printf '%s\n' "${{ secrets.DEPLOY_SSH_KEY }}" > ~/.ssh/deploy_key + chmod 600 ~/.ssh/deploy_key + ssh-keyscan -H "${{ vars.DEPLOY_HOST }}" >> ~/.ssh/known_hosts + + - name: Upload compose file + run: | + scp -i ~/.ssh/deploy_key docker-compose.yml \ + "${{ vars.DEPLOY_USER }}@${{ vars.DEPLOY_HOST }}:${{ vars.DEPLOY_PATH }}/docker-compose.yml" + + - name: Roll out + run: | + ssh -i ~/.ssh/deploy_key "${{ vars.DEPLOY_USER }}@${{ vars.DEPLOY_HOST }}" " + set -e + cd '${{ vars.DEPLOY_PATH }}' + sed -i '/^IMAGE_TAG=/d' .env + echo 'IMAGE_TAG=${{ needs.prepare.outputs.tag }}' >> .env + docker compose pull + docker compose up -d --remove-orphans + docker image prune -f + " + + - name: Cleanup SSH key + if: always() + run: rm -f ~/.ssh/deploy_key diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..35d8fff --- /dev/null +++ b/Dockerfile @@ -0,0 +1,16 @@ +# syntax=docker/dockerfile:1 + +FROM node:20-alpine AS build +WORKDIR /app + +COPY package.json package-lock.json .npmrc ./ +RUN npm ci + +COPY . . +RUN npm run build + +FROM nginx:1.27-alpine AS runtime +COPY nginx.conf /etc/nginx/conf.d/default.conf +COPY --from=build /app/dist /usr/share/nginx/html + +EXPOSE 80 diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..7213e65 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,8 @@ +services: + hr-appeals-web: + image: nexus.tnsu.kz:8082/hr-appeals-web:${IMAGE_TAG:-latest} + restart: unless-stopped + ports: + - "8080:80" + + diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 0000000..3aa17e6 --- /dev/null +++ b/nginx.conf @@ -0,0 +1,11 @@ +server { + listen 80; + server_name _; + + root /usr/share/nginx/html; + index index.html; + + location / { + try_files $uri $uri/ /index.html; + } +}