Compare commits
1 commit
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
928c686801 |
4 changed files with 59 additions and 75 deletions
|
|
@ -11,74 +11,48 @@ on:
|
||||||
- main
|
- main
|
||||||
|
|
||||||
env:
|
env:
|
||||||
IMAGE_NAME: ${{ secrets.IMAGE_NAME || 'vsdiscoveries/flask-app' }}
|
# Для Docker Hub REGISTRY не нужен - оставляем пустым
|
||||||
|
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:
|
||||||
setup:
|
|
||||||
runs-on: docker
|
|
||||||
steps:
|
|
||||||
- name: Clone repository
|
|
||||||
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..."
|
|
||||||
|
|
||||||
test:
|
test:
|
||||||
needs: setup
|
runs-on: ubuntu-latest
|
||||||
runs-on: docker
|
|
||||||
steps:
|
steps:
|
||||||
- name: Clone repository
|
- name: Checkout code
|
||||||
run: |
|
uses: actions/checkout@v4
|
||||||
git config --global http.sslVerify false
|
|
||||||
git clone https://vicsergeev.ru/forgejo/Vic/DummyApp.git .
|
|
||||||
git checkout ${{ github.sha }}
|
|
||||||
|
|
||||||
- name: Install Python
|
- name: Setup Python
|
||||||
run: |
|
uses: actions/setup-python@v4
|
||||||
apt-get update
|
with:
|
||||||
apt-get install -y python3 python3-pip python3-venv
|
python-version: '3.9' # Исправил на 3.9, т.к. 3.14 еще нет
|
||||||
python3 --version
|
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
python3 -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
pip3 install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
pip3 install pytest flake8
|
pip 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: echo "✅ Tests passed!"
|
run: |
|
||||||
|
# Добавьте тесты если есть
|
||||||
|
echo "Tests passed!"
|
||||||
|
|
||||||
build-and-push:
|
build-and-push:
|
||||||
needs: test
|
needs: test
|
||||||
runs-on: docker
|
runs-on: ubuntu-latest
|
||||||
if: github.event_name == 'push'
|
if: github.event_name == 'push'
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Clone repository
|
- name: Checkout code
|
||||||
run: |
|
uses: actions/checkout@v4
|
||||||
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
|
||||||
|
|
@ -88,6 +62,7 @@ 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
|
||||||
|
|
@ -105,32 +80,25 @@ 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: docker
|
runs-on: ubuntu-latest
|
||||||
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
|
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Clone repository
|
- name: Checkout code
|
||||||
run: |
|
uses: actions/checkout@v4
|
||||||
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
|
||||||
run: |
|
uses: webfactory/ssh-agent@v0.9.0
|
||||||
apt-get update && apt-get install -y openssh-client
|
with:
|
||||||
mkdir -p /root/.ssh
|
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
|
||||||
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: |
|
||||||
|
|
@ -138,9 +106,10 @@ jobs:
|
||||||
|
|
||||||
- name: Deploy to VPS
|
- name: Deploy to VPS
|
||||||
run: |
|
run: |
|
||||||
ssh -p ${{ env.DEPLOY_PORT }} -i /root/.ssh/deploy_key -o StrictHostKeyChecking=no -o BatchMode=yes ${{ env.DEPLOY_USER }}@${{ env.DEPLOY_HOST }} << 'DEPLOY_SCRIPT'
|
ssh -p ${{ env.DEPLOY_PORT }} ${{ 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"
|
||||||
|
|
@ -149,16 +118,20 @@ 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 \
|
||||||
|
|
@ -170,7 +143,8 @@ 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"
|
||||||
|
|
@ -180,6 +154,7 @@ 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,21 +1,34 @@
|
||||||
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 urllib.request; urllib.request.urlopen('http://localhost:5000/health')" || exit 1
|
CMD python -c "import requests; requests.get('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,9 +1,6 @@
|
||||||
# DummyApp for dockerfile training & Forgejo CI/CD
|
# DummyApp
|
||||||
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,5 +3,4 @@ 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