chore: add deployment infrastructure
This commit is contained in:
24
.dockerignore
Normal file
24
.dockerignore
Normal file
@@ -0,0 +1,24 @@
|
||||
.git
|
||||
.gitignore
|
||||
.gitea
|
||||
.vscode
|
||||
.idea
|
||||
.fleet
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
coverage
|
||||
playwright-report
|
||||
test-results
|
||||
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
!.env.*.example
|
||||
|
||||
*.log
|
||||
*.swp
|
||||
*.swo
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
84
.gitea/workflows/ci-pr.yml
Normal file
84
.gitea/workflows/ci-pr.yml
Normal file
@@ -0,0 +1,84 @@
|
||||
name: ci-pr
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- dev
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ci-pr-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
gate:
|
||||
runs-on: [frontend, ubuntu-latest]
|
||||
timeout-minutes: 120
|
||||
env:
|
||||
PLAYWRIGHT_BROWSERS_PATH: /root/.cache/ms-playwright
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4.2.2
|
||||
|
||||
- name: Setup Bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: 1.3.9
|
||||
|
||||
- name: Cache Bun dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
/root/.bun/install/cache
|
||||
node_modules
|
||||
key: ${{ runner.os }}-${{ runner.arch }}-bun-${{ hashFiles('bun.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-${{ runner.arch }}-bun-
|
||||
|
||||
- name: Install dependencies
|
||||
run: bun install --frozen-lockfile
|
||||
|
||||
- name: Generate API clients
|
||||
run: bun run apigen
|
||||
|
||||
- name: Lint check
|
||||
run: bun run lint
|
||||
|
||||
- name: Type check
|
||||
run: bun run typecheck
|
||||
|
||||
- name: Unit tests
|
||||
run: bun run test:unit
|
||||
|
||||
- name: Build
|
||||
run: bun run build
|
||||
|
||||
- name: Resolve Playwright version
|
||||
id: playwright-version
|
||||
run: |
|
||||
version="$(bunx playwright --version | awk '{ print $2 }')"
|
||||
echo "version=${version}" >> "${GITHUB_OUTPUT}"
|
||||
|
||||
- name: Cache Playwright browsers
|
||||
id: playwright-cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: /root/.cache/ms-playwright
|
||||
key: ${{ runner.os }}-${{ runner.arch }}-playwright-${{ steps.playwright-version.outputs.version }}
|
||||
|
||||
- name: Install Playwright system deps
|
||||
run: bunx playwright install-deps chromium
|
||||
|
||||
- name: Install Playwright browsers
|
||||
if: steps.playwright-cache.outputs.cache-hit != 'true'
|
||||
run: bunx playwright install chromium
|
||||
|
||||
- name: E2E
|
||||
env:
|
||||
CI: '1'
|
||||
PLAYWRIGHT_HTML_OPEN: never
|
||||
run: bun run test:e2e -- --reporter=line
|
||||
|
||||
- name: Docker build
|
||||
run: docker build --pull -t analytical-panel-frontend:ci .
|
||||
39
Dockerfile
Normal file
39
Dockerfile
Normal file
@@ -0,0 +1,39 @@
|
||||
# syntax=docker/dockerfile:1.7
|
||||
|
||||
FROM oven/bun:1.3.9 AS builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
ENV HUSKY=0
|
||||
|
||||
RUN apt-get update \
|
||||
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends nodejs \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
COPY package.json bun.lock ./
|
||||
|
||||
RUN bun install --frozen-lockfile
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN bun run apigen
|
||||
RUN bun run build
|
||||
|
||||
|
||||
FROM nginx:1.29-alpine AS runner
|
||||
|
||||
RUN apk add --no-cache gettext
|
||||
|
||||
COPY docker/nginx.conf.template /etc/nginx/templates/default.conf.template
|
||||
COPY docker/docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
|
||||
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
|
||||
|
||||
COPY --from=builder /app/dist/ /usr/share/nginx/html/
|
||||
|
||||
EXPOSE 80
|
||||
|
||||
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
|
||||
CMD wget -q -O /dev/null http://127.0.0.1/ || exit 1
|
||||
|
||||
ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
17
docker-compose.yml
Normal file
17
docker-compose.yml
Normal file
@@ -0,0 +1,17 @@
|
||||
services:
|
||||
frontend:
|
||||
container_name: analytical-panel-frontend
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
environment:
|
||||
BACKEND_UPSTREAM: '${BACKEND_UPSTREAM:-https://anal-back.dev.nii-ecos.ru}'
|
||||
ports:
|
||||
- '${FRONTEND_PORT:-8080}:80'
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- analytical_panel
|
||||
|
||||
networks:
|
||||
analytical_panel:
|
||||
name: analytical_panel_network
|
||||
13
docker/docker-entrypoint.sh
Executable file
13
docker/docker-entrypoint.sh
Executable file
@@ -0,0 +1,13 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
: "${BACKEND_UPSTREAM:?BACKEND_UPSTREAM is required (example: http://analytical-panel-backend:8000)}"
|
||||
|
||||
BACKEND_UPSTREAM="${BACKEND_UPSTREAM%/}"
|
||||
export BACKEND_UPSTREAM
|
||||
|
||||
envsubst '${BACKEND_UPSTREAM}' \
|
||||
< /etc/nginx/templates/default.conf.template \
|
||||
> /etc/nginx/conf.d/default.conf
|
||||
|
||||
exec "$@"
|
||||
61
docker/nginx.conf.template
Normal file
61
docker/nginx.conf.template
Normal file
@@ -0,0 +1,61 @@
|
||||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
proxy_connect_timeout 300s;
|
||||
proxy_send_timeout 300s;
|
||||
proxy_read_timeout 300s;
|
||||
proxy_ssl_server_name on;
|
||||
send_timeout 300s;
|
||||
|
||||
location = /index.html {
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate" always;
|
||||
add_header Pragma "no-cache" always;
|
||||
add_header Expires "0" always;
|
||||
try_files /index.html =404;
|
||||
}
|
||||
|
||||
location ~* \.(?:js|mjs|css|png|jpg|jpeg|gif|svg|ico|webp|avif|woff2?|ttf|otf|map)$ {
|
||||
add_header Cache-Control "public, max-age=31536000, immutable" always;
|
||||
try_files $uri =404;
|
||||
}
|
||||
|
||||
location = /api {
|
||||
proxy_pass ${BACKEND_UPSTREAM}/api;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
|
||||
location /api/ {
|
||||
proxy_pass ${BACKEND_UPSTREAM}/api/;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
|
||||
location = /health {
|
||||
proxy_pass ${BACKEND_UPSTREAM}/health/;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
|
||||
location /health/ {
|
||||
proxy_pass ${BACKEND_UPSTREAM}/health/;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
}
|
||||
@@ -1031,9 +1031,13 @@ select {
|
||||
|
||||
.analytics-table th:first-child,
|
||||
.analytics-table td:first-child {
|
||||
width: 28px;
|
||||
width: 44px;
|
||||
min-width: 44px;
|
||||
max-width: 44px;
|
||||
padding-right: 0;
|
||||
padding-left: 0;
|
||||
color: rgb(2 36 86 / 0.5);
|
||||
text-align: right;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.analytics-table th:last-child,
|
||||
@@ -1057,11 +1061,6 @@ select {
|
||||
white-space: normal !important;
|
||||
}
|
||||
|
||||
.technopark-area-table th:first-child,
|
||||
.technopark-area-table td:first-child {
|
||||
width: 30px;
|
||||
}
|
||||
|
||||
.sez-priority-table th:nth-child(3),
|
||||
.sez-priority-table td:nth-child(3) {
|
||||
width: 140px;
|
||||
@@ -1183,7 +1182,7 @@ select {
|
||||
}
|
||||
|
||||
.ranking-table__rank-col {
|
||||
width: 36px;
|
||||
width: 44px;
|
||||
}
|
||||
|
||||
.ranking-table__region-col {
|
||||
@@ -1229,8 +1228,13 @@ select {
|
||||
|
||||
.ranking-table th:first-child,
|
||||
.ranking-table td:first-child {
|
||||
width: 44px;
|
||||
min-width: 44px;
|
||||
max-width: 44px;
|
||||
padding-right: 0;
|
||||
padding-left: 0;
|
||||
color: rgb(2 36 86 / 0.56);
|
||||
text-align: right;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.ranking-table th:nth-child(n + 3),
|
||||
@@ -1662,7 +1666,14 @@ select {
|
||||
}
|
||||
|
||||
.ranking-table__rank-col {
|
||||
width: 24px;
|
||||
width: 36px;
|
||||
}
|
||||
|
||||
.ranking-table th:first-child,
|
||||
.ranking-table td:first-child {
|
||||
width: 36px;
|
||||
min-width: 36px;
|
||||
max-width: 36px;
|
||||
}
|
||||
|
||||
.ranking-table__metric-col {
|
||||
|
||||
Reference in New Issue
Block a user