feat(devops)
Some checks failed
Deploy to Production / build-and-deploy (push) Failing after 1m57s

This commit is contained in:
A'zamov Samandar
2025-11-05 16:36:21 +05:00
commit 425987e9d5
13 changed files with 418 additions and 0 deletions

0
.env.example Normal file
View File

103
.github/workflows/deploy.yaml vendored Normal file
View File

@@ -0,0 +1,103 @@
name: Deploy to Production
on:
push:
branches:
- main
env:
PROJECT_NAME: taxi-auth
permissions:
contents: write
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Copy env
run: |
cp .env.example .env
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build Docker image
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile.web
push: false
load: true
tags: ${{ env.PROJECT_NAME }}:test
no-cache: true
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Tag and push to Docker Hub
run: |
docker tag ${{ env.PROJECT_NAME }}:test ${{ secrets.DOCKER_USERNAME }}/${{ env.PROJECT_NAME }}:latest
docker tag ${{ env.PROJECT_NAME }}:test ${{ secrets.DOCKER_USERNAME }}/${{ env.PROJECT_NAME }}:${{ github.run_number }}
docker push ${{ secrets.DOCKER_USERNAME }}/${{ env.PROJECT_NAME }}:latest
docker push ${{ secrets.DOCKER_USERNAME }}/${{ env.PROJECT_NAME }}:${{ github.run_number }}
echo "SUCCESS TAGS: latest, ${{ github.run_number }}"
- name: Update stack.yaml and version
run: |
sed -i 's|image: ${{ secrets.DOCKER_USERNAME }}/${{ env.PROJECT_NAME }}:.*|image: ${{ secrets.DOCKER_USERNAME }}/${{ env.PROJECT_NAME }}:${{ github.run_number }}|' stack.yaml
- name: Commit and push updated version
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add .
git commit -m "🔄 Update image to ${{ github.run_number }} [CI SKIP]" || echo "No changes"
git pull origin main --rebase
git push origin main
- name: Deploy to server via SSH
uses: appleboy/ssh-action@v1.2.2
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
password: ${{ secrets.PASSWORD }}
port: ${{ secrets.PORT }}
script: |
PROJECTS=/opt/projects/
DIR=/opt/projects/${{ env.PROJECT_NAME }}/
if [ -d "$PROJECTS" ]; then
echo "projects papkasi mavjud"
else
mkdir -p $PROJECTS
echo "projects papkasi yaratildi"
fi
if [ -d "$DIR" ]; then
echo "loyiha mavjud"
else
cd $PROJECTS
git clone git@gitea.felixits.uz:${{ github.repository }}.git ${{ env.PROJECT_NAME }}
echo "Clone qilindi";
fi
cd $DIR
git fetch origin main
git reset --hard origin/main
cp .env.example .env
update_env() {
local env_file=".env"
cp .env.example "$env_file"
for kv in "$@"; do
local key="${kv%%=*}"
local value="${kv#*=}"
sed -i "s|^$key=.*|$key=$value|" "$env_file"
done
}
export PORT=${{ vars.PORT }}
docker stack deploy -c stack.yaml ${{ env.PROJECT_NAME }}

69
.gitignore vendored Normal file
View File

@@ -0,0 +1,69 @@
# Python
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Django
*.log
*.pot
*.pyc
*.pyo
*.pyd
*.db
*.sqlite3
db.sqlite3
media/
staticfiles/
local_settings.py
# If using Django collectstatic
/static/
/media/
# Virtual environment
venv/
env/
ENV/
.venv/
.env/
*.env
# IDE / Editor
.vscode/
.idea/
*.swp
*.swo
# Mac / Linux / Windows system files
.DS_Store
Thumbs.db
ehthumbs.db
# pytest / coverage
.coverage
htmlcov/
.tox/
.pytest_cache/
nosetests.xml
coverage.xml
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Django migrations (optional, agar har bir devda qayta yaratilsa)
# **EHTIYOT BO'LING:** agar migration fayllarni saqlamoqchi bolsangiz, bu qatorni ochiring.
#migrations/
# Docker
*.pid
*.log
docker-compose.override.yml
# VSCode settings (optional)
.vscode/

20
Dockerfile Normal file
View File

@@ -0,0 +1,20 @@
FROM python:3.14.0
ARG SCRIPT="entrypoint.sh"
ENV SCRIPT=$SCRIPT
WORKDIR /code
COPY requirements.txt /code/requirements.txt
RUN pip install -r requirements.txt
COPY ./ /code
COPY $SCRIPT /code/$SCRIPT
RUN chmod +x $SCRIPT
CMD sh $SCRIPT

0
config/__init__.py Normal file
View File

16
config/asgi.py Normal file
View File

@@ -0,0 +1,16 @@
"""
ASGI config for config project.
It exposes the ASGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/5.1/howto/deployment/asgi/
"""
import os
from django.core.asgi import get_asgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings")
application = get_asgi_application()

99
config/settings.py Normal file
View File

@@ -0,0 +1,99 @@
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent.parent
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = "django-insecure-5s(cd7jbyv*&ru3&m$5*8qorh0))k*j4*6p400joal6$t-y6a7"
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
]
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
]
ROOT_URLCONF = "config.urls"
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [],
"APP_DIRS": True,
"OPTIONS": {
"context_processors": [
"django.template.context_processors.debug",
"django.template.context_processors.request",
"django.contrib.auth.context_processors.auth",
"django.contrib.messages.context_processors.messages",
],
},
},
]
WSGI_APPLICATION = "config.wsgi.application"
# Database
# https://docs.djangoproject.com/en/5.1/ref/settings/#databases
DATABASES = {
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": BASE_DIR / "db.sqlite3",
}
}
# Password validation
# https://docs.djangoproject.com/en/5.1/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
},
{
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
},
{
"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
},
{
"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
},
]
# Internationalization
# https://docs.djangoproject.com/en/5.1/topics/i18n/
LANGUAGE_CODE = "en-us"
TIME_ZONE = "UTC"
USE_I18N = True
USE_TZ = True
STATIC_URL = "static/"
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"

23
config/urls.py Normal file
View File

@@ -0,0 +1,23 @@
"""
URL configuration for config project.
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/5.1/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path
urlpatterns = [
path("admin/", admin.site.urls),
]

16
config/wsgi.py Normal file
View File

@@ -0,0 +1,16 @@
"""
WSGI config for config project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/5.1/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings")
application = get_wsgi_application()

5
entrypoint.sh Normal file
View File

@@ -0,0 +1,5 @@
#!/bin/bash
gunicorn config.wsgi:application -k gevent
exit $?

22
manage.py Executable file
View File

@@ -0,0 +1,22 @@
#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys
def main():
"""Run administrative tasks."""
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings")
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(sys.argv)
if __name__ == "__main__":
main()

3
requirements.txt Normal file
View File

@@ -0,0 +1,3 @@
django
gunicorn
gevent

42
stack.yaml Normal file
View File

@@ -0,0 +1,42 @@
version: "3.8"
services:
auth:
image: jscorptech/taxi-auth:latest
env_file:
- .env
networks:
- taxi
deploy:
mode: replicated
replicas: 1
restart_policy:
condition: any
update_config:
parallelism: 1
order: start-first
failure_action: rollback
monitor: 30s
delay: 30s
max_failure_ratio: 0.2
resources:
limits:
cpus: '3'
memory: '4096M'
# healthcheck:
# test: ["CMD-SHELL", "curl -f http://localhost:8000/health/ || exit 1"]
# interval: 15s
# timeout: 15s
# retries: 5
# start_period: 30s
logging:
driver: json-file
options:
max-size: "50m"
max-file: "5"
networks:
taxi:
driver: overlay
attachable: true
external: true