Compare commits
20 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1fc4c03380 | ||
|
|
2fb52e107c | ||
| 28d0bae9b1 | |||
| 6f30776050 | |||
|
|
b70f362131 | ||
|
|
f1e3f4defc | ||
|
|
d5720b7cc1 | ||
|
|
31400f6420 | ||
|
|
3c83bfaf9b | ||
|
|
c86a6ba77d | ||
|
|
edaecea449 | ||
|
|
fa10819073 | ||
|
|
47cbbc92cf | ||
|
|
91e11cdd7c | ||
|
|
2d285d8b32 | ||
|
|
ad8ba72f71 | ||
|
|
977238fe7d | ||
|
|
743226fcbf | ||
|
|
879a8f4e01 | ||
| 83eddb64e3 |
4 changed files with 76 additions and 60 deletions
|
|
@ -11,48 +11,74 @@ on:
|
||||||
- main
|
- main
|
||||||
|
|
||||||
env:
|
env:
|
||||||
# Для Docker Hub REGISTRY не нужен - оставляем пустым
|
IMAGE_NAME: ${{ secrets.IMAGE_NAME || 'vsdiscoveries/flask-app' }}
|
||||||
IMAGE_NAME: ${{ secrets.IMAGE_NAME || 'flask-app' }}
|
|
||||||
DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
|
DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
|
||||||
DEPLOY_USER: ${{ secrets.DEPLOY_USER }}
|
DEPLOY_USER: ${{ secrets.DEPLOY_USER }}
|
||||||
DEPLOY_PORT: ${{ secrets.DEPLOY_PORT || '22' }}
|
DEPLOY_PORT: ${{ secrets.DEPLOY_PORT || '22' }}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
setup:
|
||||||
runs-on: ubuntu-latest
|
runs-on: docker
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Clone repository
|
||||||
uses: actions/checkout@v4
|
run: |
|
||||||
|
git config --global http.sslVerify false
|
||||||
|
git clone https://vicsergeev.ru/forgejo/Vic/DummyApp.git .
|
||||||
|
git checkout ${{ github.sha }}
|
||||||
|
ls -la
|
||||||
|
- name: Setup complete
|
||||||
|
run: echo "✅ Setup complete, starting pipeline..."
|
||||||
|
|
||||||
- name: Setup Python
|
test:
|
||||||
uses: actions/setup-python@v4
|
needs: setup
|
||||||
with:
|
runs-on: docker
|
||||||
python-version: '3.9' # Исправил на 3.9, т.к. 3.14 еще нет
|
steps:
|
||||||
|
- name: Clone repository
|
||||||
|
run: |
|
||||||
|
git config --global http.sslVerify false
|
||||||
|
git clone https://vicsergeev.ru/forgejo/Vic/DummyApp.git .
|
||||||
|
git checkout ${{ github.sha }}
|
||||||
|
|
||||||
|
- name: Install Python
|
||||||
|
run: |
|
||||||
|
apt-get update
|
||||||
|
apt-get install -y python3 python3-pip python3-venv
|
||||||
|
python3 --version
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
python -m pip install --upgrade pip
|
python3 -m pip install --upgrade pip
|
||||||
pip install -r requirements.txt
|
pip3 install -r requirements.txt
|
||||||
pip install pytest flake8
|
pip3 install pytest flake8
|
||||||
|
|
||||||
- name: Lint with flake8
|
- name: Lint with flake8
|
||||||
run: |
|
run: |
|
||||||
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
|
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
|
||||||
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
|
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
|
||||||
|
|
||||||
- name: Test with pytest (опционально)
|
- name: Test with pytest
|
||||||
run: |
|
run: echo "✅ Tests passed!"
|
||||||
# Добавьте тесты если есть
|
|
||||||
echo "Tests passed!"
|
|
||||||
|
|
||||||
build-and-push:
|
build-and-push:
|
||||||
needs: test
|
needs: test
|
||||||
runs-on: ubuntu-latest
|
runs-on: docker
|
||||||
if: github.event_name == 'push'
|
if: github.event_name == 'push'
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Clone repository
|
||||||
uses: actions/checkout@v4
|
run: |
|
||||||
|
git config --global http.sslVerify false
|
||||||
|
git clone https://vicsergeev.ru/forgejo/Vic/DummyApp.git .
|
||||||
|
git checkout ${{ github.sha }}
|
||||||
|
|
||||||
|
- name: Install Docker CLI
|
||||||
|
run: |
|
||||||
|
apt-get update
|
||||||
|
apt-get install -y ca-certificates curl
|
||||||
|
curl -fsSL https://download.docker.com/linux/static/stable/x86_64/docker-26.1.4.tgz -o docker.tgz
|
||||||
|
tar xzf docker.tgz --strip-components=1 -C /usr/bin docker/docker
|
||||||
|
rm docker.tgz
|
||||||
|
docker --version
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
@ -62,7 +88,6 @@ jobs:
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.REGISTRY_USERNAME }}
|
username: ${{ secrets.REGISTRY_USERNAME }}
|
||||||
password: ${{ secrets.REGISTRY_PASSWORD }}
|
password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||||
# registry не указываем - по умолчанию Docker Hub
|
|
||||||
|
|
||||||
- name: Get commit hash
|
- name: Get commit hash
|
||||||
id: vars
|
id: vars
|
||||||
|
|
@ -80,25 +105,32 @@ jobs:
|
||||||
${{ env.IMAGE_NAME }}:latest
|
${{ env.IMAGE_NAME }}:latest
|
||||||
${{ env.IMAGE_NAME }}:${{ steps.vars.outputs.IMAGE_TAG }}
|
${{ env.IMAGE_NAME }}:${{ steps.vars.outputs.IMAGE_TAG }}
|
||||||
${{ env.IMAGE_NAME }}:${{ github.run_number }}
|
${{ env.IMAGE_NAME }}:${{ github.run_number }}
|
||||||
cache-from: type=gha
|
|
||||||
cache-to: type=gha,mode=max
|
|
||||||
build-args: |
|
build-args: |
|
||||||
COMMIT_HASH=${{ steps.vars.outputs.COMMIT_HASH }}
|
COMMIT_HASH=${{ steps.vars.outputs.COMMIT_HASH }}
|
||||||
BUILD_TIME=${{ steps.vars.outputs.BUILD_TIME }}
|
BUILD_TIME=${{ steps.vars.outputs.BUILD_TIME }}
|
||||||
|
|
||||||
deploy:
|
deploy:
|
||||||
needs: build-and-push
|
needs: build-and-push
|
||||||
runs-on: ubuntu-latest
|
runs-on: docker
|
||||||
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
|
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Clone repository
|
||||||
uses: actions/checkout@v4
|
run: |
|
||||||
|
git config --global http.sslVerify false
|
||||||
|
git clone https://vicsergeev.ru/forgejo/Vic/DummyApp.git .
|
||||||
|
git checkout ${{ github.sha }}
|
||||||
|
|
||||||
- name: Setup SSH
|
- name: Setup SSH
|
||||||
uses: webfactory/ssh-agent@v0.9.0
|
run: |
|
||||||
with:
|
apt-get update && apt-get install -y openssh-client
|
||||||
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
|
mkdir -p /root/.ssh
|
||||||
|
chmod 700 /root/.ssh
|
||||||
|
|
||||||
|
echo "${{ secrets.SSH_PRIVATE_KEY }}" | sed 's/\r$//' > /root/.ssh/deploy_key
|
||||||
|
chmod 600 /root/.ssh/deploy_key
|
||||||
|
|
||||||
|
ssh-keyscan -p ${{ env.DEPLOY_PORT }} -H ${{ env.DEPLOY_HOST }} > /root/.ssh/known_hosts 2>/dev/null || true
|
||||||
|
|
||||||
- name: Add host to known_hosts
|
- name: Add host to known_hosts
|
||||||
run: |
|
run: |
|
||||||
|
|
@ -106,10 +138,9 @@ jobs:
|
||||||
|
|
||||||
- name: Deploy to VPS
|
- name: Deploy to VPS
|
||||||
run: |
|
run: |
|
||||||
ssh -p ${{ env.DEPLOY_PORT }} ${{ env.DEPLOY_USER }}@${{ env.DEPLOY_HOST }} << 'DEPLOY_SCRIPT'
|
ssh -p ${{ env.DEPLOY_PORT }} -i /root/.ssh/deploy_key -o StrictHostKeyChecking=no -o BatchMode=yes ${{ env.DEPLOY_USER }}@${{ env.DEPLOY_HOST }} << 'DEPLOY_SCRIPT'
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
# Переменные для деплоя
|
|
||||||
APP_NAME="flask-app"
|
APP_NAME="flask-app"
|
||||||
IMAGE_NAME="${{ env.IMAGE_NAME }}"
|
IMAGE_NAME="${{ env.IMAGE_NAME }}"
|
||||||
CONTAINER_NAME="flask-app"
|
CONTAINER_NAME="flask-app"
|
||||||
|
|
@ -118,20 +149,16 @@ jobs:
|
||||||
|
|
||||||
echo "🚀 Начинаем деплой на сервер..."
|
echo "🚀 Начинаем деплой на сервер..."
|
||||||
|
|
||||||
# Создаем директорию для приложения если её нет
|
|
||||||
mkdir -p $DEPLOY_DIR
|
mkdir -p $DEPLOY_DIR
|
||||||
cd $DEPLOY_DIR
|
cd $DEPLOY_DIR
|
||||||
|
|
||||||
# Загружаем последний образ (без REGISTRY, т.к. Docker Hub по умолчанию)
|
|
||||||
echo "📦 Загружаем Docker образ с Docker Hub..."
|
echo "📦 Загружаем Docker образ с Docker Hub..."
|
||||||
docker pull $IMAGE_NAME:latest
|
docker pull $IMAGE_NAME:latest
|
||||||
|
|
||||||
# Останавливаем и удаляем старый контейнер
|
|
||||||
echo "🔄 Перезапускаем контейнер..."
|
echo "🔄 Перезапускаем контейнер..."
|
||||||
docker stop $CONTAINER_NAME 2>/dev/null || true
|
docker stop $CONTAINER_NAME 2>/dev/null || true
|
||||||
docker rm $CONTAINER_NAME 2>/dev/null || true
|
docker rm $CONTAINER_NAME 2>/dev/null || true
|
||||||
|
|
||||||
# Запускаем новый контейнер
|
|
||||||
echo "🚀 Запускаем новый контейнер..."
|
echo "🚀 Запускаем новый контейнер..."
|
||||||
docker run -d \
|
docker run -d \
|
||||||
--name $CONTAINER_NAME \
|
--name $CONTAINER_NAME \
|
||||||
|
|
@ -143,8 +170,7 @@ jobs:
|
||||||
-e SECRET_KEY="${{ secrets.APP_SECRET_KEY }}" \
|
-e SECRET_KEY="${{ secrets.APP_SECRET_KEY }}" \
|
||||||
$IMAGE_NAME:latest
|
$IMAGE_NAME:latest
|
||||||
|
|
||||||
# Проверяем здоровье
|
sleep 15
|
||||||
sleep 5
|
|
||||||
if curl -f http://localhost:$PORT/health; then
|
if curl -f http://localhost:$PORT/health; then
|
||||||
echo "✅ Деплой успешно завершен!"
|
echo "✅ Деплой успешно завершен!"
|
||||||
echo "🌐 Приложение доступно на порту $PORT"
|
echo "🌐 Приложение доступно на порту $PORT"
|
||||||
|
|
@ -154,7 +180,6 @@ jobs:
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Очищаем старые образы
|
|
||||||
echo "🧹 Очищаем старые Docker образы..."
|
echo "🧹 Очищаем старые Docker образы..."
|
||||||
docker image prune -f
|
docker image prune -f
|
||||||
DEPLOY_SCRIPT
|
DEPLOY_SCRIPT
|
||||||
|
|
|
||||||
23
Dockerfile
23
Dockerfile
|
|
@ -1,34 +1,21 @@
|
||||||
# Многоступенчатая сборка для оптимизации
|
FROM python:3.12-slim
|
||||||
FROM python:3.14-slim as builder
|
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
# Устанавливаем зависимости
|
# Ставим зависимости глобально (доступно всем пользователям)
|
||||||
COPY requirements.txt .
|
COPY requirements.txt .
|
||||||
RUN pip install --no-cache-dir -r requirements.txt
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
# Финальный образ
|
# Копируем код
|
||||||
FROM python:3.14-slim
|
|
||||||
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
# Копируем зависимости из builder
|
|
||||||
COPY --from=builder /usr/local/lib/python3.9/site-packages /usr/local/lib/python3.9/site-packages
|
|
||||||
COPY --from=builder /usr/local/bin /usr/local/bin
|
|
||||||
|
|
||||||
# Копируем код приложения
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
# Создаем пользователя для запуска
|
# Создаём пользователя и даём права на /app
|
||||||
RUN useradd -m -u 1000 appuser && chown -R appuser:appuser /app
|
RUN useradd -m -u 1000 appuser && chown -R appuser:appuser /app
|
||||||
USER appuser
|
USER appuser
|
||||||
|
|
||||||
# Экспортируем порт
|
|
||||||
EXPOSE 5000
|
EXPOSE 5000
|
||||||
|
|
||||||
# Health check
|
|
||||||
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
||||||
CMD python -c "import requests; requests.get('http://localhost:5000/health')" || exit 1
|
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:5000/health')" || exit 1
|
||||||
|
|
||||||
# Запускаем через gunicorn
|
|
||||||
CMD ["gunicorn", "--bind", "0.0.0.0:5000", "--workers", "4", "run:app"]
|
CMD ["gunicorn", "--bind", "0.0.0.0:5000", "--workers", "4", "run:app"]
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
# DummyApp
|
# DummyApp for dockerfile training & Forgejo CI/CD
|
||||||
|
My first time
|
||||||
Dummy app for dockerfile training
|
|
||||||
|
|
||||||
### Dockerfile
|
### Dockerfile
|
||||||
is created with AI, used as best practices example
|
is created with AI, used as best practices example
|
||||||
|
|
||||||
|
### Success
|
||||||
|
I started with very difficult one - Forgejo has to be setup manualy, runner install seperatly
|
||||||
|
All secerest also done manually. "How to" doc is going to be created in nearest future
|
||||||
1
run.py
1
run.py
|
|
@ -3,4 +3,5 @@ from app import create_app
|
||||||
app = create_app()
|
app = create_app()
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
print("Starting server...")
|
||||||
app.run(host='0.0.0.0', port=5000, debug=True)
|
app.run(host='0.0.0.0', port=5000, debug=True)
|
||||||
Loading…
Add table
Add a link
Reference in a new issue