تو این مطلب می خوام یکم از ارتفاع بالاتری بعد فنی نحوه ی راه اندازی وب سایت انجمن رو براتون شرح بدم. صرفا از هسته ی اصلی کار می نویسم و سعی می کنم از توضیح ریز جزئیات و چالش هایی که توی پیاده سازی feature های دیگر پروژه برامون پیش اومد، صرف نظر کنم. چالش هایی هم که توی بعد غیر فنی پیاده سازی پروژه داشتیم، نیاز به مطلب جداگانه ای داره.
برای اینکه یه وب سایت برای انجمن مون راه اندازی می کردیم، متاسفانه مشکلات و موانع بزرگی پیش رو داشتیم:
به طور خلاصه اگر امکانات رو لحاظ نمی کردیم، دو گزینه سر راست در اختیارمون بود:
اگه می خواستیم یه وب سایت کاربر محور راه اندازی کنیم، اول باید بک اند وب سایت رو خودمون می زدیم و حتما هم نیاز به سرور بود. از طرفی حتی اگه سرور در اختیار داشتیم، نگهداری و پشتیبانی بک اند وب سایت نیازمند وقت و حوصله بود و بالاخره یک جایی متوقف می شد؛ چرا چون سورس کد پروژه از لحاظ امنیتی فقط در اختیار تیم بک اند قرار می گرفت که خب پیدا کردن دانشجویی که بخواد بک اند وب سایت رو فی سبیل الله پشتیبانی کنه هم مصیبت بود.
هر سال هم با تغییر اعضای مرکزی انجمن نیاز به جلسه توجیهی بود که یه نفر از دانشجویان گروه رو پیدا کنیم، پشتیبانی بک اند وب سایت رو گردن بگیره. از اونجایی هم که از دوران کودکی با کار تیمی بزرگ شدیم، یه تشر و کنایه به بک اند کار می تونست فاتحه پروژه رو بخونه یا در حداقل ترین صورت دیگه طرف ادامه نده.
از همه ی اینها گذشته، اگه همه چیز خوب پیش می رفت یعنی هم سرور در اختیار داشتیم و هم تیم بک اند، با وجود این همه شبکه های اجتماعی در دسترس، کی اصلا حوصله ی عضویت در وب سایت انجمن به عنوان کاربر داشت؟! ملت خسته تر از این حرفان.
تو راحت ترین حالت یه وب سایت وردپرسی می تونستیم داشته باشیم با هاست رایگان یعنی سخت افزار بسیار محدود و یک ارباب به عنوان ادمین. از توضیح محدودیت های شدیدش بگذریم؛ اگه یه جایی اشتباه میشد، یکی یه جایی رو میخواست تغییر بده و یا یه چیزی پیاده کنه، باید به ارباب میگفت؛ یه روز ارباب حوصله داشت، یه روز نداشت… و خب با زیاد شدن حجم کارها، ارباب یا قید وب سایت رو میزنه یا تو بهترین حالت چند نفر رعیت رو فقط به عنوان نویسنده اضافه میکنه. آخر قصه هم از اول معلومه؛ با شیطنت دوستان خودمون که حوزه امنیت کار می کنن وب سایت دود میشد میرفت هوا. اگر هم باز همه چیز خوب پیش میرفت، به خاطر ساختار ارباب-رعیتی و دسترسی بسیار محدود، زیاد دوام نمی آورد.
اما راه اندازی یک وب سایت متن باز با قابلیت مشارکت جمعی از طریق گیتهاب به نظرم سخت ترین، پر از چالش و در عین حال خلاقانه ترین حرکتی بود که می تونستیم پیاده کنیم؛ تا نه تنها هزینه خرید سرور رو متحمل نشیم، بلکه از سیستم ارباب-رعیتی هم خلاص بشیم تا همه ی دانشجویان گروه با دسترسی بیشتری، امکان مشارکت در تمامی امور وب سایت خودشون رو داشته باشن.
احتمالا تا به حال دیدید که بیشتر پروژه های متن باز گیتهاب، خودشون یه وب سایت دارن که توضیحات پروژه و مستندات شون قابل دسترسی هستش. اما چطور؟ گیتهاب قابلیتی به نام Github Pages داره که هر repository اگر public باشه، به صورت رایگان میشه وب سایت استاتیک براش بالا آورد؛ یعنی به صورت خلاصه با هر بار تغییرات پروژه، گیتهاب میاد و پروژه شما رو داخل یک وب سرور مخصوصی که در فضای ابری قرار داره، clone می کنه و از اون برای deploy کردن وب سایت استفاده میکنه.
مثل موقعی که یه پروژه وب رو داخل وب سرور کارفرما آپلود می کردید. با Github Pages اگه یه فایل index.html داخل مسیر اصلی پروژه باشه اون رو به عنوان صفحه اصلی بالا میاره. کاربردی ترین و راحت ترین استفاده Github Pages هم برای ساخت وب سایت شخصی هستش که تنها یک سری اطلاعات قابل نمایش و دریافت به کاربر نشون داده میشه. و البته این امکان هم فراهم هست تا دامنه ای که خریدید رو به جای دامنه ی پیش فرض استفاده کنید.
خب گفتیم بریم از Github Pages به صورت مستقیم استفاده کنیم؛ ولی اینجا دوتا مشکل بزرگ داشتیم:
یعنی اصلا کی حوصله داشت داخل فایل html محتوا بنویسه، تگ های html رو رعایت کنه؟! با گذشت زمان هم لینک دادن صفحات پروژه و مدیریت شون خودش معضل بزرگی میشد. علاوه بر این در مورد Github Pages کاربر نمی تونه در خواست بفرسته و ما هم نمی تونیم بک اند داشته باشیم که درخواست رو پردازش کنیم و جواب بفرستیم. صرفا یه وب سایت استاتیک هستش که بک اند نداره کلا. یعنی کاربر فقط صفحات html رو می تونه از مرورگرش دریافت کنه.
احتمالا تا به حال متوجه شدید که اکثر پروژه ها توی گیتهاب یک فایل README.md
داخل شون هست که پروژه رو توضیح داده و موقعی که صفحه گیتهاب اون پروژه رو باز می کنید در واقع محتوای اون فایل رو برای شما به صورت html نمایش میده. اگه خود اون فایل رو با یک ادیتور باز کنید یا از طریق گیتهاب فایل خامش رو نگاه کنید، متوجه میشید که قابلیت خوانایی زیادی داره؛ با اینکه به صورت html رندر میشه، پیچیدگی های فایل html رو نداره.
فایل های markdown در واقع فایل هایی هستند که امکان پردازش و تبدیل به فایل های html دارند. یعنی چون سینتکس خیلی راحتی دارند و از طرفی قابلیت خوانایی بسیار بالاتری از html دارند، برای توضیحات پروژه و مستندسازی ازشون استفاده میشه. مثل همین مطلبی که دارید می خونید.
این شد که با یه جستجوی ساده متوجه شدیم ابزاری به نام jekyll وجود داره که با یه ساختار شبیه mvc و حتی استفاده از یه template engine به نام liquid میشه به راحتی صفحات و لینک ها رو مدیریت کنیم و در عین حال از فایل های txt و markdown برای تولید محتوای صفحات استفاده کنیم. هر دو مشکل ما حل شده بود؛ هم از یه ابزاری استفاده می کردیم که کار رو راحت تر می کرد و هم دوستان می تونستن از طریق گیتهاب محتوای صفحات و مطالب رو تغییر بدن و اصلاح کنن.
اوایل در استفاده از jekyll هر بار که کارمون رو انجام می دادیم، و خروجی کار رو به صورت لوکال می دیدیم و راضی می شدیم، می اومدیم دستور build اش رو اجرا می کردیم؛ jekyll می اومد تمام صفحات سایت مون رو داخل فولدر build تو مسیر اصلی پروژه می ساخت و ما تمام تغییرات، حتی فولدر build رو کامیت و پوش می کردیم؛ چون فولدر build لازم بود که داخل ریپوی گیتهاب حتما باشه تا وب سایت بالا بیاد. در واقع تو بخش Github Pages تعریف کرده بودیم که داخل فولدر build رو deploy کنه.
یعنی در کل به صورت دستی build می گرفتیم و تغییرات رو پوش می کردیم تا وب سایت بالا بیاد. خب اینطوری وب سایت مون کاملا ارباب-رعیتی بود؛ برای مثال در نظر داشته باشید که اگه یه نفر یه مطلبی اضافه میکرد یا یه چیزی رو تغییر میداد، تغییرات اصلا تو وب سایت مشاهده نمی شد؛ چون بیلد نگرفته بودیم. دوباره خودم باید به صورت لوکال بیلد می گرفتم و تغییرات رو پوش می کردم تا وب سایت بروز رسانی بشه. خود طرف هم اصلا چرا باید قاطی فرآیند build میشد ؟ یه مطلب اضافه کردن که این داستان ها رو نباید درست می کرد.
بعضی مواقع هم خودم فایل ها و تنظیمات پروژه رو آپدیت می کردم ولی یادم می رفت دوباره ازش build بگیریم و تغییرات اش رو پوش کنم؛ یعنی فقط تغییرات پروژه پوش میشد و فولدر بیلد ما دست نخورده می موند. اینجوری می شد که هیچ بروزرسانی از وب سایت نمی دیدیم. از اینها گذشته، هم فایل های پروژه کامیت می خورد و هم فایل های بیلد شده؛ کسی هم که می خواست کمک کنه اشتباهی می رفت مثلا استایل هایی که داخل بیلد شده ها قرار داشت عوض می کرد.
یکم که گشتیم متوجه شدیم که بیشتر توسعه دهندگان، می آیند و فرآیندهای تست، build و deploy رو موقع پوش شدن کامیت ها به یه سرور میسپارن تا انجام شون بده؛ کاری که دوستان عملیاتتوسعه(DevOps) متخصص اش هستند. توی گیتهاب این فرآیندها به کمک Github Actions انجام می شن.
پس فهمیدیم که می تونیم فرایند build رو موقع پوش شدن تغییرات به گیتهاب بسپاریم تا اتوماتیک انجامش بده؛ یعنی قبلا موقع پوش فقط عملیات deploy توسط گیتهاب انجام می شد؛ ولی به کمک یه Github Action که مختص build کردن پروژه های jekyll بود، تونستیم فرآیند build رو هم اتوماتیک کنیم. دیگه به صورت لوکال build نمی گرفتیم؛ با پوش شدن هر کامیت، Github Pages می اومد پروژه رو تو وب سرور مخصوصش clone می کرد و بعد عملیات build و deploy رو انجام می داد. دیگه حتی نیاز نبود که فولدر build رو توی تاریخچه تغییرات git داشته باشیم؛ هم حذفش کردیم و هم انداختیمش توی gitignore.
مشکل بعدی این بود که چون محتوای صفحات، مطالب وبلاگ و سورس کد پروژه همشون توی یک repository بودن، هم مدیریت pull request ها می تونست سخت بشه و هم اینکه نوشتن راهنمایی برای نحوه مشارکت سخت میشد. تو فایل راهنمای پروژه README.me اوایل می نوشتیم که اینجا جیزه، اونجا هم جیزه، اینطوری قدم بردار، اونطوری برندار و … خب طرف یه مطلب ساده برای وبلاگ می خواست بذاره، همون اول قید کار رو میزد؛ از این گذشته چرا باید اصلا کل پروژه رو clone می کرد تا بتونه مطلب بذاره یا مثلا رویداد درست کنه یا محتوای یکی از صفحات وب سایت رو تغییر بده ؟!
این شد که به فکر افتادیم یه جوری سورس کد پروژه و محتوا رو از هم جدا کنیم. خب خیلی سریع دست به کار شدیم و به ازای هر مجموعه محتوایی که داشتیم یه repository جداگانه درست کردیم. رویدادها توی یه ریپو، مطالب وبلاگ توی یه ریپوی دیگه تا الی آخر. همون فولدرها رو هم از ریپوی وب سایت حذف کردیم. خب الان یه چالش داشتیم که چطوری اون محتواها که توی ریپوهای جدا از ریپوی وب سایت بودن، بتونیم موقع بیلد دوباره داشته باشیم تا صفحه خالی بیلد نشه؟!
با یه چیزی به نام Git Module آشنا شدیم؛ می تونستیم داخل ریپوی وب سایت، ریپوهای دیگه ای رو اضافه کنیم، بدون اینکه تاریخچه ی کامل اونها اضافه بشه. صرفا فقط آی دی کامیت و محتوای اون ریپو در نظر گرفته می شد؛ نه بیشتر. انگار مثل یه پکیج از ریپوهای دیگه داخل پروژه استفاده می کردیم. ولی خب اشتباها فکر می کردم که هر Git Module به آخرین کامیت خودش اشاره می کنه و اگه کامیت جدیدی بزنیم، خودش وضعیت اش رو به آخرین کامیت تغییر میده؛ اما اینطور نبود. باز اگه کسی ریپوهای محتوا رو عوض می کرد و کامیت میزد، باید خودم به صورت دستی Git Module ها رو به آخرین وضعیت کامیت شون تغییر می دادم و این تغییر رو هم باز کامیت می کردم و پوش می کردم. یعنی عملا باز هم به مشکل ارباب-رعیتی برخورده بودیم.
تازه اگر هم Git Module مثل انتظاری که داشتیم کار می کرد، باید کلی راهنمایی می کردیم که آقا جان من موقع کامیت کردن تغییرات، کل پروژه رو نبرید رو stage؛ چون گیت می اومد فولدرهای git داخل ماژول ها رو هم track می کرد؛ یعنی یه جور track کردن تو در تو که در ادامه معضل میشد. همون قضیه جیزه و اینکارو نکن و اونکارو بکن و …
فقط یه راه مونده بود و اون هم این بود که به صورت تخصصی تر Github Actions رو یاد بگیریم تا بتونیم این معضل رو حل کنیم؛ اینکه چطور موقع بیلد بتونیم محتوا رو قبلش بیاریم. بعد از اینکه یه چیزهایی از Github Actions یاد گرفتیم، یکم گشتیم و یه اکشن ای پیدا کردیم که می تونست موقع اجرا یه اکشن دیگه از یه ریپوی دیگه رو فراخوانی کنه؛ یعنی trigger کنه تا اجرا شه. برای این کار هم نیاز به دسترسی داشت؛ یعنی از یه توکن PAT به عنوان secret استفاده می کرد که بتونه دسترسی داشته باشه تا اکشن ریپوی دیگه رو فراخوانی کنه.
اومدیم کل محتواها رو که داخل ریپو وب سایت به صورت Git Module بودن حذف کردیم؛ داخل هر کدوم از ریپوهای محتوا از اون اکشن استفاده کردیم تا بیاد و اکشن بیلد کردن وب سایت رو فراخوانی کنه. داخل اکشن بیلد وب سایت هم اومدیم قبل از شروع فرآیند build، تمام ریپوهای محتوا که لازم بودن رو clone کردیم؛ دیگه همه چیز محیا بود. فایل های اضافی رو هم قبل از بیلد حذف کردیم و اینطور شد که هر موقع کسی توی ریپوهای محتوا کامیت میزد، وب سایت هم اتوماتیک بیلد میشد و بالا می اومد.
وقتی Github Actions رو یاد گرفتیم، حتی پا رو فراتر از این هم گذاشتیم. الان هر کدوم از درس ها ریپوی جداگانه دارند؛ طوری که حالا هر موقع تغییراتی توی ریپوی وب سایت اعمال بشه، ریپوی هر درس میاد و فایل ها، تنظیمات و استایل های وب سایت رو بر میداره و صفحه ی مربوط به خودش رو میسازه. یعنی این بار برعکس نمودار بالا، ریپوی وب سایت عملیات build مربوط به هر درس رو trigger می کنه.
موفق باشید