מָבוֹא למה מונורפו? כיום, אי אפשר להכחיש את ההתפתחות המהירה של פיתוח תוכנה. הצוותים גדלים, פרויקטים נוטים להיות מורכבים יותר. חברות מוציאות משאבים משמעותיים כדי לשמור על בסיס קוד מבוזר המורכב מפרגמנטים רבים. היכנסו למונורפו - מאגר יחיד ומאוחד שמאגד את כל הקוד שלכם. רחוק מלהיות טרנד, monorepos הפכו לאחרונה לגישה אדריכלית לשכן את כל בסיס הקוד במקום אחד. צוותים משיגים שיתוף הקשר משופר, שיתוף פעולה חלק וכלי שמעודד באופן טבעי שימוש חוזר בקוד. הקמת סביבות עבודה של חוט הערה: בכל מאמר זה, בכל פעם ש"Yarn" מוזכר, הוא מתייחס ספציפית ל-Yarn v4 - הגרסה העדכנית ביותר המציעה יכולות משופרות וביצועים משופרים. מהם חללי עבודה של חוט? חללי עבודה הם החבילות של המונורפו, הנקראות לעתים קרובות חבילות. הם עוזרים לך לנהל מספר חבילות במאגר אחד ללא מאמץ. עם חללי עבודה. אתה יכול: שתף תלות בקלות: שתף תלות משותפת בפרויקט שלך בצורה חלקה. פשט את ניהול התלות: Yarn מקשר אוטומטית חבילות מקומיות, מפחית כפילות ומקל על פיתוח. האץ התקנות: תיהנו ממיטוב הביצועים ומנגנוני האחסון של Yarn (כלומר, ). plug'n'play מובנה שפר את השליטה על Monorepo: הגדירו (כללים) והשתמשו כדי לשמור על עקביות. אילוצים בעשרות תוספים זמינים בעוד Yarn הוא המנהל הנבחר למאמר זה בזכות הפשטות, המהירות ואפשרויות התצורה הנרחבות שלו - חשוב לציין שהבחירה הנכונה תלויה בצרכים הספציפיים של הפרויקט שלך, בהעדפות הצוות ובזרימת העבודה הכוללת. לדוגמה, ו- הם כלים מודרניים אחרים המציעים מגוון רחב של תכונות. PNPM Turborepo תצורה ראשונית התקנת חוט היא תהליך פשוט. עקוב אחר המדריך הרשמי להתקנה והגדרה של Yarn בפרויקט שלך: . מדריך התקנת חוטים לאחר שתסיים את ההתקנה, בוא נעבור לתצורה. מכיוון שאנו משתמשים ב-plug'n'play, עליך לוודא שה-IDE שלך מזהה כראוי תלות. אם אתה משתמש ב- VSCode, הרץ: # Typescript is required for VSCode SDK to set up correctly yarn add -D typescript@^5 yarn dlx @yarnpkg/sdks vscode אם אתה משתמש בעורך קוד אחר, בדוק את ערכות ה-SDK הזמינות כאן: . ערכות SDK של Yarn Editor בשלב זה, אתה מוכן להתחיל להשתמש ב-Yarn. ארגון מבנה מונורפו כעת לאחר שמנהל החבילות מוגדר, הגיע הזמן לעצב ארגון פרויקט שניתן להרחבה. מבנה ברור ומוגדר היטב לא רק מקל על הניווט במאגר אלא גם מקדם שימוש חוזר טוב יותר בקוד. בדוגמה זו, נחלק את בסיס הקוד לשלוש קטגוריות עיקריות: : אפליקציות לקוח: מכיל את מוצרי הלקוח הסופיים הניתנים לפריסה. שרת: מכיל את מוצרי השרת הסופיים הניתנים לפריסה. : תכונות לקוח: עבור ווידג'טים של ממשק משתמש עצמאיים. שרת: עבור חלקי לוגיקה עסקיים עצמאיים. : ליבס בתים קוד משותף כגון עיצוב רכיבי מערכת, קבועים, נכסים וכלי עזר. זהו האזור נטול הקשר לאחסון לוגיקה לשימוש חוזר. כדי להדגים את העוצמה של מבנה התיקיות הזה, נתחיל בהוספת התיקיות העיקריות הללו לרשימת סביבות העבודה של Yarn. ב-root package.json שלך, הוסף את הדברים הבאים: "workspaces": [ "apps/**", "features/**", "libs/**" ] תצורה זו אומרת ל-Yarn להתייחס לחבילות בתיקיות אלו כאל חבילות מקומיות. התקנות עוקבות יבטיחו שהתלות של כל חבילה מוגדרת ומקושרת כהלכה. Bootstrapping Codebase בסעיף זה, נעבור על דוגמה מינימלית של בסיס קוד שממחישה כיצד לאתחל את המונורפו. במקום לכלול קטעי קוד מלאים, אספק דוגמאות קצרות עם קישורים לקבצים המלאים . במאגר שנוצר במיוחד עבור מאמר זה יישום שרת אתחול אנו מתחילים עם פשוט לאימות משתמשים. יישום שרת זה חושף נקודת קצה אחת ( ) המשתמשת במטפל מחבילה אחרת. Express API /auth/signIn import express from "express"; import cors from "cors"; import { signInHandler } from "@robust-monorepo-yarn-nx-changesets/sign-in-handler"; const app = express(); const port = process.env.PORT || 1234; app.use(express.json()); app.use( cors({ origin: process.env.CORS_ORIGIN || "http://localhost:3000", }) ); app.post("/auth/signIn", signInHandler); app.listen(port, () => { console.log(`Server is running at http://localhost:${port}`); }); קישור לחבילה כפי שאתה יכול לראות, נקודת הקצה משתמשת במטפל מיובא מחבילה אחרת. זה מביא אותנו לרכיב הבא שלנו: תכונת השרת. /auth/signIn תכונת שרת אתחול תכונת השרת מקפלת את היגיון האימות. בחבילה זו, אנו מגדירים את מטפל הכניסה, הממנף כלי אימות משותף מה-libs. import type { RequestHandler } from "express"; import { passwordValidator, usernameValidator, } from "@robust-monorepo-yarn-nx-changesets/validator"; const signInHandler: RequestHandler = (req, res) => { if (!req.body) { res.status(422).send("Request body is missing"); return; } if (typeof req.body !== "object") { res.status(422).send("Request body expected to be an object"); return; } const { username, password } = req.body; const usernameValidationResult = usernameValidator(username); if (typeof usernameValidationResult === "string") { res .status(422) .send("Invalid username format: " + usernameValidationResult); return; } const passwordValidationResult = passwordValidator(password); if (typeof passwordValidationResult === "string") { res .status(422) .send("Invalid password format: " + passwordValidationResult); return; } // Emulate a successful sign-in if (username === "test" && password === "test1234") { res.status(200).send("Sign in successful"); return; } return res.status(422).send("Username or password is incorrect"); }; export default signInHandler; קישור לחבילה גישה זו מסכמת את היגיון האימות בתוך החבילה שלה, ומאפשרת לפתח ולתחזק אותו באופן עצמאי. שים לב כיצד כלי האימות מיובאים מה- . lib המשותף יישום לקוח אתחול לאחר מכן, בואו נסתכל על צד הלקוח. באפליקציית הלקוח שלנו, אנו בונים אתר אינטרנט פשוט המאפשר אימות משתמשים על ידי הפעלת ה-API של השרת. "use client"; import { SignInForm } from "@robust-monorepo-yarn-nx-changesets/sign-in-form"; const API_URL = process.env.NEXT_PUBLIC_API_URL || "http://localhost:1234"; export default function Home() { const handleSubmit = async (username: string, password: string) => { const response = await fetch(`${API_URL}/auth/signIn`, { method: "POST", body: JSON.stringify({ username, password }), headers: { "Content-Type": "application/json", }, }); if (response.status === 200) { alert("Sign in successful"); return; } if (response.status === 422) { alert("Sign in failed: " + (await response.text())); return; } alert("Sign in failed"); }; return ( <div className="w-full h-screen overflow-hidden flex items-center justify-center"> <SignInForm onSubmit={handleSubmit} /> </div> ); } קישור לחבילה בדוגמה זו, רכיב מיובא מחבילת תכונות לקוח, מה שמוביל אותנו לרכיב הסופי שלנו. SignInForm תכונת לקוח אתחול חבילת תכונות הלקוח מספקת את טופס האימות יחד עם היגיון האימות המשותף. זה מונע שכפול קוד ומבטיח עקביות. import { passwordValidator, usernameValidator, } from "@robust-monorepo-yarn-nx-changesets/validator"; interface SignInFormProps { onSubmit: (username: string, password: string) => void; } const SignInForm = ({ onSubmit }: SignInFormProps) => { const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => { event.preventDefault(); const username = (event.currentTarget[0] as HTMLInputElement).value; const usernameValidationResult = usernameValidator(username); if (typeof usernameValidationResult === "string") { alert(usernameValidationResult); return; } const password = (event.currentTarget[1] as HTMLInputElement).value; const passwordValidationResult = passwordValidator(password); if (typeof passwordValidationResult === "string") { alert(passwordValidationResult); return; } onSubmit(username!, password!); }; return ( <form onSubmit={handleSubmit}> <input type="text" placeholder="Username" /> <input type="password" placeholder="Password" /> <button type="submit">Submit</button> </form> ); }; export default SignInForm; קישור לחבילה כאן, אנו שוב רואים את השימוש מהספרות המשותפות שלנו, מה שמבטיח שהלוגיקת האימות מרוכזת ומתוחזקת בקלות. במאמת זהו הדוגמה של בסיס הקוד המינימלי שלנו. זכור כי קוד זה הוא איור מפושט שנועד להדגים את המבנה הבסיסי ואת החיבור בין אפליקציות, תכונות ו-Libs ב-monrepo. אתה יכול להרחיב על דוגמאות אלה לפי הצורך כדי להתאים לדרישות הספציפיות של הפרויקט שלך. הפעלת סקריפטים עם NX ניהול סקריפטים במונורפו יכול להיות מאתגר. בעוד Yarn מאפשר לך תוך שימוש בתנאים שונים, זה עשוי לדרוש סקריפטים מותאם אישית לשליטה מפורטת יותר. כאן נכנס NX לתמונה: הוא מספק פתרון מחוץ לקופסה לביצוע סקריפט יעיל וממוקד. להריץ סקריפטים על פני מספר חבילות מבוא ל-NX NX היא מערכת בנייה מותאמת עבור monorepos עם יכולות CI מתקדמות. עם NX, אתה יכול: : נצל במקביל כדי להאיץ את הבנייה שלך. הפעל משימות ביעילות במקביל : הבן קשרים בין חבילות וסקריפטים. זיהוי יחסי תלות : הימנע מעבודה מיותרת על ידי שמירת פלטים במטמון. תוצאות ביצוע סקריפט מטמון הרחב את הפונקציונליות באמצעות . התאם אישית את ההתנהגות באמצעות תוספים: מערכת אקולוגית עשירה של תוספים ביצוע סקריפט ממוקד כדי לרתום את היכולות של NX, תחילה עלינו ליצור קובץ כדי להגדיר מערכת כללים עבור הסקריפטים שלנו. להלן דוגמה לתצורה: nx.json { "targetDefaults": { "build": { "dependsOn": [ "^build" ], "outputs": [ "{projectRoot}/dist" ], "cache": true }, "typecheck": { "dependsOn": [ "^build", "^typecheck" ] }, "lint": { "dependsOn": [ "^build", "^lint" ] } }, "defaultBase": "main" } באנגלית פשוטה, משמעות התצורה הזו היא: לִבנוֹת סקריפט עבור חבילה תלוי בבנייה המוצלחת של התלות שלה, והפלט שלה שמור במטמון. build בדיקת סוג סקריפט עבור חבילה תלוי הן בסקריפט הבנייה והן בסקריפט בדיקת הסוג של התלות שלה. typecheck מוֹך סקריפט עבור חבילה תלוי הן בסקריפט ה-build והן בסקריפט ה-lint של התלות שלה. lint כעת, בואו נוסיף סקריפטים ל- : package.json "scripts": { "build:all": "yarn nx run-many -t build", "build:affected": "yarn nx affected -t build --base=${BASE:-origin/main} --head=${HEAD:-HEAD}", "typecheck:all": "yarn nx run-many -t typecheck", "typecheck:affected": "yarn nx affected -t typecheck --base=${BASE:-origin/main} --head=${HEAD:-HEAD}", "lint:all": "yarn nx run-many -t lint", "lint:affected": "yarn nx affected -t lint --base=${BASE:-origin/main} --head=${HEAD:-HEAD}", "quality:all": "yarn nx run-many --targets=typecheck,lint", "quality:affected": "yarn nx affected --targets=typecheck,lint --base=${BASE:-origin/main} --head=${HEAD:-HEAD}" } כאן, אנו מגדירים ארבעה סוגים של סקריפטים לביצוע: בונה חבילה. build: בודק את סוגי החבילה. typecheck: מוך חבילה. מוך: מפעיל גם בדיקת סוג וגם מוך.' איכות: לכל סקריפט שתי גרסאות: מריץ את הסקריפט בכל החבילות. all: מפעיל את הסקריפט רק על חבילות שהושפעו מהשינויים האחרונים. משתני הסביבה ו- מאפשרים לך לציין טווח (ברירת המחדל למקור וה- הנוכחי), המאפשרים ביצוע מפורט בבקשות משיכה. זה יכול לחסוך באופן משמעותי זמן ומשאבים. מושפע: BASE HEAD origin/main HEAD ניהול תלות מעגלית NX מספקת גם ליצירת גרף תלות, שיכול לעזור בזיהוי מחזורי תלות. הסקריפט הבא משתמש בפלט גרף NX כדי לבדוק תלות מעגלית ונכשל אם נמצאו כאלה. פקודה מובנית צור קובץ בכתובת עם התוכן הבא: scripts/check-circulardeps.mjs import { execSync } from "child_process"; import path from "path"; import fs from "fs"; const hasCycle = (node, graph, visited, stack, path) => { if (!visited.has(node)) { visited.add(node); stack.add(node); path.push(node); const dependencies = graph.dependencies[node] || []; for (const dep of dependencies) { const depNode = dep.target; if ( !visited.has(depNode) && hasCycle(depNode, graph, visited, stack, path) ) { return true; } if (stack.has(depNode)) { path.push(depNode); return true; } } } stack.delete(node); path.pop(); return false; }; const getGraph = () => { const cwd = process.cwd(); const tempOutputFilePath = path.join(cwd, "nx-graph.json"); execSync(`nx graph --file=${tempOutputFilePath}`, { encoding: "utf-8", }); const output = fs.readFileSync(tempOutputFilePath, "utf-8"); fs.rmSync(tempOutputFilePath); return JSON.parse(output).graph; }; const checkCircularDeps = () => { const graph = getGraph(); const visited = new Set(); const stack = new Set(); for (const node of Object.keys(graph.dependencies)) { const path = []; if (hasCycle(node, graph, visited, stack, path)) { console.error("🔴 Circular dependency detected:", path.join(" → ")); process.exit(1); } } console.log("✅ No circular dependencies detected."); }; checkCircularDeps(); התסריט הזה: מבצע את פקודת NX ליצירת גרף תלות. קורא את הגרף מקובץ JSON זמני. בודק באופן רקורסיבי עבור מחזורים. רושם שגיאה ויוצא אם מזוהה תלות מעגלית. אימות תלות עם אילוצי חוט ככל שפרויקטים גדלים, שמירה על עקביות על פני תלות הופכת למאתגרת. אכיפת כללים נוקשים סביב תלות, גרסאות צומת ותצורות אחרות חיונית כדי למנוע חובות טכניים מיותרים. אילוצי חוט מציעים דרך להפוך את האימותים הללו לאוטומטיים. הבנת אילוצי חוט אילוצי חוט הם מערכת הכללים עבור חבילות ב-monorepo שלך. יתרון משמעותי בשימוש בהם הוא שאתה המנהל של הכללים הללו. לדוגמה, אתה יכול ליצור כלל כדי לאלץ את כל החבילות להשתמש באותה גרסת React. לאחר הגדרתו, לעולם לא תתקל בבעיה כאשר יישום מארח אינו יכול להשתמש בתכונה/lib עם גרסת React גבוהה יותר. בעוד שהעברה של מונורופו גדול לגרסה עיקרית חדשה של תלות עשויה להיות מורכבת, השימוש באילוצים מביא בסופו של דבר לעקביות ויציבות לכל הפרויקט. אכיפת עקביות במאגר לדוגמה שלנו, אנו משתמשים בקובץ כדי לאכוף עקביות עבור: yarn.config.cjs גרסת הצומת גרסת חוט גרסאות התלות כדי לאפשר גמישות במהלך מעברים, אתה יכול להגדיר אי הכללות כדי לעקוף זמנית בדיקות מסוימות. לְמָשָׁל: const workspaceCheckExclusions = []; const dependencyCheckExclusions = []; הקבועים האלה מאפשרים לך לא לכלול סביבות עבודה או תלות ספציפיים מתהליך האימות, ומבטיחים העברות חלקות בעת הצורך. ניהול גירסאות עם ערכות שינויים בעיה נוספת שאתה עלול להתמודד עם צמיחת המאגר היא ניהול ושחרור הגרסאות. ערכות שינויים מספקות פתרון אלגנטי לאוטומציה של תהליך זה, ומבטיחות שכל שינוי יהיה במעקב, גרסה ושחרור. מבוא ל-Changesets הוא כלי קוד פתוח שנועד לנהל ניהול גרסאות במאגרי monorepo. זה מפשט את תהליך המעקב אחר השינויים על ידי הקצאתם למסמכים קטנים הניתנים לקריאה על ידי אדם, המתעדים את כוונת השינוי. מסמכים אלו נקראים שינויים. היתרונות העיקריים כוללים: Changesets תיעוד ברור כל ערכת שינויים מתארת את השינויים שבוצעו, מה שעוזר הן למפתחים והן לצרכנים להבין למה לצפות במהדורה חדשה. בקרת גרסאות מפורטת כל חבילה מנוסחת באופן עצמאי, מה שמבטיח שרק החבילות המושפעות מתעדכנות. זה ממזער את הסיכון למכות גרסה ריקות והפסקות תלות. ידידותי לשיתוף פעולה מכיוון שכל שינוי מתועד באמצעות ערכת שינויים, צוותים יכולים לבדוק ולאשר עדכונים לפני ההפצה בפועל. אוטומציה של מהדורות אחת התכונות החזקות ביותר של Changesets היא היכולת להפוך את התהליך לאוטומטי. אתה יכול לשלב Changesets בצנרת ה-CI/CD שלך ולשכוח משינויי גרסאות ידניים ופרסום NPM. תסתכל על זרימת העבודה במאגר לדוגמה. יש לו שלב . הצעד המגובה בפעולת GitHub יוצר את כל הקסם. אתה רק צריך להגדיר לפרסום החבילות שלך. לאחר מכן, כל דחיפה לסניף תעשה: release.yaml create-release-pull-request-or-publish שינויים/פעולת NPM_TOKEN main . בדוק אם יש מסמכי Changeset אם קיימים מסמכי ערכת שינויים, הפעולה יוצרת בקשת משיכה עם בליטות הגרסה הדרושות ועדכוני יומן השינויים. אם לא מזוהים שינויים, שום דבר לא קורה. . בדוק אם יש חבילות מוכנות לפרסום אם החבילות מוכנות לשחרור, הפעולה מפרסמת את הגרסאות החדשות ל-NPM באמצעות שסופק. אם אין חבילות מוכנות לפרסום, הפעולה יוצאת מבלי לבצע שינויים. NPM_TOKEN על ידי אוטומציה של משימות אלו, Changesets מבטיחים שהגרסאות שלך יהיו עקביות ואמינות, מצמצמות את הפוטנציאל לטעויות אנוש ומייעלות את זרימת העבודה בפיתוח שלך. שילוב זרימת עבודה עם פעולות GitHub חלק זה מתעמק כיצד לשחרר את כוחה של הארכיטקטורה שזה עתה בנינו. באמצעות GitHub Actions, נהפוך בדיקות איכות יחסי ציבור לאוטומטיות, שחרור גרסאות לספריות ותכונות ופריסות יישומים. ההתמקדות היא במקסום אוטומציה תוך שמירה על איכות הקוד ופירוט העבודה. בדוק את איכות יחסי הציבור כדי להבטיח שקוד בקשת המשיכה יישאר עקבי ויציב, אנו יוצרים זרימת עבודה ייעודית של . זרימת עבודה זו מבצעת מספר משימות, כגון להבטיח שלא יוצגו שינויים ידניים בגרסה (מכיוון שניהול הגרסה מנוהל על ידי Changesets): quality.yaml - id: check_version name: Check version changes run: | BASE_BRANCH=${{ github.event.pull_request.base.ref }} git fetch origin $BASE_BRANCH CHANGED_FILES=$(git diff --name-only origin/$BASE_BRANCH HEAD) VERSION_CHANGED=false for FILE in $CHANGED_FILES; do if [[ $FILE == */package.json ]]; then if [ -f "$FILE" ]; then HEAD_VERSION=$(grep '"version":' "$FILE" | awk -F '"' '{print $4}') else continue fi HEAD_VERSION=$(cat $FILE | grep '"version":' | awk -F '"' '{print $4}') if git cat-file -e origin/$BASE_BRANCH:$FILE 2>/dev/null; then BASE_VERSION=$(git show origin/$BASE_BRANCH:$FILE | grep '"version":' | awk -F '"' '{print $4}') else BASE_VERSION=$HEAD_VERSION fi if [ "$BASE_VERSION" != "$HEAD_VERSION" ]; then VERSION_CHANGED=true echo "Version change detected in $FILE" fi fi done if [ "$VERSION_CHANGED" = true ]; then echo "Manual version changes are prohibited. Use changesets instead." exit 1 fi env: GITHUB_REF: ${{ github.ref }} לצד בדיקה זו, עבודת מתקינה תלות, מאמתת אילוצים, בודקת תלות מעגלית ומאמתת את איכות הקוד הכוללת באמצעות הסקריפט שהגדרנו קודם לכן עם NX: check-quality - id: install-dependencies name: Install dependencies run: yarn --immutable - id: check-constraints name: Check constraints run: yarn constraints - id: check-circulardeps name: Check circular dependencies run: yarn check-circulardeps:all - id: check-quality name: Check quality run: BASE=origin/${{ github.event.pull_request.base.ref }} yarn quality:affected בדיקת האיכות נועדה לפעול רק על החבילות המושפעות מבקשת המשיכה הנוכחית. סיום מוצלח של עבודות אלו מעיד על כך שבקשת המשיכה מוכנה להתמזג (בנוסף לקבלת ביקורות קוד). אם נדרשות בדיקות נוספות עבור הפרויקט שלך, תוכל לעדכן ואת הסקריפט האיכותי שלך כדי לשמור על זרימת העבודה ללא שינוי. nx.json פרסם ספריות ותכונות לאחר מיזוג PR, זרימת העבודה של השחרור (כמתואר בפרק Changesets) מופעלת. זרימת עבודה זו בונה את החבילות המושפעות ויוצרת יחסי ציבור עם בליטות הגרסה. לאחר אישור ואיחוד יחסי ציבור זה, פועל שוב - הפעם, במקום ליצור PR, הוא מזהה שינויים בגרסה ומשחרר את החבילות המעודכנות ל-NPM: release.yaml - id: build-packages name: Build packages run: yarn build:affected - id: create-release-pull-request-or-publish name: Create Release Pull Request or Publish to NPM uses: changesets/action@v1 with: version: yarn changeset version publish: yarn release commit: "chore: publish new release" title: "chore: publish new release" env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }} release-apps: needs: release-libs-features uses: ./.github/workflows/release-apps.yaml with: publishedPackages: ${{ needs.release-libs-features.outputs.publishedPackages }} לאחר מכן, מבוצעת עבודה בשם , אשר אחראית על פריסות יישומים. הוא מקבל רשימה של חבילות שפורסמו מהשלב הקודם ומביא אותנו לפרק הבא. release-apps פרסם אפליקציות החלק האחרון של תהליך השחרור כולל פריסת היישומים שלך (אפליקציות אינן מתפרסמות ב-NPM, מכיוון שהן מוגדרות ב- ). זרימת העבודה מופעלת אוטומטית על ידי , או שניתן לבצעה ישירות מהכרטיסייה פעולות ב-GitHub: private package.json release-apps.yaml release.yaml name: Release Apps on: workflow_call: inputs: publishedPackages: description: "List of published packages" required: false type: string default: "[]" workflow_dispatch: inputs: publishedPackages: description: "List of published packages (optional)" required: false type: string default: "[]" זרימת עבודה זו מקבלת קלט כדי לקבוע אילו חבילות פורסמו. באמצעות אסטרטגיית מטריצה, הוא בודק כל יישום של המטריצה עבור נוכחות של תלות שפורסמה: publishedPackages - id: check-dependency-published name: Check if any app dependency is published run: | PUBLISHED_PACKAGES="${{ inputs.publishedPackages }}" PACKAGE_NAME="${{ matrix.package }}" APP="${{ matrix.app }}" DEPENDENCIES=$(jq -r '.dependencies // {} | keys[]' "apps/$APP/package.json") for DEP in $DEPENDENCIES; do if echo "$PUBLISHED_PACKAGES" | grep -w "$DEP"; then echo "published=true" >> $GITHUB_OUTPUT exit 0 fi done echo "published=false" >> $GITHUB_OUTPUT בדיקה זו היא תנאי אחד לתחילת פריסת אפליקציה. התנאי הנוסף מבטיח שגרסת האפליקציה שונתה (מה שמציין שיש צורך בפריסה מחדש גם אם לא עודכנו תלות): - id: check-version-change name: Check if app version has changed run: | APP="${{ matrix.app }}" PACKAGE_JSON_PATH="apps/$APP/package.json" CURRENT_VERSION=$(jq -r '.version' "$PACKAGE_JSON_PATH") PREVIOUS_VERSION=$(git show HEAD~1:"$PACKAGE_JSON_PATH" | jq -r '.version' || echo "") if [[ "$CURRENT_VERSION" == "$PREVIOUS_VERSION" ]]; then echo "changed=false" >> $GITHUB_OUTPUT else echo "changed=true" >> $GITHUB_OUTPUT fi לבסוף, לאחר אישור שלאפליקציה יש תלות מעודכנת או שהגרסה שלה השתנתה, זרימת העבודה מאחזרת את הגרסה החדשה וממשיכה לבנות ולפרוס את האפליקציה: - id: set-up-docker name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - id: get-app-version name: Get the app version from package.json run: echo "app-version=$(cat ./apps/${{ matrix.app }}/package.json | jq -r '.version')" >> $GITHUB_OUTPUT - id: build-image name: Build image if: steps.check-dependency-published.outputs.published == 'true' || steps.check-version-change.outputs.changed == 'true' uses: docker/build-push-action@v4 with: build-contexts: | workspace=./ context: "./apps/${{ matrix.app }}" load: true push: false tags: | ${{ matrix.app }}:v${{ steps.get-app-version.outputs.app-version }} בדוגמה זו, אנו בונים את תמונת Docker מבלי לדחוף אותה לרישום. בזרימת העבודה של הייצור, החלף שלב זה בתהליך הפריסה בפועל. מַסְקָנָה תקציר של שיטות עבודה מומלצות לאורך המאמר הזה, בדקנו את ההגדרה של מונורפואי חזק ואת הכלים שעוזרים לנהל אותו ביעילות. על ידי ריכוז בסיס הקוד שלך, אתה לא רק מפשט את ניהול התלות אלא גם מייעל את שיתוף הפעולה בין צוותים. הדגמנו כיצד ניתן למנף את Yarn לשיתוף תלות, להאיץ את ההתקנה עם PnP ולשפר את עקביות הפרויקט הכוללת. בנוסף, שילוב NX לביצוע סקריפט ממוקד מבטיח שה-CI מהיר ויעיל. ערכות שינויים עזרו לאוטומציה של ניהול גרסאות, צמצום שגיאות ידניות וייעול מהדורות. לבסוף, יצרנו צינור CI/CD מוכן לייצור עם פעולות GitHub שמבצעת רק את המשימות הדרושות. השלבים הבאים : התחל בהקמת מונורפואו בקנה מידה קטן כדי לבדוק את השיטות המומלצות הללו. נסה עם מבני תיקיות שונים, והרחיב בהדרגה כדי לכלול חבילות נוספות ככל שהביטחון שלך גדל. ניסוי והתאם : שקול לשלב כלים משלימים כמו PNPM או Turborepo בהתבסס על הדרישות הייחודיות של הפרויקט שלך והעדפות הצוות. שלב כלים נוספים : כוונן את זרימות העבודה של GitHub Actions שלך כדי לכלול בדיקות איכות נוספות, כיסוי קוד וסריקות אבטחה המותאמות לפרויקט שלך. שפר את צינורות ה-CI/CD : הישאר מעודכן עם המהדורות האחרונות של Yarn, NX ו-Changesets. צור קשר עם הקהילה כדי לחלוק תובנות וללמוד על מגמות מתפתחות בניהול מונורפו. קהילה ועדכונים אֶמְצָעִי : מאגר לדוגמה גש למאגר הדוגמה המלא שנוצר עבור מדריך זה. חקור את מבנה הפרויקט, דוגמאות קוד ותסריטים המציגים את התקנת monorepo בפעולה. : חבילות NPM שפורסמו בדוק את חבילת NPM בפועל שפורסמה כחלק מהפרויקט הזה. חבילות אלו מדגימות שימוש ויישום בעולם האמיתי של המושגים הנדונים במאמר.