Compare commits

9 Commits

Author SHA1 Message Date
6a8cbf7d8f feat: move to dedicated examples-for-kids namespace
All checks were successful
continuous-integration/drone/push Build is passing
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 19:10:40 +03:00
18e45644d5 clear trash files
All checks were successful
continuous-integration/drone/push Build is passing
2026-02-08 12:40:51 +03:00
a835e9ca58 remove notify step
Some checks failed
continuous-integration/drone/push Build is passing
Deploy to pages / build (push) Has been cancelled
2026-02-08 12:34:03 +03:00
642ef2133a fix ci
Some checks failed
continuous-integration/drone/push Build is passing
Deploy to pages / build (push) Has been cancelled
2026-02-08 12:03:35 +03:00
b49ee9c524 migrate to ci-templates
Some checks reported errors
continuous-integration/drone/push Build encountered an error
Deploy to pages / build (push) Has been cancelled
2026-02-08 11:47:23 +03:00
684f7af92b add clauide file
Some checks failed
continuous-integration/drone/push Build is passing
Deploy to pages / build (push) Has been cancelled
2026-02-05 00:00:10 +03:00
25e9a86e55 fix kube ip
Some checks failed
continuous-integration/drone/push Build is passing
Deploy to pages / build (push) Has been cancelled
2025-12-19 22:27:43 +03:00
b9ca40735d fix
Some checks failed
continuous-integration/drone/push Build is failing
Deploy to pages / build (push) Has been cancelled
2025-12-19 22:22:21 +03:00
49a297108e add dev
Some checks reported errors
continuous-integration/drone/push Build was killed
Deploy to pages / build (push) Has been cancelled
2025-11-04 17:28:05 +03:00
15 changed files with 100 additions and 235 deletions

View File

@ -1,89 +1,43 @@
## Universal .drone.yml for all project types
## Configure your project via service.yaml (see ci-templates/docs/requirements.md)
kind: pipeline
type: kubernetes
name: deploy-frontend
# Триггеры: запускать на push в ветки main и develop
trigger:
branch:
- master
- develop
event:
- push
name: ci
steps:
# --- Шаг 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: 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
# --- Шаг 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: 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
# --- Шаг 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"'
- 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]

View File

@ -1,37 +0,0 @@
# 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
View File

@ -1,6 +1,6 @@
/node_modules
/.vscode
/build
/dist
/coverage
**/junit.xml
.dependencies-cache

42
CLAUDE.md Normal file
View File

@ -0,0 +1,42 @@
# 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

View File

@ -1,14 +0,0 @@
# 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;"]

View File

@ -1,6 +0,0 @@
version: '3.8'
services:
frontend:
image: examples-for-kids:${BUILD_NUMBER:-latest}
ports:
- "3000:80"

View File

@ -1,6 +0,0 @@
version: '3.8'
services:
frontend:
image: examples-for-kids:${BUILD_NUMBER:-latest}
ports:
- "3001:80"

View File

@ -1,22 +0,0 @@
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

View File

@ -1,23 +0,0 @@
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

View File

@ -1,11 +0,0 @@
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

View File

@ -1,29 +0,0 @@
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;
}
}
}

View File

@ -9,6 +9,7 @@
<body class="h-100">
<div class="container h-100">
<div id="dev-marker" class="d-flex justify-content-start pt-3"></div>
<div class="d-flex justify-content-end pt-3">
<button class="btn btn-primary" type="button" id="reset-button"><i class="fas fa-sync"></i> Сбросить</button>
</div>

7
service.yaml Normal file
View File

@ -0,0 +1,7 @@
service:
name: examples-for-kids
type: web-frontend
deploy:
namespace: examples-for-kids
domain: examples-for-kids.vigdorov.ru

View File

@ -127,7 +127,6 @@ const getExample = () => {
default:
return isPlusMinusKind ? getMinusExample() : getDivisionExample();
}
};
export const getDifficultyById = id => {
@ -323,3 +322,13 @@ resetButton.addEventListener(EVENTS.CLICK, () => {
});
renderPage();
const markDev = () => {
if (['localhost', 'dev'].some(v => location.href.includes(v))) {
const marker = document.getElementById('dev-marker');
marker.innerText = 'DEVELOP';
}
};
markDev();

View File

@ -8,7 +8,7 @@ module.exports = {
entry: './src/script.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'build'),
path: path.resolve(__dirname, 'dist'),
},
devServer: {
open: true,