This commit is contained in:
0
.env.example
Normal file
0
.env.example
Normal file
103
.github/workflows/deploy.yaml
vendored
Normal file
103
.github/workflows/deploy.yaml
vendored
Normal 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
69
.gitignore
vendored
Normal 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 bo‘lsangiz, bu qatorni o‘chiring.
|
||||||
|
#migrations/
|
||||||
|
|
||||||
|
# Docker
|
||||||
|
*.pid
|
||||||
|
*.log
|
||||||
|
docker-compose.override.yml
|
||||||
|
|
||||||
|
# VSCode settings (optional)
|
||||||
|
.vscode/
|
||||||
|
|
||||||
20
Dockerfile
Normal file
20
Dockerfile
Normal 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
0
config/__init__.py
Normal file
16
config/asgi.py
Normal file
16
config/asgi.py
Normal 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
99
config/settings.py
Normal 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
23
config/urls.py
Normal 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
16
config/wsgi.py
Normal 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
5
entrypoint.sh
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
gunicorn config.wsgi:application -k gevent
|
||||||
|
|
||||||
|
exit $?
|
||||||
22
manage.py
Executable file
22
manage.py
Executable 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
3
requirements.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
django
|
||||||
|
gunicorn
|
||||||
|
gevent
|
||||||
42
stack.yaml
Normal file
42
stack.yaml
Normal 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
|
||||||
Reference in New Issue
Block a user