Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 75a5881d40 |
120
.drone.yml
120
.drone.yml
@ -1,43 +1,89 @@
|
||||
## Universal .drone.yml for all project types
|
||||
## Configure your project via service.yaml (see ci-templates/docs/requirements.md)
|
||||
|
||||
kind: pipeline
|
||||
type: kubernetes
|
||||
name: ci
|
||||
name: deploy-frontend
|
||||
|
||||
# Триггеры: запускать на push в ветки main и develop
|
||||
trigger:
|
||||
branch:
|
||||
- master
|
||||
- develop
|
||||
event:
|
||||
- push
|
||||
|
||||
steps:
|
||||
- name: prepare
|
||||
image: alpine:3.19
|
||||
environment:
|
||||
GITEA_TOKEN:
|
||||
from_secret: GITEA_TOKEN
|
||||
commands:
|
||||
- apk add --no-cache git bash yq
|
||||
- git clone --depth 1 https://token:$GITEA_TOKEN@git.vigdorov.ru/vigdorov/ci-templates.git .ci
|
||||
- chmod +x .ci/scripts/*.sh
|
||||
- bash .ci/scripts/prepare.sh
|
||||
# --- Шаг 1: Сборка и отправка образа в Harbor ---
|
||||
# Этот шаг выполняется для любой из веток (main или develop)
|
||||
- name: build-and-push
|
||||
image: plugins/docker
|
||||
settings:
|
||||
# Укажите ваш домен Harbor
|
||||
registry: registry.vigdorov.ru
|
||||
# Имя репозитория в Harbor (например, в проекте library)
|
||||
repo: registry.vigdorov.ru/library/examples-for-kids-app
|
||||
# Тег будет равен первым 7 символам хеша коммита (например, a1b2c3d)
|
||||
tags:
|
||||
- ${DRONE_COMMIT_SHA:0:7}
|
||||
# Используем секреты, которые мы создали в Drone
|
||||
username:
|
||||
from_secret: HARBOR_USER
|
||||
password:
|
||||
from_secret: HARBOR_PASSWORD
|
||||
|
||||
- name: build
|
||||
image: gcr.io/kaniko-project/executor:v1.23.2-debug
|
||||
depends_on: [prepare]
|
||||
environment:
|
||||
HARBOR_USER:
|
||||
from_secret: HARBOR_USER
|
||||
HARBOR_PASSWORD:
|
||||
from_secret: HARBOR_PASSWORD
|
||||
commands:
|
||||
- /busybox/sh .ci/scripts/build.sh
|
||||
# --- Шаг 2: Развертывание в DEV-окружение ---
|
||||
- name: deploy-dev
|
||||
image: alpine/k8s:1.28.2 # Образ с kubectl и другими утилитами
|
||||
# Запускать этот шаг ТОЛЬКО для ветки 'develop'
|
||||
when:
|
||||
branch:
|
||||
- develop
|
||||
environment:
|
||||
# Используем секрет с kubeconfig
|
||||
KUBECONFIG:
|
||||
from_secret: KUBE_CONFIG
|
||||
commands:
|
||||
# Готовим переменные для dev-окружения
|
||||
- 'export APP_NAMESPACE="dev-ns"' # Будем деплоить в отдельный неймспейс
|
||||
- 'export HOSTNAME="dev_examples-for-kids.vigdorov.ru"'
|
||||
- 'export IMAGE_TAG="${DRONE_COMMIT_SHA:0:7}"'
|
||||
- 'export IMAGE_NAME="ci.vigdorov.ru/library/examples-for-kids-app"'
|
||||
- 'export SECRET_NAME="wildcard-cert"'
|
||||
# Создаем неймспейс, если его нет
|
||||
- 'kubectl create namespace $APP_NAMESPACE --dry-run=client -o yaml | kubectl apply -f -'
|
||||
# Заменяем метки в шаблонах на реальные значения и применяем
|
||||
- 'sed -e "s|__IMAGE__|$IMAGE_NAME:$IMAGE_TAG|g" k8s/deployment.yaml | kubectl apply -n $APP_NAMESPACE -f -'
|
||||
- 'kubectl apply -n $APP_NAMESPACE -f k8s/service.yaml'
|
||||
- 'sed -e "s|__HOSTNAME__|$HOSTNAME|g" -e "s|__SECRET_NAME__|$SECRET_NAME|g" k8s/ingress.yaml | kubectl apply -n $APP_NAMESPACE -f -'
|
||||
- 'echo "Deployed to DEV: https://$HOSTNAME"'
|
||||
|
||||
- name: deploy
|
||||
image: alpine:3.19
|
||||
depends_on: [build]
|
||||
environment:
|
||||
KUBE_CONFIG:
|
||||
from_secret: KUBE_CONFIG
|
||||
commands:
|
||||
- apk add --no-cache bash yq kubectl helm
|
||||
- bash .ci/scripts/deploy.sh
|
||||
|
||||
trigger:
|
||||
branch: [master, develop]
|
||||
event: [push]
|
||||
# --- Шаг 3: Развертывание в PROD-окружение ---
|
||||
- name: deploy-prod
|
||||
image: alpine/k8s:1.28.2
|
||||
# Запускать этот шаг ТОЛЬКО для ветки 'main'
|
||||
when:
|
||||
branch:
|
||||
- master
|
||||
environment:
|
||||
KUBE_CONFIG_CONTENT:
|
||||
from_secret: KUBE_CONFIG
|
||||
commands:
|
||||
# Создаем kubeconfig файл из секрета
|
||||
- 'mkdir -p ~/.kube'
|
||||
- 'echo "$KUBE_CONFIG_CONTENT" > ~/.kube/config'
|
||||
- 'chmod 600 ~/.kube/config'
|
||||
# Заменяем localhost на внешний IP сервера
|
||||
- 'sed -i "s|https://127.0.0.1:6443|https://192.168.1.55:6443|g" ~/.kube/config'
|
||||
# Готовим переменные для prod-окружения
|
||||
- 'export APP_NAMESPACE="prod-ns"'
|
||||
- 'export HOSTNAME="examples-for-kids.vigdorov.ru"'
|
||||
- 'export IMAGE_TAG="${DRONE_COMMIT_SHA:0:7}"'
|
||||
- 'export IMAGE_NAME="registry.vigdorov.ru/library/examples-for-kids-app"'
|
||||
- 'export SECRET_NAME="wildcard-cert"'
|
||||
# Проверяем подключение к кластеру
|
||||
- 'kubectl cluster-info'
|
||||
# Создаем неймспейс
|
||||
- 'kubectl create namespace $APP_NAMESPACE --dry-run=client -o yaml | kubectl apply -f -'
|
||||
# Разворачиваем приложение
|
||||
- 'sed -e "s|__IMAGE__|$IMAGE_NAME:$IMAGE_TAG|g" k8s/deployment.yaml | kubectl apply -n $APP_NAMESPACE -f -'
|
||||
- 'kubectl apply -n $APP_NAMESPACE -f k8s/service.yaml'
|
||||
- 'sed -e "s|__HOSTNAME__|$HOSTNAME|g" -e "s|__SECRET_NAME__|$SECRET_NAME|g" k8s/ingress.yaml | kubectl apply -n $APP_NAMESPACE -f -'
|
||||
- 'echo "Deployed to PROD: https://$HOSTNAME"'
|
||||
37
.github/workflows/deploy.yml
vendored
Normal file
37
.github/workflows/deploy.yml
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
# This is a basic workflow to help you get started with Actions
|
||||
|
||||
name: Deploy to pages
|
||||
|
||||
# Controls when the action will run.
|
||||
on:
|
||||
# Triggers the workflow on push or pull request events but only for the master branch
|
||||
push:
|
||||
branches: [ master ]
|
||||
|
||||
# Allows you to run this workflow manually from the Actions tab
|
||||
workflow_dispatch:
|
||||
|
||||
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
|
||||
jobs:
|
||||
# This workflow contains a single job called "build"
|
||||
build:
|
||||
# The type of runner that the job will run on
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
# Steps represent a sequence of tasks that will be executed as part of the job
|
||||
steps:
|
||||
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Install and Build 🔧 # This example project is built using npm and outputs the result to the 'build' folder. Replace with the commands required to build your project, or remove this step entirely if your site is pre-built.
|
||||
run: |
|
||||
npm i
|
||||
npm run build
|
||||
|
||||
- name: Deploy 🚀
|
||||
uses: JamesIves/github-pages-deploy-action@3.7.1
|
||||
with:
|
||||
ACCESS_TOKEN: ${{ secrets.GIT_TOKEN_V1 }}
|
||||
BRANCH: gh-pages # The branch the action should deploy to.
|
||||
FOLDER: build # The folder the action should deploy.
|
||||
CLEAN: true # Automatically remove deleted files from the deploy branch
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,6 +1,6 @@
|
||||
/node_modules
|
||||
/.vscode
|
||||
/dist
|
||||
/build
|
||||
/coverage
|
||||
**/junit.xml
|
||||
.dependencies-cache
|
||||
|
||||
42
CLAUDE.md
42
CLAUDE.md
@ -1,42 +0,0 @@
|
||||
# Examples for Kids
|
||||
|
||||
Веб-приложение для детей — тренажер решения математических примеров.
|
||||
|
||||
## Назначение
|
||||
|
||||
Интерактивный веб-интерфейс для практики арифметики. Дети решают примеры, получают обратную связь.
|
||||
|
||||
## Стек
|
||||
|
||||
- **Frontend:** Vanilla JS + Webpack
|
||||
- **Styling:** Bootstrap 5
|
||||
- **Testing:** Jest
|
||||
- **Build:** Webpack 5
|
||||
|
||||
## Команды
|
||||
|
||||
```bash
|
||||
npm install # Установка зависимостей
|
||||
npm run dev # Запуск dev-сервера
|
||||
npm run build # Production сборка
|
||||
npm run test # Запуск тестов
|
||||
npm run lint # Проверка линтером
|
||||
```
|
||||
|
||||
## Структура
|
||||
|
||||
```
|
||||
examples-for-kids/
|
||||
├── src/ # Исходный код
|
||||
├── public/ # Статические файлы
|
||||
├── coverage/ # Отчеты покрытия тестами
|
||||
├── service.yaml # Конфиг для ci-templates
|
||||
└── .drone.yml # CI/CD пайплайн (универсальный)
|
||||
```
|
||||
|
||||
## Деплой
|
||||
|
||||
- Тип: `web-frontend` (ci-templates)
|
||||
- Dockerfile, nginx.conf, Helm chart — предоставляются ci-templates автоматически
|
||||
- Конфигурация через `service.yaml`
|
||||
- Домен: examples-for-kids.vigdorov.ru
|
||||
14
Dockerfile
Normal file
14
Dockerfile
Normal file
@ -0,0 +1,14 @@
|
||||
# build environment
|
||||
FROM node:18 as builder
|
||||
WORKDIR /app
|
||||
COPY package*.json ./
|
||||
RUN npm ci
|
||||
COPY . .
|
||||
RUN npm run build
|
||||
|
||||
# production environment
|
||||
FROM nginx:alpine
|
||||
COPY --from=builder /app/build /usr/share/nginx/html
|
||||
COPY nginx.conf /etc/nginx/nginx.conf
|
||||
EXPOSE 80
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
6
docker-compose.prod.yml
Normal file
6
docker-compose.prod.yml
Normal file
@ -0,0 +1,6 @@
|
||||
version: '3.8'
|
||||
services:
|
||||
frontend:
|
||||
image: examples-for-kids:${BUILD_NUMBER:-latest}
|
||||
ports:
|
||||
- "3000:80"
|
||||
6
docker-compose.staging.yml
Normal file
6
docker-compose.staging.yml
Normal file
@ -0,0 +1,6 @@
|
||||
version: '3.8'
|
||||
services:
|
||||
frontend:
|
||||
image: examples-for-kids:${BUILD_NUMBER:-latest}
|
||||
ports:
|
||||
- "3001:80"
|
||||
22
k8s/deployment.yaml
Normal file
22
k8s/deployment.yaml
Normal file
@ -0,0 +1,22 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: examples-for-kids-app
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: examples-for-kids-app
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: examples-for-kids-app
|
||||
spec:
|
||||
imagePullSecrets:
|
||||
- name: harbor-creds # Имя секрета, который мы создали на Шаге 1
|
||||
containers:
|
||||
- name: examples-for-kids-app
|
||||
# __IMAGE__ - это метка, которую заменит Drone
|
||||
image: __IMAGE__
|
||||
ports:
|
||||
- containerPort: 80
|
||||
23
k8s/ingress.yaml
Normal file
23
k8s/ingress.yaml
Normal file
@ -0,0 +1,23 @@
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: examples-for-kids-ingress
|
||||
spec:
|
||||
ingressClassName: traefik
|
||||
tls:
|
||||
- hosts:
|
||||
# __HOSTNAME__ - метка для домена
|
||||
- __HOSTNAME__
|
||||
# __SECRET_NAME__ - метка для имени секрета с сертификатом
|
||||
secretName: __SECRET_NAME__
|
||||
rules:
|
||||
- host: __HOSTNAME__
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: examples-for-kids-app-service
|
||||
port:
|
||||
number: 80
|
||||
11
k8s/service.yaml
Normal file
11
k8s/service.yaml
Normal file
@ -0,0 +1,11 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: examples-for-kids-app-service
|
||||
spec:
|
||||
selector:
|
||||
app: examples-for-kids-app
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 80
|
||||
targetPort: 80
|
||||
29
nginx.conf
Normal file
29
nginx.conf
Normal file
@ -0,0 +1,29 @@
|
||||
events {}
|
||||
|
||||
http {
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
|
||||
location /stub_status {
|
||||
stub_status on;
|
||||
allow all;
|
||||
}
|
||||
|
||||
location / {
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
try_files $uri $uri/ $uri.html =404;
|
||||
}
|
||||
|
||||
location /assets/ {
|
||||
root /usr/share/nginx/html;
|
||||
}
|
||||
|
||||
location ~* \.(js|css|ico|png|jpg|jpeg|gif|svg|woff|woff2|ttf|eot)$ {
|
||||
root /usr/share/nginx/html;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,7 +0,0 @@
|
||||
service:
|
||||
name: examples-for-kids
|
||||
type: web-frontend
|
||||
|
||||
deploy:
|
||||
namespace: examples-for-kids
|
||||
domain: examples-for-kids.vigdorov.ru
|
||||
@ -8,7 +8,7 @@ module.exports = {
|
||||
entry: './src/script.js',
|
||||
output: {
|
||||
filename: 'main.js',
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
path: path.resolve(__dirname, 'build'),
|
||||
},
|
||||
devServer: {
|
||||
open: true,
|
||||
|
||||
Reference in New Issue
Block a user