الأدلة التقنية

تثبيت Taiga لإدارة المشاريع بأسلوب Scrum وKanban

إذا كانت مهام فريقك موزعة بين جداول Excel وأدوات سحابية مثل Jira وTrello، فهذا الدليل يشرح كيف تشغل Taiga على سيرفرك لتدير العمل بأسلوب Scrum وKanban، وتدعو الأعضاء، وتجهز البريد والنسخ الاحتياطي والتحديث.

تثبيت Taiga لإدارة المشاريع بأسلوب Scrum وKanban

لنفرض أن فريقك يعمل بأسلوب Scrum أو Kanban، فالحل الأول الذي يخطر على البال غالباً هو جدول Excel مشترك أو سبورة بيضاء Whiteboard عليها أوراق ملونة، وهذا الحل يعمل في الأسبوع الأول، ولكنه يفشل بسرعة لأنك لا تعرف من غير حالة المهمة ومتى، ولا يصل إشعار Notification لمن ينتظر أن ينهي زميله عمله، وتضيع المرفقات والنقاشات بين البريد والمحادثات. والحل الثاني هو أن تنتقل إلى Jira أو Trello، وهما أداتان ممتازتان، ولكن بيانات مشاريعك كلها سوف تكون على سيرفرات Servers الشركة المزودة، وتدفع اشتراكاً عن كل مستخدم، وقد تفرض عليك الأنظمة أو العقود في مؤسستك أن تبقى هذه البيانات داخلها.

لذلك فالحل الأنسب لكثير من الفرق هو أداة مفتوحة المصدر Open Source تعمل على سيرفرك أنت، وTaiga واحدة من أشهر هذه الأدوات، وهي منصة لإدارة المشاريع Project Management بالأساليب الرشيقة Agile، بنيت في الأصل لفرق تطوير البرمجيات ثم صارت تستخدمها فرق التصميم والتسويق والعمليات أيضاً، ففي المشروع الواحد تجد لوحة Kanban، وقائمة مهام Scrum التي تسمى Backlog مقسمة إلى سبرنتات Sprints مع نقاط التقدير Story Points، وتجد الملاحم Epics التي تجمع قصص المستخدمين User Stories الكبيرة، ومتتبعاً للمشكلات Issues، وWiki، وفيها كذلك تقارير Burndown وWebhooks وتكاملات مع GitHub وGitLab.

وقد يتساءل البعض: هل تصلح Taiga لكل أنواع المشاريع؟ والإجابة أنها تناسب الفريق الذي يريد أداة واضحة وسهلة التعلم تعمل على سيرفراته، وأما إذا كنت تدير محافظ مشاريع كبيرة Project Portfolios وتحتاج إلى مخططات Gantt وإدارة تفصيلية للموارد Resource Management، فهي ليست الخيار الأفضل لك، والسبب أنها مبنية حول الفريق وقصصه وسبرنتاته، وليست مبنية حول الجداول الزمنية وتوزيع الموارد.

لكن تذكر: الإصدار 6 من Taiga هو الذي يحصل اليوم على التحديثات، حيث تتولى صيانته شركة Taiga Cloud Services وأولويتها إصلاح الأخطاء Bug Fixes وتحسين الأداء Performance أكثر من إضافة الميزات، وأما الجيل التالي الذي كان اسمه Taiga Next فقد انتقل إلى فريق آخر وصار منتجاً مستقلاً باسم Tenzu، لذلك لا تنتظر من Taiga 6 تغييرات كبيرة، وإنما إصدارات صيانة Maintenance Releases مثل الإصدار 6.10 الذي نستخدمه في هذا الدليل.

وسوف نناقش في هذا المقال ما يلي:

  • الخدمات التسع التي تتكون منها Taiga ودور كل واحدة منها، ثم التثبيت من المستودع الرسمي taiga-docker مع ملف .env بأسرار مولدة وتثبيت إصدار كل Image.
  • إنشاء حساب المدير، وربط النطاق مع شهادة TLS عبر Reverse Proxy يدعم WebSocket.
  • إنشاء أول مشروع Kanban، ودعوة الأعضاء بالبريد، وتحديد من يحق له إنشاء المشاريع.
  • النسخ الاحتياطي والاستعادة والتحديث، ثم أشهر المشكلات وحلولها.

ما تحتاجه قبل أن تبدأ Requirements

  • سيرفر Linux بمعالجين CPU وذاكرة RAM بحجم 4 GB، فالحزمة تشغل تسع Containers، ولكن أغلبها خفيف ولا يستهلك إلا القليل.
  • Docker Engine مع ملحق Docker Compose كما في دليل تثبيت Docker على Ubuntu، ومعهما git.
  • نطاق فرعي Subdomain مثل tasks.example.com يشير إلى السيرفر (203.0.113.10)، وReverse Proxy يدعم WebSocket ويصدر شهادة TLS، مثل Nginx Proxy Manager.
  • حساب SMTP ترسل منه Taiga الدعوات وإشعارات التغيير ورسائل إعادة تعيين كلمة المرور Password Reset.

مكونات Taiga وما يفعله كل جزء منها Architecture

قد تبدو Taiga من الوهلة الأولى تطبيقاً واحداً، لكن في الواقع فهي تسع خدمات Services تعمل معاً، ومن المهم أن تعرف دور كل واحدة منها قبل التثبيت، والسبب أن أغلب المشكلات التي سوف تواجهها لاحقاً، مثل التحديثات الفورية التي لا تصل أو المرفقات التي لا تفتح، تعود إلى خدمة بعينها من هذه الخدمات:

الخدمةالدور
taiga-backواجهة ال API المبنية على Django، ومعها لوحة الإدارة Admin Panel على المسار /admin/
taiga-asyncنفس ال Image، ولكنها تعمل هنا عاملاً Worker ينفذ مهام Celery في الخلفية Background، مثل التصدير Export والاستيراد Import والإشعارات المؤجلة
taiga-frontالواجهة Frontend، وهي تطبيق AngularJS يقدمه nginx
taiga-eventsسيرفر WebSocket يرسل التحديثات الفورية Real-time Updates إلى المتصفحات
taiga-protectedيتحقق من توكنات الوصول Access Tokens قبل أن يقدم المرفقات Attachments، فلا يفتحها إلا من يملك الصلاحية Permission
taiga-dbقاعدة البيانات Database، وهي PostgreSQL
taiga-async-rabbitmq وtaiga-events-rabbitmqوسيطا رسائل Message Brokers من RabbitMQ، الأول لطوابير Queues مهام Celery، والثاني ينقل الأحداث Events من الخلفية Backend إلى taiga-events
taiga-gatewayسيرفر nginx داخلي يجمع الخدمات السابقة خلف منفذ Port واحد، ويوجه المسارات Paths /api و/admin و/events و/media كل منها إلى خدمته

تثبيت Taiga خطوة بخطوة Installation

تنزيل المستودع الرسمي taiga-docker

سوف تجد في الإنترنت ملفات Compose جاهزة وImages يبنيها أفراد لتشغيل Taiga، ولا ينصح باستخدامها لأنك لا تعرف متى تتوقف صيانتها ولا ما الذي تغير فيها، لذلك نعتمد على التثبيت الرسمي المبني على المستودع Repository المسمى taiga-docker، وفيه ملف Compose وإعداد nginx للبوابة Gateway وسكربتان مساعدان، وسوف نعمل على الفرع Branch المسمى stable لأنه الفرع الذي يشير إليه التوثيق:

cd /opt
sudo git clone https://github.com/taigaio/taiga-docker.git
cd taiga-docker
sudo git checkout stable

وفي المجلد سوف تجد الملفات التالية:

  • docker-compose.yml: الخدمات التسع.
  • docker-compose-inits.yml: خدمة مؤقتة اسمها taiga-manage لتنفيذ أوامر Django الإدارية Management Commands.
  • taiga-gateway/taiga.conf: إعداد nginx الخاص بالبوابة.
  • launch-taiga.sh وtaiga-manage.sh: سكربتان يغلفان أوامر docker compose حتى لا تكتبها كاملة كل مرة.
  • .env: ملف الإعدادات Config File، وقيمه الافتراضية غير آمنة، فغيرها كلها كما في الخطوة التالية.

ملف .env بأسرار مولدة Secrets

القيم الافتراضية في ملف .env منشورة في المستودع نفسه، فقيمة SECRET_KEY فيه هي taiga-secret-key وكلمة مرور قاعدة البيانات هي taiga، وأي شخص يقرأ المستودع يعرفها، وهذا خطير لأن SECRET_KEY هو المفتاح الذي توقع به Taiga توكنات الدخول وروابط المرفقات، لذلك قم بتوليد قيم عشوائية أولاً:

for n in SECRET_KEY POSTGRES_PASSWORD RABBITMQ_PASS RABBITMQ_ERLANG_COOKIE; do echo "$n=$(openssl rand -hex 32)"; done

وسوف يطبع الأمر أربعة أسطر بالشكل NAME=قيمة، وكل قيمة منها 64 حرفاً من النظام الست عشري Hexadecimal، والآن قم بكتابة ملف .env كاملاً بهذه القيم، ومعها نطاقك Domain وبيانات SMTP:

sudo nano /opt/taiga-docker/.env
# عنوان Taiga
TAIGA_SCHEME=https
TAIGA_DOMAIN=tasks.example.com
SUBPATH=""
WEBSOCKETS_SCHEME=wss

# مفتاح التوقيع؛ يستخدمه back وevents وprotected معاً
SECRET_KEY="ضع_قيمة_SECRET_KEY"

# قاعدة البيانات
POSTGRES_USER=taiga
POSTGRES_PASSWORD=ضع_قيمة_POSTGRES_PASSWORD

# البريد
EMAIL_BACKEND=smtp
EMAIL_HOST=smtp.example.com
EMAIL_PORT=587
[email protected]
EMAIL_HOST_PASSWORD=كلمة_مرور_SMTP
[email protected]
EMAIL_USE_TLS=True
EMAIL_USE_SSL=False

# RabbitMQ
RABBITMQ_USER=taiga
RABBITMQ_PASS=ضع_قيمة_RABBITMQ_PASS
RABBITMQ_VHOST=taiga
RABBITMQ_ERLANG_COOKIE=ضع_قيمة_RABBITMQ_ERLANG_COOKIE

# مدة صلاحية روابط المرفقات بالثواني
ATTACHMENTS_MAX_AGE=360

# التسجيل الذاتي والقياس عن بعد
PUBLIC_REGISTER_ENABLED=False
ENABLE_TELEMETRY=False

في الإعداد أعلاه لاحظ التالي:

  • TAIGA_DOMAIN هو النطاق كما يكتبه المستخدم في المتصفح دون https://، ومع HTTPS اجعل WEBSOCKETS_SCHEME=wss، والسبب أن المتصفح يرفض أن يفتح اتصال WebSocket غير مشفر Unencrypted من صفحة مشفرة.
  • لا تقم بتفعيل EMAIL_USE_TLS وEMAIL_USE_SSL معاً، فالأول للمنفذ 587 مع STARTTLS والثاني للمنفذ 465، وإذا أردت أن تجرب دون سيرفر بريد Mail Server فاجعل EMAIL_BACKEND=console، وهو خيار في Django يكتب الرسائل في سجل Log الخدمة taiga-back ولا يرسلها.
  • PUBLIC_REGISTER_ENABLED=False يمنع إنشاء الحسابات من صفحة الدخول، فلا ينضم أحد إلا بدعوة Invitation، وهذا ما تريده غالباً لسيرفر يخص فريقك وحده.
  • ENABLE_TELEMETRY=False يوقف إرسال بيانات الاستخدام المجهولة Anonymous Telemetry، وقيمته في الملف الرسمي True.

وبعد الحفظ اجعل الملف مقروءاً للمالك فقط، لأنه يحمل كل كلمات المرور:

sudo chmod 600 /opt/taiga-docker/.env

تثبيت الإصدارات Version Pinning في docker-compose.yml

الحل الأسهل هو أن تشغل ملف Compose الرسمي كما هو، ولكن هذا الحل غير مناسب لسيرفر إنتاج Production لعدة أسباب، فالملف يستخدم الوسم Tag المسمى latest لكل Images الخاصة بـ Taiga، وهذا يعني أن أي أمر pull قد يرقيك إلى إصدار جديد دون أن تقصد، وتنفذ معه ترحيلات قاعدة البيانات Migrations تلقائياً فلا رجوع عنها إلا من نسخة احتياطية، ويعتمد الملف كذلك إصدارات قديمة انتهى دعمها End of Life هي PostgreSQL (12.3) وRabbitMQ (3.8) وnginx (1.19)، وينشر البوابة على المنفذ 9000 لكل عناوين السيرفر. لذلك عدلنا الملف، فثبتنا كل Image على إصدار محدد وحديث، وأطلنا فحص صحة Healthcheck قاعدة البيانات، ومررنا إعداد التسجيل الذاتي Self Registration، وربطنا البوابة بالعنوان المحلي Localhost خلف ال Reverse Proxy.

وأما فحص الصحة فسبب تعديله أن الفحص الأصلي ينتهي على بعض الأقراص قبل أن يكمل PostgreSQL تهيئته الأولى Initialization، فتفشل بقية الخدمات بالخطأ dependency failed to start، والملف بعد التعديل سوف يكون كما يلي:

sudo nano /opt/taiga-docker/docker-compose.yml
x-environment:
  &default-back-environment
  # قاعدة البيانات
  POSTGRES_DB: "taiga"
  POSTGRES_USER: "${POSTGRES_USER}"
  POSTGRES_PASSWORD: "${POSTGRES_PASSWORD}"
  POSTGRES_HOST: "taiga-db"
  # Taiga
  TAIGA_SECRET_KEY: "${SECRET_KEY}"
  TAIGA_SITES_SCHEME: "${TAIGA_SCHEME}"
  TAIGA_SITES_DOMAIN: "${TAIGA_DOMAIN}"
  TAIGA_SUBPATH: "${SUBPATH}"
  PUBLIC_REGISTER_ENABLED: "${PUBLIC_REGISTER_ENABLED}"
  # البريد
  EMAIL_BACKEND: "django.core.mail.backends.${EMAIL_BACKEND}.EmailBackend"
  DEFAULT_FROM_EMAIL: "${EMAIL_DEFAULT_FROM}"
  EMAIL_USE_TLS: "${EMAIL_USE_TLS}"
  EMAIL_USE_SSL: "${EMAIL_USE_SSL}"
  EMAIL_HOST: "${EMAIL_HOST}"
  EMAIL_PORT: "${EMAIL_PORT}"
  EMAIL_HOST_USER: "${EMAIL_HOST_USER}"
  EMAIL_HOST_PASSWORD: "${EMAIL_HOST_PASSWORD}"
  # RabbitMQ
  RABBITMQ_USER: "${RABBITMQ_USER}"
  RABBITMQ_PASS: "${RABBITMQ_PASS}"
  # القياس عن بعد
  ENABLE_TELEMETRY: "${ENABLE_TELEMETRY}"

x-volumes:
  &default-back-volumes
  - taiga-static-data:/taiga-back/static
  - taiga-media-data:/taiga-back/media
  # - ./config.py:/taiga-back/settings/config.py

services:
  taiga-db:
    image: postgres:16.15-alpine
    environment:
      POSTGRES_DB: "taiga"
      POSTGRES_USER: "${POSTGRES_USER}"
      POSTGRES_PASSWORD: "${POSTGRES_PASSWORD}"
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"]
      interval: 5s
      timeout: 15s
      retries: 20
      start_period: 30s
    volumes:
      - taiga-db-data:/var/lib/postgresql/data
    networks:
      - taiga
    restart: unless-stopped

  taiga-back:
    image: taigaio/taiga-back:6.10.2
    environment: *default-back-environment
    volumes: *default-back-volumes
    networks:
      - taiga
    depends_on:
      taiga-db:
        condition: service_healthy
      taiga-events-rabbitmq:
        condition: service_started
      taiga-async-rabbitmq:
        condition: service_started
    restart: unless-stopped

  taiga-async:
    image: taigaio/taiga-back:6.10.2
    entrypoint: ["/taiga-back/docker/async_entrypoint.sh"]
    environment: *default-back-environment
    volumes: *default-back-volumes
    networks:
      - taiga
    depends_on:
      taiga-db:
        condition: service_healthy
      taiga-events-rabbitmq:
        condition: service_started
      taiga-async-rabbitmq:
        condition: service_started
    restart: unless-stopped

  taiga-async-rabbitmq:
    image: rabbitmq:4.3.6-management-alpine
    environment:
      RABBITMQ_ERLANG_COOKIE: "${RABBITMQ_ERLANG_COOKIE}"
      RABBITMQ_DEFAULT_USER: "${RABBITMQ_USER}"
      RABBITMQ_DEFAULT_PASS: "${RABBITMQ_PASS}"
      RABBITMQ_DEFAULT_VHOST: "${RABBITMQ_VHOST}"
    hostname: "taiga-async-rabbitmq"
    volumes:
      - taiga-async-rabbitmq-data:/var/lib/rabbitmq
    networks:
      - taiga
    restart: unless-stopped

  taiga-front:
    image: taigaio/taiga-front:6.10.3
    environment:
      TAIGA_URL: "${TAIGA_SCHEME}://${TAIGA_DOMAIN}"
      TAIGA_WEBSOCKETS_URL: "${WEBSOCKETS_SCHEME}://${TAIGA_DOMAIN}"
      TAIGA_SUBPATH: "${SUBPATH}"
      # الواجهة تكتب القيمة في conf.json كما هي، لذلك تكتب بحروف صغيرة: "true" أو "false"
      PUBLIC_REGISTER_ENABLED: "false"
    networks:
      - taiga
    restart: unless-stopped

  taiga-events:
    image: taigaio/taiga-events:6.10.0
    environment:
      RABBITMQ_USER: "${RABBITMQ_USER}"
      RABBITMQ_PASS: "${RABBITMQ_PASS}"
      TAIGA_SECRET_KEY: "${SECRET_KEY}"
    networks:
      - taiga
    depends_on:
      taiga-events-rabbitmq:
        condition: service_started
    restart: unless-stopped

  taiga-events-rabbitmq:
    image: rabbitmq:4.3.6-management-alpine
    environment:
      RABBITMQ_ERLANG_COOKIE: "${RABBITMQ_ERLANG_COOKIE}"
      RABBITMQ_DEFAULT_USER: "${RABBITMQ_USER}"
      RABBITMQ_DEFAULT_PASS: "${RABBITMQ_PASS}"
      RABBITMQ_DEFAULT_VHOST: "${RABBITMQ_VHOST}"
    hostname: "taiga-events-rabbitmq"
    volumes:
      - taiga-events-rabbitmq-data:/var/lib/rabbitmq
    networks:
      - taiga
    restart: unless-stopped

  taiga-protected:
    image: taigaio/taiga-protected:6.10.1
    environment:
      MAX_AGE: "${ATTACHMENTS_MAX_AGE}"
      SECRET_KEY: "${SECRET_KEY}"
    networks:
      - taiga
    restart: unless-stopped

  taiga-gateway:
    image: nginx:1.30.5-alpine
    ports:
      - "127.0.0.1:9000:80"
    volumes:
      - ./taiga-gateway/taiga.conf:/etc/nginx/conf.d/default.conf
      - taiga-static-data:/taiga/static
      - taiga-media-data:/taiga/media
    networks:
      - taiga
    depends_on:
      - taiga-front
      - taiga-back
      - taiga-events
    restart: unless-stopped

volumes:
  taiga-static-data:
  taiga-media-data:
  taiga-db-data:
  taiga-async-rabbitmq-data:
  taiga-events-rabbitmq-data:

networks:
  taiga:

في الإعداد أعلاه لاحظ التالي:

  • كل Image مثبتة على رقم إصدار كامل مثل taigaio/taiga-back:6.10.2، وبالتالي لا يتغير شيء على السيرفر إلا عندما تغير أنت الرقم بنفسك كما في قسم التحديث.
  • منفذ البوابة مربوط بالعنوان 127.0.0.1 فقط، والسبب أن ال Reverse Proxy وحده هو الذي يجب أن يصل إليه، فلا يستطيع أحد أن يتجاوز شهادة TLS ويفتح Taiga عبر http://203.0.113.10:9000.
  • PUBLIC_REGISTER_ENABLED يكتب بحرف كبير False في taiga-back لأن Django يقارنه بالنص True، ويكتب بحروف صغيرة false في taiga-front لأن الواجهة تنسخ القيمة كما هي إلى ملف conf.json، وإذا وصلتها False بحرف كبير صار الملف JSON غير صالح فلا تستطيع الواجهة قراءة إعداداتها، وهذا هو الشكل الذي يطلبه توثيق Taiga لكل خدمة.
  • restart: unless-stopped يعيد تشغيل الخدمات تلقائياً بعد إعادة تشغيل السيرفر، وهو غير موجود في الملف الرسمي.

وثبت الوسم أيضاً في docker-compose-inits.yml، حتى تعمل الأوامر الإدارية بنفس الإصدار:

sudo sed -i 's|taigaio/taiga-back:latest|taigaio/taiga-back:6.10.2|' /opt/taiga-docker/docker-compose-inits.yml
💡
اخترنا PostgreSQL 16 وRabbitMQ 4.3 بدلاً من الإصدارين القديمين في الملف الرسمي لأنهما خارج نطاق الدعم الأمني، ويعمل عليهما التثبيت والترحيلات وإنشاء المشاريع والدعوات دون مشكلات، ولكن إذا كان لديك تثبيت قائم على postgres:12.3 فلا تقم بتغيير الوسم مباشرة، بل اتبع قسم التحديث.

التشغيل وإنشاء حساب المدير Admin

الآن سوف نقوم بتشغيل الخدمات كلها بالسكربت المساعد:

cd /opt/taiga-docker
sudo ./launch-taiga.sh

وفي التشغيل الأول ينفذ taiga-back ترحيلات قاعدة البيانات Migrations ويجمع الملفات الثابتة Static Files، وهذا قد يأخذ دقيقة أو اثنتين، لذلك تابع السجل حتى يظهر السطر Listening at: http://0.0.0.0:8000، وهو السطر الذي يعني أن ال API جاهز لاستقبال الطلبات:

sudo docker compose logs -f taiga-back

بعد ذلك قم بإنشاء حساب المدير superuser بالسكربت المساعد، وهو يشغل python manage.py في Container مؤقت، ويسألك عن الاسم والبريد وكلمة المرور، وعند انتهائه سوف تجد الرسالة Superuser created successfully.:

sudo ./taiga-manage.sh createsuperuser

ثم تأكد أن الواجهة وال API يعملان عبر البوابة، فالأمر الأول يجب أن يطبع 200، والثاني يجب أن يعرض الخدمات التسع كلها بحالة running:

curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:9000/api/v1/
sudo docker compose ps

ربط النطاق مع شهادة TLS

البوابة taiga-gateway تجمع المسارات كلها على المنفذ المحلي 9000، وبالتالي يكفي أن يوجه ال Reverse Proxy كل الطلبات Requests إليها، ففي Nginx Proxy Manager أنشئ Proxy Host للنطاق tasks.example.com يوجه الطلبات إلى المنفذ 9000 على السيرفر، بهذه الإعدادات:

  • فعل Websockets Support، والسبب أن المسار /events اتصال WebSocket يبقى مفتوحاً مدة طويلة، ودونه لا تصل التحديثات الفورية إلى المتصفحات.
  • اطلب شهادة Let's Encrypt مع Force SSL وHTTP/2.
  • في تبويب Advanced ضع حداً لحجم الرفع Upload Size يساوي حد البوابة وهو 100M، ومهلة Timeout طويلة لاتصالات الأحداث:
client_max_body_size 100M;
proxy_read_timeout 1d;
proxy_send_timeout 1d;

وإذا كنت تستخدم Caddy فيكفي هذا الإعداد، لأن Caddy يمرر اتصالات WebSocket دون إعداد إضافي:

tasks.example.com {
    reverse_proxy 127.0.0.1:9000
}

وبعد تفعيل HTTPS تأكد أن TAIGA_SCHEME=https وWEBSOCKETS_SCHEME=wss في ملف .env، ثم أعد إنشاء ال Containers حتى تقرأ القيم الجديدة:

cd /opt/taiga-docker
sudo docker compose up -d --force-recreate

الاستخدام الأساسي

الدخول وإنشاء أول مشروع

افتح https://tasks.example.com وادخل باسم المدير وكلمة مروره، ولاحظ أن صفحة الدخول لا تعرض رابطاً للتسجيل لأن التسجيل الذاتي معطل:

صفحة دخول Taiga
صفحة الدخول دون رابط للتسجيل، لأن PUBLIC_REGISTER_ENABLED معطل

ومن Projects ← New project تختار قالب المشروع Project Template، فاختر Scrum إذا كان فريقك يعمل بسبرنتات ويقدر المهام بالنقاط، وKanban إذا كان العمل تدفقاً مستمراً Workflow من مهام مستقلة، ويمكنك أيضاً أن تنسخ إعدادات مشروع موجود، أو تستورد مشاريعك من Trello وJira وGitHub:

اختيار قالب المشروع في Taiga
قوالب المشاريع الأربعة

وفي مثالنا اخترنا Kanban، وأدخلنا اسم المشروع ووصفه، وجعلناه خاصاً Private حتى لا يراه إلا أعضاؤه:

نموذج بيانات المشروع الجديد
بيانات المشروع، مع خيار جعله عاماً أو خاصاً

لوحة Kanban وقصص المستخدمين

البطاقة في Taiga اسمها «قصة مستخدم» User Story، وتنتقل القصة بين أعمدة الحالات Statuses وهي New وReady وIn progress وReady for test وDone، وتستطيع أن تضيف قصة بالزر + أعلى أي عمود، أو تضيف عدة قصص دفعة واحدة بالزر الذي بجانبه فيصبح كل سطر تكتبه قصة مستقلة، ثم تسحب البطاقات بين الأعمدة كلما تقدم العمل:

لوحة Kanban في Taiga وعليها قصص مستخدمين
لوحة المشروع وعليها ثماني قصص موزعة على الحالات

وعندما تفتح أي قصة تستطيع أن تضيف إليها وصفاً ومرفقات ومهام فرعية Tasks وتعليقات، ومن الصفحة نفسها تسندها Assign إلى عضو، وتضيف مراقبين Watchers يصلهم إشعار بكل تغيير، وتقدر نقاطها لكل دور من الأدوار UX وDesign وFront وBack:

تفاصيل قصة مستخدم في Taiga
صفحة القصة: الحالة، والنقاط لكل دور، والإسناد، والمهام، والتعليقات

ولاحظ أن القالب يحدد الوحدات المفعلة Modules فقط، ويمكنك أن تغيرها لاحقاً من Settings ← Project ← Modules، ففعل Scrum لتحصل على Backlog وسبرنتات، وIssues لتتبع الأخطاء Bugs، وWiki للتوثيق Documentation، وEpics لتجميع القصص الكبيرة:

إعدادات وحدات المشروع في Taiga
وحدات المشروع، وليس فيها سوى Kanban مفعلاً

ومن Settings ← Attributes تعدل أسماء الحالات وألوانها، وأنواع المشكلات وأولوياتها Priorities، بحسب طريقة عمل فريقك، ومن Settings ← Integrations تضيف Webhooks ترسل كل تغيير إلى نظام آخر، ومن هناك أيضاً تربط المشروع بمستودع في GitHub أو GitLab أو Bitbucket أو Gogs، فتتحدث القصص من رسائل ال commit.

دعوة الأعضاء Members وتحديد أدوارهم Roles

من Settings ← Members ← New member تدعو الأعضاء بالبريد وتحدد دور كل واحد منهم، والأدوار الافتراضية هي UX وDesign وFront وBack وProduct Owner وStakeholder، وصلاحيات كل دور تضبطها من Settings ← Permissions، ويمكنك أن تجعل أي عضو مديراً للمشروع:

قائمة أعضاء المشروع مع دعوة معلقة
عضوة مدعوة تبقى بحالة Pending إلى أن تقبل الدعوة

وتصل الدعوة بالبريد، ومنها ينشئ العضو حسابه حتى والتسجيل العام معطل، وهذا هو الفرق بين الدعوة والتسجيل الذاتي، والرسالة كما يعرضها Mailpit سوف تكون كما يلي:

رسالة دعوة Taiga للانضمام إلى المشروع
رسالة الدعوة إلى المشروع، وفيها زر لقبولها

من يحق له إنشاء المشاريع؟

افتراضياً يستطيع أي مستخدم أن ينشئ ما يشاء من المشاريع دون حد، والمؤسسات غالباً لا تريد ذلك، لأن كل مستخدم سوف ينشئ مشروعه الخاص وتتفرق المهام من جديد. ويمكنك أن تضع حداً لكل مستخدم من لوحة إدارة Django على https://tasks.example.com/admin/ بحساب ال superuser، فافتح Users واختر المستخدم، وفي قسم Project ownerships restrictions حدد أقصى عدد من المشاريع الخاصة والعامة التي يملكها، أو ضع 0 لتمنعه من إنشاء أي مشروع، ثم اضغط Save:

حدود ملكية المشاريع لمستخدم في لوحة إدارة Django
حدود ملكية المشاريع في لوحة الإدارة، مع إحصاءات المشاريع التي يملكها المستخدم

ولكن هذا الحد يطبق على كل مستخدم موجود وحده، وإذا أردت أن تكون القيمة الافتراضية للمستخدمين الجدد صفراً، فلا يوجد متغير بيئة Environment Variable لذلك، وإنما تستخرج ملف الإعداد الذي تستخدمه ال Image، وتضيف إليه سطرين، ثم تربطه Mount بال Containers المسماة taiga-back وtaiga-async، والربط موجود أصلاً في قسم x-volumes كسطر معلق Commented Line هو ./config.py:/taiga-back/settings/config.py. ولاحظ أن الملف المربوط يحل محل الأصلي كله، لذلك نبدأ من نسخة منه:

cd /opt/taiga-docker
sudo docker compose cp taiga-back:/taiga-back/settings/config.py ./config.py
printf "
MAX_PRIVATE_PROJECTS_PER_USER = 0
MAX_PUBLIC_PROJECTS_PER_USER = 0
" | sudo tee -a config.py

ثم أزل علامة التعليق عن السطر - ./config.py:/taiga-back/settings/config.py في docker-compose.yml، ونفذ sudo docker compose up -d، وبعده sudo docker compose restart taiga-gateway، والسبب أن البوابة تحفظ عنوان taiga-back القديم وتعيد الخطأ 502 بعد إعادة إنشائه، كما سيأتي في المشكلات الشائعة. وبعد ذلك ارفع الحد يدوياً من لوحة الإدارة لمن يحق له إنشاء المشاريع. لكن تذكر: أنشئ حساباً تجريبياً وتأكد من القيمة الافتراضية قبل أن تعتمد عليها، فهذا الإعداد لا يظهر في واجهة Taiga نفسها.

الدخول الموحد SSO (اختياري)

ال Images الرسمية لـ Taiga تدعم الدخول بحساب GitHub أو GitLab، ومنه GitLab المستضاف ذاتياً Self-hosted، وتفعله عبر متغيرات البيئة مثل ENABLE_GITLAB_AUTH وGITLAB_API_CLIENT_ID وGITLAB_URL في taiga-back، وGITLAB_CLIENT_ID وGITLAB_URL في taiga-front. ولكن انتبه إلى أن Taiga تخفي أزرار الدخول بـ GitHub وGitLab وتعطل إضافتهما ما دام التسجيل الذاتي معطلاً، فلا يعمل هذا الدخول إلا إذا جعلت PUBLIC_REGISTER_ENABLED مفعلاً في الخدمتين، وهذا يعني أن أي شخص لديه حساب على سيرفر GitLab يستطيع أن ينشئ لنفسه حساباً في Taiga، لذلك لا تفعله إلا إذا كان سيرفر GitLab خاصاً بمؤسستك.

وأما الدخول عبر OpenID Connect العام، وهو ما تحتاجه لربط Taiga بنظام مثل Authentik، فليس موجوداً في ال Images الرسمية، والإضافات المتاحة له قديمة أو يصونها أفراد وتحتاج إلى بناء Images مخصصة، لذلك لا ننصح بها في بيئة الإنتاج، والحل الأبسط إذا أردت طبقة حماية موحدة أمام Taiga هو أن تضعها خلف Authentik Forward Auth وتبقي على حسابات Taiga المحلية Local Accounts.

كيف تتأكد أن كل شيء يعمل

  • ال API يستجيب عبر النطاق، فالأمر curl -s -o /dev/null -w '%{http_code}\n' https://tasks.example.com/api/v1/ يعيد 200.
  • افتح اللوحة نفسها في متصفحين بحسابين مختلفين وحرك بطاقة في أحدهما، فيجب أن تتحرك في الآخر دون تحديث الصفحة Refresh، وإذا لم يحدث ذلك فاتصال /events لا يمر عبر ال Reverse Proxy.
  • ارفع مرفقاً إلى قصة وافتحه، ثم انسخ رابطه وافتحه من نافذة خاصة Private Window بعد ست دقائق، فيجب أن يرفض الطلب، والسبب أن صلاحية الرابط لا تتجاوز عدد الثواني في ATTACHMENTS_MAX_AGE.
  • ادع عضواً وتأكد أن الدعوة وصلته.

ال Containers التسع تعمل، وحالة taiga-db هي healthy:

cd /opt/taiga-docker
sudo docker compose ps

النسخ الاحتياطي Backup والاستعادة Restore

هناك ثلاثة أشياء يجب أن تنسخها: قاعدة PostgreSQL، وال Volume المسمى taiga-media-data وفيه المرفقات والصور الرمزية Avatars وملفات التصدير، وملفات الإعداد .env وdocker-compose.yml وtaiga-gateway/taiga.conf، وأما Volumes الخاصة بـ RabbitMQ والملفات الثابتة فلا تحتاج إلى نسخها لأن Taiga تعيد بناءها عند التشغيل. والأوامر سوف تكون كما يلي:

sudo mkdir -p /opt/backup/taiga
cd /opt/taiga-docker
sudo docker compose exec -T taiga-db pg_dump -U taiga -Fc taiga | sudo tee /opt/backup/taiga/taiga-$(date +%F).dump >/dev/null
sudo docker compose run --rm --no-deps -v /opt/backup/taiga:/backup --entrypoint sh taiga-back -c 'tar czf /backup/media-$(date +%F).tgz -C /taiga-back media'
sudo tar czf /opt/backup/taiga/config-$(date +%F).tgz .env docker-compose.yml docker-compose-inits.yml taiga-gateway

ولا تترك هذه الملفات على نفس السيرفر، لأن النسخة التي تضيع مع السيرفر ليست نسخة احتياطية، فانقلها إلى مكان آخر بعد كل تشغيل.

وأما الاستعادة على سيرفر جديد فتكون بأن تعيد ملفات الإعداد إلى /opt/taiga-docker، وتشغل قاعدة البيانات وحدها، ثم تستورد النسخة، وتعيد المرفقات، وبعدها تشغل الخدمات كلها:

cd /opt/taiga-docker
sudo docker compose up -d taiga-db
sudo docker compose exec -T taiga-db pg_restore -U taiga -d taiga --clean --if-exists --no-owner < /opt/backup/taiga/taiga-2026-09-26.dump
sudo docker compose run --rm --no-deps -v /opt/backup/taiga:/backup --entrypoint sh taiga-back -c 'tar xzf /backup/media-2026-09-26.tgz -C /taiga-back'
sudo ./launch-taiga.sh

ويمكنك أيضاً أن تصدر كل مشروع وحده بصيغة JSON من Settings ← Project ← Export، ثم تستورده في أي تثبيت Taiga آخر، وهذا مفيد لنقل مشروع بعينه، ولكنه لا يغني عن نسخ قاعدة البيانات لأنه لا يحمل المستخدمين وإعدادات السيرفر.

التحديث Upgrade إلى إصدار أحدث

خذ نسخة احتياطية أولاً، ثم غير وسوم ال Images الأربع الخاصة بـ Taiga إلى الإصدار الجديد، واسحبها، وأعد التشغيل، وانتبه إلى أن Images الأربع back وfront وevents وprotected قد تختلف أرقام إصداراتها قليلاً، فراجع صفحات الوسوم وملاحظات الإصدار. وعند الإقلاع ينفذ taiga-back الترحيلات الجديدة تلقائياً، وبعد ذلك أعد تشغيل البوابة حتى تعرف العناوين الجديدة للخدمات:

cd /opt/taiga-docker
sudo nano docker-compose.yml
sudo sed -i 's|taigaio/taiga-back:6.10.2|taigaio/taiga-back:6.10.3|' docker-compose-inits.yml
sudo docker compose pull
sudo ./launch-taiga.sh
sudo docker compose restart taiga-gateway
sudo docker compose logs -f taiga-back

والرقم 6.10.3 هنا مثال فقط، فاستخدم الإصدار الذي تريد الانتقال إليه فعلاً.

📌
في 28 أكتوبر 2025 نشر فريق Taiga تنبيهاً أمنياً للثغرة CVE-2025-62368 في taiga-back، وهي ثغرة تنفيذ أوامر عن بعد Remote Code Execution سببها فك تسلسل بيانات غير موثوقة Unsafe Deserialization في ال API، ويستطيع استغلالها أي مستخدم لديه حساب في Taiga، وصنفت حرجة Critical، وأصابت الإصدارات 6.8.3 وما قبلها حتى أصلحها الإصدار 6.9.0. والدرس هنا مزدوج، فكل حساب على السيرفر هو شخص قد يستغل ثغرة كهذه، لذلك أبق التسجيل الذاتي معطلاً، وتثبيت الإصدارات لا يعني أن تنسى التحديث، فتابع ملاحظات الإصدار والتنبيهات الأمنية وحدث عند صدورها.

وأما ترقية PostgreSQL من تثبيت قديم على postgres:12.3 إلى الإصدار 16 فلا يكفي فيها تغيير الوسم، والسبب أن ملفات البيانات غير متوافقة بين الإصدارات الرئيسية Major Versions، فإذا غيرت الوسم فقط فلن يقلع PostgreSQL الجديد على ملفات الإصدار القديم. والطريقة الآمنة هي أن تأخذ نسخة pg_dump كما في قسم النسخ الاحتياطي، وتوقف الخدمات، وتحذف Volume قاعدة البيانات القديم، وتغير الوسم، ثم تشغل taiga-db فارغة وتستعيد النسخة بالأمر pg_restore:

cd /opt/taiga-docker
sudo docker compose exec -T taiga-db pg_dump -U taiga -Fc taiga | sudo tee /opt/backup/taiga/before-pg16.dump >/dev/null
sudo docker compose down
sudo docker volume rm taiga-docker_taiga-db-data
sudo sed -i 's|postgres:12.3|postgres:16.15-alpine|' docker-compose.yml
sudo docker compose up -d taiga-db
sudo docker compose exec -T taiga-db pg_restore -U taiga -d taiga --no-owner < /opt/backup/taiga/before-pg16.dump
sudo ./launch-taiga.sh

وقبل الحذف تأكد من اسم ال Volume بالأمر docker volume ls، فاسمه يبدأ باسم المجلد الذي فيه ملف Compose.

🛑
لا تقم بحذف Volume قاعدة البيانات قبل أن تتأكد من وجود ملف النسخة وأن حجمه منطقي، ومن أنك جربت استعادته مرة على سيرفر تجريبي، والسبب أن هذه الخطوة هي الوحيدة في الدليل التي قد تفقد فيها بياناتك نهائياً إن أخطأت.

مشكلات شائعة وحلولها

dependency failed to start: taiga-db is unhealthy

فحص الصحة في الملف الرسمي يجري 5 محاولات كل ثانيتين، وهذه مدة أقصر من التهيئة الأولى لـ PostgreSQL على بعض السيرفرات، لذلك أطل مدة الفحص كما في الملف أعلاه، ثم شغل ./launch-taiga.sh مرة أخرى فتبدأ ال Containers التي لم تعمل.

التغييرات لا تظهر فوراً لدى الآخرين

السبب هنا أن اتصال WebSocket على /events لا يعمل، فتأكد أن Websockets Support مفعل في ال Reverse Proxy، وأن WEBSOCKETS_SCHEME=wss مع HTTPS، ثم راجع سجل taiga-events، فإذا ظهر فيه خطأ في الاتصال بـ RabbitMQ فتأكد أن RABBITMQ_USER وRABBITMQ_PASS متطابقان في كل الخدمات، وإذا كانت taiga-events-rabbitmq قد أنشئت أول مرة بكلمة مرور مختلفة فأعد إنشاءها بـ Volume فارغ، لأن RabbitMQ يقرأ كلمة المرور من المتغيرات عند الإنشاء الأول فقط.

المرفقات لا تفتح أو تعطي 404

تأكد أن قيمة SECRET_KEY واحدة في taiga-back وtaiga-protected، فكلاهما يقرأ القيمة نفسها من .env، وتأكد أن ال Reverse Proxy لا يعيد كتابة Rewrite المسار /media/، وتذكر أن الحجم الأقصى للمرفق يحدده client_max_body_size في البوابة وفي ال Reverse Proxy معاً.

خطأ CSRF أو تسجيل خروج Logout متكرر

يحدث هذا عندما لا تطابق قيمتا TAIGA_SCHEME وTAIGA_DOMAIN العنوان الذي يفتحه المستخدم فعلاً، مثلاً أن يفتح الموقع عبر HTTPS والإعداد http، والحل أن تصحح القيمتين ثم تعيد إنشاء ال Containers.

الدعوات لا تصل

ابحث عن أخطاء SMTP في سجلي taiga-back وtaiga-async، وتذكر أن EMAIL_BACKEND=console لا يرسل شيئاً وإنما يكتب الرسائل في السجل، وأن EMAIL_USE_TLS وEMAIL_USE_SSL لا يعملان معاً.

الخطأ 502 Bad Gateway بعد تحديث أو إعادة إنشاء خدمة

إذا أعدت إنشاء taiga-back أو taiga-front وحدها، كما يحدث بعد docker compose up -d مع ملف معدل أو بعد سحب إصدار جديد، فسوف تجد أن الموقع يعيد 502، وفي سجل taiga-gateway رسالة connect() failed (111: Connection refused) إلى عنوان IP قديم، والسبب أن nginx في البوابة يحفظ عناوين الخدمات عند إقلاعه ولا يعرف أن ال Container الجديد أخذ عنواناً آخر، والحل أن تعيد تشغيل البوابة بالأمر sudo docker compose restart taiga-gateway.

الخلاصة

وصلنا لنهاية الموضوع، وأهم ما فيه:

  • جداول Excel لا تكفي لإدارة عمل فريق، وJira وTrello يضعان بياناتك على سيرفرات غيرك، وTaiga تعطيك Scrum وKanban على سيرفرك من المستودع الرسمي taiga-docker.
  • ولد كل الأسرار في .env، وثبت إصدار كل Image بدل latest، واربط البوابة بالعنوان 127.0.0.1 خلف Reverse Proxy يدعم WebSocket.
  • أبق التسجيل الذاتي معطلاً وادع الأعضاء بالبريد، واكتب PUBLIC_REGISTER_ENABLED بحرف كبير في taiga-back وبحروف صغيرة في taiga-front.
  • انسخ قاعدة البيانات وال Volume المسمى taiga-media-data وملفات الإعداد، وانقلها خارج السيرفر.
  • تابع التنبيهات الأمنية وحدث عند صدورها، وأعد تشغيل البوابة بعد كل تحديث.

سجل التحديثات (Changelog)

  • سبتمبر 2026: كتابة الدليل واختباره على Taiga 6.10 (taiga-back 6.10.2).
  • أكتوبر 2026: مراجعة تقنية وإعادة اختبار على نفس الإصدارات، وتصحيح قيمة PUBLIC_REGISTER_ENABLED في taiga-front، وإضافة إعادة تشغيل البوابة بعد التحديث، وتصحيح قسم الدخول الموحد، وتحديث حالة صيانة Taiga 6.
نشرة عرب رووت | ArabRoot

معرفة تستحق مكاناً في بريدك.

مقالات مختارة وأدوات مفيدة وأفكار لمشروعك القادم، في رسالة واحدة كل أسبوع.

يمكنك إلغاء الاشتراك متى شئت. الخصوصية

تم استلام طلبك. افتح بريدك واضغط رابط التأكيد لإتمام الاشتراك.