diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml deleted file mode 100644 index ea84793..0000000 --- a/.github/workflows/cd.yml +++ /dev/null @@ -1,193 +0,0 @@ -name: CD - Build & Deploy - -on: - push: - branches: [ main ] - tags: - - 'v*' - -env: - REGISTRY: ghcr.io - IMAGE_NAME: ${{ github.repository }} - -jobs: - build-and-push: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - - outputs: - image-tag: ${{ steps.meta.outputs.tags }} - image-version: ${{ steps.meta.outputs.version }} - - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 - - - name: Log in to Container Registry - uses: docker/login-action@v2 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Extract metadata - id: meta - uses: docker/metadata-action@v4 - with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - tags: | - type=ref,event=branch - type=semver,pattern={{version}} - type=semver,pattern={{major}}.{{minor}} - type=sha,prefix={{branch}}- - type=raw,value=latest,enable={{is_default_branch}} - - - name: Build and push Docker image - uses: docker/build-push-action@v4 - with: - context: . - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:buildcache - cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:buildcache,mode=max - platforms: linux/amd64 - - - name: Image digest - run: echo "Image pushed with digest ${{ steps.build.outputs.digest }}" - - package-helm: - runs-on: ubuntu-latest - needs: build-and-push - - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Install Helm - uses: azure/setup-helm@v3 - with: - version: 'latest' - - - name: Package Helm chart - run: | - helm package helm/peikarband --destination . - helm repo index . --url https://github.com/${{ github.repository }}/releases/download/${{ github.ref_name }} - - - name: Upload Helm chart artifact - uses: actions/upload-artifact@v3 - with: - name: helm-chart - path: | - *.tgz - index.yaml - - deploy-staging: - runs-on: ubuntu-latest - needs: [build-and-push, package-helm] - if: github.ref == 'refs/heads/main' - environment: - name: staging - url: https://staging.peikarband.ir - - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Install kubectl - uses: azure/setup-kubectl@v3 - - - name: Install Helm - uses: azure/setup-helm@v3 - - - name: Configure kubectl - run: | - echo "${{ secrets.KUBECONFIG_STAGING }}" | base64 -d > kubeconfig - export KUBECONFIG=kubeconfig - - - name: Deploy to Staging - run: | - export KUBECONFIG=kubeconfig - helm upgrade --install peikarband-staging ./helm/peikarband \ - --namespace staging \ - --create-namespace \ - --set image.tag=${{ needs.build-and-push.outputs.image-version }} \ - --set image.repository=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} \ - --set ingress.hosts[0].host=staging.peikarband.ir \ - --wait \ - --timeout 5m - - deploy-production: - runs-on: ubuntu-latest - needs: [build-and-push, package-helm] - if: startsWith(github.ref, 'refs/tags/v') - environment: - name: production - url: https://peikarband.ir - - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Install kubectl - uses: azure/setup-kubectl@v3 - - - name: Install Helm - uses: azure/setup-helm@v3 - - - name: Configure kubectl - run: | - echo "${{ secrets.KUBECONFIG_PRODUCTION }}" | base64 -d > kubeconfig - export KUBECONFIG=kubeconfig - - - name: Deploy to Production - run: | - export KUBECONFIG=kubeconfig - helm upgrade --install peikarband-prod ./helm/peikarband \ - --namespace production \ - --create-namespace \ - --set image.tag=${{ needs.build-and-push.outputs.image-version }} \ - --set image.repository=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} \ - --set replicaCount=3 \ - --set autoscaling.enabled=true \ - --values helm/peikarband/values-production.yaml \ - --wait \ - --timeout 10m - - - name: Verify deployment - run: | - export KUBECONFIG=kubeconfig - kubectl rollout status deployment/peikarband-prod -n production - kubectl get pods -n production - - release: - runs-on: ubuntu-latest - needs: [build-and-push, package-helm, deploy-production] - if: startsWith(github.ref, 'refs/tags/v') - permissions: - contents: write - - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Download Helm chart - uses: actions/download-artifact@v3 - with: - name: helm-chart - - - name: Create Release - uses: softprops/action-gh-release@v1 - with: - files: | - *.tgz - index.yaml - generate_release_notes: true - draft: false - prerelease: false - diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index 7313779..0000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,121 +0,0 @@ -name: CI - -on: - push: - branches: [ main, develop ] - pull_request: - branches: [ main, develop ] - -jobs: - test: - runs-on: ubuntu-latest - - strategy: - matrix: - python-version: ['3.11', '3.12'] - - services: - postgres: - image: postgres:14 - env: - POSTGRES_USER: postgres - POSTGRES_PASSWORD: postgres - POSTGRES_DB: peikarband_test - ports: - - 5432:5432 - options: >- - --health-cmd pg_isready - --health-interval 10s - --health-timeout 5s - --health-retries 5 - - redis: - image: redis:7 - ports: - - 6379:6379 - options: >- - --health-cmd "redis-cli ping" - --health-interval 10s - --health-timeout 5s - --health-retries 5 - - steps: - - uses: actions/checkout@v3 - - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 - with: - python-version: ${{ matrix.python-version }} - - - name: Cache pip packages - uses: actions/cache@v3 - with: - path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements*.txt') }} - restore-keys: | - ${{ runner.os }}-pip- - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -r requirements.txt - pip install -r requirements-dev.txt - - - name: Lint with flake8 - run: | - flake8 src/ --count --select=E9,F63,F7,F82 --show-source --statistics - flake8 src/ --count --max-complexity=10 --max-line-length=120 --statistics - - - name: Type check with mypy - run: | - mypy src/ - - - name: Check formatting with black - run: | - black --check src/ - - - name: Check imports with isort - run: | - isort --check-only src/ - - - name: Run tests with pytest - env: - DATABASE_URL: postgresql://postgres:postgres@localhost:5432/peikarband_test - REDIS_URL: redis://localhost:6379/0 - SECRET_KEY: test-secret-key - JWT_SECRET_KEY: test-jwt-secret - CELERY_BROKER_URL: redis://localhost:6379/1 - CELERY_RESULT_BACKEND: redis://localhost:6379/2 - run: | - pytest tests/ -v --cov=src --cov-report=xml --cov-report=term-missing - - - name: Upload coverage to Codecov - uses: codecov/codecov-action@v3 - with: - file: ./coverage.xml - fail_ci_if_error: false - - security: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: '3.11' - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install bandit safety - - - name: Run Bandit security scan - run: | - bandit -r src/ -f json -o bandit-report.json || true - - - name: Run Safety check - run: | - safety check --json || true - diff --git a/.woodpecker-base.yml b/.woodpecker-base.yml new file mode 100644 index 0000000..2785b01 --- /dev/null +++ b/.woodpecker-base.yml @@ -0,0 +1,142 @@ +# Woodpecker Pipeline - Base Image Builder +# +# این pipeline فقط برای ساخت base image است +# Trigger: Manual یا زمانی که Dockerfile.base تغییر کند +# +# Usage: +# Manual: در Woodpecker UI -> Manual trigger +# Auto: هر بار که docker/Dockerfile.base تغییر کند + +when: + - event: [manual, push] + path: + - docker/Dockerfile.base + - .woodpecker-base.yml + +pipeline: + # ============================================ + # Check if base image needs rebuild + # ============================================ + + check-base-exists: + image: alpine:latest + commands: + - apk add --no-cache curl jq + - | + echo "Checking if base image exists in registry..." + REGISTRY="hub.peikarband.ir" + REPO="peikarband/base" + TAG="python3.11-node20" + + # Try to check if image exists (will fail if doesn't exist) + if curl -f -u "$HARBOR_USERNAME:$HARBOR_PASSWORD" \ + "https://$REGISTRY/v2/$REPO/manifests/$TAG" > /dev/null 2>&1; then + echo "✓ Base image exists: $REGISTRY/$REPO:$TAG" + echo "BASE_EXISTS=true" >> /tmp/base_status + else + echo "⚠️ Base image not found, will build new one" + echo "BASE_EXISTS=false" >> /tmp/base_status + fi + secrets: [HARBOR_USERNAME, HARBOR_PASSWORD] + + # ============================================ + # Build Base Image + # ============================================ + + build-base: + image: woodpeckerci/plugin-docker-buildx + settings: + registry: hub.peikarband.ir + repo: hub.peikarband.ir/peikarband/base + username: + from_secret: HARBOR_USERNAME + password: + from_secret: HARBOR_PASSWORD + + dockerfile: docker/Dockerfile.base + context: . + platforms: linux/amd64 + + # Multi-tag strategy + tags: + - latest + - python3.11-node20 + - python3.11-node20-${CI_COMMIT_SHA:0:8} + + build_args: + - PYTHON_VERSION=3.11 + - NODE_VERSION=20 + + labels: + - org.opencontainers.image.created=${CI_PIPELINE_CREATED} + - org.opencontainers.image.source=${CI_REPO_LINK} + - org.opencontainers.image.revision=${CI_COMMIT_SHA} + - org.opencontainers.image.version=python3.11-node20 + - org.opencontainers.image.title=Peikarband Base Image + - org.opencontainers.image.description=Base image with Python 3.11, Node.js 20, bun + + provenance: false + push: true + + when: + - event: [manual, push] + + # ============================================ + # Verify Base Image + # ============================================ + + verify-base: + image: alpine:latest + commands: + - apk add --no-cache curl + - | + echo "Verifying base image was pushed successfully..." + sleep 5 # Wait for registry to sync + + REGISTRY="hub.peikarband.ir" + REPO="peikarband/base" + TAG="latest" + + if curl -f -u "$HARBOR_USERNAME:$HARBOR_PASSWORD" \ + "https://$REGISTRY/v2/$REPO/manifests/$TAG" > /dev/null 2>&1; then + echo "✓ Base image verified: $REGISTRY/$REPO:$TAG" + echo "✓ Base image is ready for use in application builds" + else + echo "❌ Failed to verify base image" + exit 1 + fi + secrets: [HARBOR_USERNAME, HARBOR_PASSWORD] + when: + - event: [manual, push] + + # ============================================ + # Notification + # ============================================ + + notify-complete: + image: alpine:latest + commands: + - | + echo "================================================" + echo "🎉 Base Image Build Complete!" + echo "================================================" + echo "" + echo "📦 Image Details:" + echo " Registry: hub.peikarband.ir/peikarband/base" + echo " Tags: latest, python3.11-node20, python3.11-node20-${CI_COMMIT_SHA:0:8}" + echo "" + echo "✓ Python: 3.11" + echo "✓ Node.js: 20" + echo "✓ Bun: latest" + echo "✓ Build tools: gcc, g++, make" + echo "" + echo "📝 Next Steps:" + echo " 1. Application builds will now use this base image" + echo " 2. Build time will be significantly faster" + echo " 3. Network reliability improved (no repeated npm/bun installs)" + echo "" + echo "================================================" + when: + - event: [manual, push] + status: success + diff --git a/.woodpecker.yml b/.woodpecker.yml index 023965e..da3d93f 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -1,9 +1,49 @@ +# Woodpecker CI/CD Pipeline - Peikarband Landing +# Application build pipeline (uses pre-built base image) + +variables: + - &python_image 'python:3.11-slim' + - &helm_image 'alpine/helm:latest' + - &base_image 'hub.peikarband.ir/peikarband/base:latest' + +when: + - event: [push, pull_request, tag, manual] pipeline: - publish_landing: + # ============================================ + # Stage 1: Check Base Image Availability + # ============================================ + + check-base-image: + image: alpine:latest + commands: + - apk add --no-cache curl + - | + echo "Checking if base image is available..." + REGISTRY="hub.peikarband.ir" + REPO="peikarband/base" + TAG="latest" + + if curl -f -u "$HARBOR_USERNAME:$HARBOR_PASSWORD" \ + "https://$REGISTRY/v2/$REPO/manifests/$TAG" > /dev/null 2>&1; then + echo "✓ Base image found: $REGISTRY/$REPO:$TAG" + else + echo "❌ Base image not found!" + echo "Please run .woodpecker-base.yml pipeline first to build base image" + echo "Or trigger it manually in Woodpecker UI" + exit 1 + fi + secrets: [HARBOR_USERNAME, HARBOR_PASSWORD] + when: + - event: [push, tag] + + # ============================================ + # Stage 2: Build Application Image + # ============================================ + + build-image: image: woodpeckerci/plugin-docker-buildx settings: - # تنظیمات ریجیستری Harbor registry: hub.peikarband.ir repo: hub.peikarband.ir/peikarband/landing username: @@ -11,20 +51,19 @@ pipeline: password: from_secret: HARBOR_PASSWORD - # تگ‌گذاری - tags: - - latest - - ${CI_COMMIT_SHA:0:8} - dockerfile: docker/Dockerfile - context: peikarband/ + context: . platforms: linux/amd64 - + + # استفاده از base image build_args: + - BASE_IMAGE=hub.peikarband.ir/peikarband/base:latest - VERSION=${CI_COMMIT_SHA:0:8} - BUILD_DATE=${CI_PIPELINE_CREATED} - - PYTHON_VERSION=3.11 - - NODE_VERSION=20 + + # فقط build می‌کنیم، بدون push + tags: + - ${CI_COMMIT_SHA:0:8} labels: - org.opencontainers.image.created=${CI_PIPELINE_CREATED} @@ -35,12 +74,277 @@ pipeline: - org.opencontainers.image.title=Peikarband Landing - org.opencontainers.image.description=Peikarband hosting platform landing page - cache: inline - provenance: true - push: true + cache_from: type=registry,ref=hub.peikarband.ir/peikarband/landing:buildcache + cache_to: type=inline + provenance: false + # فقط build، بدون push + push: false + load: false + when: - - event: [push, tag, manual] - branch: main # معمولاً فقط روی برنچ اصلی پوش انجام می‌شود + - event: [push, tag] + branch: [main, develop] - timeout: 30m \ No newline at end of file + # ============================================ + # Stage 3: Push Image with Multi-Tags + # ============================================ + + push-image: + image: woodpeckerci/plugin-docker-buildx + settings: + registry: hub.peikarband.ir + repo: hub.peikarband.ir/peikarband/landing + username: + from_secret: HARBOR_USERNAME + password: + from_secret: HARBOR_PASSWORD + + dockerfile: docker/Dockerfile + context: . + platforms: linux/amd64 + + build_args: + - BASE_IMAGE=hub.peikarband.ir/peikarband/base:latest + - VERSION=${CI_COMMIT_SHA:0:8} + - BUILD_DATE=${CI_PIPELINE_CREATED} + + # Multi-tagging strategy + tags: + - latest + - ${CI_COMMIT_SHA:0:8} + - ${CI_COMMIT_BRANCH} + + labels: + - org.opencontainers.image.created=${CI_PIPELINE_CREATED} + - org.opencontainers.image.source=${CI_REPO_LINK} + - org.opencontainers.image.url=${CI_REPO_LINK} + - org.opencontainers.image.revision=${CI_COMMIT_SHA} + - org.opencontainers.image.version=${CI_COMMIT_SHA:0:8} + - org.opencontainers.image.title=Peikarband Landing + - org.opencontainers.image.description=Peikarband hosting platform landing page + + cache_from: type=registry,ref=hub.peikarband.ir/peikarband/landing:buildcache + cache_to: type=inline + provenance: false + + # حالا push می‌کنیم + push: true + + when: + - event: [push, tag] + branch: [main, develop] + + # ============================================ + # Stage 4: Verify Push + # ============================================ + + verify-push: + image: alpine:latest + commands: + - apk add --no-cache curl + - | + echo "Verifying image was pushed successfully..." + sleep 3 # Wait for registry sync + + REGISTRY="hub.peikarband.ir" + REPO="peikarband/landing" + TAG="${CI_COMMIT_SHA:0:8}" + + if curl -f -u "$HARBOR_USERNAME:$HARBOR_PASSWORD" \ + "https://$REGISTRY/v2/$REPO/manifests/$TAG" > /dev/null 2>&1; then + echo "✓ Image verified: $REGISTRY/$REPO:$TAG" + else + echo "❌ Failed to verify image push" + exit 1 + fi + secrets: [HARBOR_USERNAME, HARBOR_PASSWORD] + when: + - event: [push, tag] + branch: [main, develop] + + # ============================================ + # Stages below are commented for now + # Uncomment when ready to use + # ============================================ + + # # ============================================ + # # Stage 1: Code Quality & Linting + # # ============================================ + + # lint-flake8: + # image: *python_image + # commands: + # - pip install --no-cache-dir flake8 + # - cd peikarband + # - flake8 src/ --count --select=E9,F63,F7,F82 --show-source --statistics + # - flake8 src/ --count --max-complexity=10 --max-line-length=120 --statistics --exit-zero + # when: + # - event: [push, pull_request, tag] + + # lint-black: + # image: *python_image + # commands: + # - pip install --no-cache-dir black + # - cd peikarband + # - black --check src/ || echo "⚠️ Black formatting issues found (non-blocking)" + # when: + # - event: [push, pull_request, tag] + + # lint-isort: + # image: *python_image + # commands: + # - pip install --no-cache-dir isort + # - cd peikarband + # - isort --check-only src/ || echo "⚠️ Import sorting issues found (non-blocking)" + # when: + # - event: [push, pull_request, tag] + + # type-check: + # image: *python_image + # commands: + # - pip install --no-cache-dir mypy types-redis types-requests + # - cd peikarband + # - mypy src/ --config-file=config/mypy.ini || echo "⚠️ Type checking issues found (non-blocking)" + # when: + # - event: [push, pull_request, tag] + + # # ============================================ + # # Stage 2: Security Scanning + # # ============================================ + + # security-bandit: + # image: *python_image + # commands: + # - pip install --no-cache-dir bandit[toml] + # - cd peikarband + # - bandit -r src/ -f json -o bandit-report.json || true + # - bandit -r src/ -ll || echo "⚠️ Security issues found (non-blocking)" + # when: + # - event: [push, pull_request, tag] + + # security-safety: + # image: *python_image + # commands: + # - pip install --no-cache-dir safety + # - cd peikarband + # - safety check -r requirements.txt --json || echo "⚠️ Dependency vulnerabilities found (non-blocking)" + # when: + # - event: [push, pull_request, tag] + + # # ============================================ + # # Stage 3: Testing + # # ============================================ + + # test: + # image: *python_image + # commands: + # - apt-get update && apt-get install -y --no-install-recommends curl + # - pip install --no-cache-dir -r peikarband/requirements.txt + # - pip install --no-cache-dir -r peikarband/requirements-dev.txt + # - cd peikarband + # - pytest tests/ -v --cov=src --cov-report=term-missing --cov-report=xml || echo "⚠️ Tests failed (non-blocking)" + # when: + # - event: [push, pull_request, tag] + + # # ============================================ + # # Stage 4: Helm Validation + # # ============================================ + + # helm-lint: + # image: *helm_image + # commands: + # - helm version + # - helm lint helm/peikarband + # - echo "✓ Helm chart validation passed" + # when: + # - event: [push, pull_request, tag] + + # helm-template: + # image: *helm_image + # commands: + # - helm template peikarband helm/peikarband -f helm/peikarband/values-production.yaml --debug > /dev/null + # - echo "✓ Helm template rendering successful" + # when: + # - event: [push, pull_request, tag] + + # # ============================================ + # # Stage 6: Deployment - Staging + # # ============================================ + + # deploy-staging: + # image: *helm_image + # commands: + # - apk add --no-cache kubectl + # - echo "$KUBECONFIG_STAGING" | base64 -d > /tmp/kubeconfig + # - export KUBECONFIG=/tmp/kubeconfig + # - | + # helm upgrade --install peikarband-staging helm/peikarband \ + # --namespace staging \ + # --create-namespace \ + # --set image.repository=hub.peikarband.ir/peikarband/landing \ + # --set image.tag=${CI_COMMIT_SHA:0:8} \ + # --set image.pullPolicy=Always \ + # --values helm/peikarband/values-staging.yaml \ + # --wait \ + # --timeout 5m + # - kubectl get pods -n staging + # - echo "✓ Deployed to staging successfully" + # secrets: [KUBECONFIG_STAGING] + # when: + # - event: push + # branch: [main, develop] + + # # ============================================ + # # Stage 7: Deployment - Production + # # ============================================ + + # deploy-production: + # image: *helm_image + # commands: + # - apk add --no-cache kubectl + # - echo "$KUBECONFIG_PRODUCTION" | base64 -d > /tmp/kubeconfig + # - export KUBECONFIG=/tmp/kubeconfig + # - | + # helm upgrade --install peikarband helm/peikarband \ + # --namespace production \ + # --create-namespace \ + # --set image.repository=hub.peikarband.ir/peikarband/landing \ + # --set image.tag=${CI_COMMIT_TAG} \ + # --set image.pullPolicy=Always \ + # --values helm/peikarband/values-production.yaml \ + # --wait \ + # --timeout 10m + # - kubectl rollout status deployment/peikarband -n production + # - kubectl get pods -n production + # - echo "✓ Deployed to production successfully" + # secrets: [KUBECONFIG_PRODUCTION] + # when: + # - event: tag + # ref: refs/tags/v* + + # ============================================ + # Stage 8: Notifications + # ============================================ + + notify-success: + image: alpine:latest + commands: + - echo "🎉 Pipeline completed successfully!" + - echo "Branch: ${CI_COMMIT_BRANCH}" + - echo "Commit: ${CI_COMMIT_SHA:0:8}" + - echo "Image: hub.peikarband.ir/peikarband/landing:${CI_COMMIT_SHA:0:8}" + when: + - event: [push, tag] + status: success + + notify-failure: + image: alpine:latest + commands: + - echo "❌ Pipeline failed!" + - echo "Branch: ${CI_COMMIT_BRANCH}" + - echo "Commit: ${CI_COMMIT_SHA:0:8}" + - echo "Please check the logs above" + when: + - event: [push, tag] + status: failure diff --git a/Makefile b/Makefile index ec73638..b1c5bc3 100644 --- a/Makefile +++ b/Makefile @@ -83,7 +83,7 @@ docker-build: -t $(IMAGE_NAME):latest \ --build-arg VERSION=$(VERSION) \ --build-arg BUILD_DATE=$(shell date -u +'%Y-%m-%dT%H:%M:%SZ') \ - peikarband/ + . docker-push: docker tag $(IMAGE_NAME):$(VERSION) $(REGISTRY)/$(IMAGE_NAME):$(VERSION) diff --git a/docker/Dockerfile b/docker/Dockerfile index 8b0bbc5..0f52429 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,19 +1,18 @@ # Peikarband Platform - Production Dockerfile # Multi-stage build for optimized image size and security +# Uses pre-built base image for faster builds # Build arguments -ARG PYTHON_VERSION=3.11 -ARG NODE_VERSION=20 +ARG BASE_IMAGE=hub.peikarband.ir/peikarband/base:latest ARG VERSION=latest ARG BUILD_DATE # ============================================ -# Stage 1: Builder +# Stage 1: Builder (using base image) # ============================================ -FROM python:${PYTHON_VERSION}-slim AS builder +FROM ${BASE_IMAGE} AS builder # Re-declare ARGs for this stage -ARG NODE_VERSION=20 ARG VERSION=latest ARG BUILD_DATE @@ -25,47 +24,30 @@ LABEL org.opencontainers.image.created="${BUILD_DATE}" WORKDIR /build -# Install build dependencies -RUN apt-get update && apt-get install -y --no-install-recommends \ - gcc \ - g++ \ - make \ - curl \ - gnupg \ - ca-certificates \ - unzip \ - && rm -rf /var/lib/apt/lists/* +# Base image already has: +# - Python 3.11 +# - Node.js 20 +# - bun +# - gcc, g++, make +# - npm configured with retries -# Install Node.js (required for Reflex) -RUN curl -fsSL https://deb.nodesource.com/setup_${NODE_VERSION}.x | bash - \ - && apt-get install -y --no-install-recommends nodejs \ - && rm -rf /var/lib/apt/lists/* \ - && npm config set fetch-retry-mintimeout 20000 \ - && npm config set fetch-retry-maxtimeout 120000 \ - && npm config set fetch-retries 5 \ - && npm config set fetch-timeout 300000 \ - && npm config set registry https://registry.npmjs.org/ - -# Install bun (required by Reflex for frontend build) -# Retry mechanism for network issues -RUN set -ex && \ - for i in 1 2 3 4 5; do \ - curl -fsSL https://bun.sh/install | bash && break || \ - (echo "Attempt $i failed, retrying in 5 seconds..." && sleep 5); \ - done || (echo "Failed to install bun after 5 attempts" && exit 1) - -# Add bun to PATH -ENV PATH="/root/.bun/bin:${PATH}" +# Verify tools are available +RUN echo "=== Build Environment ===" && \ + python --version && \ + node --version && \ + npm --version && \ + bun --version && \ + echo "========================" # Copy only requirements first (for better layer caching) -COPY requirements.txt . +COPY peikarband/requirements.txt . # Install Python dependencies in user space RUN pip install --no-cache-dir --upgrade pip setuptools wheel && \ pip install --no-cache-dir --user -r requirements.txt # Copy application code (excluding .dockerignore items) -COPY --chown=root:root . . +COPY --chown=root:root peikarband/ . # Build and export Reflex app for production # Note: API_URL will be updated at runtime from environment variable @@ -148,9 +130,8 @@ COPY --from=builder /root/.local /home/peikarband/.local # Copy application code from builder COPY --from=builder /build /app -# Copy and set up runtime script -# Context is peikarband/, so paths are relative to that -COPY --chown=peikarband:peikarband tools/scripts/update-env-json.sh /app/tools/scripts/update-env-json.sh +# Copy and set up runtime script +COPY --chown=peikarband:peikarband peikarband/tools/scripts/update-env-json.sh /app/tools/scripts/update-env-json.sh RUN chmod +x /app/tools/scripts/update-env-json.sh # Fix ownership diff --git a/docker/Dockerfile.base b/docker/Dockerfile.base index 5284fa4..612aae8 100644 --- a/docker/Dockerfile.base +++ b/docker/Dockerfile.base @@ -1,25 +1,24 @@ -# Base Image for Peikarband Projects +# Peikarband Base Image +# Pre-built image with Python, Node.js, bun, and build tools # -# This Dockerfile should be in a SEPARATE repository: peikarband/base -# It's kept here for reference only. +# Usage: +# Build: docker build -f docker/Dockerfile.base -t hub.peikarband.ir/peikarband/base:latest . +# Or use Woodpecker: trigger .woodpecker-base.yml pipeline # -# Purpose: Pre-installed build tools (Python, Node.js, bun, gcc, etc.) -# Registry: hub.peikarband.ir/peikarband/base:latest -# -# This image is built once and cached, making subsequent builds much faster -# All Peikarband projects should use this base image +# This image is built once and reused by all Peikarband projects ARG PYTHON_VERSION=3.11 ARG NODE_VERSION=20 -FROM python:${PYTHON_VERSION}-slim AS base +FROM python:${PYTHON_VERSION}-slim LABEL maintainer="Peikarband Team " -LABEL description="Base image with Python, Node.js, bun, and build tools" +LABEL org.opencontainers.image.title="Peikarband Base" +LABEL org.opencontainers.image.description="Base image with Python, Node.js, bun, and build tools" WORKDIR /build -# Install build dependencies +# Install system dependencies RUN apt-get update && apt-get install -y --no-install-recommends \ gcc \ g++ \ @@ -28,12 +27,19 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ gnupg \ ca-certificates \ unzip \ + git \ && rm -rf /var/lib/apt/lists/* -# Install Node.js (required for Reflex) +# Install Node.js +ARG NODE_VERSION RUN curl -fsSL https://deb.nodesource.com/setup_${NODE_VERSION}.x | bash - \ && apt-get install -y --no-install-recommends nodejs \ - && rm -rf /var/lib/apt/lists/* + && rm -rf /var/lib/apt/lists/* \ + && npm config set fetch-retry-mintimeout 20000 \ + && npm config set fetch-retry-maxtimeout 120000 \ + && npm config set fetch-retries 5 \ + && npm config set fetch-timeout 300000 \ + && npm config set registry https://registry.npmjs.org/ # Install bun (required by Reflex for frontend build) # Retry mechanism for network issues @@ -46,9 +52,19 @@ RUN set -ex && \ # Add bun to PATH ENV PATH="/root/.bun/bin:${PATH}" +# Upgrade pip, setuptools, wheel +RUN pip install --no-cache-dir --upgrade pip setuptools wheel + # Verify installations RUN python --version && \ node --version && \ npm --version && \ - bun --version + bun --version && \ + pip --version + +# Set working directory +WORKDIR /build + +# Default command +CMD ["/bin/bash"] diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index f424bf5..d9e07e6 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -35,7 +35,9 @@ services: # Peikarband Application app: - build: . + build: + context: .. + dockerfile: docker/Dockerfile container_name: peikarband-app depends_on: - postgres @@ -57,7 +59,9 @@ services: # Celery Worker celery: - build: . + build: + context: .. + dockerfile: docker/Dockerfile container_name: peikarband-celery command: celery -A src.infrastructure.tasks.celery_app worker -l info depends_on: @@ -74,7 +78,9 @@ services: # Flower (Celery Monitoring) flower: - build: . + build: + context: .. + dockerfile: docker/Dockerfile container_name: peikarband-flower command: celery -A src.infrastructure.tasks.celery_app flower depends_on: diff --git a/docs/BASE_IMAGE_MANAGEMENT.md b/docs/BASE_IMAGE_MANAGEMENT.md new file mode 100644 index 0000000..9c676d6 --- /dev/null +++ b/docs/BASE_IMAGE_MANAGEMENT.md @@ -0,0 +1,409 @@ +# مدیریت Base Image + +این مستند راهنمای کامل برای مدیریت و استفاده از base image در پروژه Peikarband است. + +## نمای کلی + +**Base Image چیست؟** + +Base image یک Docker image از پیش ساخته شده است که شامل تمام ابزارهای مورد نیاز برای build اپلیکیشن است: +- Python 3.11 +- Node.js 20 +- Bun (برای Reflex frontend) +- Build tools (gcc, g++, make) +- Git و curl + +**چرا Base Image؟** + +✅ **سرعت Build:** 8-10 دقیقه → 2-3 دقیقه +✅ **قابلیت اطمینان:** بدون نیاز به دانلود مکرر npm/bun +✅ **Consistency:** همه builds از همان environment استفاده می‌کنند +✅ **Network Resilience:** مشکلات network کمتر + +## ساختار فایل‌ها + +``` +. +├── docker/ +│ ├── Dockerfile # استفاده از base image +│ └── Dockerfile.base # تعریف base image +├── .woodpecker.yml # Build اپلیکیشن (از base استفاده می‌کند) +└── .woodpecker-base.yml # Build base image (manual/on-change) +``` + +## Base Image Tags + +``` +hub.peikarband.ir/peikarband/base:latest # آخرین version +hub.peikarband.ir/peikarband/base:python3.11-node20 # Version specific +hub.peikarband.ir/peikarband/base:python3.11-node20-a1b2c3d4 # With commit SHA +``` + +## چگونه Base Image را Build کنیم؟ + +### روش 1: Manual Trigger در Woodpecker (پیشنهادی) + +1. رفتن به Woodpecker UI +2. انتخاب repository: `peikarband/landing` +3. کلیک روی "Pipelines" +4. کلیک روی "New Pipeline" +5. انتخاب pipeline: `.woodpecker-base.yml` +6. کلیک روی "Start" + +### روش 2: Push تغییرات Dockerfile.base + +هر بار که `docker/Dockerfile.base` تغییر کند، pipeline به‌طور خودکار trigger می‌شود: + +```bash +# تغییر Dockerfile.base +vim docker/Dockerfile.base + +# Commit و push +git add docker/Dockerfile.base +git commit -m "chore: update base image to Node.js 21" +git push origin main + +# Pipeline به‌طور خودکار اجرا می‌شود +``` + +### روش 3: Local Build (برای تست) + +```bash +# Build locally +docker build -f docker/Dockerfile.base \ + -t hub.peikarband.ir/peikarband/base:latest \ + --build-arg PYTHON_VERSION=3.11 \ + --build-arg NODE_VERSION=20 \ + . + +# Test locally +docker run --rm hub.peikarband.ir/peikarband/base:latest \ + bash -c "python --version && node --version && bun --version" + +# Push to registry +docker login hub.peikarband.ir +docker push hub.peikarband.ir/peikarband/base:latest +docker push hub.peikarband.ir/peikarband/base:python3.11-node20 +``` + +## چگونه Application از Base Image استفاده می‌کند؟ + +Pipeline اصلی (`.woodpecker.yml`) به‌طور خودکار از base image استفاده می‌کند: + +### مرحله 1: Check Base Image + +```yaml +check-base-image: + # بررسی می‌کند که base image در registry موجود است + # اگر نباشد، error می‌دهد و راهنمایی می‌کند +``` + +### مرحله 2: Build Application + +```yaml +build-image: + build_args: + - BASE_IMAGE=hub.peikarband.ir/peikarband/base:latest + push: false # فقط build، بدون push +``` + +### مرحله 3: Push Application + +```yaml +push-image: + tags: + - latest + - ${CI_COMMIT_SHA:0:8} + - ${CI_COMMIT_BRANCH} + push: true # حالا push می‌کنیم +``` + +### مرحله 4: Verify + +```yaml +verify-push: + # تایید می‌کند که image با موفقیت push شده +``` + +## چه زمانی باید Base Image را Rebuild کنیم؟ + +### Rebuild ضروری است: + +1. **تغییر Python version:** + ```bash + # در Dockerfile.base + ARG PYTHON_VERSION=3.12 # تغییر از 3.11 + ``` + +2. **تغییر Node.js version:** + ```bash + # در Dockerfile.base + ARG NODE_VERSION=21 # تغییر از 20 + ``` + +3. **اضافه کردن system dependencies:** + ```dockerfile + RUN apt-get install -y \ + gcc g++ make \ + postgresql-dev # جدید + ``` + +4. **تغییر bun installation:** + ```dockerfile + # اگر روش نصب bun تغییر کند + ``` + +### Rebuild اختیاری است: + +1. **تغییرات جزئی در Dockerfile اصلی** +2. **تغییر کد اپلیکیشن** +3. **تغییر Helm charts** + +## مدیریت Versions + +### Strategy ما: + +``` +latest → همیشه آخرین version +python3.11-node20 → Version مشخص (stable) +python3.11-node20-a1b2c3d4 → با commit SHA (rollback) +``` + +### مثال: Update به Python 3.12 + +```bash +# 1. تغییر Dockerfile.base +vim docker/Dockerfile.base +# ARG PYTHON_VERSION=3.12 + +# 2. Commit +git add docker/Dockerfile.base +git commit -m "chore: upgrade base image to Python 3.12" +git push origin main + +# 3. Wait for .woodpecker-base.yml to complete + +# 4. تغییر تگ در application Dockerfile (اختیاری) +vim docker/Dockerfile +# ARG BASE_IMAGE=hub.peikarband.ir/peikarband/base:python3.12-node20 + +# 5. Test application build +git add docker/Dockerfile +git commit -m "chore: use Python 3.12 base image" +git push origin main +``` + +## Troubleshooting + +### خطا: "Base image not found" + +**علت:** Base image هنوز build نشده یا در registry موجود نیست + +**راه‌حل:** + +```bash +# 1. بررسی کنید که base image در registry موجود است +curl -u "admin:password" \ + https://hub.peikarband.ir/v2/peikarband/base/tags/list + +# 2. اگر موجود نیست، pipeline base را اجرا کنید +# Manual trigger در Woodpecker UI → .woodpecker-base.yml + +# 3. یا local build: +docker build -f docker/Dockerfile.base -t hub.peikarband.ir/peikarband/base:latest . +docker push hub.peikarband.ir/peikarband/base:latest +``` + +### خطا: "Failed to pull base image" + +**علت:** Registry authentication مشکل دارد + +**راه‌حل:** + +```bash +# 1. بررسی credentials در Woodpecker secrets +# Repository → Settings → Secrets → HARBOR_USERNAME, HARBOR_PASSWORD + +# 2. Test login locally +docker login hub.peikarband.ir +Username: admin +Password: [your-password] + +# 3. Test pull +docker pull hub.peikarband.ir/peikarband/base:latest +``` + +### Base Image خیلی بزرگ است + +**بررسی اندازه:** + +```bash +# Check image size +docker images hub.peikarband.ir/peikarband/base +``` + +**Optimization:** + +```dockerfile +# در Dockerfile.base + +# 1. حذف apt cache +RUN apt-get update && apt-get install -y ... \ + && rm -rf /var/lib/apt/lists/* # این خط مهم است + +# 2. حذف npm cache +RUN npm cache clean --force + +# 3. استفاده از slim image +FROM python:3.11-slim # نه python:3.11 +``` + +### Build Time هنوز کند است + +**بررسی:** + +```bash +# 1. آیا واقعاً از base image استفاده می‌شود؟ +docker history hub.peikarband.ir/peikarband/landing:latest | grep base + +# 2. آیا cache درست کار می‌کند؟ +# در .woodpecker.yml: +cache_from: type=registry,ref=... +``` + +**بهبود:** + +```yaml +# در .woodpecker.yml +build-image: + settings: + # Pull base image first for caching + pull: true + cache_from: + - type=registry,ref=hub.peikarband.ir/peikarband/base:latest + - type=registry,ref=hub.peikarband.ir/peikarband/landing:buildcache +``` + +## Best Practices + +### 1. Version Pinning + +❌ **بد:** +```dockerfile +FROM hub.peikarband.ir/peikarband/base:latest +``` + +✅ **خوب (برای production):** +```dockerfile +FROM hub.peikarband.ir/peikarband/base:python3.11-node20 +``` + +### 2. Testing Base Changes + +قبل از اینکه base image جدید را در production استفاده کنید: + +```bash +# 1. Build base با tag test +docker build -f docker/Dockerfile.base \ + -t hub.peikarband.ir/peikarband/base:test . + +# 2. Test application با این base +docker build --build-arg BASE_IMAGE=hub.peikarband.ir/peikarband/base:test \ + -f docker/Dockerfile . + +# 3. اگر موفق بود، tag را به latest تغییر دهید +docker tag hub.peikarband.ir/peikarband/base:test \ + hub.peikarband.ir/peikarband/base:latest +docker push hub.peikarband.ir/peikarband/base:latest +``` + +### 3. Documentation + +هر بار که base image را تغییر می‌دهید، در CHANGELOG.md یادداشت کنید: + +```markdown +## [Base Image] 2024-12-30 + +### Changed +- Upgraded Python from 3.11 to 3.12 +- Updated Node.js from 20 to 21 +- Added postgresql-dev for database support + +### Impact +- All future builds will use new base +- Rebuild takes ~10 minutes +- Application builds will be ~30% faster +``` + +### 4. Cleanup Old Images + +```bash +# List all base images +curl -u "admin:password" \ + https://hub.peikarband.ir/v2/peikarband/base/tags/list | jq + +# حذف تگ‌های قدیمی (از Harbor UI) +# Repository → peikarband/base → Tags → Select → Delete +``` + +## Monitoring + +### چگونه بفهمیم base image استفاده می‌شود؟ + +```bash +# 1. از Docker history +docker history hub.peikarband.ir/peikarband/landing:latest + +# 2. از image labels +docker inspect hub.peikarband.ir/peikarband/landing:latest | \ + jq '.[0].Config.Labels' + +# 3. از build logs در Woodpecker +# Stage "check-base-image" باید "✓ Base image found" نمایش دهد +``` + +### Metrics مفید: + +```bash +# Build time comparison +# Before base image: 8-10 min +# After base image: 2-3 min +# Improvement: 60-70% + +# Network usage +# Before: ~500 MB download per build (npm, bun, etc.) +# After: ~50 MB (only base image pull if not cached) +# Improvement: 90% +``` + +## FAQ + +**Q: چند وقت یکبار باید base را rebuild کنیم؟** +A: فقط وقتی که dependencies (Python, Node.js, bun) تغییر می‌کنند. معمولاً هر 2-3 ماه یکبار. + +**Q: آیا می‌توانیم چند base image داشته باشیم؟** +A: بله! مثلاً: +- `base:python3.11-node20` → برای پروژه‌های قدیمی +- `base:python3.12-node21` → برای پروژه‌های جدید + +**Q: اگر base image corrupt شود چه کنیم؟** +A: Application Dockerfile می‌تواند به `python:3.11-slim` fallback کند: +```dockerfile +ARG BASE_IMAGE=hub.peikarband.ir/peikarband/base:latest +FROM ${BASE_IMAGE:-python:3.11-slim} AS builder +``` + +**Q: چگونه base را به پروژه‌های دیگر منتقل کنیم؟** +A: Base image در registry مرکزی است، تمام پروژه‌ها می‌توانند از آن استفاده کنند: +```dockerfile +# در هر پروژه دیگر +FROM hub.peikarband.ir/peikarband/base:latest +``` + +## مراجع + +- [Multi-stage Docker Builds](https://docs.docker.com/build/building/multi-stage/) +- [Docker Build Cache](https://docs.docker.com/build/cache/) +- [Harbor Registry Management](https://goharbor.io/docs/latest/) +- [Woodpecker CI Documentation](https://woodpecker-ci.org/docs/) + diff --git a/docs/WOODPECKER_CI_CD.md b/docs/WOODPECKER_CI_CD.md new file mode 100644 index 0000000..92760a3 --- /dev/null +++ b/docs/WOODPECKER_CI_CD.md @@ -0,0 +1,586 @@ +# Woodpecker CI/CD Documentation + +این مستند راهنمای کامل برای راه‌اندازی و استفاده از Woodpecker CI/CD pipeline برای پروژه Peikarband است. + +## نمای کلی Pipeline + +Pipeline ما شامل 8 مرحله اصلی است: + +``` +1. Code Quality & Linting + ├── flake8 (Python linting) + ├── black (Code formatting check) + ├── isort (Import sorting) + └── mypy (Type checking) + +2. Security Scanning + ├── bandit (Security vulnerability scan) + └── safety (Dependency vulnerability check) + +3. Testing + └── pytest (Unit & Integration tests with coverage) + +4. Helm Validation + ├── helm lint + └── helm template + +5. Docker Build & Push + └── Multi-platform build with caching + +6. Deploy to Staging + └── Auto-deploy on main/develop branches + +7. Deploy to Production + └── Manual trigger on version tags (v*) + +8. Notifications + └── Success/Failure notifications +``` + +## تنظیمات Secrets + +برای اجرای کامل pipeline، باید secrets زیر را در Woodpecker تنظیم کنید: + +### 1. Registry Secrets (الزامی برای Build) + +```bash +HARBOR_USERNAME=admin +HARBOR_PASSWORD=your_harbor_password +``` + +**نحوه تنظیم در Woodpecker UI:** +1. رفتن به Repository Settings +2. کلیک روی "Secrets" +3. اضافه کردن secret جدید: + - Name: `HARBOR_USERNAME` + - Value: `admin` + - Events: `push, tag` +4. تکرار برای `HARBOR_PASSWORD` + +### 2. Kubernetes Secrets (الزامی برای Deployment) + +#### Staging Environment + +```bash +# Generate base64 encoded kubeconfig +cat ~/.kube/config-staging | base64 -w 0 > kubeconfig-staging-base64.txt + +# Add to Woodpecker as secret +KUBECONFIG_STAGING= +``` + +#### Production Environment + +```bash +# Generate base64 encoded kubeconfig +cat ~/.kube/config-production | base64 -w 0 > kubeconfig-production-base64.txt + +# Add to Woodpecker as secret +KUBECONFIG_PRODUCTION= +``` + +**⚠️ نکات امنیتی:** +- هرگز kubeconfig را در Git commit نکنید +- از RBAC برای محدود کردن دسترسی kubeconfig استفاده کنید +- به‌طور منظم kubeconfig را rotate کنید +- فقط namespace های staging و production دسترسی داشته باشند + +### 3. Optional: ArgoCD Integration + +اگر می‌خواهید از ArgoCD برای deployment استفاده کنید: + +```bash +ARGOCD_SERVER=argocd.peikarband.ir +ARGOCD_AUTH_TOKEN=your_argocd_token +``` + +## Branch Strategy + +Pipeline بر اساس branch و event متفاوت رفتار می‌کند: + +### Pull Request (PR) + +```yaml +Stages: Lint + Test + Security + Helm Lint +Skip: Build, Deploy +Purpose: Code quality validation +``` + +**مثال:** + +```bash +# Create PR +git checkout -b feature/new-feature +git push origin feature/new-feature +# Open PR in GitLab/GitHub -> Pipeline runs automatically +``` + +### Main Branch (Push) + +```yaml +Stages: All stages +Deploy: Staging (automatic) +Tags: latest, main-, +``` + +**مثال:** + +```bash +git checkout main +git pull origin main +git merge feature/new-feature +git push origin main +# -> Automatic: Test -> Build -> Deploy to Staging +``` + +### Develop Branch (Push) + +```yaml +Stages: All stages +Deploy: Staging (automatic) +Tags: develop, develop-, +``` + +**مثال:** + +```bash +git checkout develop +git push origin develop +# -> Automatic: Test -> Build -> Deploy to Staging +``` + +### Version Tags (Production) + +```yaml +Stages: All stages +Deploy: Production (automatic) +Tags: latest, v1.2.3, +``` + +**مثال:** + +```bash +# Create and push version tag +git checkout main +git tag -a v1.0.0 -m "Release v1.0.0" +git push origin v1.0.0 +# -> Automatic: Test -> Build -> Deploy to Production +``` + +## Pipeline Triggers + +### Automatic Triggers + +1. **Push به branch:** + ```bash + git push origin main # Trigger full pipeline + deploy staging + git push origin develop # Trigger full pipeline + deploy staging + git push origin feature/* # No trigger (manual only) + ``` + +2. **Tag push:** + ```bash + git push origin v1.0.0 # Trigger full pipeline + deploy production + ``` + +3. **Pull Request:** + ```bash + # Any PR -> Triggers lint/test/security only + ``` + +### Manual Triggers + +در Woodpecker UI: +1. رفتن به Repository +2. کلیک روی "Pipelines" +3. کلیک روی "New Pipeline" +4. انتخاب branch/commit +5. کلیک روی "Start" + +## Docker Image Tagging + +Pipeline به‌طور خودکار images را با تگ‌های مختلف می‌سازد: + +### Main Branch + +``` +hub.peikarband.ir/peikarband/landing:latest +hub.peikarband.ir/peikarband/landing:main +hub.peikarband.ir/peikarband/landing:a1b2c3d4 # commit SHA +``` + +### Develop Branch + +``` +hub.peikarband.ir/peikarband/landing:develop +hub.peikarband.ir/peikarband/landing:develop-a1b2c3d4 +hub.peikarband.ir/peikarband/landing:a1b2c3d4 +``` + +### Version Tags + +``` +hub.peikarband.ir/peikarband/landing:latest +hub.peikarband.ir/peikarband/landing:v1.0.0 +hub.peikarband.ir/peikarband/landing:a1b2c3d4 +``` + +## Deployment Process + +### Staging Deployment + +**Trigger:** هر push به `main` یا `develop` + +**فرایند:** +1. Tests pass +2. Build Docker image +3. Push to registry with tag `-` +4. Helm upgrade to `staging` namespace +5. Wait for rollout (timeout: 5 minutes) +6. Show pod status + +**Rollback:** + +```bash +# List helm releases +helm list -n staging + +# Rollback to previous version +helm rollback peikarband-staging -n staging + +# Or rollback to specific revision +helm rollback peikarband-staging 5 -n staging +``` + +### Production Deployment + +**Trigger:** Push tag با pattern `v*` (مثل `v1.0.0`) + +**فرایند:** +1. Tests pass +2. Build Docker image +3. Push to registry with tags `latest`, `v1.0.0`, `` +4. Helm upgrade to `production` namespace with production values +5. Wait for rollout (timeout: 10 minutes) +6. Verify deployment +7. Show pod status + +**Rollback:** + +```bash +# Check current status +kubectl get pods -n production + +# Rollback via Helm +helm rollback peikarband -n production + +# Or rollback via kubectl +kubectl rollout undo deployment/peikarband -n production + +# Check rollout status +kubectl rollout status deployment/peikarband -n production +``` + +## Monitoring Pipeline + +### Via Woodpecker UI + +1. رفتن به: `https://woodpecker.peikarband.ir` (یا آدرس Woodpecker شما) +2. انتخاب repository +3. مشاهده لیست pipeline runs +4. کلیک روی یک run برای مشاهده جزئیات + +### Via CLI + +```bash +# Install Woodpecker CLI +curl -L https://github.com/woodpecker-ci/woodpecker/releases/latest/download/woodpecker-cli_linux_amd64.tar.gz | tar xz +sudo mv woodpecker-cli /usr/local/bin/ + +# Configure +export WOODPECKER_SERVER=https://woodpecker.peikarband.ir +export WOODPECKER_TOKEN=your_token + +# List pipelines +woodpecker pipeline ls + +# Show pipeline info +woodpecker pipeline info + +# Show logs +woodpecker pipeline logs + +# Approve waiting pipeline +woodpecker pipeline approve +``` + +## Troubleshooting + +### Pipeline Fails at Lint Stage + +**مشکل:** کد formatting یا linting مشکل دارد + +**راه‌حل:** + +```bash +cd peikarband + +# Fix formatting +black src/ +isort src/ + +# Check linting +flake8 src/ + +# Commit fixes +git add . +git commit -m "fix: code formatting and linting" +git push +``` + +### Pipeline Fails at Test Stage + +**مشکل:** تست‌ها fail می‌شوند + +**راه‌حل:** + +```bash +cd peikarband + +# Run tests locally +pytest tests/ -v + +# Run with coverage +pytest tests/ -v --cov=src + +# Fix tests and re-run +git add . +git commit -m "fix: failing tests" +git push +``` + +### Pipeline Fails at Docker Build + +**مشکل:** Docker build error + +**راه‌حل:** + +```bash +# Test build locally +docker build -f docker/Dockerfile -t test:latest . + +# Check Dockerfile syntax +docker build --check -f docker/Dockerfile . + +# Check build context +ls -la peikarband/ + +# Common issues: +# 1. Missing files in context +# 2. COPY path wrong +# 3. Build args missing +``` + +### Pipeline Fails at Deployment + +**مشکل:** Helm deployment fail + +**راه‌حل:** + +```bash +# Test Helm locally +helm lint helm/peikarband +helm template peikarband helm/peikarband -f helm/peikarband/values-staging.yaml + +# Check kubectl access +kubectl get pods -n staging + +# Check secrets +kubectl get secrets -n staging + +# Check image pull +kubectl describe pod -n staging +``` + +### Secret Not Found Error + +**مشکل:** `Secret not found: HARBOR_USERNAME` + +**راه‌حل:** +1. رفتن به Woodpecker UI > Repository > Settings > Secrets +2. بررسی که secret با نام درست اضافه شده +3. بررسی که secret برای event درست (push, tag, etc.) فعال است +4. بررسی که secret برای branch درست در دسترس است + +### Kubeconfig Invalid + +**مشکل:** `Unable to connect to the server` + +**راه‌حل:** + +```bash +# Test kubeconfig locally +export KUBECONFIG=/path/to/your/kubeconfig +kubectl get pods + +# Re-encode kubeconfig +cat ~/.kube/config | base64 -w 0 + +# Update secret in Woodpecker +# Copy new base64 string to KUBECONFIG_STAGING or KUBECONFIG_PRODUCTION +``` + +## Performance Optimization + +### Build Cache + +Pipeline از Docker layer caching استفاده می‌کند: + +```yaml +cache_from: type=registry,ref=hub.peikarband.ir/peikarband/landing:buildcache +cache_to: type=inline +``` + +**بهینه‌سازی بیشتر:** + +1. **Dependencies Caching:** requirements.txt را قبل از کد اصلی COPY کنید +2. **Multi-stage Build:** از multi-stage builds استفاده کنید +3. **Parallel Stages:** مراحل مستقل را parallel اجرا کنید + +### Pipeline Duration + +زمان تقریبی هر stage: + +``` +Lint stages: ~1-2 minutes +Security scan: ~2-3 minutes +Tests: ~3-5 minutes +Helm validation: ~30 seconds +Docker build: ~5-10 minutes (first time), ~2-3 minutes (cached) +Deployment: ~2-5 minutes +Total: ~15-30 minutes (full pipeline) +``` + +## Best Practices + +### 1. Commit Messages + +از conventional commits استفاده کنید: + +```bash +feat: add new feature +fix: bug fix +docs: documentation changes +style: formatting changes +refactor: code refactoring +test: test changes +chore: build/CI changes +``` + +### 2. Version Tagging + +```bash +# Semantic versioning +v1.0.0 # Major.Minor.Patch +v1.0.1 # Patch release +v1.1.0 # Minor release +v2.0.0 # Major release + +# Pre-release versions +v1.0.0-rc.1 # Release candidate +v1.0.0-beta.1 # Beta release +v1.0.0-alpha.1 # Alpha release +``` + +### 3. Feature Branches + +```bash +# Create feature branch +git checkout -b feature/user-authentication +# ... make changes ... +git add . +git commit -m "feat: add user authentication" +git push origin feature/user-authentication + +# Create PR +# After approval, merge to develop +git checkout develop +git merge feature/user-authentication +git push origin develop +# -> Triggers pipeline + deploy to staging + +# After testing in staging, merge to main +git checkout main +git merge develop +git push origin main +# -> Triggers pipeline + deploy to staging + +# Create production release +git tag -a v1.1.0 -m "Release v1.1.0: Add user authentication" +git push origin v1.1.0 +# -> Triggers pipeline + deploy to production +``` + +### 4. Hotfix Process + +```bash +# Create hotfix branch from main +git checkout -b hotfix/critical-bug main + +# Fix the bug +git add . +git commit -m "fix: critical security vulnerability" + +# Merge to main +git checkout main +git merge hotfix/critical-bug + +# Tag immediately +git tag -a v1.0.1 -m "Hotfix v1.0.1: Security patch" +git push origin main v1.0.1 +# -> Triggers pipeline + deploy to production + +# Merge back to develop +git checkout develop +git merge hotfix/critical-bug +git push origin develop +``` + +## Environment Variables + +### Available in Pipeline + +```bash +CI=woodpecker # Always set +CI_REPO=username/peikarband # Repository name +CI_REPO_LINK=https://git.../peikarband # Repository URL +CI_COMMIT_SHA=a1b2c3d4e5f6... # Full commit hash +CI_COMMIT_BRANCH=main # Branch name +CI_COMMIT_TAG=v1.0.0 # Tag (if triggered by tag) +CI_COMMIT_MESSAGE=feat: new feature # Commit message +CI_PIPELINE_CREATED=2024-01-01T... # Pipeline creation time +CI_PIPELINE_NUMBER=123 # Pipeline number +``` + +### Usage Example + +```bash +# In pipeline step +echo "Building commit ${CI_COMMIT_SHA:0:8} from branch ${CI_COMMIT_BRANCH}" +echo "Image tag: hub.peikarband.ir/peikarband/landing:${CI_COMMIT_SHA:0:8}" +``` + +## Support & Contact + +برای مشکلات و سوالات: +- **Documentation:** این فایل +- **Issues:** GitLab/GitHub Issues +- **Team Contact:** dev@peikarband.ir + +## مراجع + +- [Woodpecker CI Documentation](https://woodpecker-ci.org/docs/intro) +- [Docker Build Best Practices](https://docs.docker.com/develop/dev-best-practices/) +- [Helm Documentation](https://helm.sh/docs/) +- [Kubernetes Deployments](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/) + diff --git a/helm/peikarband/argocd/README.md b/helm/peikarband/argocd/README.md new file mode 100644 index 0000000..69ae87b --- /dev/null +++ b/helm/peikarband/argocd/README.md @@ -0,0 +1,257 @@ +# ArgoCD Application Manifests + +این پوشه شامل ArgoCD Application manifests برای deployment خودکار با GitOps است. + +## ساختار + +``` +argocd/ +├── application.yaml # Production deployment (namespace: production) +├── application-staging.yaml # Staging deployment (namespace: staging) +├── secrets/ # Kubernetes secrets (DO NOT commit real secrets) +└── README.md # این فایل +``` + +## استفاده + +### 1. Deploy به Production + +```bash +# Apply ArgoCD Application +kubectl apply -f helm/peikarband/argocd/application.yaml + +# Check sync status +argocd app get peikarband-landing-prod + +# Sync manually if needed +argocd app sync peikarband-landing-prod + +# Watch deployment +kubectl get pods -n production -w +``` + +### 2. Deploy به Staging + +```bash +# Apply ArgoCD Application +kubectl apply -f helm/peikarband/argocd/application-staging.yaml + +# Check sync status +argocd app get peikarband-landing-staging + +# Sync manually +argocd app sync peikarband-landing-staging +``` + +### 3. Update Image Tag + +#### روش اول: از طریق ArgoCD UI +1. باز کردن ArgoCD UI +2. رفتن به Application مورد نظر +3. کلیک روی "Parameters" +4. تغییر `image.tag` به tag جدید +5. کلیک روی "Sync" + +#### روش دوم: از طریق CLI + +```bash +# Update production +argocd app set peikarband-landing-prod \ + -p image.tag=v1.2.3 + +# Update staging +argocd app set peikarband-landing-staging \ + -p image.tag=develop +``` + +#### روش سوم: تغییر در Git (GitOps روش اصلی) + +```bash +# Update values-production.yaml +vim helm/peikarband/values-production.yaml +# Change image.tag value + +# Commit & Push +git add helm/peikarband/values-production.yaml +git commit -m "chore: update image to v1.2.3" +git push origin main + +# ArgoCD will auto-sync (if automated sync enabled) +``` + +## تنظیمات مهم + +### Auto-Sync + +Her دو application روی `automated sync` تنظیم شده‌اند: +- **prune**: true → منابع اضافی حذف میشن +- **selfHeal**: true → تغییرات manual برگشت میخورن +- **allowEmpty**: false → deploy خالی مجاز نیست + +### Sync Options + +- **CreateNamespace**: true → namespace خودکار ساخته میشه +- **PrunePropagationPolicy**: foreground → منابع به ترتیب حذف میشن +- **PruneLast**: true → prune در آخر انجام میشه + +### Retry Policy + +در صورت شکست: +- تا 5 بار تلاش مجدد +- با backoff exponential (5s, 10s, 20s, 40s, 80s) +- حداکثر 3 دقیقه delay + +## Secrets Management + +⚠️ **هشدار امنیتی:** + +Secrets نباید در Git commit بشن! از یکی از روش‌های زیر استفاده کنید: + +### روش 1: Manual Secret Creation + +```bash +# Create registry secret +kubectl create secret docker-registry hub-registry-secret \ + --docker-server=hub.peikarband.ir \ + --docker-username=admin \ + --docker-password=YOUR_PASSWORD \ + --namespace=production + +# Create application secrets +kubectl create secret generic peikarband-prod-secrets \ + --from-literal=db-username=peikarband \ + --from-literal=db-password=YOUR_DB_PASSWORD \ + --from-literal=redis-password=YOUR_REDIS_PASSWORD \ + --namespace=production +``` + +### روش 2: Sealed Secrets (پیشنهاد شده) + +```bash +# Install sealed-secrets controller +kubectl apply -f https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.24.0/controller.yaml + +# Create sealed secret +kubeseal --format=yaml < secret.yaml > sealed-secret.yaml + +# Commit sealed-secret.yaml (امن است) +git add helm/peikarband/argocd/secrets/sealed-secret.yaml +git commit -m "chore: add sealed secrets" +``` + +### روش 3: External Secrets Operator + +```bash +# Install external-secrets +helm install external-secrets external-secrets/external-secrets \ + --namespace external-secrets-system \ + --create-namespace + +# Create SecretStore +kubectl apply -f argocd/secrets/secret-store.yaml + +# Create ExternalSecret +kubectl apply -f argocd/secrets/external-secret.yaml +``` + +## Health Checks + +ArgoCD health checks: +- **Deployment**: Ready replicas == Desired replicas +- **Service**: Endpoint موجود باشه +- **Ingress**: Backend service موجود باشه +- **HPA**: Status normal باشه + +## Rollback + +در صورت مشکل: + +```bash +# List revisions +argocd app history peikarband-landing-prod + +# Rollback to specific revision +argocd app rollback peikarband-landing-prod 5 + +# Or rollback to previous +argocd app rollback peikarband-landing-prod +``` + +## Monitoring + +```bash +# Watch application status +argocd app watch peikarband-landing-prod + +# Get detailed status +argocd app get peikarband-landing-prod --refresh + +# Show sync differences +argocd app diff peikarband-landing-prod + +# View logs +kubectl logs -n production -l app.kubernetes.io/name=peikarband -f +``` + +## Troubleshooting + +### App won't sync + +```bash +# Check sync status +argocd app get peikarband-landing-prod + +# Force refresh +argocd app get peikarband-landing-prod --refresh --hard-refresh + +# Delete and recreate +argocd app delete peikarband-landing-prod +kubectl apply -f helm/peikarband/argocd/application.yaml +``` + +### Pods not starting + +```bash +# Check pod status +kubectl get pods -n production + +# Check pod logs +kubectl logs -n production + +# Describe pod for events +kubectl describe pod -n production + +# Check image pull secrets +kubectl get secrets -n production +``` + +### Health check failing + +```bash +# Check service endpoints +kubectl get endpoints -n production + +# Test health endpoint +kubectl port-forward -n production svc/peikarband 8000:8000 +curl http://localhost:8000/ping +``` + +## CI/CD Integration + +در Woodpecker/GitHub Actions: + +```yaml +- name: Update ArgoCD Image + image: argoproj/argocd:latest + commands: + - argocd login argocd.peikarband.ir --username admin --password $ARGOCD_PASSWORD + - argocd app set peikarband-landing-prod -p image.tag=${CI_COMMIT_SHA:0:8} + - argocd app sync peikarband-landing-prod --timeout 300 +``` + +## مستندات بیشتر + +- [ArgoCD Documentation](https://argo-cd.readthedocs.io/) +- [Helm Best Practices](https://helm.sh/docs/chart_best_practices/) +- [Kubernetes Secrets Management](https://kubernetes.io/docs/concepts/configuration/secret/) + diff --git a/helm/peikarband/argocd/application-staging.yaml b/helm/peikarband/argocd/application-staging.yaml new file mode 100644 index 0000000..4f907cb --- /dev/null +++ b/helm/peikarband/argocd/application-staging.yaml @@ -0,0 +1,54 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: peikarband-landing-staging + namespace: argocd + finalizers: + - resources-finalizer.argocd.argoproj.io +spec: + project: default + + source: + repoURL: http://git.peikarband.ir/ehsan-minadd/peikarband + targetRevision: develop + path: helm/peikarband + helm: + releaseName: peikarband-staging + valueFiles: + - values.yaml + - values-staging.yaml + parameters: + - name: image.tag + value: develop + - name: image.repository + value: hub.peikarband.ir/peikarband/landing + + destination: + server: https://kubernetes.default.svc + namespace: staging + + syncPolicy: + automated: + prune: true + selfHeal: true + allowEmpty: false + syncOptions: + - CreateNamespace=true + - PrunePropagationPolicy=foreground + - PruneLast=true + retry: + limit: 5 + backoff: + duration: 5s + factor: 2 + maxDuration: 3m + + revisionHistoryLimit: 10 + + # Health assessment + ignoreDifferences: + - group: apps + kind: Deployment + jsonPointers: + - /spec/replicas + diff --git a/helm/peikarband/argocd/application.yaml b/helm/peikarband/argocd/application.yaml new file mode 100644 index 0000000..eb8c759 --- /dev/null +++ b/helm/peikarband/argocd/application.yaml @@ -0,0 +1,54 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: peikarband-landing-prod + namespace: argocd + finalizers: + - resources-finalizer.argocd.argoproj.io +spec: + project: default + + source: + repoURL: http://git.peikarband.ir/ehsan-minadd/peikarband + targetRevision: main + path: helm/peikarband + helm: + releaseName: peikarband + valueFiles: + - values.yaml + - values-production.yaml + parameters: + - name: image.tag + value: latest + - name: image.repository + value: hub.peikarband.ir/peikarband/landing + + destination: + server: https://kubernetes.default.svc + namespace: production + + syncPolicy: + automated: + prune: true + selfHeal: true + allowEmpty: false + syncOptions: + - CreateNamespace=true + - PrunePropagationPolicy=foreground + - PruneLast=true + retry: + limit: 5 + backoff: + duration: 5s + factor: 2 + maxDuration: 3m + + revisionHistoryLimit: 10 + + # Health assessment + ignoreDifferences: + - group: apps + kind: Deployment + jsonPointers: + - /spec/replicas + diff --git a/peikarband/rxconfig.py b/peikarband/rxconfig.py index b5eef8f..95486f8 100644 --- a/peikarband/rxconfig.py +++ b/peikarband/rxconfig.py @@ -10,7 +10,7 @@ import reflex as rx API_URL = os.getenv("API_URL", "http://localhost:8000") FRONTEND_PORT = int(os.getenv("FRONTEND_PORT", "3000")) BACKEND_PORT = int(os.getenv("BACKEND_PORT", "8000")) -DB_URL = os.getenv("DATABASE_URL", "sqlite:///data/reflex.db") +DB_URL = os.getenv("DATABASE_URL", "sqlite:////app/data/reflex.db") config = rx.Config( app_name="peikarband",