[{"data":1,"prerenderedAt":21530},["ShallowReactive",2],{"navigation_docs":3,"-web-development-testing-documentation-deployment-08-cicd-pipelines":5074,"-web-development-testing-documentation-deployment-08-cicd-pipelines-surround":21527},[4,1707,2040,2512,2693,2819,3026,3148,3198,3255,3289,3415,3492,3506,3645,3939,4016,4749,4924,5017,5070],{"title":5,"icon":6,"path":7,"stem":8,"children":9},"C#","i-devicon-csharp","\u002Fcsharp","01.csharp",[10,13,60,90,120,202,219,253,379,404,457,650,1364,1654,1703],{"title":11,"path":7,"stem":12},"C# та .NET","01.csharp\u002Findex",{"title":14,"icon":15,"path":16,"stem":17,"children":18,"page":59},"Fundamentals","i-lucide-book-open","\u002Fcsharp\u002Ffundamentals","01.csharp\u002F01.fundamentals",[19,23,27,31,35,39,43,47,51,55],{"title":20,"path":21,"stem":22},"Вступ до екосистеми .NET","\u002Fcsharp\u002Ffundamentals\u002Fintroduction-to-ecosystem","01.csharp\u002F01.fundamentals\u002F01.introduction-to-ecosystem",{"title":24,"path":25,"stem":26},"Структура програми на C#","\u002Fcsharp\u002Ffundamentals\u002Fprogram-structure","01.csharp\u002F01.fundamentals\u002F02.program-structure",{"title":28,"path":29,"stem":30},"Змінні та Типи Даних","\u002Fcsharp\u002Ffundamentals\u002Fvariables-data-types","01.csharp\u002F01.fundamentals\u002F03.variables-data-types",{"title":32,"path":33,"stem":34},"Масиви","\u002Fcsharp\u002Ffundamentals\u002Farrays","01.csharp\u002F01.fundamentals\u002F04.arrays",{"title":36,"path":37,"stem":38},"Strings & Text Handling","\u002Fcsharp\u002Ffundamentals\u002Fstrings-text-handling","01.csharp\u002F01.fundamentals\u002F05.strings-text-handling",{"title":40,"path":41,"stem":42},"Дати і Час","\u002Fcsharp\u002Ffundamentals\u002Fdates-time-handling","01.csharp\u002F01.fundamentals\u002F06.dates-time-handling",{"title":44,"path":45,"stem":46},"Потік Керування","\u002Fcsharp\u002Ffundamentals\u002Fcontrol-flow","01.csharp\u002F01.fundamentals\u002F07.control-flow",{"title":48,"path":49,"stem":50},"Методи","\u002Fcsharp\u002Ffundamentals\u002Fmethods","01.csharp\u002F01.fundamentals\u002F08.methods",{"title":52,"path":53,"stem":54},"Основи Відлагодження","\u002Fcsharp\u002Ffundamentals\u002Fdebugging-basics","01.csharp\u002F01.fundamentals\u002F09.debugging-basics",{"title":56,"path":57,"stem":58},"Інтерактивна Консоль (Classic)","\u002Fcsharp\u002Ffundamentals\u002Finteractive-console","01.csharp\u002F01.fundamentals\u002F10.interactive-console",false,{"title":61,"icon":62,"path":63,"stem":64,"children":65,"page":59},"OOP","i-lucide-box","\u002Fcsharp\u002Foop","01.csharp\u002F02.oop",[66,70,74,78,82,86],{"title":67,"path":68,"stem":69},"Package Management (Управління Пакетами)","\u002Fcsharp\u002Foop\u002Fpackage-management","01.csharp\u002F02.oop\u002F01.package-management",{"title":71,"path":72,"stem":73},"Класи та Об'єкти","\u002Fcsharp\u002Foop\u002Fclasses-objects","01.csharp\u002F02.oop\u002F02.classes-objects",{"title":75,"path":76,"stem":77},"Властивості та Поля","\u002Fcsharp\u002Foop\u002Fproperties-fields","01.csharp\u002F02.oop\u002F03.properties-fields",{"title":79,"path":80,"stem":81},"Стовпи ООП","\u002Fcsharp\u002Foop\u002Foop-pillars","01.csharp\u002F02.oop\u002F04.oop-pillars",{"title":83,"path":84,"stem":85},"Advanced Types","\u002Fcsharp\u002Foop\u002Fadvanced-types","01.csharp\u002F02.oop\u002F05.advanced-types",{"title":87,"path":88,"stem":89},"Namespaces (Простори Імен)","\u002Fcsharp\u002Foop\u002Fnamespaces","01.csharp\u002F02.oop\u002F06.namespaces",{"title":91,"icon":92,"path":93,"stem":94,"children":95,"page":59},"Advanced Core","i-lucide-zap","\u002Fcsharp\u002Fadvanced-core","01.csharp\u002F03.advanced-core",[96,100,104,108,112,116],{"title":97,"path":98,"stem":99},"Generics (Узагальнення)","\u002Fcsharp\u002Fadvanced-core\u002Fgenerics","01.csharp\u002F03.advanced-core\u002F01.generics",{"title":101,"path":102,"stem":103},"Делегати, Події та Лямбда-вирази","\u002Fcsharp\u002Fadvanced-core\u002Fdelegates-events-lambdas","01.csharp\u002F03.advanced-core\u002F02.delegates-events-lambdas",{"title":105,"path":106,"stem":107},"Interfaces Deep Dive (Інтерфейси: Поглиблений Розгляд)","\u002Fcsharp\u002Fadvanced-core\u002Finterfaces-deep-dive","01.csharp\u002F03.advanced-core\u002F03.interfaces-deep-dive",{"title":109,"path":110,"stem":111},"Обробка Винятків","\u002Fcsharp\u002Fadvanced-core\u002Fexception-handling","01.csharp\u002F03.advanced-core\u002F04.exception-handling",{"title":113,"path":114,"stem":115},"Pattern Matching","\u002Fcsharp\u002Fadvanced-core\u002Fpattern-matching","01.csharp\u002F03.advanced-core\u002F05.pattern-matching",{"title":117,"path":118,"stem":119},"Додаткові Можливості C#","\u002Fcsharp\u002Fadvanced-core\u002Fadditional-features","01.csharp\u002F03.advanced-core\u002F06.additional-features",{"title":121,"icon":122,"path":123,"stem":124,"children":125,"page":59},"Architecture Best Practices","i-lucide-building-2","\u002Fcsharp\u002Farchitecture-best-practices","01.csharp\u002F04.architecture-best-practices",[126,130,149,153,157,161,165,169],{"title":127,"path":128,"stem":129},"Software Design Principles (Частина 1)","\u002Fcsharp\u002Farchitecture-best-practices\u002Fsoftware-design-principles","01.csharp\u002F04.architecture-best-practices\u002F01.software-design-principles",{"title":131,"icon":132,"path":133,"stem":134,"children":135,"page":59},"Design Patterns","i-lucide-folder","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdesign-patterns","01.csharp\u002F04.architecture-best-practices\u002F02.design-patterns",[136],{"title":137,"icon":132,"path":138,"stem":139,"children":140,"page":59},"Creational","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdesign-patterns\u002Fcreational","01.csharp\u002F04.architecture-best-practices\u002F02.design-patterns\u002Fcreational",[141,145],{"title":142,"path":143,"stem":144},"Singleton (Одинак)","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdesign-patterns\u002Fcreational\u002Fsingleton","01.csharp\u002F04.architecture-best-practices\u002F02.design-patterns\u002Fcreational\u002F01.singleton",{"title":146,"path":147,"stem":148},"Builder (Будівельник)","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdesign-patterns\u002Fcreational\u002Fbuilder","01.csharp\u002F04.architecture-best-practices\u002F02.design-patterns\u002Fcreational\u002F02.builder",{"title":150,"path":151,"stem":152},"Building Professional CLIs","\u002Fcsharp\u002Farchitecture-best-practices\u002Fbuilding-professional-clis","01.csharp\u002F04.architecture-best-practices\u002F03.building-professional-clis",{"title":154,"path":155,"stem":156},"Validation & Flow Control","\u002Fcsharp\u002Farchitecture-best-practices\u002Fvalidation-flow-control","01.csharp\u002F04.architecture-best-practices\u002F04.validation-flow-control",{"title":158,"path":159,"stem":160},"The Modern .NET Host (Microsoft.Extensions)","\u002Fcsharp\u002Farchitecture-best-practices\u002Fmodern-dotnet-host","01.csharp\u002F04.architecture-best-practices\u002F05.modern-dotnet-host",{"title":162,"path":163,"stem":164},"Data Mapper: Repository та DAO патерни (Частина 1)","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdata-mapper-part1","01.csharp\u002F04.architecture-best-practices\u002F06.data-mapper-part1",{"title":166,"path":167,"stem":168},"Data Mapper: Repository та DAO патерни (Частина 2)","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdata-mapper-part2","01.csharp\u002F04.architecture-best-practices\u002F07.data-mapper-part2",{"title":170,"icon":132,"path":171,"stem":172,"children":173,"page":59},"Di Ioc","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc",[174,178,182,186,190,194,198],{"title":175,"path":176,"stem":177},"Проблема залежностей та Інверсія Контролю","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc\u002Fthe-dependency-problem","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc\u002F01.the-dependency-problem",{"title":179,"path":180,"stem":181},"Будуємо власний Service Container","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc\u002Fbuild-your-own-container","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc\u002F02.build-your-own-container",{"title":183,"path":184,"stem":185},"Service Locator: Паттерн та Анти-паттерн","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc\u002Fservice-locator-pattern","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc\u002F03.service-locator-pattern",{"title":187,"path":188,"stem":189},"Паттерни Dependency Injection","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc\u002Fdependency-injection-patterns","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc\u002F04.dependency-injection-patterns",{"title":191,"path":192,"stem":193},"Microsoft DI: IServiceCollection та IServiceProvider","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc\u002Fmicrosoft-di-deep-dive","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc\u002F05.microsoft-di-deep-dive",{"title":195,"path":196,"stem":197},"Service Lifetimes та Scopes","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc\u002Fservice-lifetimes-and-scopes","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc\u002F06.service-lifetimes-and-scopes",{"title":199,"path":200,"stem":201},"DI Анти-паттерни та Найкращі Практики","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc\u002Fdi-anti-patterns-and-best-practices","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc\u002F07.di-anti-patterns-and-best-practices",{"title":203,"icon":132,"path":204,"stem":205,"children":206,"page":59},"Standard Library","\u002Fcsharp\u002Fstandard-library","01.csharp\u002F05.standard-library",[207,211,215],{"title":208,"path":209,"stem":210},"Collections (Колекції)","\u002Fcsharp\u002Fstandard-library\u002Fcollections","01.csharp\u002F05.standard-library\u002F01.collections",{"title":212,"path":213,"stem":214},"High Performance Types (Високопродуктивні Типи)","\u002Fcsharp\u002Fstandard-library\u002Fhigh-performance-types","01.csharp\u002F05.standard-library\u002F02.high-performance-types",{"title":216,"path":217,"stem":218},"LINQ (Language Integrated Query)","\u002Fcsharp\u002Fstandard-library\u002Flinq","01.csharp\u002F05.standard-library\u002F03.linq",{"title":220,"icon":221,"path":222,"stem":223,"children":224,"page":59},"System Internals Concurrency","i-lucide-server","\u002Fcsharp\u002Fsystem-internals-concurrency","01.csharp\u002F06.system-internals-concurrency",[225,229,233,237,241,245,249],{"title":226,"path":227,"stem":228},"Memory Management","\u002Fcsharp\u002Fsystem-internals-concurrency\u002Fmemory-management","01.csharp\u002F06.system-internals-concurrency\u002F01.memory-management",{"title":230,"path":231,"stem":232},"Reflection API: System.Type та Метадані","\u002Fcsharp\u002Fsystem-internals-concurrency\u002Freflection-fundamentals","01.csharp\u002F06.system-internals-concurrency\u002F02.reflection-fundamentals",{"title":234,"path":235,"stem":236},"Attributes та Dynamic Language Runtime","\u002Fcsharp\u002Fsystem-internals-concurrency\u002Fattributes-dynamic","01.csharp\u002F06.system-internals-concurrency\u002F03.attributes-dynamic",{"title":238,"path":239,"stem":240},"Expression Trees: Швидка Альтернатива Рефлексії","\u002Fcsharp\u002Fsystem-internals-concurrency\u002Fexpression-trees-compiled","01.csharp\u002F06.system-internals-concurrency\u002F04.expression-trees-compiled",{"title":242,"path":243,"stem":244},"Source Generators: Compile-Time Code Generation","\u002Fcsharp\u002Fsystem-internals-concurrency\u002Fsource-generators","01.csharp\u002F06.system-internals-concurrency\u002F05.source-generators",{"title":246,"path":247,"stem":248},"Multithreading Fundamentals","\u002Fcsharp\u002Fsystem-internals-concurrency\u002Fmultithreading-fundamentals","01.csharp\u002F06.system-internals-concurrency\u002F06.multithreading-fundamentals",{"title":250,"path":251,"stem":252},"Synchronization Primitives","\u002Fcsharp\u002Fsystem-internals-concurrency\u002Fsynchronization-primitives","01.csharp\u002F06.system-internals-concurrency\u002F07.synchronization-primitives",{"title":254,"icon":255,"path":256,"stem":257,"children":258,"page":59},"System Programming Windows","i-lucide-cpu","\u002Fcsharp\u002Fsystem-programming-windows","01.csharp\u002F07.system-programming-windows",[259,263,267,271,275,279,283,287,291,295,299,303,307,311,315,319,323,327,331,335,339,343,347,351,355,359,363,367,371,375],{"title":260,"path":261,"stem":262},"Як Працює Операційна Система","\u002Fcsharp\u002Fsystem-programming-windows\u002Fhow-os-works","01.csharp\u002F07.system-programming-windows\u002F01.how-os-works",{"title":264,"path":265,"stem":266},"Процеси в .NET — API та Запуск","\u002Fcsharp\u002Fsystem-programming-windows\u002Fprocesses-in-dotnet","01.csharp\u002F07.system-programming-windows\u002F02.processes-in-dotnet",{"title":268,"path":269,"stem":270},"Процеси в .NET — IPC та Міжпроцесна Комунікація","\u002Fcsharp\u002Fsystem-programming-windows\u002F02a.processes-ipc","01.csharp\u002F07.system-programming-windows\u002F02a.processes-ipc",{"title":272,"path":273,"stem":274},"Application Domains та Збірки — AppDomain і AssemblyLoadContext","\u002Fcsharp\u002Fsystem-programming-windows\u002Fappdomains-assemblies","01.csharp\u002F07.system-programming-windows\u002F03.appdomains-assemblies",{"title":276,"path":277,"stem":278},"Application Domains та Збірки — Plug-in Система з Hot-Reload","\u002Fcsharp\u002Fsystem-programming-windows\u002F03a.appdomains-plugin-system","01.csharp\u002F07.system-programming-windows\u002F03a.appdomains-plugin-system",{"title":280,"path":281,"stem":282},"Потоки — Основи та API Thread","\u002Fcsharp\u002Fsystem-programming-windows\u002Fthread-fundamentals","01.csharp\u002F07.system-programming-windows\u002F04.thread-fundamentals",{"title":284,"path":285,"stem":286},"Потоки — Lifecycle, Пріоритети та Безпечне Завершення","\u002Fcsharp\u002Fsystem-programming-windows\u002F04a.thread-lifecycle-priorities","01.csharp\u002F07.system-programming-windows\u002F04a.thread-lifecycle-priorities",{"title":288,"path":289,"stem":290},"Проблеми Спільного Стану — Race Condition та Data Race","\u002Fcsharp\u002Fsystem-programming-windows\u002Fshared-state-problems","01.csharp\u002F07.system-programming-windows\u002F05.shared-state-problems",{"title":292,"path":293,"stem":294},"Проблеми Спільного Стану — Memory Model та volatile","\u002Fcsharp\u002Fsystem-programming-windows\u002F05a.shared-state-memory-model","01.csharp\u002F07.system-programming-windows\u002F05a.shared-state-memory-model",{"title":296,"path":297,"stem":298},"Синхронізація — Monitor, lock та еволюція примітивів","\u002Fcsharp\u002Fsystem-programming-windows\u002Fsynchronization-fundamentals","01.csharp\u002F07.system-programming-windows\u002F06.synchronization-fundamentals",{"title":300,"path":301,"stem":302},"Синхронізація — Наскрізний Приклад та Deadlock Detection","\u002Fcsharp\u002Fsystem-programming-windows\u002F06a.synchronization-walkthrough","01.csharp\u002F07.system-programming-windows\u002F06a.synchronization-walkthrough",{"title":304,"path":305,"stem":306},"Синхронізація — Mutex, Semaphore та Event-Based Primitives","\u002Fcsharp\u002Fsystem-programming-windows\u002Fsynchronization-advanced","01.csharp\u002F07.system-programming-windows\u002F07.synchronization-advanced",{"title":308,"path":309,"stem":310},"Синхронізація — Interlocked, Volatile та Lock-Free Структури","\u002Fcsharp\u002Fsystem-programming-windows\u002F07a.synchronization-advanced-walkthrough","01.csharp\u002F07.system-programming-windows\u002F07a.synchronization-advanced-walkthrough",{"title":312,"path":313,"stem":314},"Interlocked, CAS та Lock-Free Структури","\u002Fcsharp\u002Fsystem-programming-windows\u002Finterlocked-cas-lockfree","01.csharp\u002F07.system-programming-windows\u002F08.interlocked-cas-lockfree",{"title":316,"path":317,"stem":318},"Volatile, Memory Model та Spinning","\u002Fcsharp\u002Fsystem-programming-windows\u002F08a.volatile-memory-model","01.csharp\u002F07.system-programming-windows\u002F08a.volatile-memory-model",{"title":320,"path":321,"stem":322},"ThreadPool — Пул Потоків для Ефективного Виконання","\u002Fcsharp\u002Fsystem-programming-windows\u002Fthread-pool","01.csharp\u002F07.system-programming-windows\u002F09.thread-pool",{"title":324,"path":325,"stem":326},"ThreadPool — Просунуті Сценарії та Внутрішня Будова","\u002Fcsharp\u002Fsystem-programming-windows\u002F09a.thread-pool-advanced","01.csharp\u002F07.system-programming-windows\u002F09a.thread-pool-advanced",{"title":328,"path":329,"stem":330},"Concurrent та Immutable Collections","\u002Fcsharp\u002Fsystem-programming-windows\u002Fconcurrent-collections","01.csharp\u002F07.system-programming-windows\u002F10.concurrent-collections",{"title":332,"path":333,"stem":334},"TPL, Task та Композиція — Від Thread до Task","\u002Fcsharp\u002Fsystem-programming-windows\u002Ftpl-parallel-plinq","01.csharp\u002F07.system-programming-windows\u002F11.tpl-parallel-plinq",{"title":336,"path":337,"stem":338},"Parallel Class та PLINQ — Data Parallelism","\u002Fcsharp\u002Fsystem-programming-windows\u002F11a.tpl-parallel-plinq-advanced","01.csharp\u002F07.system-programming-windows\u002F11a.tpl-parallel-plinq-advanced",{"title":340,"path":341,"stem":342},"Async\u002FAwait — Фундамент Асинхронного Програмування","\u002Fcsharp\u002Fsystem-programming-windows\u002Fasync-fundamentals","01.csharp\u002F07.system-programming-windows\u002F12.async-fundamentals",{"title":344,"path":345,"stem":346},"SynchronizationContext та ConfigureAwait — Контекст Виконання","\u002Fcsharp\u002Fsystem-programming-windows\u002Fasync-context-configureawait","01.csharp\u002F07.system-programming-windows\u002F13.async-context-configureawait",{"title":348,"path":349,"stem":350},"Async — Просунуті Паттерни","\u002Fcsharp\u002Fsystem-programming-windows\u002Fasync-advanced","01.csharp\u002F07.system-programming-windows\u002F14.async-advanced",{"title":352,"path":353,"stem":354},"System.Threading.Channels — Async Producer-Consumer","\u002Fcsharp\u002Fsystem-programming-windows\u002Fchannels","01.csharp\u002F07.system-programming-windows\u002F15.channels",{"title":356,"path":357,"stem":358},"Асинхронна Синхронізація","\u002Fcsharp\u002Fsystem-programming-windows\u002Fasync-synchronization","01.csharp\u002F07.system-programming-windows\u002F16.async-synchronization",{"title":360,"path":361,"stem":362},"Unsafe Code та Вказівники","\u002Fcsharp\u002Fsystem-programming-windows\u002Funsafe-code","01.csharp\u002F07.system-programming-windows\u002F17.unsafe-code",{"title":364,"path":365,"stem":366},"P\u002FInvoke та Windows API — Міст між .NET та Native Code","\u002Fcsharp\u002Fsystem-programming-windows\u002Fpinvoke-winapi","01.csharp\u002F07.system-programming-windows\u002F18.pinvoke-winapi",{"title":368,"path":369,"stem":370},"Реєстр Windows — Центральна База Конфігурації Системи","\u002Fcsharp\u002Fsystem-programming-windows\u002Fwindows-registry","01.csharp\u002F07.system-programming-windows\u002F19.windows-registry",{"title":372,"path":373,"stem":374},"Windows Hooks, Hotkeys та Services — Глибока Інтеграція з ОС","\u002Fcsharp\u002Fsystem-programming-windows\u002Fwindows-hooks-services","01.csharp\u002F07.system-programming-windows\u002F20.windows-hooks-services",{"title":376,"path":377,"stem":378},"Системне Програмування C# (Windows) — 07.system-programming-windows","\u002Fcsharp\u002Fsystem-programming-windows\u002Fimplementation_plan","01.csharp\u002F07.system-programming-windows\u002Fimplementation_plan",{"title":380,"icon":132,"path":381,"stem":382,"children":383,"page":59},"Io","\u002Fcsharp\u002Fio","01.csharp\u002F08.io",[384,388,392,396,400],{"title":385,"path":386,"stem":387},"8.1.1. Основи роботи з файловою системою","\u002Fcsharp\u002Fio\u002Ffile-system-basics","01.csharp\u002F08.io\u002F01.file-system-basics",{"title":389,"path":390,"stem":391},"8.1.2. Потоки (Streams) та Серіалізація Даних","\u002Fcsharp\u002Fio\u002Fstreams-serialization","01.csharp\u002F08.io\u002F02.streams-serialization",{"title":393,"path":394,"stem":395},"8.2.1. JSON Serialization з System.Text.Json","\u002Fcsharp\u002Fio\u002Fjson-serialization","01.csharp\u002F08.io\u002F03.json-serialization",{"title":397,"path":398,"stem":399},"8.2.2. XML Serialization та LINQ to XML","\u002Fcsharp\u002Fio\u002Fxml-serialization","01.csharp\u002F08.io\u002F04.xml-serialization",{"title":401,"path":402,"stem":403},"8.2.3. Binary Serialization: MessagePack та Protocol Buffers","\u002Fcsharp\u002Fio\u002Fbinary-serialization","01.csharp\u002F08.io\u002F05.binary-serialization",{"title":405,"icon":132,"path":406,"stem":407,"children":408,"page":59},"Ado Net","\u002Fcsharp\u002Fado-net","01.csharp\u002F09.ado-net",[409,413,417,421,425,429,433,437,441,445,449,453],{"title":410,"path":411,"stem":412},"9.1. Введення в ADO.NET","\u002Fcsharp\u002Fado-net\u002Fintroduction-to-adonet","01.csharp\u002F09.ado-net\u002F01.introduction-to-adonet",{"title":414,"path":415,"stem":416},"9.2. Клас DbConnection — з'єднання з базою даних","\u002Fcsharp\u002Fado-net\u002Fconnection","01.csharp\u002F09.ado-net\u002F02.connection",{"title":418,"path":419,"stem":420},"9.3. Клас DbCommand — виконання SQL-запитів","\u002Fcsharp\u002Fado-net\u002Fcommand-and-queries","01.csharp\u002F09.ado-net\u002F03.command-and-queries",{"title":422,"path":423,"stem":424},"9.4. Клас DbDataReader — ефективне читання даних","\u002Fcsharp\u002Fado-net\u002Fdatareader","01.csharp\u002F09.ado-net\u002F04.datareader",{"title":426,"path":427,"stem":428},"9.5. Параметризовані запити та захист від SQL Injection","\u002Fcsharp\u002Fado-net\u002Fparameters-and-sql-injection","01.csharp\u002F09.ado-net\u002F05.parameters-and-sql-injection",{"title":430,"path":431,"stem":432},"9.6. Транзакції в ADO.NET","\u002Fcsharp\u002Fado-net\u002Ftransactions","01.csharp\u002F09.ado-net\u002F06.transactions",{"title":434,"path":435,"stem":436},"9.7. DbProviderFactory — провайдер-незалежний код","\u002Fcsharp\u002Fado-net\u002Fprovider-factory","01.csharp\u002F09.ado-net\u002F07.provider-factory",{"title":438,"path":439,"stem":440},"9.8. Асинхронний доступ до даних","\u002Fcsharp\u002Fado-net\u002Fasync-data-access","01.csharp\u002F09.ado-net\u002F08.async-data-access",{"title":442,"path":443,"stem":444},"9.9. Від'єднаний режим: DataSet, DataTable, DataRow","\u002Fcsharp\u002Fado-net\u002Fdisconnected-mode-dataset","01.csharp\u002F09.ado-net\u002F09.disconnected-mode-dataset",{"title":446,"path":447,"stem":448},"9.10. DataAdapter — міст між DataSet та базою даних","\u002Fcsharp\u002Fado-net\u002Fdata-adapter","01.csharp\u002F09.ado-net\u002F10.data-adapter",{"title":450,"path":451,"stem":452},"9.11. Data Mapper та Repository: Архітектура доступу до даних","\u002Fcsharp\u002Fado-net\u002Fdata-mapper-repository","01.csharp\u002F09.ado-net\u002F11.data-mapper-repository",{"title":454,"path":455,"stem":456},"9.12. Identity Map, Unit of Work та Specification Pattern","\u002Fcsharp\u002Fado-net\u002Fadvanced-patterns","01.csharp\u002F09.ado-net\u002F12.advanced-patterns",{"title":458,"icon":255,"path":459,"stem":460,"children":461,"page":59},"Ef Core","\u002Fcsharp\u002Fef-core","01.csharp\u002F10.ef-core",[462,466,470,474,478,482,486,490,494,498,502,506,510,514,518,522,526,532,538,542,546,550,554,558,562,566,570,574,578,582,586,590,594,598,602,606,610,614,618,622,626,630,634,638,642,646],{"title":463,"path":464,"stem":465},"Що таке ORM? Від SQL до об'єктів","\u002Fcsharp\u002Fef-core\u002Fwhat-is-orm","01.csharp\u002F10.ef-core\u002F01.what-is-orm",{"title":467,"path":468,"stem":469},"Перший проєкт — від нуля до CRUD","\u002Fcsharp\u002Fef-core\u002Ffirst-project","01.csharp\u002F10.ef-core\u002F02.first-project",{"title":471,"path":472,"stem":473},"DbContext — Серце EF Core","\u002Fcsharp\u002Fef-core\u002Fdbcontext-deep-dive","01.csharp\u002F10.ef-core\u002F03.dbcontext-deep-dive",{"title":475,"path":476,"stem":477},"Провайдери баз даних — Архітектура та Вибір СУБД","\u002Fcsharp\u002Fef-core\u002Fdatabase-providers","01.csharp\u002F10.ef-core\u002F04.database-providers",{"title":479,"path":480,"stem":481},"Конвенції EF Core — Магія без конфігурації","\u002Fcsharp\u002Fef-core\u002Fconventions","01.csharp\u002F10.ef-core\u002F05.conventions",{"title":483,"path":484,"stem":485},"Fluent API та Data Annotations — Явна конфігурація моделі","\u002Fcsharp\u002Fef-core\u002Ffluent-api-vs-annotations","01.csharp\u002F10.ef-core\u002F06.fluent-api-vs-annotations",{"title":487,"path":488,"stem":489},"Зв'язки — One-to-One та One-to-Many","\u002Fcsharp\u002Fef-core\u002Frelationships-basics","01.csharp\u002F10.ef-core\u002F07.relationships-basics",{"title":491,"path":492,"stem":493},"Зв'язки Advanced — Many-to-Many та Складні Сценарії","\u002Fcsharp\u002Fef-core\u002Frelationships-advanced","01.csharp\u002F10.ef-core\u002F08.relationships-advanced",{"title":495,"path":496,"stem":497},"Властивості — Типи, Конвертери, Компаратори (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fproperty-configuration-part1","01.csharp\u002F10.ef-core\u002F09.property-configuration-part1",{"title":499,"path":500,"stem":501},"Властивості — Value Comparers, Generators, Shadow Properties (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fproperty-configuration-part2","01.csharp\u002F10.ef-core\u002F09.property-configuration-part2",{"title":503,"path":504,"stem":505},"Складні типи — Owned Types та Complex Types (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fcomplex-types-owned-part1","01.csharp\u002F10.ef-core\u002F10.complex-types-owned-part1",{"title":507,"path":508,"stem":509},"Складні типи — Complex Types, Keyless Entities, Порівняння (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fcomplex-types-owned-part2","01.csharp\u002F10.ef-core\u002F10.complex-types-owned-part2",{"title":511,"path":512,"stem":513},"JSON Columns — Складні дані у JSON (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fjson-columns-part1","01.csharp\u002F10.ef-core\u002F11.json-columns-part1",{"title":515,"path":516,"stem":517},"JSON Columns — Value Comparers, Індекси, Провайдери (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fjson-columns-part2","01.csharp\u002F10.ef-core\u002F11.json-columns-part2",{"title":519,"path":520,"stem":521},"Успадкування — Абстрактні класи та TPH (Частина 1)","\u002Fcsharp\u002Fef-core\u002Finheritance-part1","01.csharp\u002F10.ef-core\u002F12.inheritance-part1",{"title":523,"path":524,"stem":525},"Успадкування — TPT, TPC та Порівняння Стратегій (Частина 2)","\u002Fcsharp\u002Fef-core\u002Finheritance-part2","01.csharp\u002F10.ef-core\u002F12.inheritance-part2",{"title":527,"path":528,"stem":529,"children":530},"Індекси, Обмеження та Схема (Частина 1)","\u002Fcsharp\u002Fef-core\u002Findexes-constraints-part1","01.csharp\u002F10.ef-core\u002F13.indexes-constraints-part1",[531],{"title":527,"path":528,"stem":529},{"title":533,"path":534,"stem":535,"children":536},"Індекси, Обмеження та Схема (Частина 2)","\u002Fcsharp\u002Fef-core\u002Findexes-constraints-part2","01.csharp\u002F10.ef-core\u002F13.indexes-constraints-part2",[537],{"title":533,"path":534,"stem":535},{"title":539,"path":540,"stem":541},"Seed Data — Початкові Дані (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fseeding-part1","01.csharp\u002F10.ef-core\u002F14.seeding-part1",{"title":543,"path":544,"stem":545},"Seed Data — SQL-скрипти, Bogus та Стратегії (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fseeding-part2","01.csharp\u002F10.ef-core\u002F14.seeding-part2",{"title":547,"path":548,"stem":549},"Global Query Filters — Глобальні Фільтри (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fglobal-query-filters-part1","01.csharp\u002F10.ef-core\u002F15.global-query-filters-part1",{"title":551,"path":552,"stem":553},"Global Query Filters — Підводні камені та Інтеграція (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fglobal-query-filters-part2","01.csharp\u002F10.ef-core\u002F15.global-query-filters-part2",{"title":555,"path":556,"stem":557},"LINQ-запити в EF Core (Частина 1)","\u002Fcsharp\u002Fef-core\u002Flinq-queries-part1","01.csharp\u002F10.ef-core\u002F16.linq-queries-part1",{"title":559,"path":560,"stem":561},"LINQ-запити в EF Core (Частина 2)","\u002Fcsharp\u002Fef-core\u002Flinq-queries-part2","01.csharp\u002F10.ef-core\u002F16.linq-queries-part2",{"title":563,"path":564,"stem":565},"Завантаження Пов'язаних Даних (Частина 1)","\u002Fcsharp\u002Fef-core\u002Floading-related-data-part1","01.csharp\u002F10.ef-core\u002F17.loading-related-data-part1",{"title":567,"path":568,"stem":569},"Завантаження Пов'язаних Даних (Частина 2)","\u002Fcsharp\u002Fef-core\u002Floading-related-data-part2","01.csharp\u002F10.ef-core\u002F17.loading-related-data-part2",{"title":571,"path":572,"stem":573},"Raw SQL, Views та Stored Procedures (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fraw-sql-part1","01.csharp\u002F10.ef-core\u002F18.raw-sql-part1",{"title":575,"path":576,"stem":577},"Raw SQL — Stored Procedures, DbFunction та Bulk Operations (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fraw-sql-part2","01.csharp\u002F10.ef-core\u002F18.raw-sql-part2",{"title":579,"path":580,"stem":581},"Продвинуті Запити — Compiled Queries, Bulk та Оптимізація (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fadvanced-queries-part1","01.csharp\u002F10.ef-core\u002F19.advanced-queries-part1",{"title":583,"path":584,"stem":585},"Продвинуті Запити — Query Tags, Bulk та Interceptors (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fadvanced-queries-part2","01.csharp\u002F10.ef-core\u002F19.advanced-queries-part2",{"title":587,"path":588,"stem":589},"Change Tracker — Відстеження Змін (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fchange-tracking-part1","01.csharp\u002F10.ef-core\u002F20.change-tracking-part1",{"title":591,"path":592,"stem":593},"Change Tracker — Графи Об'єктів та Disconnected (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fchange-tracking-part2","01.csharp\u002F10.ef-core\u002F20.change-tracking-part2",{"title":595,"path":596,"stem":597},"Збереження Даних та Транзакції (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fsaving-data-part1","01.csharp\u002F10.ef-core\u002F21.saving-data-part1",{"title":599,"path":600,"stem":601},"Збереження Даних — Concurrency та Outbox (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fsaving-data-part2","01.csharp\u002F10.ef-core\u002F21.saving-data-part2",{"title":603,"path":604,"stem":605},"Конкурентність та Блокування (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fconcurrency-part1","01.csharp\u002F10.ef-core\u002F22.concurrency-part1",{"title":607,"path":608,"stem":609},"Конкурентність — Дедлоки та Queue Processing (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fconcurrency-part2","01.csharp\u002F10.ef-core\u002F22.concurrency-part2",{"title":611,"path":612,"stem":613},"Міграції в EF Core — Основи (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fmigrations-basics-part1","01.csharp\u002F10.ef-core\u002F23.migrations-basics-part1",{"title":615,"path":616,"stem":617},"Міграції в EF Core — Основи (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fmigrations-basics-part2","01.csharp\u002F10.ef-core\u002F23.migrations-basics-part2",{"title":619,"path":620,"stem":621},"Міграції — Просунуті Сценарії (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fmigrations-advanced-part1","01.csharp\u002F10.ef-core\u002F24.migrations-advanced-part1",{"title":623,"path":624,"stem":625},"Міграції — Просунуті Сценарії (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fmigrations-advanced-part2","01.csharp\u002F10.ef-core\u002F24.migrations-advanced-part2",{"title":627,"path":628,"stem":629},"Управління Схемою та Database-First (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fschema-management-part1","01.csharp\u002F10.ef-core\u002F25.schema-management-part1",{"title":631,"path":632,"stem":633},"Управління Схемою та Database-First (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fschema-management-part2","01.csharp\u002F10.ef-core\u002F25.schema-management-part2",{"title":635,"path":636,"stem":637},"Продуктивність EF Core — Основи (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fperformance-fundamentals-part1","01.csharp\u002F10.ef-core\u002F26.performance-fundamentals-part1",{"title":639,"path":640,"stem":641},"Interceptors в EF Core (Частина 1)","\u002Fcsharp\u002Fef-core\u002Finterceptors-part1","01.csharp\u002F10.ef-core\u002F29.interceptors-part1",{"title":643,"path":644,"stem":645},"Interceptors в EF Core — Connection, Transaction та Materialization (Частина 2)","\u002Fcsharp\u002Fef-core\u002Finterceptors-part2","01.csharp\u002F10.ef-core\u002F29.interceptors-part2",{"title":647,"path":648,"stem":649},"План вивчення Entity Framework Core — Повний курс","\u002Fcsharp\u002Fef-core\u002Fimplementation_plan","01.csharp\u002F10.ef-core\u002Fimplementation_plan",{"title":651,"icon":652,"path":653,"stem":654,"children":655,"page":59},"ASP.NET","i-devicon-dotnetcore","\u002Fcsharp\u002Faspnet","01.csharp\u002F11.aspnet",[656,730,791,869,927,941,967,1057,1111,1182,1212,1289,1346],{"title":657,"icon":658,"path":659,"stem":660,"children":661,"page":59},"Minimal API","i-lucide-network","\u002Fcsharp\u002Faspnet\u002Fminimal-api","01.csharp\u002F11.aspnet\u002F01.minimal-api",[662,666,670,674,678,682,686,690,694,698,702,706,710,714,718,722,726],{"title":663,"path":664,"stem":665},"Вступ до ASP.NET та еволюція фреймворку","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fintroduction","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F01.introduction",{"title":667,"path":668,"stem":669},"Перший додаток на ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Ffirst-application","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F02.first-application",{"title":671,"path":672,"stem":673},"WebApplication, Builder та Dependency Injection","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fwebapplication-builder","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F03.webapplication-builder",{"title":675,"path":676,"stem":677},"Конвеєр запитів та Middleware","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Frequest-pipeline-middleware","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F04.request-pipeline-middleware",{"title":679,"path":680,"stem":681},"Маршрутизація в ASP.NET Core: Основи","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Frouting-basics","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F05.routing-basics",{"title":683,"path":684,"stem":685},"Маршрутизація в ASP.NET Core: Розширені можливості","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Frouting-advanced","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F06.routing-advanced",{"title":687,"path":688,"stem":689},"Статичні файли в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fstatic-files","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F07.static-files",{"title":691,"path":692,"stem":693},"Статичні Активи: MapStaticAssets (ASP.NET Core 9.0)","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fstatic-assets","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F08.static-assets",{"title":695,"path":696,"stem":697},"Конфігурація в ASP.NET Core: Основи","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fconfiguration-fundamentals","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F09.configuration-fundamentals",{"title":699,"path":700,"stem":701},"Конфігурація: Паттерн Options","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fconfiguration-options","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F10.configuration-options",{"title":703,"path":704,"stem":705},"Логування в ASP.NET Core: Основи","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Flogging-basics","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F11.logging-basics",{"title":707,"path":708,"stem":709},"Логування: Serilog та Middleware","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Flogging-advanced","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F12.logging-advanced",{"title":711,"path":712,"stem":713},"Управління станом: HttpContext.Items та Cookies","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fstate-management","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F13.state-management",{"title":715,"path":716,"stem":717},"Стан сесії: Sessions","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fsession-state","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F14.session-state",{"title":719,"path":720,"stem":721},"Структура проєкту: від хаосу до архітектури","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fproject-structure","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F15.project-structure",{"title":723,"path":724,"stem":725},"Scalar у Minimal API: повний проєкт і Fluent OpenAPI","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fscalar-openapi-fluent","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F16.scalar-openapi-fluent",{"title":727,"path":728,"stem":729},"Swagger \u002F Swashbuckle у Minimal API: окремий класичний шлях","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fswagger-swashbuckle","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F17.swagger-swashbuckle",{"title":731,"icon":658,"path":732,"stem":733,"children":734,"page":59},"API","\u002Fcsharp\u002Faspnet\u002Fapi","01.csharp\u002F11.aspnet\u002F02.api",[735,739,743,747,751,755,759,763,767,771,775,779,783,787],{"title":736,"path":737,"stem":738},"Що таке API. Клієнт-серверна архітектура","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fwhat-is-api","01.csharp\u002F11.aspnet\u002F02.api\u002F01.what-is-api",{"title":740,"path":741,"stem":742},"Формати даних: JSON, XML, TOML та бінарні формати","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fdata-formats","01.csharp\u002F11.aspnet\u002F02.api\u002F02.data-formats",{"title":744,"path":745,"stem":746},"Парадигми API та концепція REST","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fapi-paradigms-rest","01.csharp\u002F11.aspnet\u002F02.api\u002F03.api-paradigms-rest",{"title":748,"path":749,"stem":750},"HTTP-методи, статус-коди та заголовки","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fhttp-methods-status-codes","01.csharp\u002F11.aspnet\u002F02.api\u002F04.http-methods-status-codes",{"title":752,"path":753,"stem":754},"Організація HTTP API за принципами REST","\u002Fcsharp\u002Faspnet\u002Fapi\u002Frest-organizing","01.csharp\u002F11.aspnet\u002F02.api\u002F05.rest-organizing",{"title":756,"path":757,"stem":758},"Номенклатура URL та CRUD-операції","\u002Fcsharp\u002Faspnet\u002Fapi\u002Furl-nomenclature-crud","01.csharp\u002F11.aspnet\u002F02.api\u002F06.url-nomenclature-crud",{"title":760,"path":761,"stem":762},"Правила дизайну: іменування та стандарти","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fapi-design-naming","01.csharp\u002F11.aspnet\u002F02.api\u002F07.api-design-naming",{"title":764,"path":765,"stem":766},"Валідація, ліміти та обробка помилок","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fapi-design-validation","01.csharp\u002F11.aspnet\u002F02.api\u002F08.api-design-validation",{"title":768,"path":769,"stem":770},"Обробка помилок у Minimal API","\u002Fcsharp\u002Faspnet\u002Fapi\u002Ferror-handling-http","01.csharp\u002F11.aspnet\u002F02.api\u002F09.error-handling-http",{"title":772,"path":773,"stem":774},"Ідемпотентність та синхронізація стану","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fidempotency-sync","01.csharp\u002F11.aspnet\u002F02.api\u002F10.idempotency-sync",{"title":776,"path":777,"stem":778},"Пагінація та організація списків","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fpagination-lists","01.csharp\u002F11.aspnet\u002F02.api\u002F11.pagination-lists",{"title":780,"path":781,"stem":782},"Безпека API, кешування та інтернаціоналізація","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fsecurity-auth","01.csharp\u002F11.aspnet\u002F02.api\u002F12.security-auth",{"title":784,"path":785,"stem":786},"Процес проєктування API та документування","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fapi-design-process","01.csharp\u002F11.aspnet\u002F02.api\u002F13.api-design-process",{"title":788,"path":789,"stem":790},"OpenAPI: контракт, специфікація та документація API","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fopenapi","01.csharp\u002F11.aspnet\u002F02.api\u002F14.openapi",{"title":792,"icon":793,"path":794,"stem":795,"children":796,"page":59},"Auth","i-lucide-shield-check","\u002Fcsharp\u002Faspnet\u002Fauth","01.csharp\u002F11.aspnet\u002F03.auth",[797,801,805,809,813,817,821,825,829,833,837,841,845,849,853,857,861,865],{"title":798,"path":799,"stem":800},"Основи аутентифікації та авторизації","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fauth-fundamentals","01.csharp\u002F11.aspnet\u002F03.auth\u002F01.auth-fundamentals",{"title":802,"path":803,"stem":804},"JWT-аутентифікація","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fjwt-authentication","01.csharp\u002F11.aspnet\u002F03.auth\u002F02.jwt-authentication",{"title":806,"path":807,"stem":808},"Авторизація: ролі, політики та resource-based доступ","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fauthorization-policies","01.csharp\u002F11.aspnet\u002F03.auth\u002F03.authorization-policies",{"title":810,"path":811,"stem":812},"Cookie-аутентифікація та ASP.NET Core Identity","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fcookie-auth-identity","01.csharp\u002F11.aspnet\u002F03.auth\u002F04.cookie-auth-identity",{"title":814,"path":815,"stem":816},"JWT + Refresh Tokens (HttpOnly Cookie)","\u002Fcsharp\u002Faspnet\u002Fauth\u002F04b.identity-auth-jwt","01.csharp\u002F11.aspnet\u002F03.auth\u002F04b.identity-auth-jwt",{"title":818,"path":819,"stem":820},"Identity: Підтвердження Email та Скидання Пароля","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fidentity-email-confirmation","01.csharp\u002F11.aspnet\u002F03.auth\u002F05.identity-email-confirmation",{"title":822,"path":823,"stem":824},"Identity: Двофакторна Аутентифікація (2FA)","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fidentity-two-factor","01.csharp\u002F11.aspnet\u002F03.auth\u002F06.identity-two-factor",{"title":826,"path":827,"stem":828},"Identity: Внутрішня Архітектура та Кастомізація","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fidentity-internals","01.csharp\u002F11.aspnet\u002F03.auth\u002F07.identity-internals",{"title":830,"path":831,"stem":832},"OAuth 2.0 та зовнішні провайдери","\u002Fcsharp\u002Faspnet\u002Fauth\u002Foauth-external-providers","01.csharp\u002F11.aspnet\u002F03.auth\u002F08.oauth-external-providers",{"title":834,"path":835,"stem":836},"Безпека на практиці: CORS, HTTPS та захист від атак","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fsecurity-hardening","01.csharp\u002F11.aspnet\u002F03.auth\u002F09.security-hardening",{"title":838,"path":839,"stem":840},"Теорія OAuth 2.0: Поняття, Аналогії та Флоу","\u002Fcsharp\u002Faspnet\u002Fauth\u002Foauth-theory","01.csharp\u002F11.aspnet\u002F03.auth\u002F10.oauth-theory",{"title":842,"path":843,"stem":844},"OIDC, OAuth 2.0 та Keycloak в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fauth\u002Foidc-keycloak","01.csharp\u002F11.aspnet\u002F03.auth\u002F10.oidc-keycloak",{"title":846,"path":847,"stem":848},"API Keys аутентифікація в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fapi-keys","01.csharp\u002F11.aspnet\u002F03.auth\u002F11.api-keys",{"title":850,"path":851,"stem":852},"Rate Limiting та Throttling в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fauth\u002Frate-limiting","01.csharp\u002F11.aspnet\u002F03.auth\u002F12.rate-limiting",{"title":854,"path":855,"stem":856},"Refresh Token Rotation в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fauth\u002Frefresh-token-rotation","01.csharp\u002F11.aspnet\u002F03.auth\u002F13.refresh-token-rotation",{"title":858,"path":859,"stem":860},"Certificate Authentication та mTLS в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fcertificate-auth","01.csharp\u002F11.aspnet\u002F03.auth\u002F14.certificate-auth",{"title":862,"path":863,"stem":864},"RBAC, ABAC та ReBAC в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fauth\u002Frbac-abac-rebac","01.csharp\u002F11.aspnet\u002F03.auth\u002F15.rbac-abac-rebac",{"title":866,"path":867,"stem":868},"Multi-tenancy та ізоляція даних в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fmulti-tenancy","01.csharp\u002F11.aspnet\u002F03.auth\u002F16.multi-tenancy",{"title":870,"icon":871,"path":872,"stem":873,"children":874,"page":59},"Нотифікації","i-lucide-bell","\u002Fcsharp\u002Faspnet\u002Fnotifications","01.csharp\u002F11.aspnet\u002F04.notifications",[875,879,883,887,891,895,899,903,907,911,915,919,923],{"title":876,"path":877,"stem":878},"In-App нотифікації через базу даних","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fin-app-database-notifications","01.csharp\u002F11.aspnet\u002F04.notifications\u002F01.in-app-database-notifications",{"title":880,"path":881,"stem":882},"Polling: Регулярний запит оновлень","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fpolling","01.csharp\u002F11.aspnet\u002F04.notifications\u002F02.polling",{"title":884,"path":885,"stem":886},"Server-Sent Events: Однострімовий push від сервера","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fserver-sent-events","01.csharp\u002F11.aspnet\u002F04.notifications\u002F03.server-sent-events",{"title":888,"path":889,"stem":890},"WebSockets: Двостороннє з'єднання в реальному часі","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fwebsockets","01.csharp\u002F11.aspnet\u002F04.notifications\u002F04.websockets",{"title":892,"path":893,"stem":894},"SignalR: Абстракція над транспортами реального часу","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fsignalr","01.csharp\u002F11.aspnet\u002F04.notifications\u002F05.signalr",{"title":896,"path":897,"stem":898},"Background Services: Фонові задачі в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fbackground-services","01.csharp\u002F11.aspnet\u002F04.notifications\u002F06.background-services",{"title":900,"path":901,"stem":902},"Web Push нотифікації","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fweb-push","01.csharp\u002F11.aspnet\u002F04.notifications\u002F07.web-push",{"title":904,"path":905,"stem":906},"Email нотифікації","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Femail-notifications","01.csharp\u002F11.aspnet\u002F04.notifications\u002F08.email-notifications",{"title":908,"path":909,"stem":910},"Порівняння підходів: Як вибрати правильну технологію нотифікацій","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fchoosing-the-right-approach","01.csharp\u002F11.aspnet\u002F04.notifications\u002F09.choosing-the-right-approach",{"title":912,"path":913,"stem":914},"Hangfire: Надійне планування фонових задач","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fhangfire","01.csharp\u002F11.aspnet\u002F04.notifications\u002F10.hangfire",{"title":916,"path":917,"stem":918},"Практика: Конвертація зображень у WebP через Hangfire","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fhangfire-image-webp","01.csharp\u002F11.aspnet\u002F04.notifications\u002F11.hangfire-image-webp",{"title":920,"path":921,"stem":922},"Практика: Підготовка відео до HLS-стрімінгу через Hangfire","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fhangfire-video-hls","01.csharp\u002F11.aspnet\u002F04.notifications\u002F12.hangfire-video-hls",{"title":924,"path":925,"stem":926},"Telegram-нотифікації: від одного повідомлення до масових розсилок і мульти-канального підходу","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Ftelegram-notifications","01.csharp\u002F11.aspnet\u002F04.notifications\u002F13.telegram-notifications",{"title":928,"icon":929,"path":930,"stem":931,"children":932,"page":59},"Інтернаціоналізація","i-lucide-languages","\u002Fcsharp\u002Faspnet\u002Fi18n","01.csharp\u002F11.aspnet\u002F05.i18n",[933,937],{"title":934,"path":935,"stem":936},"Інтернаціоналізація (i18n) у Minimal API: від A до Я","\u002Fcsharp\u002Faspnet\u002Fi18n\u002Finternationalization","01.csharp\u002F11.aspnet\u002F05.i18n\u002F01.internationalization",{"title":938,"path":939,"stem":940},"Humanizer: людиномовні рядки у .NET","\u002Fcsharp\u002Faspnet\u002Fi18n\u002Fhumanizer","01.csharp\u002F11.aspnet\u002F05.i18n\u002F02.humanizer",{"title":942,"icon":943,"path":944,"stem":945,"children":946,"page":59},"Кешування","i-lucide-layers","\u002Fcsharp\u002Faspnet\u002Fcaching","01.csharp\u002F11.aspnet\u002F06.caching",[947,951,955,959,963],{"title":948,"path":949,"stem":950},"Огляд кешування: чотири рівні і коли що обирати","\u002Fcsharp\u002Faspnet\u002Fcaching\u002Fcaching","01.csharp\u002F11.aspnet\u002F06.caching\u002F01.caching",{"title":952,"path":953,"stem":954},"IMemoryCache: кеш в оперативній пам'яті","\u002Fcsharp\u002Faspnet\u002Fcaching\u002Fmemory-cache","01.csharp\u002F11.aspnet\u002F06.caching\u002F02.memory-cache",{"title":956,"path":957,"stem":958},"IDistributedCache і Redis: розподілений кеш","\u002Fcsharp\u002Faspnet\u002Fcaching\u002Fdistributed-cache","01.csharp\u002F11.aspnet\u002F06.caching\u002F03.distributed-cache",{"title":960,"path":961,"stem":962},"Response Cache: HTTP-кешування через Cache-Control","\u002Fcsharp\u002Faspnet\u002Fcaching\u002Fresponse-cache","01.csharp\u002F11.aspnet\u002F06.caching\u002F04.response-cache",{"title":964,"path":965,"stem":966},"Output Cache: серверний кеш HTTP-відповідей (.NET 7+)","\u002Fcsharp\u002Faspnet\u002Fcaching\u002Foutput-cache","01.csharp\u002F11.aspnet\u002F06.caching\u002F05.output-cache",{"title":968,"icon":969,"path":970,"stem":971,"children":972,"page":59},"Тестування","i-lucide-test-tube","\u002Fcsharp\u002Faspnet\u002Ftesting","01.csharp\u002F11.aspnet\u002F07.testing",[973,977,981,985,989,993,997,1001,1005,1009,1013,1017,1021,1025,1029,1033,1037,1041,1045,1049,1053],{"title":974,"path":975,"stem":976},"Що таке тестування? Від інтуїції до науки","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fwhat-is-testing","01.csharp\u002F11.aspnet\u002F07.testing\u002F01.what-is-testing",{"title":978,"path":979,"stem":980},"Піраміда тестування — Стратегія, а не Догма","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Ftesting-pyramid","01.csharp\u002F11.aspnet\u002F07.testing\u002F02.testing-pyramid",{"title":982,"path":983,"stem":984},"Дві Школи Тестування — Лондон проти Детройту","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Ftesting-schools","01.csharp\u002F11.aspnet\u002F07.testing\u002F03.testing-schools",{"title":986,"path":987,"stem":988},"TDD та BDD — Тести як Дизайн-інструмент","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Ftdd-and-bdd","01.csharp\u002F11.aspnet\u002F07.testing\u002F04.tdd-and-bdd",{"title":990,"path":991,"stem":992},"Що саме тестувати — Техніки аналізу та Циклomatична складність","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fwhat-to-test","01.csharp\u002F11.aspnet\u002F07.testing\u002F05.what-to-test",{"title":994,"path":995,"stem":996},"Тестові Фреймворки — Навіщо вони і що всередині","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Ftest-frameworks","01.csharp\u002F11.aspnet\u002F07.testing\u002F06.test-frameworks",{"title":998,"path":999,"stem":1000},"xUnit — Факти, Теорії та Lifecycle тестів","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fxunit-basics","01.csharp\u002F11.aspnet\u002F07.testing\u002F07.xunit-basics",{"title":1002,"path":1003,"stem":1004},"xUnit Advanced — Fixtures, Кастомізація та Розширення","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fxunit-advanced","01.csharp\u002F11.aspnet\u002F07.testing\u002F08.xunit-advanced",{"title":1006,"path":1007,"stem":1008},"Moq — Глибоке занурення в мокування","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fmocking-with-moq","01.csharp\u002F11.aspnet\u002F07.testing\u002F09.mocking-with-moq",{"title":1010,"path":1011,"stem":1012},"Тестування Баз Даних — EF Core, SQLite та Testcontainers","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fdatabase-testing","01.csharp\u002F11.aspnet\u002F07.testing\u002F10.database-testing",{"title":1014,"path":1015,"stem":1016},"Integration Testing — Частина 1 [Теорія та WebApplicationFactory]","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fintegration-testing","01.csharp\u002F11.aspnet\u002F07.testing\u002F11.integration-testing",{"title":1018,"path":1019,"stem":1020},"Інтеграційне тестування — Практика","\u002Fcsharp\u002Faspnet\u002Ftesting\u002F11a.integration-testing-practice","01.csharp\u002F11.aspnet\u002F07.testing\u002F11a.integration-testing-practice",{"title":1022,"path":1023,"stem":1024},"Integration Testing — Частина 2 [Просунуті Сценарії та Testcontainers]","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fintegration-testing-advanced","01.csharp\u002F11.aspnet\u002F07.testing\u002F12.integration-testing-advanced",{"title":1026,"path":1027,"stem":1028},"Професійний Postman: Колекції, Змінні та GitHub Інтеграція","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fpostman-professional","01.csharp\u002F11.aspnet\u002F07.testing\u002F13.postman-professional",{"title":1030,"path":1031,"stem":1032},"HttpClient у Тестах Частина 1: Архітектура та MockHttpMessageHandler","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fhttpclient-testing","01.csharp\u002F11.aspnet\u002F07.testing\u002F14.httpclient-testing",{"title":1034,"path":1035,"stem":1036},"HttpClient у Тестах Частина 2: WireMock.Net та Resilience","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fwiremock-net","01.csharp\u002F11.aspnet\u002F07.testing\u002F15.wiremock-net",{"title":1038,"path":1039,"stem":1040},"Патерни та Анти-патерни Тестування: Test Smells","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Ftesting-patterns","01.csharp\u002F11.aspnet\u002F07.testing\u002F16.testing-patterns",{"title":1042,"path":1043,"stem":1044},"Просунуті інструменти: Time, Snapshots та Властивості","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fadvanced-testing-tools","01.csharp\u002F11.aspnet\u002F07.testing\u002F17.advanced-testing-tools",{"title":1046,"path":1047,"stem":1048},"Тестування Архітектури з NetArchTest","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Farchitecture-testing","01.csharp\u002F11.aspnet\u002F07.testing\u002F18.architecture-testing",{"title":1050,"path":1051,"stem":1052},"Тестування Продуктивності: BenchmarkDotNet, NBomber та k6","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fperformance-testing","01.csharp\u002F11.aspnet\u002F07.testing\u002F19.performance-testing",{"title":1054,"path":1055,"stem":1056},"Залишок плану для курсу \"Тестування ASP.NET Minimal API\"","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fremaining_plan","01.csharp\u002F11.aspnet\u002F07.testing\u002Fremaining_plan",{"title":1058,"icon":1059,"path":1060,"stem":1061,"children":1062,"page":59},"Платежі","i-lucide-credit-card","\u002Fcsharp\u002Faspnet\u002Fpayments","01.csharp\u002F11.aspnet\u002F08.payments",[1063,1067,1071,1075,1079,1083,1087,1091,1095,1099,1103,1107],{"title":1064,"path":1065,"stem":1066},"Основи платіжної інфраструктури","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fpayment-fundamentals","01.csharp\u002F11.aspnet\u002F08.payments\u002F01.payment-fundamentals",{"title":1068,"path":1069,"stem":1070},"Методи оплати в Україні","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fpayment-methods-ukraine","01.csharp\u002F11.aspnet\u002F08.payments\u002F02.payment-methods-ukraine",{"title":1072,"path":1073,"stem":1074},"PCI DSS та безпека платежів","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fpci-dss-security","01.csharp\u002F11.aspnet\u002F08.payments\u002F03.pci-dss-security",{"title":1076,"path":1077,"stem":1078},"Архітектура платіжної підсистеми","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fpayment-architecture","01.csharp\u002F11.aspnet\u002F08.payments\u002F04.payment-architecture",{"title":1080,"path":1081,"stem":1082},"Інтеграція LiqPay (ПриватБанк)","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fliqpay-integration","01.csharp\u002F11.aspnet\u002F08.payments\u002F05.liqpay-integration",{"title":1084,"path":1085,"stem":1086},"Інтеграція Monobank Acquiring API","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fmonobank-acquiring","01.csharp\u002F11.aspnet\u002F08.payments\u002F06.monobank-acquiring",{"title":1088,"path":1089,"stem":1090},"Інтеграція Stripe","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fstripe-integration","01.csharp\u002F11.aspnet\u002F08.payments\u002F07.stripe-integration",{"title":1092,"path":1093,"stem":1094},"Webhooks — глибоке занурення","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fwebhooks-deep-dive","01.csharp\u002F11.aspnet\u002F08.payments\u002F08.webhooks-deep-dive",{"title":1096,"path":1097,"stem":1098},"Підписки та рекурентні платежі","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fsubscriptions-recurring","01.csharp\u002F11.aspnet\u002F08.payments\u002F09.subscriptions-recurring",{"title":1100,"path":1101,"stem":1102},"Повернення коштів та диспути","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Frefunds-disputes","01.csharp\u002F11.aspnet\u002F08.payments\u002F10.refunds-disputes",{"title":1104,"path":1105,"stem":1106},"Тестування платіжних інтеграцій","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Ftesting-payments","01.csharp\u002F11.aspnet\u002F08.payments\u002F11.testing-payments",{"title":1108,"path":1109,"stem":1110},"Чекліст виходу в Production","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fproduction-checklist","01.csharp\u002F11.aspnet\u002F08.payments\u002F12.production-checklist",{"title":1112,"icon":1113,"items":1114,"path":1127,"stem":1128,"children":1129,"page":59},"Популярні бібліотеки","lucide:box",[1115,1116,1117,1118,1119,1120,1121,1122,1123,1124,1125,1126],"01.fluent-validation","02.mapster","03.erroror-result-pattern","04.serilog","05.mediatr","06.polly","07.health-checks","08.feature-management","09.fluent-email","10.quest-pdf","11.bogus","12.humanizer-guard","\u002Fcsharp\u002Faspnet\u002Flibraries","01.csharp\u002F11.aspnet\u002F09.libraries",[1130,1134,1138,1142,1146,1150,1154,1158,1162,1166,1170,1174,1178],{"title":1131,"path":1132,"stem":1133},"Валідація з FluentValidation в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Ffluent-validation","01.csharp\u002F11.aspnet\u002F09.libraries\u002F01.fluent-validation",{"title":1135,"path":1136,"stem":1137},"Маппінг об","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fmapster","01.csharp\u002F11.aspnet\u002F09.libraries\u002F02.mapster",{"title":1139,"path":1140,"stem":1141},"Обробка помилок з ErrorOr та Result Pattern в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Ferroror-result-pattern","01.csharp\u002F11.aspnet\u002F09.libraries\u002F03.erroror-result-pattern",{"title":1143,"path":1144,"stem":1145},"Структуроване логування з Serilog в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fserilog","01.csharp\u002F11.aspnet\u002F09.libraries\u002F04.serilog",{"title":1147,"path":1148,"stem":1149},"CQRS та Mediator з MediatR в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fmediatr","01.csharp\u002F11.aspnet\u002F09.libraries\u002F05.mediatr",{"title":1151,"path":1152,"stem":1153},"Відмовостійкість з Polly в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fpolly","01.csharp\u002F11.aspnet\u002F09.libraries\u002F06.polly",{"title":1155,"path":1156,"stem":1157},"Health Checks в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fhealth-checks","01.csharp\u002F11.aspnet\u002F09.libraries\u002F07.health-checks",{"title":1159,"path":1160,"stem":1161},"Feature Management та Feature Flags в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Ffeature-management","01.csharp\u002F11.aspnet\u002F09.libraries\u002F08.feature-management",{"title":1163,"path":1164,"stem":1165},"Відправка Email з FluentEmail в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Ffluent-email","01.csharp\u002F11.aspnet\u002F09.libraries\u002F09.fluent-email",{"title":1167,"path":1168,"stem":1169},"Генерація PDF з QuestPDF в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fquest-pdf","01.csharp\u002F11.aspnet\u002F09.libraries\u002F10.quest-pdf",{"title":1171,"path":1172,"stem":1173},"Генерація тестових даних з Bogus в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fbogus","01.csharp\u002F11.aspnet\u002F09.libraries\u002F11.bogus",{"title":1175,"path":1176,"stem":1177},"Humanizer та Guard Clauses в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fhumanizer-guard","01.csharp\u002F11.aspnet\u002F09.libraries\u002F12.humanizer-guard",{"title":1179,"path":1180,"stem":1181},"План модуля 10.libraries — Популярні бібліотеки ASP.NET","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fplan","01.csharp\u002F11.aspnet\u002F09.libraries\u002Fplan",{"title":1183,"icon":1184,"path":1185,"stem":1186,"children":1187,"page":59},"Razor Pages","i-lucide-layout-template","\u002Fcsharp\u002Faspnet\u002Frazor-pages","01.csharp\u002F11.aspnet\u002F10.razor-pages",[1188,1192,1196,1200,1204,1208],{"title":1189,"path":1190,"stem":1191},"Від Minimal API до Razor Pages: концептуальний перехід","\u002Fcsharp\u002Faspnet\u002Frazor-pages\u002Ffrom-minimal-api","01.csharp\u002F11.aspnet\u002F10.razor-pages\u002F01.from-minimal-api",{"title":1193,"path":1194,"stem":1195},"PageModel: логіка сторінки Razor Pages","\u002Fcsharp\u002Faspnet\u002Frazor-pages\u002Fpage-model","01.csharp\u002F11.aspnet\u002F10.razor-pages\u002F02.page-model",{"title":1197,"path":1198,"stem":1199},"Razor синтаксис: шаблонізатор у .cshtml","\u002Fcsharp\u002Faspnet\u002Frazor-pages\u002Frazor-syntax","01.csharp\u002F11.aspnet\u002F10.razor-pages\u002F03.razor-syntax",{"title":1201,"path":1202,"stem":1203},"Tag Helpers: типізований HTML","\u002Fcsharp\u002Faspnet\u002Frazor-pages\u002Ftag-helpers","01.csharp\u002F11.aspnet\u002F10.razor-pages\u002F04.tag-helpers",{"title":1205,"path":1206,"stem":1207},"Форми і валідація: повний цикл обробки даних","\u002Fcsharp\u002Faspnet\u002Frazor-pages\u002Fforms-validation","01.csharp\u002F11.aspnet\u002F10.razor-pages\u002F05.forms-validation",{"title":1209,"path":1210,"stem":1211},"Практичний проєкт: TaskManager на Razor Pages","\u002Fcsharp\u002Faspnet\u002Frazor-pages\u002Fproject-task-manager","01.csharp\u002F11.aspnet\u002F10.razor-pages\u002F06.project-task-manager",{"title":1213,"path":1214,"stem":1215,"children":1216,"page":59},"ASP.NET Core MVC","\u002Fcsharp\u002Faspnet\u002Fmvc","01.csharp\u002F11.aspnet\u002F11.mvc",[1217,1221,1225,1229,1233,1237,1241,1245,1249,1253,1257,1261,1265,1269,1273,1277,1281,1285],{"title":1218,"path":1219,"stem":1220},"Патерн MVC: архітектура, що змінила веб","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fmvc-pattern","01.csharp\u002F11.aspnet\u002F11.mvc\u002F01.mvc-pattern",{"title":1222,"path":1223,"stem":1224},"Від Razor Pages до MVC: концептуальний перехід","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Ffrom-razor-pages","01.csharp\u002F11.aspnet\u002F11.mvc\u002F02.from-razor-pages",{"title":1226,"path":1227,"stem":1228},"Controllers та Actions: серце MVC","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fcontrollers-actions","01.csharp\u002F11.aspnet\u002F11.mvc\u002F03.controllers-actions",{"title":1230,"path":1231,"stem":1232},"Маршрутизація в MVC: Convention vs Attribute Routing","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Frouting-mvc","01.csharp\u002F11.aspnet\u002F11.mvc\u002F04.routing-mvc",{"title":1234,"path":1235,"stem":1236},"Model Binding: від HTTP до C#","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fmodel-binding","01.csharp\u002F11.aspnet\u002F11.mvc\u002F05.model-binding",{"title":1238,"path":1239,"stem":1240},"Views, ViewData, ViewBag, TempData і ViewModel","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fviews-viewdata-tempdata","01.csharp\u002F11.aspnet\u002F11.mvc\u002F06.views-viewdata-tempdata",{"title":1242,"path":1243,"stem":1244},"Filters: аспектно-орієнтоване програмування в MVC","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Ffilters","01.csharp\u002F11.aspnet\u002F11.mvc\u002F07.filters",{"title":1246,"path":1247,"stem":1248},"Areas: структурування великих застосунків","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fareas","01.csharp\u002F11.aspnet\u002F11.mvc\u002F08.areas",{"title":1250,"path":1251,"stem":1252},"View Components: повторювані незалежні блоки UI","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fview-components","01.csharp\u002F11.aspnet\u002F11.mvc\u002F09.view-components",{"title":1254,"path":1255,"stem":1256},"Display та Editor Templates","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fdisplay-editor-templates","01.csharp\u002F11.aspnet\u002F11.mvc\u002F10.display-editor-templates",{"title":1258,"path":1259,"stem":1260},"Валідація: IValidatableObject та FluentValidation","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fvalidation-advanced","01.csharp\u002F11.aspnet\u002F11.mvc\u002F11.validation-advanced",{"title":1262,"path":1263,"stem":1264},"HTMX: інтерактивність через HTML-атрибути","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fhtmx","01.csharp\u002F11.aspnet\u002F11.mvc\u002F12.htmx",{"title":1266,"path":1267,"stem":1268},"HTMX у ASP.NET Core MVC: серверна інтеграція","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fajax-htmx-mvc","01.csharp\u002F11.aspnet\u002F11.mvc\u002F13.ajax-htmx-mvc",{"title":1270,"path":1271,"stem":1272},"Практичний проєкт: Каталог товарів з HTMX","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fhtmx-project","01.csharp\u002F11.aspnet\u002F11.mvc\u002F14.htmx-project",{"title":1274,"path":1275,"stem":1276},"Завантаження та обробка файлів","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Ffile-upload","01.csharp\u002F11.aspnet\u002F11.mvc\u002F15.file-upload",{"title":1278,"path":1279,"stem":1280},"Глобалізація та Локалізація MVC","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fglobalization-localization","01.csharp\u002F11.aspnet\u002F11.mvc\u002F16.globalization-localization",{"title":1282,"path":1283,"stem":1284},"Підсумковий проєкт: Блог-платформа","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fmvc-project","01.csharp\u002F11.aspnet\u002F11.mvc\u002F17.mvc-project",{"title":1286,"path":1287,"stem":1288},"План курсу: ASP.NET Core MVC","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fplan","01.csharp\u002F11.aspnet\u002F11.mvc\u002Fplan",{"title":1290,"path":1291,"stem":1292,"children":1293,"page":59},"Web Api","\u002Fcsharp\u002Faspnet\u002Fweb-api","01.csharp\u002F11.aspnet\u002F12.web-api",[1294,1298,1302,1306,1310,1314,1318,1322,1326,1330,1334,1338,1342],{"title":1295,"path":1296,"stem":1297},"Від Minimal API до Controller-based API","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Ffrom-minimal-api-to-controllers","01.csharp\u002F11.aspnet\u002F12.web-api\u002F01.from-minimal-api-to-controllers",{"title":1299,"path":1300,"stem":1301},"ControllerBase, ActionResult\u003CT> та Response Types","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fcontroller-base-actionresult","01.csharp\u002F11.aspnet\u002F12.web-api\u002F02.controller-base-actionresult",{"title":1303,"path":1304,"stem":1305},"Content Negotiation - JSON, XML та власні форматери","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fcontent-negotiation","01.csharp\u002F11.aspnet\u002F12.web-api\u002F03.content-negotiation",{"title":1307,"path":1308,"stem":1309},"Версіонування API","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fapi-versioning","01.csharp\u002F11.aspnet\u002F12.web-api\u002F04.api-versioning",{"title":1311,"path":1312,"stem":1313},"ProblemDetails та структурована обробка помилок","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fproblemdetails-error-handling","01.csharp\u002F11.aspnet\u002F12.web-api\u002F05.problemdetails-error-handling",{"title":1315,"path":1316,"stem":1317},"Фільтри у Web API контексті","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Ffilters-for-api","01.csharp\u002F11.aspnet\u002F12.web-api\u002F06.filters-for-api",{"title":1319,"path":1320,"stem":1321},"Пагінація, фільтрація та сортування","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fpagination-filtering-sorting","01.csharp\u002F11.aspnet\u002F12.web-api\u002F07.pagination-filtering-sorting",{"title":1323,"path":1324,"stem":1325},"HATEOAS та Resource Expansion","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fhateoas-resource-expansion","01.csharp\u002F11.aspnet\u002F12.web-api\u002F08.hateoas-resource-expansion",{"title":1327,"path":1328,"stem":1329},"Гібридна архітектура - Minimal API + Controllers","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fminimal-api-vs-controllers-hybrid","01.csharp\u002F11.aspnet\u002F12.web-api\u002F09.minimal-api-vs-controllers-hybrid",{"title":1331,"path":1332,"stem":1333},"Документація API - Swashbuckle, NSwag та генерація клієнтів","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fapi-documentation-generation","01.csharp\u002F11.aspnet\u002F12.web-api\u002F10.api-documentation-generation",{"title":1335,"path":1336,"stem":1337},"Health Checks та моніторинг API","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fhealth-checks-monitoring","01.csharp\u002F11.aspnet\u002F12.web-api\u002F11.health-checks-monitoring",{"title":1339,"path":1340,"stem":1341},"Підсумковий проєкт - Production-Ready REST API","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fweb-api-project","01.csharp\u002F11.aspnet\u002F12.web-api\u002F12.web-api-project",{"title":1343,"path":1344,"stem":1345},"План курсу: ASP.NET Core Web API (Controllers)","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fplan","01.csharp\u002F11.aspnet\u002F12.web-api\u002Fplan",{"title":1347,"icon":1348,"path":1349,"stem":1350,"children":1351,"page":59},"Моніторинг","i-lucide-activity","\u002Fcsharp\u002Faspnet\u002Fmonitoring","01.csharp\u002F11.aspnet\u002F13.monitoring",[1352,1356,1360],{"title":1353,"path":1354,"stem":1355},"Спостережуваність: від console.log до production-систем","\u002Fcsharp\u002Faspnet\u002Fmonitoring\u002Fobservability-intro","01.csharp\u002F11.aspnet\u002F13.monitoring\u002F01.observability-intro",{"title":1357,"path":1358,"stem":1359},"Health Checks: перший рівень observability","\u002Fcsharp\u002Faspnet\u002Fmonitoring\u002Fhealth-checks","01.csharp\u002F11.aspnet\u002F13.monitoring\u002F02.health-checks",{"title":1361,"path":1362,"stem":1363},"Вбудовані метрики .NET 10 та System.Diagnostics.Metrics","\u002Fcsharp\u002Faspnet\u002Fmonitoring\u002Fdotnet-metrics","01.csharp\u002F11.aspnet\u002F13.monitoring\u002F03.dotnet-metrics",{"title":1365,"icon":1366,"path":1367,"stem":1368,"children":1369,"page":59},"Desktop UI","i-lucide-app-window","\u002Fcsharp\u002Fdesktop-ui","01.csharp\u002F12.desktop-ui",[1370,1374,1378,1382,1386,1390,1394,1398,1402,1406,1410,1414,1418,1422,1426,1430,1434,1438,1442,1446,1450,1454,1458,1462,1466,1470,1474,1478,1482,1486,1490,1494,1498,1502,1506,1510,1514,1518,1522,1526,1530,1534,1538,1542,1546,1550,1554,1558,1562,1566,1570,1574,1578,1582,1586,1590,1594,1598,1602,1606,1610,1614,1618,1622,1626,1630,1634,1638,1642,1646,1650],{"title":1371,"path":1372,"stem":1373},"Що таке десктопна розробка?","\u002Fcsharp\u002Fdesktop-ui\u002Fwhat-is-desktop-dev","01.csharp\u002F12.desktop-ui\u002F01.what-is-desktop-dev",{"title":1375,"path":1376,"stem":1377},"Архітектура WPF — як влаштований графічний інтерфейс","\u002Fcsharp\u002Fdesktop-ui\u002Fwpf-architecture","01.csharp\u002F12.desktop-ui\u002F02.wpf-architecture",{"title":1379,"path":1380,"stem":1381},"Перший WPF-проєкт — від нуля до вікна","\u002Fcsharp\u002Fdesktop-ui\u002Ffirst-wpf-app","01.csharp\u002F12.desktop-ui\u002F03.first-wpf-app",{"title":1383,"path":1384,"stem":1385},"Перший Avalonia-проєкт: WPF для всіх платформ","\u002Fcsharp\u002Fdesktop-ui\u002F03a.first-avalonia-app","01.csharp\u002F12.desktop-ui\u002F03a.first-avalonia-app",{"title":1387,"path":1388,"stem":1389},"XAML: декларативний інтерфейс","\u002Fcsharp\u002Fdesktop-ui\u002Fxaml-basics","01.csharp\u002F12.desktop-ui\u002F04.xaml-basics",{"title":1391,"path":1392,"stem":1393},"Fluent UI у WPF — сучасний дизайн Windows 11","\u002Fcsharp\u002Fdesktop-ui\u002F04a.wpf-fluent-ui","01.csharp\u002F12.desktop-ui\u002F04a.wpf-fluent-ui",{"title":1395,"path":1396,"stem":1397},"WPF UI — сучасна бібліотека Fluent контролів","\u002Fcsharp\u002Fdesktop-ui\u002F04b.wpf-ui-library","01.csharp\u002F12.desktop-ui\u002F04b.wpf-ui-library",{"title":1399,"path":1400,"stem":1401},"HandyControl — велика бібліотека UI контролів для WPF","\u002Fcsharp\u002Fdesktop-ui\u002F04c.handycontrol-library","01.csharp\u002F12.desktop-ui\u002F04c.handycontrol-library",{"title":1403,"path":1404,"stem":1405},"Простори імен та ресурси XAML","\u002Fcsharp\u002Fdesktop-ui\u002Fxaml-namespaces-resources","01.csharp\u002F12.desktop-ui\u002F05.xaml-namespaces-resources",{"title":1407,"path":1408,"stem":1409},"XAML в Avalonia: ключові відмінності від WPF","\u002Fcsharp\u002Fdesktop-ui\u002F05a.avalonia-xaml-differences","01.csharp\u002F12.desktop-ui\u002F05a.avalonia-xaml-differences",{"title":1411,"path":1412,"stem":1413},"Розширення розмітки XAML (Markup Extensions)","\u002Fcsharp\u002Fdesktop-ui\u002Fxaml-markup-extensions","01.csharp\u002F12.desktop-ui\u002F06.xaml-markup-extensions",{"title":1415,"path":1416,"stem":1417},"Панелі Layout: StackPanel, WrapPanel, DockPanel","\u002Fcsharp\u002Fdesktop-ui\u002Flayout-panels-part1","01.csharp\u002F12.desktop-ui\u002F07.layout-panels-part1",{"title":1419,"path":1420,"stem":1421},"Grid, Canvas, UniformGrid","\u002Fcsharp\u002Fdesktop-ui\u002Flayout-panels-part2","01.csharp\u002F12.desktop-ui\u002F07.layout-panels-part2",{"title":1423,"path":1424,"stem":1425},"Просунуті техніки Layout","\u002Fcsharp\u002Fdesktop-ui\u002Flayout-advanced","01.csharp\u002F12.desktop-ui\u002F08.layout-advanced",{"title":1427,"path":1428,"stem":1429},"Адаптивний Layout та найкращі практики","\u002Fcsharp\u002Fdesktop-ui\u002Flayout-responsive","01.csharp\u002F12.desktop-ui\u002F09.layout-responsive",{"title":1431,"path":1432,"stem":1433},"Layout в Avalonia: відмінності та нові можливості","\u002Fcsharp\u002Fdesktop-ui\u002F09a.layout-avalonia","01.csharp\u002F12.desktop-ui\u002F09a.layout-avalonia",{"title":1435,"path":1436,"stem":1437},"Button, Image, ProgressBar та інші базові контроли","\u002Fcsharp\u002Fdesktop-ui\u002Fbasic-controls","01.csharp\u002F12.desktop-ui\u002F10.basic-controls",{"title":1439,"path":1440,"stem":1441},"Контроли в Avalonia: відмінності від WPF","\u002Fcsharp\u002Fdesktop-ui\u002F10a.controls-avalonia","01.csharp\u002F12.desktop-ui\u002F10a.controls-avalonia",{"title":1443,"path":1444,"stem":1445},"Текстові контроли — TextBlock, TextBox, RichTextBox","\u002Fcsharp\u002Fdesktop-ui\u002Ftext-controls","01.csharp\u002F12.desktop-ui\u002F11.text-controls",{"title":1447,"path":1448,"stem":1449},"Контроли вибору — CheckBox, RadioButton, ComboBox, ListBox, DatePicker","\u002Fcsharp\u002Fdesktop-ui\u002Fselection-controls","01.csharp\u002F12.desktop-ui\u002F12.selection-controls",{"title":1451,"path":1452,"stem":1453},"Content Model — GroupBox, Expander, TabControl, StatusBar","\u002Fcsharp\u002Fdesktop-ui\u002Fcontent-controls","01.csharp\u002F12.desktop-ui\u002F13.content-controls",{"title":1455,"path":1456,"stem":1457},"UI\u002FUX принципи десктопних застосунків","\u002Fcsharp\u002Fdesktop-ui\u002F13a.ui-ux-principles","01.csharp\u002F12.desktop-ui\u002F13a.ui-ux-principles",{"title":1459,"path":1460,"stem":1461},"Dependency Properties — Концепція та Value Resolution","\u002Fcsharp\u002Fdesktop-ui\u002Fdependency-properties-part1","01.csharp\u002F12.desktop-ui\u002F14.dependency-properties-part1",{"title":1463,"path":1464,"stem":1465},"Avalonia Property System — StyledProperty та DirectProperty","\u002Fcsharp\u002Fdesktop-ui\u002F14a.avalonia-property-system","01.csharp\u002F12.desktop-ui\u002F14a.avalonia-property-system",{"title":1467,"path":1468,"stem":1469},"Attached Properties — Властивості без меж","\u002Fcsharp\u002Fdesktop-ui\u002Fattached-properties","01.csharp\u002F12.desktop-ui\u002F15.attached-properties",{"title":1471,"path":1472,"stem":1473},"Routed Events — Маршрутизація подій у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Frouted-events","01.csharp\u002F12.desktop-ui\u002F16.routed-events",{"title":1475,"path":1476,"stem":1477},"Data Binding — Від Code-Behind до Декларативності","\u002Fcsharp\u002Fdesktop-ui\u002Fdata-binding-basics-part1","01.csharp\u002F12.desktop-ui\u002F17.data-binding-basics-part1",{"title":1479,"path":1480,"stem":1481},"INotifyPropertyChanged — Живе оновлення UI","\u002Fcsharp\u002Fdesktop-ui\u002Fdata-binding-basics-part2","01.csharp\u002F12.desktop-ui\u002F17.data-binding-basics-part2",{"title":1483,"path":1484,"stem":1485},"Compiled Bindings в Avalonia — Безпека на етапі компіляції","\u002Fcsharp\u002Fdesktop-ui\u002F17a.avalonia-compiled-bindings","01.csharp\u002F12.desktop-ui\u002F17a.avalonia-compiled-bindings",{"title":1487,"path":1488,"stem":1489},"Просунутий Data Binding — ElementName, RelativeSource, MultiBinding","\u002Fcsharp\u002Fdesktop-ui\u002Fdata-binding-advanced","01.csharp\u002F12.desktop-ui\u002F18.data-binding-advanced",{"title":1491,"path":1492,"stem":1493},"Value Converters — Перетворення типів даних у Data Binding","\u002Fcsharp\u002Fdesktop-ui\u002Fvalue-converters","01.csharp\u002F12.desktop-ui\u002F19.value-converters",{"title":1495,"path":1496,"stem":1497},"Data Templates — Візуалізація об'єктів у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Fdata-templates","01.csharp\u002F12.desktop-ui\u002F20.data-templates",{"title":1499,"path":1500,"stem":1501},"Collections Binding Part 1 — ObservableCollection та ItemsControl","\u002Fcsharp\u002Fdesktop-ui\u002Fcollections-binding-part1","01.csharp\u002F12.desktop-ui\u002F21.collections-binding-part1",{"title":1503,"path":1504,"stem":1505},"Collections Binding Part 2 — ICollectionView, Filtering, Sorting та Virtualization","\u002Fcsharp\u002Fdesktop-ui\u002Fcollections-binding-part2","01.csharp\u002F12.desktop-ui\u002F21.collections-binding-part2",{"title":1507,"path":1508,"stem":1509},"MVVM Pattern — Від Spaghetti Code до архітектури","\u002Fcsharp\u002Fdesktop-ui\u002Fmvvm-pattern","01.csharp\u002F12.desktop-ui\u002F22.mvvm-pattern",{"title":1511,"path":1512,"stem":1513},"ViewModel Implementation — Від BaseViewModel до валідації","\u002Fcsharp\u002Fdesktop-ui\u002Fviewmodel-implementation","01.csharp\u002F12.desktop-ui\u002F23.viewmodel-implementation",{"title":1515,"path":1516,"stem":1517},"Commands — Від event handlers до декларативних команд","\u002Fcsharp\u002Fdesktop-ui\u002Fcommands","01.csharp\u002F12.desktop-ui\u002F24.commands",{"title":1519,"path":1520,"stem":1521},"MVVM Toolkit — MVVM без boilerplate через Source Generators","\u002Fcsharp\u002Fdesktop-ui\u002Fmvvm-toolkit","01.csharp\u002F12.desktop-ui\u002F25.mvvm-toolkit",{"title":1523,"path":1524,"stem":1525},"Messenger Pattern — Комунікація між ViewModel без прямих посилань","\u002Fcsharp\u002Fdesktop-ui\u002Fmessenger-pattern","01.csharp\u002F12.desktop-ui\u002F26.messenger-pattern",{"title":1527,"path":1528,"stem":1529},"Стилі WPF — CSS для десктопу","\u002Fcsharp\u002Fdesktop-ui\u002Fstyles-basics","01.csharp\u002F12.desktop-ui\u002F27.styles-basics",{"title":1531,"path":1532,"stem":1533},"CSS-like стилі Avalonia","\u002Fcsharp\u002Fdesktop-ui\u002F27a.avalonia-css-styling","01.csharp\u002F12.desktop-ui\u002F27a.avalonia-css-styling",{"title":1535,"path":1536,"stem":1537},"Control Templates — Частина 1. Концепція та TemplateBinding","\u002Fcsharp\u002Fdesktop-ui\u002Fcontrol-templates-part1","01.csharp\u002F12.desktop-ui\u002F28.control-templates-part1",{"title":1539,"path":1540,"stem":1541},"Control Templates — Частина 2. Named Parts та ContentPresenter","\u002Fcsharp\u002Fdesktop-ui\u002Fcontrol-templates-part2","01.csharp\u002F12.desktop-ui\u002F28.control-templates-part2",{"title":1543,"path":1544,"stem":1545},"Control Themes в Avalonia — нова ера стилізації","\u002Fcsharp\u002Fdesktop-ui\u002F28a.avalonia-control-themes","01.csharp\u002F12.desktop-ui\u002F28a.avalonia-control-themes",{"title":1547,"path":1548,"stem":1549},"Triggers та Visual State Manager у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Ftriggers-visual-states","01.csharp\u002F12.desktop-ui\u002F29.triggers-visual-states",{"title":1551,"path":1552,"stem":1553},"Pseudo-classes в Avalonia — замість WPF Triggers","\u002Fcsharp\u002Fdesktop-ui\u002F29a.avalonia-pseudo-classes","01.csharp\u002F12.desktop-ui\u002F29a.avalonia-pseudo-classes",{"title":1555,"path":1556,"stem":1557},"Теми та ресурсні словники у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Fresources-themes","01.csharp\u002F12.desktop-ui\u002F30.resources-themes",{"title":1559,"path":1560,"stem":1561},"Avalonia Themes — Fluent Design та система тематизації","\u002Fcsharp\u002Fdesktop-ui\u002F30a.avalonia-themes-fluent","01.csharp\u002F12.desktop-ui\u002F30a.avalonia-themes-fluent",{"title":1563,"path":1564,"stem":1565},"Контроли колекцій — глибоке занурення","\u002Fcsharp\u002Fdesktop-ui\u002Fcollection-controls","01.csharp\u002F12.desktop-ui\u002F31.collection-controls",{"title":1567,"path":1568,"stem":1569},"DataGrid — колонки та базове відображення","\u002Fcsharp\u002Fdesktop-ui\u002Fdatagrid-part1","01.csharp\u002F12.desktop-ui\u002F32.datagrid-part1",{"title":1571,"path":1572,"stem":1573},"DataGrid — сортування, фільтрація, редагування","\u002Fcsharp\u002Fdesktop-ui\u002Fdatagrid-part2","01.csharp\u002F12.desktop-ui\u002F32.datagrid-part2",{"title":1575,"path":1576,"stem":1577},"TreeView та GridView","\u002Fcsharp\u002Fdesktop-ui\u002Ftreeview-listview","01.csharp\u002F12.desktop-ui\u002F33.treeview-listview",{"title":1579,"path":1580,"stem":1581},"Меню, Toolbar, ContextMenu, StatusBar","\u002Fcsharp\u002Fdesktop-ui\u002Fmenus-toolbars","01.csharp\u002F12.desktop-ui\u002F34.menus-toolbars",{"title":1583,"path":1584,"stem":1585},"Навігація та керування вікнами. Частина 1: вікна та сторінки","\u002Fcsharp\u002Fdesktop-ui\u002Fnavigation-windows-part1","01.csharp\u002F12.desktop-ui\u002F35.navigation-windows-part1",{"title":1587,"path":1588,"stem":1589},"Навігація та керування вікнами. Частина 2: MVVM-навігація","\u002Fcsharp\u002Fdesktop-ui\u002Fnavigation-windows-part2","01.csharp\u002F12.desktop-ui\u002F35.navigation-windows-part2",{"title":1591,"path":1592,"stem":1593},"Avalonia — Навігація та діалоги","\u002Fcsharp\u002Fdesktop-ui\u002F35a.avalonia-navigation-dialogs","01.csharp\u002F12.desktop-ui\u002F35a.avalonia-navigation-dialogs",{"title":1595,"path":1596,"stem":1597},"Діалоги та File Pickers у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Fdialogs-file-pickers","01.csharp\u002F12.desktop-ui\u002F36.dialogs-file-pickers",{"title":1599,"path":1600,"stem":1601},"UserControl: компонентний підхід у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Fuser-controls","01.csharp\u002F12.desktop-ui\u002F37.user-controls",{"title":1603,"path":1604,"stem":1605},"Custom Controls: Lookless Controls у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Fcustom-controls","01.csharp\u002F12.desktop-ui\u002F38.custom-controls",{"title":1607,"path":1608,"stem":1609},"Avalonia TemplatedControl — Lookless Controls","\u002Fcsharp\u002Fdesktop-ui\u002F38a.avalonia-templated-controls","01.csharp\u002F12.desktop-ui\u002F38a.avalonia-templated-controls",{"title":1611,"path":1612,"stem":1613},"Анімації у WPF: Storyboard та Easing Functions","\u002Fcsharp\u002Fdesktop-ui\u002Fanimations-transitions","01.csharp\u002F12.desktop-ui\u002F39.animations-transitions",{"title":1615,"path":1616,"stem":1617},"Анімації в Avalonia","\u002Fcsharp\u002Fdesktop-ui\u002F39a.avalonia-animations","01.csharp\u002F12.desktop-ui\u002F39a.avalonia-animations",{"title":1619,"path":1620,"stem":1621},"2D Графіка та Мультимедіа у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Fmedia-graphics","01.csharp\u002F12.desktop-ui\u002F40.media-graphics",{"title":1623,"path":1624,"stem":1625},"Dependency Injection у WPF та Avalonia","\u002Fcsharp\u002Fdesktop-ui\u002Fdi-integration","01.csharp\u002F12.desktop-ui\u002F41.di-integration",{"title":1627,"path":1628,"stem":1629},"SQLite та EF Core у десктопних додатках","\u002Fcsharp\u002Fdesktop-ui\u002Fdata-persistence-part1","01.csharp\u002F12.desktop-ui\u002F42.data-persistence-part1",{"title":1631,"path":1632,"stem":1633},"Repository Pattern та Unit of Work","\u002Fcsharp\u002Fdesktop-ui\u002Fdata-persistence-part2","01.csharp\u002F12.desktop-ui\u002F43.data-persistence-part2",{"title":1635,"path":1636,"stem":1637},"Тестування ViewModels","\u002Fcsharp\u002Fdesktop-ui\u002Fviewmodel-testing","01.csharp\u002F12.desktop-ui\u002F44.viewmodel-testing",{"title":1639,"path":1640,"stem":1641},"Avalonia Headless Testing — тестування UI без вікон","\u002Fcsharp\u002Fdesktop-ui\u002F44a.avalonia-headless-testing","01.csharp\u002F12.desktop-ui\u002F44a.avalonia-headless-testing",{"title":1643,"path":1644,"stem":1645},"Кросплатформна розробка з Avalonia","\u002Fcsharp\u002Fdesktop-ui\u002Favalonia-cross-platform","01.csharp\u002F12.desktop-ui\u002F45.avalonia-cross-platform",{"title":1647,"path":1648,"stem":1649},"Пакування та розгортання Avalonia додатків","\u002Fcsharp\u002Fdesktop-ui\u002Favalonia-packaging-deployment","01.csharp\u002F12.desktop-ui\u002F46.avalonia-packaging-deployment",{"title":1651,"path":1652,"stem":1653},"Розгортання WPF застосунків","\u002Fcsharp\u002Fdesktop-ui\u002Fwpf-packaging-deployment","01.csharp\u002F12.desktop-ui\u002F47.wpf-packaging-deployment",{"title":1655,"icon":658,"path":1656,"stem":1657,"children":1658,"page":59},"Network Programming","\u002Fcsharp\u002Fnetwork-programming","01.csharp\u002F13.network-programming",[1659,1663,1667,1671,1675,1679,1683,1687,1691,1695,1699],{"title":1660,"path":1661,"stem":1662},"Основи комп'ютерних мереж","\u002Fcsharp\u002Fnetwork-programming\u002Ffoundations","01.csharp\u002F13.network-programming\u002F01.foundations",{"title":1664,"path":1665,"stem":1666},"Модель OSI та стек TCP\u002FIP","\u002Fcsharp\u002Fnetwork-programming\u002Fosi-model","01.csharp\u002F13.network-programming\u002F02.osi-model",{"title":1668,"path":1669,"stem":1670},"IP-протокол та адресація","\u002Fcsharp\u002Fnetwork-programming\u002Fip-addressing","01.csharp\u002F13.network-programming\u002F03.ip-addressing",{"title":1672,"path":1673,"stem":1674},"UDP — протокол без з'єднання","\u002Fcsharp\u002Fnetwork-programming\u002Fudp","01.csharp\u002F13.network-programming\u002F05.udp",{"title":1676,"path":1677,"stem":1678},"UDP Broadcast та Multicast","\u002Fcsharp\u002Fnetwork-programming\u002Fudp-broadcast-multicast","01.csharp\u002F13.network-programming\u002F06.udp-broadcast-multicast",{"title":1680,"path":1681,"stem":1682},"HTTP — протокол вебу","\u002Fcsharp\u002Fnetwork-programming\u002Fhttp-fundamentals","01.csharp\u002F13.network-programming\u002F07.http-fundamentals",{"title":1684,"path":1685,"stem":1686},"HttpListener — вбудований HTTP-сервер .NET","\u002Fcsharp\u002Fnetwork-programming\u002F07a.http-listener","01.csharp\u002F13.network-programming\u002F07a.http-listener",{"title":1688,"path":1689,"stem":1690},"HTTP Advanced — cookies, аутентифікація та HTTPS","\u002Fcsharp\u002Fnetwork-programming\u002Fhttp-advanced","01.csharp\u002F13.network-programming\u002F08.http-advanced",{"title":1692,"path":1693,"stem":1694},"SMTP та протоколи електронної пошти","\u002Fcsharp\u002Fnetwork-programming\u002Fsmtp","01.csharp\u002F13.network-programming\u002F09.smtp",{"title":1696,"path":1697,"stem":1698},"WebSocket — повнодуплексний протокол реального часу","\u002Fcsharp\u002Fnetwork-programming\u002Fwebsockets","01.csharp\u002F13.network-programming\u002F10.websockets",{"title":1700,"path":1701,"stem":1702},"TLS\u002FSSL — криптографічний захист мережевих з'єднань","\u002Fcsharp\u002Fnetwork-programming\u002Ftls-ssl","01.csharp\u002F13.network-programming\u002F11.tls-ssl",{"title":1704,"path":1705,"stem":1706},"C# & .NET: The Ultimate Roadmap","\u002Fcsharp\u002Froadmap","01.csharp\u002Froadmap",{"title":1708,"icon":1709,"path":1710,"stem":1711,"children":1712,"page":59},"C++","i-devicon-cplusplus","\u002Fcpp","02.cpp",[1713,1717,1721,1725,1729,1733,1737,1741,1745,1748,1752,1756,1760,1764,1768,1772,1776,1780,1784,1788,1792,1796,1800,1804,1808,1812,1816,1820,1824,1828,1832,1836,1840,1844,1848,1852,1856,1860,1864,1868,1872,1876,1880,1884,1888,1892,1896,1900,1904,1908,1912,1916,1920,1924,1928,1932,1936,1940,1944,1948,1952,1956,1960,1964,1968,1972,1976,1980,1984,1988,1992,1996,2000,2004,2008,2012,2016,2020,2024,2028,2032,2036],{"title":1714,"path":1715,"stem":1716},"Вступ у програмування та алгоритми","\u002Fcpp\u002Fintro-algorithms","02.cpp\u002F01.intro-algorithms",{"title":1718,"path":1719,"stem":1720},"Code Style: угоди про оформлення коду","\u002Fcpp\u002Fcode-style","02.cpp\u002F02.code-style",{"title":1722,"path":1723,"stem":1724},"Середовище розробки та перший проєкт","\u002Fcpp\u002Fide-setup","02.cpp\u002F03.ide-setup",{"title":1726,"path":1727,"stem":1728},"Вивід даних на екран","\u002Fcpp\u002Fdata-output","02.cpp\u002F04.data-output",{"title":1730,"path":1731,"stem":1732},"Типи даних, змінні та константи","\u002Fcpp\u002Fdata-types-variables","02.cpp\u002F05.data-types-variables",{"title":1734,"path":1735,"stem":1736},"Ввід даних з клавіатури","\u002Fcpp\u002Fdata-input","02.cpp\u002F06.data-input",{"title":1738,"path":1739,"stem":1740},"Оператори, перетворення типів та логічні операції","\u002Fcpp\u002Foperators-type-conversion","02.cpp\u002F07.operators-type-conversion",{"title":1742,"path":1743,"stem":1744},"Цикли","\u002Fcpp\u002Floops","02.cpp\u002F08.loops",{"title":32,"path":1746,"stem":1747},"\u002Fcpp\u002Farrays","02.cpp\u002F09.arrays",{"title":1749,"path":1750,"stem":1751},"Алгоритми сортування та аналіз складності","\u002Fcpp\u002Fsorting","02.cpp\u002F10.sorting",{"title":1753,"path":1754,"stem":1755},"Алгоритми пошуку","\u002Fcpp\u002Fsearching","02.cpp\u002F11.searching",{"title":1757,"path":1758,"stem":1759},"Функції: основи","\u002Fcpp\u002Ffunctions-basics","02.cpp\u002F12.functions-basics",{"title":1761,"path":1762,"stem":1763},"Функції: прототипи, область видимості та додаткові можливості","\u002Fcpp\u002Ffunctions-scope","02.cpp\u002F13.functions-scope",{"title":1765,"path":1766,"stem":1767},"Функції: перевантаження та шаблони","\u002Fcpp\u002Ffunctions-overloading-templates","02.cpp\u002F14.functions-overloading-templates",{"title":1769,"path":1770,"stem":1771},"Вказівники: основи","\u002Fcpp\u002Fpointers-basics","02.cpp\u002F15.pointers-basics",{"title":1773,"path":1774,"stem":1775},"Посилання (References)","\u002Fcpp\u002Freferences","02.cpp\u002F16.references",{"title":1777,"path":1778,"stem":1779},"Вказівники, const і масиви","\u002Fcpp\u002Fpointers-const-arrays","02.cpp\u002F17.pointers-const-arrays",{"title":1781,"path":1782,"stem":1783},"Адресна арифметика","\u002Fcpp\u002Fpointer-arithmetic","02.cpp\u002F18.pointer-arithmetic",{"title":1785,"path":1786,"stem":1787},"Динамічна пам'ять","\u002Fcpp\u002Fdynamic-memory","02.cpp\u002F19.dynamic-memory",{"title":1789,"path":1790,"stem":1791},"Вказівники типу void","\u002Fcpp\u002Fvoid-pointers","02.cpp\u002F20.void-pointers",{"title":1793,"path":1794,"stem":1795},"Вказівники на вказівники","\u002Fcpp\u002Fpointers-to-pointers","02.cpp\u002F21.pointers-to-pointers",{"title":1797,"path":1798,"stem":1799},"Оператор доступу до членів через вказівник (->)","\u002Fcpp\u002Fmember-access-operator","02.cpp\u002F22.member-access-operator",{"title":1801,"path":1802,"stem":1803},"Цикл for-each (Range-based for)","\u002Fcpp\u002Fforeach-loop","02.cpp\u002F23.foreach-loop",{"title":1805,"path":1806,"stem":1807},"Вказівники на функції","\u002Fcpp\u002Ffunction-pointers","02.cpp\u002F24.function-pointers",{"title":1809,"path":1810,"stem":1811},"Лямбда-вирази","\u002Fcpp\u002Flambdas","02.cpp\u002F25.lambdas",{"title":1813,"path":1814,"stem":1815},"Лямбда-захоплення","\u002Fcpp\u002Flambda-captures","02.cpp\u002F26.lambda-captures",{"title":1817,"path":1818,"stem":1819},"Еліпсис","\u002Fcpp\u002Fellipsis","02.cpp\u002F27.ellipsis",{"title":1821,"path":1822,"stem":1823},"Безпечні альтернативи еліпсису","\u002Fcpp\u002F27a.ellipsis","02.cpp\u002F27a.ellipsis",{"title":1825,"path":1826,"stem":1827},"Аргументи командного рядка","\u002Fcpp\u002Fcommand-line-arguments","02.cpp\u002F28.command-line-arguments",{"title":1829,"path":1830,"stem":1831},"Перерахування (enum)","\u002Fcpp\u002Fenum","02.cpp\u002F29.enum",{"title":1833,"path":1834,"stem":1835},"Класи-перерахування (enum class)","\u002Fcpp\u002Fenum-class","02.cpp\u002F30.enum-class",{"title":1837,"path":1838,"stem":1839},"Псевдоніми типів (typedef і using)","\u002Fcpp\u002Ftype-aliases","02.cpp\u002F31.type-aliases",{"title":1841,"path":1842,"stem":1843},"Системи числення та двійкова арифметика","\u002Fcpp\u002Fnumber-systems","02.cpp\u002F32.number-systems",{"title":1845,"path":1846,"stem":1847},"Структури (struct): агрегування даних","\u002Fcpp\u002Fstruct","02.cpp\u002F33.struct",{"title":1849,"path":1850,"stem":1851},"Структури у функціях","\u002Fcpp\u002Fstruct-functions","02.cpp\u002F34.struct-functions",{"title":1853,"path":1854,"stem":1855},"Масиви структур і вкладені структури","\u002Fcpp\u002Fstruct-arrays","02.cpp\u002F35.struct-arrays",{"title":1857,"path":1858,"stem":1859},"Патерни struct та межі застосування","\u002Fcpp\u002Fstruct-patterns","02.cpp\u002F36.struct-patterns",{"title":1861,"path":1862,"stem":1863},"Символи та таблиця ASCII","\u002Fcpp\u002Fascii-characters","02.cpp\u002F37.ascii-characters",{"title":1865,"path":1866,"stem":1867},"Unicode та кодування UTF","\u002Fcpp\u002Funicode-utf","02.cpp\u002F38.unicode-utf",{"title":1869,"path":1870,"stem":1871},"C-style рядки","\u002Fcpp\u002Fc-strings","02.cpp\u002F39.c-strings",{"title":1873,"path":1874,"stem":1875},"Вступ до std::string","\u002Fcpp\u002Fstd-string-intro","02.cpp\u002F40.std-string-intro",{"title":1877,"path":1878,"stem":1879},"Довжина, ємність та доступ до символів std::string","\u002Fcpp\u002Fstd-string-capacity-access","02.cpp\u002F41.std-string-capacity-access",{"title":1881,"path":1882,"stem":1883},"Модифікація std::string: присвоювання, додавання, вставка, видалення та заміна","\u002Fcpp\u002Fstd-string-modification","02.cpp\u002F42.std-string-modification",{"title":1885,"path":1886,"stem":1887},"Пошук у std::string: find, npos та практичні патерни","\u002Fcpp\u002Fstd-string-search","02.cpp\u002F43.std-string-search",{"title":1889,"path":1890,"stem":1891},"std::string_view: невласницький погляд на рядок без копіювання","\u002Fcpp\u002Fstd-string-view","02.cpp\u002F44.std-string-view",{"title":1893,"path":1894,"stem":1895},"Об'єднання (union): один блок пам'яті, кілька інтерпретацій","\u002Fcpp\u002Funion","02.cpp\u002F45.union",{"title":1897,"path":1898,"stem":1899},"Організація коду: файли, препроцесор, простори імен","\u002Fcpp\u002Fmultifile-programs","02.cpp\u002F46.multifile-programs",{"title":1901,"path":1902,"stem":1903},"Робота з файлами: C-стиль (stdio.h)","\u002Fcpp\u002Fc-style-files","02.cpp\u002F47.c-style-files",{"title":1905,"path":1906,"stem":1907},"Робота з файлами: C++-стиль (fstream)","\u002Fcpp\u002Fcpp-style-files","02.cpp\u002F48.cpp-style-files",{"title":1909,"path":1910,"stem":1911},"Введення в ООП: від процедур до об'єктів","\u002Fcpp\u002Foop-intro","02.cpp\u002F49.oop-intro",{"title":1913,"path":1914,"stem":1915},"Класи, об'єкти та методи","\u002Fcpp\u002Fclasses-objects-methods","02.cpp\u002F50.classes-objects-methods",{"title":1917,"path":1918,"stem":1919},"Специфікатори доступу та інкапсуляція","\u002Fcpp\u002Faccess-specifiers-encapsulation","02.cpp\u002F51.access-specifiers-encapsulation",{"title":1921,"path":1922,"stem":1923},"Конструктори: гарантована ініціалізація об'єктів","\u002Fcpp\u002Fconstructors","02.cpp\u002F52.constructors",{"title":1925,"path":1926,"stem":1927},"Списки ініціалізації та делегуючі конструктори","\u002Fcpp\u002Finit-lists-delegating","02.cpp\u002F53.init-lists-delegating",{"title":1929,"path":1930,"stem":1931},"Деструктори та ідіома RAII","\u002Fcpp\u002Fdestructors-raii","02.cpp\u002F54.destructors-raii",{"title":1933,"path":1934,"stem":1935},"Прихований покажчик this","\u002Fcpp\u002Fthis-pointer","02.cpp\u002F55.this-pointer",{"title":1937,"path":1938,"stem":1939},"Розподіл класу на заголовковий файл та файл реалізації","\u002Fcpp\u002Fclass-header-source","02.cpp\u002F56.class-header-source",{"title":1941,"path":1942,"stem":1943},"Класи та const","\u002Fcpp\u002Fconst-classes","02.cpp\u002F57.const-classes",{"title":1945,"path":1946,"stem":1947},"Статичні члени класу","\u002Fcpp\u002Fstatic-members","02.cpp\u002F58.static-members",{"title":1949,"path":1950,"stem":1951},"Конструктор копіювання та запобігання копіюванню","\u002Fcpp\u002Fcopy-constructor","02.cpp\u002F59.copy-constructor",{"title":1953,"path":1954,"stem":1955},"Дружні функції та класи","\u002Fcpp\u002Ffriend-functions-classes","02.cpp\u002F60.friend-functions-classes",{"title":1957,"path":1958,"stem":1959},"Анонімні об'єкти та вкладені типи","\u002Fcpp\u002Fanonymous-objects-nested-types","02.cpp\u002F61.anonymous-objects-nested-types",{"title":1961,"path":1962,"stem":1963},"Вимірювання часу виконання: RAII-таймер","\u002Fcpp\u002Ftiming-raii-timer","02.cpp\u002F62.timing-raii-timer",{"title":1965,"path":1966,"stem":1967},"Перевантаження операторів: основи та арифметика","\u002Fcpp\u002Foperator-overloading-basics","02.cpp\u002F63.operator-overloading-basics",{"title":1969,"path":1970,"stem":1971},"Перевантаження операторів: методи класу та спеціальні оператори","\u002Fcpp\u002Foperator-overloading-methods","02.cpp\u002F64.operator-overloading-methods",{"title":1973,"path":1974,"stem":1975},"Приведення типів, explicit та delete","\u002Fcpp\u002Ftypecast-explicit-delete","02.cpp\u002F65.typecast-explicit-delete",{"title":1977,"path":1978,"stem":1979},"Глибоке копіювання та оператор присвоювання","\u002Fcpp\u002Fdeep-copy-assignment","02.cpp\u002F66.deep-copy-assignment",{"title":1981,"path":1982,"stem":1983},"Зв'язки між об'єктами: композиція та агрегація","\u002Fcpp\u002Fcomposition-aggregation","02.cpp\u002F67.composition-aggregation",{"title":1985,"path":1986,"stem":1987},"Асоціація та залежність між об","\u002Fcpp\u002Fassociation-dependency","02.cpp\u002F68.association-dependency",{"title":1989,"path":1990,"stem":1991},"Контейнерні класи та `std::initializer_list`","\u002Fcpp\u002Fcontainer-classes-initializer-list","02.cpp\u002F69.container-classes-initializer-list",{"title":1993,"path":1994,"stem":1995},"Конструктори, деструктори та порядок ініціалізації в ієрархіях","\u002Fcpp\u002Finheritance-constructors-destructors","02.cpp\u002F71.inheritance-constructors-destructors",{"title":1997,"path":1998,"stem":1999},"Специфікатор `protected` та види наслідування","\u002Fcpp\u002Finheritance-access-specifiers","02.cpp\u002F72.inheritance-access-specifiers",{"title":2001,"path":2002,"stem":2003},"Перевизначення методів та приховування імен","\u002Fcpp\u002Fmethod-overriding-hiding","02.cpp\u002F73.method-overriding-hiding",{"title":2005,"path":2006,"stem":2007},"Множинне наслідування та проблема ромба","\u002Fcpp\u002Fmultiple-inheritance-diamond","02.cpp\u002F74.multiple-inheritance-diamond",{"title":2009,"path":2010,"stem":2011},"Вказівники на базовий клас та віртуальні функції","\u002Fcpp\u002Fvirtual-functions-polymorphism","02.cpp\u002F75.virtual-functions-polymorphism",{"title":2013,"path":2014,"stem":2015},"Специфікатори override, final та віртуальні деструктори","\u002Fcpp\u002Foverride-final-virtual-destructors","02.cpp\u002F76.override-final-virtual-destructors",{"title":2017,"path":2018,"stem":2019},"Внутрішні механізми поліморфізму: vtable, vptr та зв'язування","\u002Fcpp\u002Fvirtual-table-binding","02.cpp\u002F77.virtual-table-binding",{"title":2021,"path":2022,"stem":2023},"Абстрактні класи та чисті віртуальні функції","\u002Fcpp\u002Fabstract-classes-interfaces","02.cpp\u002F78.abstract-classes-interfaces",{"title":2025,"path":2026,"stem":2027},"Зрізка об'єктів, віртуальне наслідування та поліморфний вивід","\u002Fcpp\u002Fslicing-virtual-base-io","02.cpp\u002F79.slicing-virtual-base-io",{"title":2029,"path":2030,"stem":2031},"Динамічне приведення типів (`dynamic_cast`) та RTTI","\u002Fcpp\u002Fdynamic-cast-rtti","02.cpp\u002F80.dynamic-cast-rtti",{"title":2033,"path":2034,"stem":2035},"Шаблони функцій: узагальнене програмування","\u002Fcpp\u002Ffunction-templates","02.cpp\u002F81.function-templates",{"title":2037,"path":2038,"stem":2039},"Шаблони класів та NTTP-параметри","\u002Fcpp\u002Fclass-templates-nttp","02.cpp\u002F82.class-templates-nttp",{"title":2041,"icon":2042,"path":2043,"stem":2044,"children":2045,"page":59},"JavaScript","i-devicon-javascript","\u002Fjavascript","03.javascript",[2046,2072,2126,2148,2452,2490,2508],{"title":2047,"icon":2048,"path":2049,"stem":2050,"children":2051,"page":59},"Events","i-lucide-mouse-pointer-click","\u002Fjavascript\u002Fevents","03.javascript\u002F01.events",[2052,2056,2060,2064,2068],{"title":2053,"path":2054,"stem":2055},"Вступ до подій браузера","\u002Fjavascript\u002Fevents\u002Fintro","03.javascript\u002F01.events\u002F01.intro",{"title":2057,"path":2058,"stem":2059},"Бульбашковий механізм (Bubbling) та занурення (Capturing)","\u002Fjavascript\u002Fevents\u002Fbubbling-capturing","03.javascript\u002F01.events\u002F02.bubbling-capturing",{"title":2061,"path":2062,"stem":2063},"Делегування подій (Event Delegation)","\u002Fjavascript\u002Fevents\u002Fdelegate-events","03.javascript\u002F01.events\u002F03.delegate-events",{"title":2065,"path":2066,"stem":2067},"Типові дії браузера та preventDefault()","\u002Fjavascript\u002Fevents\u002Fprevent-default","03.javascript\u002F01.events\u002F04.prevent-default",{"title":2069,"path":2070,"stem":2071},"Запуск користувацьких подій (Custom Events)","\u002Fjavascript\u002Fevents\u002Fcustom-events","03.javascript\u002F01.events\u002F05.custom-events",{"title":2073,"icon":2074,"path":2075,"stem":2076,"children":2077,"page":59},"Network","i-lucide-globe","\u002Fjavascript\u002Fnetwork","03.javascript\u002F02.network",[2078,2082,2086,2090,2094,2098,2102,2106,2110,2114,2118,2122],{"title":2079,"path":2080,"stem":2081},"Fetch API - Сучасний підхід до HTTP-запитів","\u002Fjavascript\u002Fnetwork\u002F01-fetch-api","03.javascript\u002F02.network\u002F01-fetch-api",{"title":2083,"path":2084,"stem":2085},"FormData - Робота з формами та файлами","\u002Fjavascript\u002Fnetwork\u002F02-formdata","03.javascript\u002F02.network\u002F02-formdata",{"title":2087,"path":2088,"stem":2089},"Відстеження прогресу завантаження","\u002Fjavascript\u002Fnetwork\u002F03-download-progress","03.javascript\u002F02.network\u002F03-download-progress",{"title":2091,"path":2092,"stem":2093},"Переривання fetch-запитів","\u002Fjavascript\u002Fnetwork\u002F04-abort-requests","03.javascript\u002F02.network\u002F04-abort-requests",{"title":2095,"path":2096,"stem":2097},"CORS - Запити між різними джерелами","\u002Fjavascript\u002Fnetwork\u002F05-cors","03.javascript\u002F02.network\u002F05-cors",{"title":2099,"path":2100,"stem":2101},"Fetch API - Повний довідник опцій","\u002Fjavascript\u002Fnetwork\u002F06-fetch-options","03.javascript\u002F02.network\u002F06-fetch-options",{"title":2103,"path":2104,"stem":2105},"URL Objects - Робота з посиланнями","\u002Fjavascript\u002Fnetwork\u002F07-url-objects","03.javascript\u002F02.network\u002F07-url-objects",{"title":2107,"path":2108,"stem":2109},"XMLHttpRequest - AJAX та низькорівневі запити","\u002Fjavascript\u002Fnetwork\u002F08-xmlhttprequest","03.javascript\u002F02.network\u002F08-xmlhttprequest",{"title":2111,"path":2112,"stem":2113},"Відновлюване завантаження файлів","\u002Fjavascript\u002Fnetwork\u002F09-resumable-upload","03.javascript\u002F02.network\u002F09-resumable-upload",{"title":2115,"path":2116,"stem":2117},"Cookies, document.cookie та світ після \"Cookiepocalypse\"","\u002Fjavascript\u002Fnetwork\u002F10-cookies","03.javascript\u002F02.network\u002F10-cookies",{"title":2119,"path":2120,"stem":2121},"js-cookie: Керування Cookies без Болю","\u002Fjavascript\u002Fnetwork\u002F11-js-cookie","03.javascript\u002F02.network\u002F11-js-cookie",{"title":2123,"path":2124,"stem":2125},"Axios: Потужний HTTP-клієнт для JavaScript","\u002Fjavascript\u002Fnetwork\u002F12-axios","03.javascript\u002F02.network\u002F12-axios",{"title":2127,"icon":2128,"path":2129,"stem":2130,"children":2131,"page":59},"Bom","i-lucide-monitor","\u002Fjavascript\u002Fbom","03.javascript\u002F03.bom",[2132,2136,2140,2144],{"title":2133,"path":2134,"stem":2135},"LocalStorage, SessionStorage та patterns збереження даних","\u002Fjavascript\u002Fbom\u002F01-localstorage","03.javascript\u002F03.bom\u002F01-localstorage",{"title":2137,"path":2138,"stem":2139},"Location Object - Керування адресою сторінки","\u002Fjavascript\u002Fbom\u002F02-location-object","03.javascript\u002F03.bom\u002F02-location-object",{"title":2141,"path":2142,"stem":2143},"History API - Керування історією браузера","\u002Fjavascript\u002Fbom\u002F03-history-api","03.javascript\u002F03.bom\u002F03-history-api",{"title":2145,"path":2146,"stem":2147},"Navigator Object - Ідентифікація та Можливості Пристрою","\u002Fjavascript\u002Fbom\u002F04-navigator-object","03.javascript\u002F03.bom\u002F04-navigator-object",{"title":2149,"icon":2150,"path":2151,"stem":2152,"children":2153},"React","i-devicon-react","\u002Fjavascript\u002Freact","03.javascript\u002F04.react\u002Findex",[2154,2155,2159,2163,2167,2171,2234,2269,2421],{"title":2149,"path":2151,"stem":2152},{"title":2156,"path":2157,"stem":2158},"Робота з Формами в React","\u002Fjavascript\u002Freact\u002Freact-forms","03.javascript\u002F04.react\u002F01.react-forms",{"title":2160,"path":2161,"stem":2162},"React Hook Form: Професійна Робота з Формами","\u002Fjavascript\u002Freact\u002Freact-hook-form","03.javascript\u002F04.react\u002F02.react-hook-form",{"title":2164,"path":2165,"stem":2166},"React Hook Form: Глибоке Розуміння Архітектури та Оптимізації","\u002Fjavascript\u002Freact\u002Freact-hook-form-new","03.javascript\u002F04.react\u002F02.react-hook-form-new",{"title":2168,"path":2169,"stem":2170},"Axios та React: Професійна Архітектура Запитів","\u002Fjavascript\u002Freact\u002Fdata-fetching-axios","03.javascript\u002F04.react\u002F03.data-fetching-axios",{"title":2172,"icon":132,"path":2173,"stem":2174,"children":2175},"Tanstack Query","\u002Fjavascript\u002Freact\u002Ftanstack-query","03.javascript\u002F04.react\u002F04.tanstack-query\u002Findex",[2176,2178,2182,2186,2190,2194,2198,2202,2206,2210,2214,2218,2222,2226,2230],{"title":2177,"path":2173,"stem":2174},"TanStack Query: Майстерність Керування Станом Сервера",{"title":2179,"path":2180,"stem":2181},"Парадигма Server State: Чому useEffect недостатньо","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fserver-state-paradigm","03.javascript\u002F04.react\u002F04.tanstack-query\u002F01.server-state-paradigm",{"title":2183,"path":2184,"stem":2185},"Встановлення та Налаштування: Фундамент","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Finstallation-and-devtools","03.javascript\u002F04.react\u002F04.tanstack-query\u002F02.installation-and-devtools",{"title":2187,"path":2188,"stem":2189},"Основи Запитів та Магія Ключів","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fquery-basics-and-keys","03.javascript\u002F04.react\u002F04.tanstack-query\u002F03.query-basics-and-keys",{"title":2191,"path":2192,"stem":2193},"Синхронізація Даних: Життєвий Цикл Запиту","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fdata-synchronization","03.javascript\u002F04.react\u002F04.tanstack-query\u002F04.data-synchronization",{"title":2195,"path":2196,"stem":2197},"Мутації та Інвалідація: Зміна Даних","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fmutations-and-invalidation","03.javascript\u002F04.react\u002F04.tanstack-query\u002F05.mutations-and-invalidation",{"title":2199,"path":2200,"stem":2201},"Оптимістичні Оновлення: Швидше за Світло","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Foptimistic-updates","03.javascript\u002F04.react\u002F04.tanstack-query\u002F06.optimistic-updates",{"title":2203,"path":2204,"stem":2205},"Пагінація та Infinite Scroll","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fpagination-and-load-more","03.javascript\u002F04.react\u002F04.tanstack-query\u002F07.pagination-and-load-more",{"title":2207,"path":2208,"stem":2209},"Просунуті Патерни та Оптимізація","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fadvanced-patterns","03.javascript\u002F04.react\u002F04.tanstack-query\u002F08.advanced-patterns",{"title":2211,"path":2212,"stem":2213},"Архітектура та Best Practices","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Farchitecture-and-best-practices","03.javascript\u002F04.react\u002F04.tanstack-query\u002F09.architecture-and-best-practices",{"title":2215,"path":2216,"stem":2217},"Server-Side Rendering (SSR) та Гідратація","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fserver-side-rendering","03.javascript\u002F04.react\u002F04.tanstack-query\u002F10.server-side-rendering",{"title":2219,"path":2220,"stem":2221},"Стратегії Тестування","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Ftesting-strategies","03.javascript\u002F04.react\u002F04.tanstack-query\u002F11.testing-strategies",{"title":2223,"path":2224,"stem":2225},"Аутентифікація та Обробка Помилок","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fauthentication-and-errors","03.javascript\u002F04.react\u002F04.tanstack-query\u002F12.authentication-and-errors",{"title":2227,"path":2228,"stem":2229},"React Suspense та Майбутнє","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Freact-suspense","03.javascript\u002F04.react\u002F04.tanstack-query\u002F13.react-suspense",{"title":2231,"path":2232,"stem":2233},"Глибоке Занурення в Продуктивність","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fperformance-deep-dive","03.javascript\u002F04.react\u002F04.tanstack-query\u002F14.performance-deep-dive",{"title":2235,"icon":2150,"path":2236,"stem":2237,"children":2238},"React Router","\u002Fjavascript\u002Freact\u002Freact-router","03.javascript\u002F04.react\u002F05.react-router\u002Findex",[2239,2241,2245,2249,2253,2257,2261,2265],{"title":2240,"path":2236,"stem":2237},"React Router: Навігаційна система сучасного вебу",{"title":2242,"path":2243,"stem":2244},"Налаштування та Базовий Роутинг","\u002Fjavascript\u002Freact\u002Freact-router\u002Fsetup-and-basic-routing","03.javascript\u002F04.react\u002F05.react-router\u002F01.setup-and-basic-routing",{"title":2246,"path":2247,"stem":2248},"Динамічна Навігація","\u002Fjavascript\u002Freact\u002Freact-router\u002Fnavigation-and-links","03.javascript\u002F04.react\u002F05.react-router\u002F02.navigation-and-links",{"title":2250,"path":2251,"stem":2252},"Вкладені Маршрути та Макети","\u002Fjavascript\u002Freact\u002Freact-router\u002Fnested-routes-and-layouts","03.javascript\u002F04.react\u002F05.react-router\u002F03.nested-routes-and-layouts",{"title":2254,"path":2255,"stem":2256},"Динамічні Маршрути та Параметри","\u002Fjavascript\u002Freact\u002Freact-router\u002Fdynamic-routing","03.javascript\u002F04.react\u002F05.react-router\u002F04.dynamic-routing",{"title":2258,"path":2259,"stem":2260},"Data APIs: Loaders та Actions","\u002Fjavascript\u002Freact\u002Freact-router\u002Fdata-loading","03.javascript\u002F04.react\u002F05.react-router\u002F05.data-loading",{"title":2262,"path":2263,"stem":2264},"Просунуті Патерни","\u002Fjavascript\u002Freact\u002Freact-router\u002Fadvanced-patterns","03.javascript\u002F04.react\u002F05.react-router\u002F06.advanced-patterns",{"title":2266,"path":2267,"stem":2268},"Legacy Routing: Компонентний підхід","\u002Fjavascript\u002Freact\u002Freact-router\u002Flegacy-routing","03.javascript\u002F04.react\u002F05.react-router\u002F07.legacy-routing",{"title":2270,"icon":132,"path":2271,"stem":2272,"children":2273},"Redux","\u002Fjavascript\u002Freact\u002Fredux","03.javascript\u002F04.react\u002F06.redux\u002Findex",[2274,2276,2292,2321,2330,2351,2367,2396],{"title":2275,"path":2271,"stem":2272},"Redux: Еволюція управління станом",{"title":14,"icon":15,"path":2277,"stem":2278,"children":2279,"page":59},"\u002Fjavascript\u002Freact\u002Fredux\u002Ffundamentals","03.javascript\u002F04.react\u002F06.redux\u002F01.fundamentals",[2280,2284,2288],{"title":2281,"path":2282,"stem":2283},"Вступ до State Management","\u002Fjavascript\u002Freact\u002Fredux\u002Ffundamentals\u002Fintro-state-management","03.javascript\u002F04.react\u002F06.redux\u002F01.fundamentals\u002F01.intro-state-management",{"title":2285,"path":2286,"stem":2287},"Філософія Redux та Три Принципи","\u002Fjavascript\u002Freact\u002Fredux\u002Ffundamentals\u002Fredux-philosophy","03.javascript\u002F04.react\u002F06.redux\u002F01.fundamentals\u002F02.redux-philosophy",{"title":2289,"path":2290,"stem":2291},"Чисті функції та Іммутабельність","\u002Fjavascript\u002Freact\u002Fredux\u002Ffundamentals\u002Fpure-functions-immutability","03.javascript\u002F04.react\u002F06.redux\u002F01.fundamentals\u002F03.pure-functions-immutability",{"title":2293,"icon":132,"path":2294,"stem":2295,"children":2296,"page":59},"Classic Redux","\u002Fjavascript\u002Freact\u002Fredux\u002Fclassic-redux","03.javascript\u002F04.react\u002F06.redux\u002F02.classic-redux",[2297,2301,2305,2309,2313,2317],{"title":2298,"path":2299,"stem":2300},"Створення Store (Classic Redux)","\u002Fjavascript\u002Freact\u002Fredux\u002Fclassic-redux\u002Fstore-setup","03.javascript\u002F04.react\u002F06.redux\u002F02.classic-redux\u002F01.store-setup",{"title":2302,"path":2303,"stem":2304},"Actions, Constants та Action Creators","\u002Fjavascript\u002Freact\u002Fredux\u002Fclassic-redux\u002Factions-constants","03.javascript\u002F04.react\u002F06.redux\u002F02.classic-redux\u002F02.actions-constants",{"title":2306,"path":2307,"stem":2308},"Логіка Reducers","\u002Fjavascript\u002Freact\u002Fredux\u002Fclassic-redux\u002Freducers","03.javascript\u002F04.react\u002F06.redux\u002F02.classic-redux\u002F03.reducers",{"title":2310,"path":2311,"stem":2312},"Комбінування Reducers (Root Reducer)","\u002Fjavascript\u002Freact\u002Fredux\u002Fclassic-redux\u002Fdata-flow","03.javascript\u002F04.react\u002F06.redux\u002F02.classic-redux\u002F04.data-flow",{"title":2314,"path":2315,"stem":2316},"Підключення до React (React-Redux)","\u002Fjavascript\u002Freact\u002Fredux\u002Fclassic-redux\u002Freact-redux-connection","03.javascript\u002F04.react\u002F06.redux\u002F02.classic-redux\u002F05.react-redux-connection",{"title":2318,"path":2319,"stem":2320},"Middleware та Асинхронність (Redux Thunk)","\u002Fjavascript\u002Freact\u002Fredux\u002Fclassic-redux\u002Fmiddleware-thunk","03.javascript\u002F04.react\u002F06.redux\u002F02.classic-redux\u002F06.middleware-thunk",{"title":2322,"icon":132,"path":2323,"stem":2324,"children":2325,"page":59},"Transition To Rtk","\u002Fjavascript\u002Freact\u002Fredux\u002Ftransition-to-rtk","03.javascript\u002F04.react\u002F06.redux\u002F03.transition-to-rtk",[2326],{"title":2327,"path":2328,"stem":2329},"Проблеми класичного Redux","\u002Fjavascript\u002Freact\u002Fredux\u002Ftransition-to-rtk\u002Fproblems-with-classic","03.javascript\u002F04.react\u002F06.redux\u002F03.transition-to-rtk\u002F01.problems-with-classic",{"title":2331,"icon":132,"path":2332,"stem":2333,"children":2334,"page":59},"Redux Toolkit","\u002Fjavascript\u002Freact\u002Fredux\u002Fredux-toolkit","03.javascript\u002F04.react\u002F06.redux\u002F04.redux-toolkit",[2335,2339,2343,2347],{"title":2336,"path":2337,"stem":2338},"Налаштування Store з configureStore","\u002Fjavascript\u002Freact\u002Fredux\u002Fredux-toolkit\u002Fconfigure-store","03.javascript\u002F04.react\u002F06.redux\u002F04.redux-toolkit\u002F01.configure-store",{"title":2340,"path":2341,"stem":2342},"createSlice: Революція в Redux","\u002Fjavascript\u002Freact\u002Fredux\u002Fredux-toolkit\u002Fcreate-slice","03.javascript\u002F04.react\u002F06.redux\u002F04.redux-toolkit\u002F02.create-slice",{"title":2344,"path":2345,"stem":2346},"Асинхронність з createAsyncThunk","\u002Fjavascript\u002Freact\u002Fredux\u002Fredux-toolkit\u002Fasync-thunks","03.javascript\u002F04.react\u002F06.redux\u002F04.redux-toolkit\u002F03.async-thunks",{"title":2348,"path":2349,"stem":2350},"04. Entity Adapter: Керування нормалізованим станом","\u002Fjavascript\u002Freact\u002Fredux\u002Fredux-toolkit\u002Fentity-adapter","03.javascript\u002F04.react\u002F06.redux\u002F04.redux-toolkit\u002F04.entity-adapter",{"title":2352,"icon":92,"path":2353,"stem":2354,"children":2355,"page":59},"Advanced","\u002Fjavascript\u002Freact\u002Fredux\u002Fadvanced","03.javascript\u002F04.react\u002F06.redux\u002F05.advanced",[2356,2360,2364],{"title":2357,"path":2358,"stem":2359},"Мемоізація та Селектори: Повний Гайд по Reselect","\u002Fjavascript\u002Freact\u002Fredux\u002Fadvanced\u002Fselectors-reselect","03.javascript\u002F04.react\u002F06.redux\u002F05.advanced\u002F01.selectors-reselect",{"title":2361,"path":2362,"stem":2363},"RTK Query: Архітектура Серверного Кешу","\u002Fjavascript\u002Freact\u002Fredux\u002Fadvanced\u002Frtk-query-intro","03.javascript\u002F04.react\u002F06.redux\u002F05.advanced\u002F02.rtk-query-intro",{"title":2211,"path":2365,"stem":2366},"\u002Fjavascript\u002Freact\u002Fredux\u002Fadvanced\u002Farchitecture-best-practices","03.javascript\u002F04.react\u002F06.redux\u002F05.advanced\u002F03.architecture-best-practices",{"title":2368,"icon":132,"path":2369,"stem":2370,"children":2371,"page":59},"Project Kanban","\u002Fjavascript\u002Freact\u002Fredux\u002Fproject-kanban","03.javascript\u002F04.react\u002F06.redux\u002F06.project-kanban",[2372,2376,2380,2384,2388,2392],{"title":2373,"path":2374,"stem":2375},"Проєкт: Kanban Board (Trello Clone)","\u002Fjavascript\u002Freact\u002Fredux\u002Fproject-kanban\u002Fproject-overview","03.javascript\u002F04.react\u002F06.redux\u002F06.project-kanban\u002F01.project-overview",{"title":2377,"path":2378,"stem":2379},"Налаштування та Типізація","\u002Fjavascript\u002Freact\u002Fredux\u002Fproject-kanban\u002Fsetup-and-types","03.javascript\u002F04.react\u002F06.redux\u002F06.project-kanban\u002F02.setup-and-types",{"title":2381,"path":2382,"stem":2383},"Board Slice: Серце Дошки","\u002Fjavascript\u002Freact\u002Fredux\u002Fproject-kanban\u002Fboard-slice","03.javascript\u002F04.react\u002F06.redux\u002F06.project-kanban\u002F03.board-slice",{"title":2385,"path":2386,"stem":2387},"Логіка Drag & Drop","\u002Fjavascript\u002Freact\u002Fredux\u002Fproject-kanban\u002Fdrag-and-drop-logic","03.javascript\u002F04.react\u002F06.redux\u002F06.project-kanban\u002F04.drag-and-drop-logic",{"title":2389,"path":2390,"stem":2391},"Інтеграція з RTK Query","\u002Fjavascript\u002Freact\u002Fredux\u002Fproject-kanban\u002Frtk-query-integration","03.javascript\u002F04.react\u002F06.redux\u002F06.project-kanban\u002F05.rtk-query-integration",{"title":2393,"path":2394,"stem":2395},"Optimistic Updates","\u002Fjavascript\u002Freact\u002Fredux\u002Fproject-kanban\u002Foptimistic-updates","03.javascript\u002F04.react\u002F06.redux\u002F06.project-kanban\u002F06.optimistic-updates",{"title":2397,"icon":132,"path":2398,"stem":2399,"children":2400,"page":59},"Testing","\u002Fjavascript\u002Freact\u002Fredux\u002Ftesting","03.javascript\u002F04.react\u002F06.redux\u002F07.testing",[2401,2405,2409,2413,2417],{"title":2402,"path":2403,"stem":2404},"Тестування Redux","\u002Fjavascript\u002Freact\u002Fredux\u002Ftesting\u002Fintro-testing","03.javascript\u002F04.react\u002F06.redux\u002F07.testing\u002F01.intro-testing",{"title":2406,"path":2407,"stem":2408},"Тестування Reducers","\u002Fjavascript\u002Freact\u002Fredux\u002Ftesting\u002Ftesting-reducers","03.javascript\u002F04.react\u002F06.redux\u002F07.testing\u002F02.testing-reducers",{"title":2410,"path":2411,"stem":2412},"Тестування Селекторів","\u002Fjavascript\u002Freact\u002Fredux\u002Ftesting\u002Ftesting-selectors","03.javascript\u002F04.react\u002F06.redux\u002F07.testing\u002F03.testing-selectors",{"title":2414,"path":2415,"stem":2416},"Тестування Компонентів (Integration)","\u002Fjavascript\u002Freact\u002Fredux\u002Ftesting\u002Ftesting-components","03.javascript\u002F04.react\u002F06.redux\u002F07.testing\u002F04.testing-components",{"title":2418,"path":2419,"stem":2420},"Тестування Async Thunks","\u002Fjavascript\u002Freact\u002Fredux\u002Ftesting\u002Ftesting-thunks","03.javascript\u002F04.react\u002F06.redux\u002F07.testing\u002F05.testing-thunks",{"title":2422,"icon":132,"path":2423,"stem":2424,"children":2425},"Ui Libraries","\u002Fjavascript\u002Freact\u002Fui-libraries","03.javascript\u002F04.react\u002F07.ui-libraries\u002Findex",[2426,2428,2432,2436,2440,2444,2448],{"title":2427,"path":2423,"stem":2424},"UI Бібліотеки в React",{"title":2429,"path":2430,"stem":2431},"Вступ до UI Бібліотек: Навіщо Винаходити Велосипед Двічі?","\u002Fjavascript\u002Freact\u002Fui-libraries\u002Fintroduction-to-ui-libraries","03.javascript\u002F04.react\u002F07.ui-libraries\u002F01.introduction-to-ui-libraries",{"title":2433,"path":2434,"stem":2435},"Філософія shadcn\u002Fui: \"Not a Component Library\"","\u002Fjavascript\u002Freact\u002Fui-libraries\u002Fshadcn-philosophy","03.javascript\u002F04.react\u002F07.ui-libraries\u002F02.shadcn-philosophy",{"title":2437,"path":2438,"stem":2439},"Установка та Налаштування shadcn\u002Fui","\u002Fjavascript\u002Freact\u002Fui-libraries\u002Fshadcn-installation","03.javascript\u002F04.react\u002F07.ui-libraries\u002F03.shadcn-installation",{"title":2441,"path":2442,"stem":2443},"Базові Компоненти shadcn\u002Fui: Фундамент Інтерфейсу","\u002Fjavascript\u002Freact\u002Fui-libraries\u002Fshadcn-components-basics","03.javascript\u002F04.react\u002F07.ui-libraries\u002F04.shadcn-components-basics",{"title":2445,"path":2446,"stem":2447},"Компоненти Форм: Побудова Інтерактивних Form","\u002Fjavascript\u002Freact\u002Fui-libraries\u002Fshadcn-components-forms","03.javascript\u002F04.react\u002F07.ui-libraries\u002F05.shadcn-components-forms",{"title":2449,"path":2450,"stem":2451},"Складні Компоненти: Dialog, Dropdown, Table та Command","\u002Fjavascript\u002Freact\u002Fui-libraries\u002Fshadcn-components-advanced","03.javascript\u002F04.react\u002F07.ui-libraries\u002F06.shadcn-components-advanced",{"title":2453,"icon":2454,"path":2455,"stem":2456,"children":2457,"page":59},"TypeScript","i-devicon-typescript","\u002Fjavascript\u002Ftypescript","03.javascript\u002F05.typescript",[2458,2462,2466,2470,2474,2478,2482,2486],{"title":2459,"path":2460,"stem":2461},"TypeScript: Броня для вашого коду","\u002Fjavascript\u002Ftypescript\u002Fintro-and-basic-types","03.javascript\u002F05.typescript\u002F01.intro-and-basic-types",{"title":2463,"path":2464,"stem":2465},"Майстерність Моделювання Даних: Інтерфейси та Просунуті Типи","\u002Fjavascript\u002Ftypescript\u002Finterfaces-and-advanced-types","03.javascript\u002F05.typescript\u002F02.interfaces-and-advanced-types",{"title":2467,"path":2468,"stem":2469},"Алхімія Типів: Generics та Utility Types","\u002Fjavascript\u002Ftypescript\u002Fgenerics-and-utilities","03.javascript\u002F05.typescript\u002F03.generics-and-utilities",{"title":2471,"path":2472,"stem":2473},"Архітектура та Шаблони: Класи в TypeScript","\u002Fjavascript\u002Ftypescript\u002Fclasses-and-oop","03.javascript\u002F05.typescript\u002F04.classes-and-oop",{"title":2475,"path":2476,"stem":2477},"Продакшн та Екосистема: Advanced Config & Workflow","\u002Fjavascript\u002Ftypescript\u002Fadvanced-patterns-and-config","03.javascript\u002F05.typescript\u002F05.advanced-patterns-and-config",{"title":2479,"path":2480,"stem":2481},"TypeScript у світі React","\u002Fjavascript\u002Ftypescript\u002Freact-basics","03.javascript\u002F05.typescript\u002F06.react-basics",{"title":2483,"path":2484,"stem":2485},"React + TypeScript: Продвинуті патерни","\u002Fjavascript\u002Ftypescript\u002Freact-advanced","03.javascript\u002F05.typescript\u002F07.react-advanced",{"title":2487,"path":2488,"stem":2489},"React + TypeScript: Екосистема та бібліотеки","\u002Fjavascript\u002Ftypescript\u002Freact-ecosystem","03.javascript\u002F05.typescript\u002F08.react-ecosystem",{"title":2491,"icon":2492,"path":2493,"stem":2494,"children":2495,"page":59},"Асинхронність","i-lucide-refresh-cw","\u002Fjavascript\u002Fasync-programming","03.javascript\u002F06.async-programming",[2496,2500,2504],{"title":2497,"path":2498,"stem":2499},"Event Loop: серце асинхронності в JavaScript","\u002Fjavascript\u002Fasync-programming\u002Fevent-loop-fundamentals","03.javascript\u002F06.async-programming\u002F01.event-loop-fundamentals",{"title":2501,"path":2502,"stem":2503},"Task Queue та Microtask Queue: пріоритети виконання","\u002Fjavascript\u002Fasync-programming\u002Ftask-queues","03.javascript\u002F06.async-programming\u002F02.task-queues",{"title":2505,"path":2506,"stem":2507},"Асинхронні патерни: від Callbacks до Async\u002FAwait","\u002Fjavascript\u002Fasync-programming\u002Fasync-patterns","03.javascript\u002F06.async-programming\u002F03.async-patterns",{"title":2509,"path":2510,"stem":2511},"Atomic Design","\u002Fjavascript\u002Fatomic-design","03.javascript\u002F2.atomic-design",{"title":2513,"icon":2514,"path":2515,"stem":2516,"children":2517,"page":59},"Java","i-devicon-java","\u002Fjava","04.java",[2518,2521,2524,2528,2532,2536,2540],{"title":162,"path":2519,"stem":2520},"\u002Fjava\u002Fdata-mapper-part1","04.java\u002F01.data-mapper-part1",{"title":166,"path":2522,"stem":2523},"\u002Fjava\u002Fdata-mapper-part2","04.java\u002F02.data-mapper-part2",{"title":2525,"path":2526,"stem":2527},"Service Layer: Організація бізнес-логіки","\u002Fjava\u002Fservice-layer","04.java\u002F03.service-layer",{"title":2529,"path":2530,"stem":2531},"Rich Domain Model та State Pattern","\u002Fjava\u002Frich-domain-model","04.java\u002F04.rich-domain-model",{"title":2533,"path":2534,"stem":2535},"Патерни для складної бізнес-логіки","\u002Fjava\u002Fbusiness-logic-patterns","04.java\u002F05.business-logic-patterns",{"title":2537,"path":2538,"stem":2539},"Обробка помилок та валідація","\u002Fjava\u002Ferror-handling-validation","04.java\u002F06.error-handling-validation",{"title":2541,"path":2542,"stem":2543,"children":2544,"page":59},"Проектування баз даних","\u002Fjava\u002Fpr2","04.java\u002Fpr2",[2545,2549,2553,2557,2561,2565,2569,2573,2577,2581,2585,2589,2593,2597,2601,2605,2609,2613,2617,2621,2625,2629,2633,2637,2641,2645,2649,2653,2657,2661,2665,2669,2673,2677,2681,2685,2689],{"title":2546,"path":2547,"stem":2548},"Концептуальне моделювання: Мистецтво розуміння предметної області","\u002Fjava\u002Fpr2\u002Fconceptual-modeling","04.java\u002Fpr2\u002F01.conceptual-modeling",{"title":2550,"path":2551,"stem":2552},"Логічне моделювання: Від бізнес-ідей до структур даних","\u002Fjava\u002Fpr2\u002Flogical-modeling","04.java\u002Fpr2\u002F02.logical-modeling",{"title":2554,"path":2555,"stem":2556},"Нормалізація: Гігієна даних та боротьба з аномаліями","\u002Fjava\u002Fpr2\u002Fnormalization","04.java\u002Fpr2\u002F03.normalization",{"title":2558,"path":2559,"stem":2560},"Фізична схема: Від абстракції до DDL","\u002Fjava\u002Fpr2\u002Fphysical-schema","04.java\u002Fpr2\u002F04.physical-schema",{"title":2562,"path":2563,"stem":2564},"Архітектурна класифікація таблиць","\u002Fjava\u002Fpr2\u002Ftable-classification","04.java\u002Fpr2\u002F05.table-classification",{"title":2566,"path":2567,"stem":2568},"Database Migrations: Версіонування схеми з Flyway","\u002Fjava\u002Fpr2\u002Fdatabase-migrations","04.java\u002Fpr2\u002F06.database-migrations",{"title":2570,"path":2571,"stem":2572},"А що, якби це була не реляційна БД?","\u002Fjava\u002Fpr2\u002Fbeyond-relational","04.java\u002Fpr2\u002F07.beyond-relational",{"title":2574,"path":2575,"stem":2576},"Object-Relational Impedance Mismatch: Два світи, що не хочуть дружити","\u002Fjava\u002Fpr2\u002Fimpedance-mismatch","04.java\u002Fpr2\u002F09.impedance-mismatch",{"title":2578,"path":2579,"stem":2580},"JDBC: Перший контакт із базою даних","\u002Fjava\u002Fpr2\u002Fjdbc-fundamentals","04.java\u002Fpr2\u002F10.jdbc-fundamentals",{"title":2582,"path":2583,"stem":2584},"Якість коду: Spotless, SpotBugs та SonarQube","\u002Fjava\u002Fpr2\u002F10a.code-quality","04.java\u002Fpr2\u002F10a.code-quality",{"title":2586,"path":2587,"stem":2588},"Connection Pool: Патерн Object Pool для JDBC-з'єднань","\u002Fjava\u002Fpr2\u002Fconnection-pool","04.java\u002Fpr2\u002F11.connection-pool",{"title":2590,"path":2591,"stem":2592},"Row Data Gateway: Об'єкт як обгортка рядка таблиці","\u002Fjava\u002Fpr2\u002Frow-data-gateway","04.java\u002Fpr2\u002F12.row-data-gateway",{"title":2594,"path":2595,"stem":2596},"Table Data Gateway: Фасад таблиці як архітектурний відступ","\u002Fjava\u002Fpr2\u002Ftable-data-gateway","04.java\u002Fpr2\u002F13.table-data-gateway",{"title":2598,"path":2599,"stem":2600},"Repository + Data Mapper: Правильна шарова архітектура з JDBC","\u002Fjava\u002Fpr2\u002Frepository-data-mapper","04.java\u002Fpr2\u002F14.repository-data-mapper",{"title":2602,"path":2603,"stem":2604},"Identity Map: Кешування сутностей у рамках сесії","\u002Fjava\u002Fpr2\u002Fidentity-map","04.java\u002Fpr2\u002F15.identity-map",{"title":2606,"path":2607,"stem":2608},"Unit of Work: Відстеження змін і координація JDBC-транзакцій","\u002Fjava\u002Fpr2\u002Funit-of-work","04.java\u002Fpr2\u002F16.unit-of-work",{"title":2610,"path":2611,"stem":2612},"Strategy: Замінювані SQL-стратегії для підтримки різних СУБД","\u002Fjava\u002Fpr2\u002Fstrategy-sql","04.java\u002Fpr2\u002F17.strategy-sql",{"title":2614,"path":2615,"stem":2616},"Proxy: Lazy Loading для One-To-Many колекцій","\u002Fjava\u002Fpr2\u002Fproxy-lazy-loading","04.java\u002Fpr2\u002F18.proxy-lazy-loading",{"title":2618,"path":2619,"stem":2620},"Generic Repository через Java Reflection: анотації та динамічний SQL","\u002Fjava\u002Fpr2\u002Fgeneric-repository-reflection","04.java\u002Fpr2\u002F19.generic-repository-reflection",{"title":2622,"path":2623,"stem":2624},"Specification Pattern: Композиція бізнес-правил для складних запитів","\u002Fjava\u002Fpr2\u002Fspecification-pattern","04.java\u002Fpr2\u002F20.specification-pattern",{"title":2626,"path":2627,"stem":2628},"Розширені можливості Specification Pattern: підзапити, агрегації та гібридний підхід","\u002Fjava\u002Fpr2\u002F20a.advanced-specifications","04.java\u002Fpr2\u002F20a.advanced-specifications",{"title":2630,"path":2631,"stem":2632},"Асинхронність у JDBC: Від блокуючих викликів до CompletableFuture","\u002Fjava\u002Fpr2\u002Fasynchronous-jdbc","04.java\u002Fpr2\u002F21.asynchronous-jdbc",{"title":2634,"path":2635,"stem":2636},"Інтеграційне тестування JDBC-репозиторіїв: Embedded H2 та патерн AAA","\u002Fjava\u002Fpr2\u002Fintegration-testing-h2","04.java\u002Fpr2\u002F22.integration-testing-h2",{"title":2638,"path":2639,"stem":2640},"Testcontainers: Тестування з реальною PostgreSQL у Docker-контейнерах","\u002Fjava\u002Fpr2\u002Fintegration-testing-testcontainers","04.java\u002Fpr2\u002F23.integration-testing-testcontainers",{"title":2642,"path":2643,"stem":2644},"Google Guice: Впровадження залежностей у JavaFX-проєкті","\u002Fjava\u002Fpr2\u002Fdependency-injection-guice","04.java\u002Fpr2\u002F24.dependency-injection-guice",{"title":2646,"path":2647,"stem":2648},"JavaFX: Основи побудови графічних інтерфейсів","\u002Fjava\u002Fpr2\u002Fjavafx-fundamentals","04.java\u002Fpr2\u002F25.javafx-fundamentals",{"title":2650,"path":2651,"stem":2652},"Properties та Bindings: Реактивність у JavaFX","\u002Fjava\u002Fpr2\u002Fjavafx-properties-bindings","04.java\u002Fpr2\u002F26.javafx-properties-bindings",{"title":2654,"path":2655,"stem":2656},"MVC vs MVP vs MVVM: Еволюція архітектурних патернів UI","\u002Fjava\u002Fpr2\u002Fui-architecture-patterns","04.java\u002Fpr2\u002F27.ui-architecture-patterns",{"title":2658,"path":2659,"stem":2660},"MVVM на практиці: Побудова ViewModel","\u002Fjava\u002Fpr2\u002Fmvvm-viewmodel-implementation","04.java\u002Fpr2\u002F28.mvvm-viewmodel-implementation",{"title":2662,"path":2663,"stem":2664},"View та Controller: Зв'язування з ViewModel через FXML","\u002Fjava\u002Fpr2\u002Fmvvm-view-controller","04.java\u002Fpr2\u002F29.mvvm-view-controller",{"title":2666,"path":2667,"stem":2668},"Інтеграція MVVM з Guice: Автоматична ін'єкція залежностей","\u002Fjava\u002Fpr2\u002Fmvvm-guice-integration","04.java\u002Fpr2\u002F30.mvvm-guice-integration",{"title":2670,"path":2671,"stem":2672},"Валідація та обробка помилок у MVVM","\u002Fjava\u002Fpr2\u002Fmvvm-validation-error-handling","04.java\u002Fpr2\u002F31.mvvm-validation-error-handling",{"title":2674,"path":2675,"stem":2676},"Навігація та управління екранами у JavaFX MVVM","\u002Fjava\u002Fpr2\u002Fmvvm-navigation-screen-management","04.java\u002Fpr2\u002F32.mvvm-navigation-screen-management",{"title":2678,"path":2679,"stem":2680},"Тестування JavaFX MVVM-додатків","\u002Fjava\u002Fpr2\u002Fmvvm-testing","04.java\u002Fpr2\u002F33.mvvm-testing",{"title":2682,"path":2683,"stem":2684},"Стилізація та теми у JavaFX: CSS та User Experience","\u002Fjava\u002Fpr2\u002Fjavafx-styling-themes","04.java\u002Fpr2\u002F34.javafx-styling-themes",{"title":2686,"path":2687,"stem":2688},"AtlantaFX: Сучасні теми для JavaFX додатків","\u002Fjava\u002Fpr2\u002Fatlantafx-modern-themes","04.java\u002Fpr2\u002F35.atlantafx-modern-themes",{"title":2690,"path":2691,"stem":2692},"Пакування та розповсюдження JavaFX-додатків","\u002Fjava\u002Fpr2\u002Fjar-packaging-distribution","04.java\u002Fpr2\u002F36.jar-packaging-distribution",{"title":2694,"icon":2695,"path":2696,"stem":2697,"children":2698,"page":59},"Python","i-devicon-python","\u002Fpython","05.python",[2699,2703,2706,2710,2714,2718,2722,2726,2730,2734,2738,2742,2746,2750,2754,2815],{"title":2700,"path":2701,"stem":2702},"Модулі, Пакети та Віртуальні Середовища","\u002Fpython\u002Fmodules-packages-venv","05.python\u002F00.modules-packages-venv",{"title":71,"path":2704,"stem":2705},"\u002Fpython\u002Fclasses-objects","05.python\u002F01.classes-objects",{"title":2707,"path":2708,"stem":2709},"Інкапсуляція, Керування Доступом та Властивості","\u002Fpython\u002Fencapsulation","05.python\u002F02.encapsulation",{"title":2711,"path":2712,"stem":2713},"Наслідування, MRO та суперсила super()","\u002Fpython\u002Finheritance-mro","05.python\u002F03.inheritance-mro",{"title":2715,"path":2716,"stem":2717},"Абстракція — ABC проти Статичних Протоколів (PEP 544)","\u002Fpython\u002Fabstraction-protocols","05.python\u002F04.abstraction-protocols",{"title":2719,"path":2720,"stem":2721},"Магічні методи (Dunder) та Емуляція протоколів","\u002Fpython\u002Fdunder-methods","05.python\u002F05.dunder-methods",{"title":2723,"path":2724,"stem":2725},"Декоратори та Керування життєвим циклом методів","\u002Fpython\u002Fdecorators-static-class","05.python\u002F06.decorators-static-class",{"title":2727,"path":2728,"stem":2729},"Дескриптори — Магія доступу до атрибутів","\u002Fpython\u002Fdescriptors","05.python\u002F07.descriptors",{"title":2731,"path":2732,"stem":2733},"Метакласи — Динамічне створення класів під капотом CPython","\u002Fpython\u002Fmetaclasses","05.python\u002F08.metaclasses",{"title":2735,"path":2736,"stem":2737},"Dataclasses, NamedTuple та сучасні контейнери Python","\u002Fpython\u002Fmodern-containers","05.python\u002F09.modern-containers",{"title":2739,"path":2740,"stem":2741},"GIL та модель конкурентності CPython — фундамент перед потоками і процесами","\u002Fpython\u002Fgil-concurrency-intro","05.python\u002F11.gil-concurrency-intro",{"title":2743,"path":2744,"stem":2745},"Threading — конкурентність для I\u002FO-bound задач","\u002Fpython\u002Fthreading","05.python\u002F12.threading",{"title":2747,"path":2748,"stem":2749},"Multiprocessing — справжній паралелізм для CPU-bound задач","\u002Fpython\u002Fmultiprocessing","05.python\u002F13.multiprocessing",{"title":2751,"path":2752,"stem":2753},"asyncio — кооперативна конкурентність та event loop","\u002Fpython\u002Fasyncio","05.python\u002F14.asyncio",{"title":2755,"icon":92,"path":2756,"stem":2757,"children":2758,"page":59},"FastAPI","\u002Fpython\u002Ffastapi","05.python\u002Ffastapi",[2759,2763,2767,2771,2775,2779,2783,2787,2791,2795,2799,2803,2807,2811],{"title":2760,"path":2761,"stem":2762},"Глибокий Typing та Pydantic v2 — від анотацій до валідації","\u002Fpython\u002Ffastapi\u002Ftyping-pydantic","05.python\u002Ffastapi\u002F15.typing-pydantic",{"title":2764,"path":2765,"stem":2766},"WSGI, ASGI та Python Web-екосистема","\u002Fpython\u002Ffastapi\u002Fwsgi-asgi-ecosystem","05.python\u002Ffastapi\u002F16.wsgi-asgi-ecosystem",{"title":2768,"path":2769,"stem":2770},"FastAPI: Перший додаток, Uvicorn та OpenAPI","\u002Fpython\u002Ffastapi\u002Ffastapi-intro","05.python\u002Ffastapi\u002F17.fastapi-intro",{"title":2772,"path":2773,"stem":2774},"Маршрутизація, параметри запитів та APIRouter","\u002Fpython\u002Ffastapi\u002Ffastapi-routing-params","05.python\u002Ffastapi\u002F18.fastapi-routing-params",{"title":2776,"path":2777,"stem":2778},"Pydantic v2 у FastAPI — схеми, валідація та серіалізація","\u002Fpython\u002Ffastapi\u002Ffastapi-pydantic-schemas","05.python\u002Ffastapi\u002F19.fastapi-pydantic-schemas",{"title":2780,"path":2781,"stem":2782},"Dependency Injection — серце архітектури FastAPI","\u002Fpython\u002Ffastapi\u002Ffastapi-dependency-injection","05.python\u002Ffastapi\u002F20.fastapi-dependency-injection",{"title":2784,"path":2785,"stem":2786},"Обробка помилок, Middleware та CORS у FastAPI","\u002Fpython\u002Ffastapi\u002Ffastapi-errors-middleware","05.python\u002Ffastapi\u002F21.fastapi-errors-middleware",{"title":2788,"path":2789,"stem":2790},"SQLAlchemy 2.0 — ORM, Core та Async Engine","\u002Fpython\u002Ffastapi\u002Fsqlalchemy-orm","05.python\u002Ffastapi\u002F22.sqlalchemy-orm",{"title":2792,"path":2793,"stem":2794},"Alembic — міграції бази даних","\u002Fpython\u002Ffastapi\u002Falembic-migrations","05.python\u002Ffastapi\u002F23.alembic-migrations",{"title":2796,"path":2797,"stem":2798},"Repository Pattern та Clean Architecture у FastAPI","\u002Fpython\u002Ffastapi\u002Ffastapi-repository-clean-arch","05.python\u002Ffastapi\u002F24.fastapi-repository-clean-arch",{"title":2800,"path":2801,"stem":2802},"Автентифікація та авторизація — JWT, OAuth2, RBAC","\u002Fpython\u002Ffastapi\u002Ffastapi-auth-security","05.python\u002Ffastapi\u002F25.fastapi-auth-security",{"title":2804,"path":2805,"stem":2806},"Тестування FastAPI — pytest, httpx, fixtures","\u002Fpython\u002Ffastapi\u002Ffastapi-testing","05.python\u002Ffastapi\u002F26.fastapi-testing",{"title":2808,"path":2809,"stem":2810},"Async на повну — Background Tasks, WebSockets, SSE та Celery","\u002Fpython\u002Ffastapi\u002Ffastapi-async-advanced","05.python\u002Ffastapi\u002F27.fastapi-async-advanced",{"title":2812,"path":2813,"stem":2814},"Deployment — Docker, CI\u002FCD, Monitoring","\u002Fpython\u002Ffastapi\u002Ffastapi-deployment","05.python\u002Ffastapi\u002F28.fastapi-deployment",{"title":2816,"path":2817,"stem":2818},"[object Object]","\u002Fpython\u002Foop-plan","05.python\u002Foop-plan",{"title":2820,"icon":2821,"path":2822,"stem":2823,"children":2824,"page":59},"Бази даних","i-lucide-database","\u002Fdatabases","06.databases",[2825,2855,2878,2915,2944,2962,2996,3008,3017],{"title":2826,"icon":2827,"path":2828,"stem":2829,"children":2830,"page":59},"Intro","i-lucide-play","\u002Fdatabases\u002Fintro","06.databases\u002F01.intro",[2831,2835,2839,2843,2847,2851],{"title":2832,"path":2833,"stem":2834},"Введення в теорію баз даних","\u002Fdatabases\u002Fintro\u002Fintroduction-to-databases","06.databases\u002F01.intro\u002F01.introduction-to-databases",{"title":2836,"path":2837,"stem":2838},"Реляційна модель даних","\u002Fdatabases\u002Fintro\u002Frelational-model-theory","06.databases\u002F01.intro\u002F02.relational-model-theory",{"title":2840,"path":2841,"stem":2842},"ER-моделювання","\u002Fdatabases\u002Fintro\u002Fer-modeling","06.databases\u002F01.intro\u002F03.er-modeling",{"title":2844,"path":2845,"stem":2846},"Логічне проектування БД","\u002Fdatabases\u002Fintro\u002Flogical-schema","06.databases\u002F01.intro\u002F04.logical-schema",{"title":2848,"path":2849,"stem":2850},"Класифікація таблиць","\u002Fdatabases\u002Fintro\u002Ftable-classification","06.databases\u002F01.intro\u002F05.table-classification",{"title":2852,"path":2853,"stem":2854},"PlantUML для баз даних","\u002Fdatabases\u002Fintro\u002Fplantuml-diagrams","06.databases\u002F01.intro\u002F06.plantuml-diagrams",{"title":2856,"icon":2821,"path":2857,"stem":2858,"children":2859,"page":59},"MS SQL Server Start","\u002Fdatabases\u002Fms-sql-server-start","06.databases\u002F02.ms-sql-server-start",[2860,2864,2870,2874],{"title":2861,"path":2862,"stem":2863},"Типи даних у MS SQL Server","\u002Fdatabases\u002Fms-sql-server-start\u002Fdata-types","06.databases\u002F02.ms-sql-server-start\u002F01.data-types",{"title":2865,"path":2866,"stem":2867,"children":2868},"Індекси у MS SQL Server","\u002Fdatabases\u002Fms-sql-server-start\u002Fsql-indexes","06.databases\u002F02.ms-sql-server-start\u002F02.sql-indexes",[2869],{"title":2865,"path":2866,"stem":2867},{"title":2871,"path":2872,"stem":2873},"Системні бази даних MS SQL Server","\u002Fdatabases\u002Fms-sql-server-start\u002Fsystem-databases","06.databases\u002F02.ms-sql-server-start\u002F03.system-databases",{"title":2875,"path":2876,"stem":2877},"Огляд мови SQL та запитів","\u002Fdatabases\u002Fms-sql-server-start\u002Fsql-queries-overview","06.databases\u002F02.ms-sql-server-start\u002F04.sql-queries-overview",{"title":2879,"icon":2821,"path":2880,"stem":2881,"children":2882,"page":59},"SQL","\u002Fdatabases\u002Fsql","06.databases\u002F03.sql",[2883,2887,2891,2895,2899,2903,2907,2911],{"title":2884,"path":2885,"stem":2886},"Налаштування демонстраційної бази даних","\u002Fdatabases\u002Fsql\u002Fsample-database-setup","06.databases\u002F03.sql\u002F00.sample-database-setup",{"title":2888,"path":2889,"stem":2890},"DDL - Створення таблиць (CREATE TABLE)","\u002Fdatabases\u002Fsql\u002Fddl-create-table","06.databases\u002F03.sql\u002F01.ddl-create-table",{"title":2892,"path":2893,"stem":2894},"DDL - Зміна та видалення таблиць (ALTER, DROP)","\u002Fdatabases\u002Fsql\u002Fddl-alter-drop-table","06.databases\u002F03.sql\u002F02.ddl-alter-drop-table",{"title":2896,"path":2897,"stem":2898},"SELECT запити - Основи","\u002Fdatabases\u002Fsql\u002Fselect-queries-fundamentals","06.databases\u002F03.sql\u002F03.select-queries-fundamentals",{"title":2900,"path":2901,"stem":2902},"SELECT запити - Розширені можливості","\u002Fdatabases\u002Fsql\u002Fselect-queries-advanced","06.databases\u002F03.sql\u002F04.select-queries-advanced",{"title":2904,"path":2905,"stem":2906},"INSERT запити - Додавання даних","\u002Fdatabases\u002Fsql\u002Finsert-queries","06.databases\u002F03.sql\u002F05.insert-queries",{"title":2908,"path":2909,"stem":2910},"UPDATE та DELETE запити","\u002Fdatabases\u002Fsql\u002Fupdate-delete-queries","06.databases\u002F03.sql\u002F06.update-delete-queries",{"title":2912,"path":2913,"stem":2914},"Транзакції в SQL","\u002Fdatabases\u002Fsql\u002Ftransactions","06.databases\u002F03.sql\u002F07.transactions",{"title":2916,"icon":2821,"path":2917,"stem":2918,"children":2919,"page":59},"Multi Table Databases","\u002Fdatabases\u002Fmulti-table-databases","06.databases\u002F04.multi-table-databases",[2920,2924,2928,2932,2936,2940],{"title":2921,"path":2922,"stem":2923},"Зв'язки та нормалізація БД","\u002Fdatabases\u002Fmulti-table-databases\u002Frelationships-and-normalization","06.databases\u002F04.multi-table-databases\u002F00.relationships-and-normalization",{"title":2925,"path":2926,"stem":2927},"INNER JOIN - З'єднання таблиць","\u002Fdatabases\u002Fmulti-table-databases\u002Finner-join","06.databases\u002F04.multi-table-databases\u002F01.inner-join",{"title":2929,"path":2930,"stem":2931},"OUTER JOINs - LEFT, RIGHT, FULL","\u002Fdatabases\u002Fmulti-table-databases\u002Fouter-joins","06.databases\u002F04.multi-table-databases\u002F02.outer-joins",{"title":2933,"path":2934,"stem":2935},"CROSS та SELF JOINs","\u002Fdatabases\u002Fmulti-table-databases\u002Fcross-self-joins","06.databases\u002F04.multi-table-databases\u002F03.cross-self-joins",{"title":2937,"path":2938,"stem":2939},"Підзапити (Subqueries)","\u002Fdatabases\u002Fmulti-table-databases\u002Fsubqueries","06.databases\u002F04.multi-table-databases\u002F04.subqueries",{"title":2941,"path":2942,"stem":2943},"Агрегації з JOIN","\u002Fdatabases\u002Fmulti-table-databases\u002Faggregations-with-joins","06.databases\u002F04.multi-table-databases\u002F05.aggregations-with-joins",{"title":2945,"icon":2946,"path":2947,"stem":2948,"children":2949,"page":59},"Aggregate Functions","i-lucide-calculator","\u002Fdatabases\u002Faggregate-functions","06.databases\u002F05.aggregate-functions",[2950,2954,2958],{"title":2951,"path":2952,"stem":2953},"Функції агрегування в MS SQL Server","\u002Fdatabases\u002Faggregate-functions\u002Fintroduction-aggregate-functions","06.databases\u002F05.aggregate-functions\u002F01.introduction-aggregate-functions",{"title":2955,"path":2956,"stem":2957},"Групування даних в MS SQL Server","\u002Fdatabases\u002Faggregate-functions\u002Fgrouping-data","06.databases\u002F05.aggregate-functions\u002F02.grouping-data",{"title":2959,"path":2960,"stem":2961},"Підзапити з агрегатними функціями","\u002Fdatabases\u002Faggregate-functions\u002Fsubqueries-aggregates","06.databases\u002F05.aggregate-functions\u002F03.subqueries-aggregates",{"title":2963,"icon":2964,"path":2965,"stem":2966,"children":2967,"page":59},"Тригери та зберігаємі процедури","i-lucide-database-zap","\u002Fdatabases\u002Ftriggers-stored-procedures","06.databases\u002F07.triggers-stored-procedures",[2968,2972,2976,2980,2984,2988,2992],{"title":2969,"path":2970,"stem":2971},"DML-тригери","\u002Fdatabases\u002Ftriggers-stored-procedures\u002Fdml-triggers","06.databases\u002F07.triggers-stored-procedures\u002F01.dml-triggers",{"title":2973,"path":2974,"stem":2975},"DDL-тригери","\u002Fdatabases\u002Ftriggers-stored-procedures\u002Fddl-triggers","06.databases\u002F07.triggers-stored-procedures\u002F02.ddl-triggers",{"title":2977,"path":2978,"stem":2979},"Transact-SQL розширення","\u002Fdatabases\u002Ftriggers-stored-procedures\u002Ftransact-sql-extensions","06.databases\u002F07.triggers-stored-procedures\u002F03.transact-sql-extensions",{"title":2981,"path":2982,"stem":2983},"Транзакції","\u002Fdatabases\u002Ftriggers-stored-procedures\u002Ftransactions","06.databases\u002F07.triggers-stored-procedures\u002F04.transactions",{"title":2985,"path":2986,"stem":2987},"Зберігаємі процедури","\u002Fdatabases\u002Ftriggers-stored-procedures\u002Fstored-procedures","06.databases\u002F07.triggers-stored-procedures\u002F05.stored-procedures",{"title":2989,"path":2990,"stem":2991},"Користувацькі функції","\u002Fdatabases\u002Ftriggers-stored-procedures\u002Fuser-defined-functions","06.databases\u002F07.triggers-stored-procedures\u002F06.user-defined-functions",{"title":2993,"path":2994,"stem":2995},"Безпека баз даних","\u002Fdatabases\u002Ftriggers-stored-procedures\u002Fsecurity","06.databases\u002F07.triggers-stored-procedures\u002F08.security",{"title":2993,"icon":793,"path":2997,"stem":2998,"children":2999,"page":59},"\u002Fdatabases\u002Fsecurity","06.databases\u002F08.security",[3000,3004],{"title":3001,"path":3002,"stem":3003},"Вступ до безпеки баз даних","\u002Fdatabases\u002Fsecurity\u002Fintroduction","06.databases\u002F08.security\u002F01.introduction",{"title":3005,"path":3006,"stem":3007},"Системні представлення та метадані","\u002Fdatabases\u002Fsecurity\u002Fsystem-views","06.databases\u002F08.security\u002F02.system-views",{"title":3009,"icon":3010,"path":3011,"stem":3012,"children":3013,"page":59},"Резервне копіювання та відновлення","i-lucide-database-backup","\u002Fdatabases\u002Fbackup-recovery","06.databases\u002F09.backup-recovery",[3014],{"title":3009,"path":3015,"stem":3016},"\u002Fdatabases\u002Fbackup-recovery\u002Fbackup-restore","06.databases\u002F09.backup-recovery\u002F01.backup-restore",{"title":3018,"icon":3019,"path":3020,"stem":3021,"children":3022,"page":59},"Повнотекстовий пошук","i-lucide-search","\u002Fdatabases\u002Ffull-text-search","06.databases\u002F10.full-text-search",[3023],{"title":3018,"path":3024,"stem":3025},"\u002Fdatabases\u002Ffull-text-search\u002Ffull-text-search","06.databases\u002F10.full-text-search\u002F01.full-text-search",{"title":3027,"icon":3028,"path":3029,"stem":3030,"children":3031,"page":59},"Tools","i-lucide-wrench","\u002Ftools","07.tools",[3032,3108],{"title":3033,"icon":3034,"path":3035,"stem":3036,"children":3037},"Docker","i-simple-icons-docker","\u002Ftools\u002Fdocker","07.tools\u002F01.docker\u002Findex",[3038,3040,3044,3048,3052,3056,3060,3064,3068,3072,3076,3080,3084,3088,3092,3096,3100,3104],{"title":3039,"path":3035,"stem":3036},"Docker: від нуля до production",{"title":3041,"path":3042,"stem":3043},"Контейнеризація — від проблеми до рішення","\u002Ftools\u002Fdocker\u002Fcontainerization-concept","07.tools\u002F01.docker\u002F01.containerization-concept",{"title":3045,"path":3046,"stem":3047},"Docker — що це і навіщо?","\u002Ftools\u002Fdocker\u002Fdocker-what-and-why","07.tools\u002F01.docker\u002F02.docker-what-and-why",{"title":3049,"path":3050,"stem":3051},"Архітектура Docker Engine","\u002Ftools\u002Fdocker\u002Fdocker-architecture","07.tools\u002F01.docker\u002F03.docker-architecture",{"title":3053,"path":3054,"stem":3055},"Встановлення Docker","\u002Ftools\u002Fdocker\u002Finstallation","07.tools\u002F01.docker\u002F04.installation",{"title":3057,"path":3058,"stem":3059},"Перший контейнер — docker run","\u002Ftools\u002Fdocker\u002Ffirst-container","07.tools\u002F01.docker\u002F05.first-container",{"title":3061,"path":3062,"stem":3063},"Життєвий цикл контейнера","\u002Ftools\u002Fdocker\u002Fcontainer-lifecycle","07.tools\u002F01.docker\u002F06.container-lifecycle",{"title":3065,"path":3066,"stem":3067},"Docker Images — фундаментальні концепції","\u002Ftools\u002Fdocker\u002Fdocker-images-fundamentals","07.tools\u002F01.docker\u002F07.docker-images-fundamentals",{"title":3069,"path":3070,"stem":3071},"Dockerfile — основи","\u002Ftools\u002Fdocker\u002Fdockerfile-basics","07.tools\u002F01.docker\u002F08.dockerfile-basics",{"title":3073,"path":3074,"stem":3075},"Dockerfile — просунуті техніки","\u002Ftools\u002Fdocker\u002Fdockerfile-advanced","07.tools\u002F01.docker\u002F09.dockerfile-advanced",{"title":3077,"path":3078,"stem":3079},"Build Context та кешування шарів","\u002Ftools\u002Fdocker\u002Fbuild-context-and-cache","07.tools\u002F01.docker\u002F10.build-context-and-cache",{"title":3081,"path":3082,"stem":3083},"Реєстри Docker-образів","\u002Ftools\u002Fdocker\u002Fimage-registries","07.tools\u002F01.docker\u002F11.image-registries",{"title":3085,"path":3086,"stem":3087},"Контейнеризація .NET додатків","\u002Ftools\u002Fdocker\u002Fdotnet-containerization","07.tools\u002F01.docker\u002F12.dotnet-containerization",{"title":3089,"path":3090,"stem":3091},"Томи та збереження даних","\u002Ftools\u002Fdocker\u002Fvolumes-and-data","07.tools\u002F01.docker\u002F13.volumes-and-data",{"title":3093,"path":3094,"stem":3095},"Основи мережі в Docker","\u002Ftools\u002Fdocker\u002Fnetworking-basics","07.tools\u002F01.docker\u002F14.networking-basics",{"title":3097,"path":3098,"stem":3099},"Змінні оточення та конфігурація","\u002Ftools\u002Fdocker\u002Fenvironment-and-configuration","07.tools\u002F01.docker\u002F15.environment-and-configuration",{"title":3101,"path":3102,"stem":3103},"Docker Compose — оркестрація контейнерів","\u002Ftools\u002Fdocker\u002Fdocker-compose-basics","07.tools\u002F01.docker\u002F16.docker-compose-basics",{"title":3105,"path":3106,"stem":3107},"Docker Compose — Multi-Service застосунки","\u002Ftools\u002Fdocker\u002Fcompose-multi-service","07.tools\u002F01.docker\u002F17.compose-multi-service",{"title":3109,"icon":3110,"path":3111,"stem":3112,"children":3113},"Kubernetes","simple-icons:kubernetes","\u002Ftools\u002Fkubernetes","07.tools\u002F02.kubernetes\u002Findex",[3114,3116,3120,3124,3128,3132,3136,3140,3144],{"title":3115,"path":3111,"stem":3112},"Kubernetes: від розробки до production",{"title":3117,"path":3118,"stem":3119},"Kubernetes — коли Docker Compose більше не вистачає","\u002Ftools\u002Fkubernetes\u002Fwhy-kubernetes","07.tools\u002F02.kubernetes\u002F01.why-kubernetes",{"title":3121,"path":3122,"stem":3123},"Архітектура Kubernetes — анатомія кластера","\u002Ftools\u002Fkubernetes\u002Fkubernetes-architecture","07.tools\u002F02.kubernetes\u002F02.kubernetes-architecture",{"title":3125,"path":3126,"stem":3127},"Локальне середовище — minikube, kind та k3s","\u002Ftools\u002Fkubernetes\u002Flocal-environment","07.tools\u002F02.kubernetes\u002F03.local-environment",{"title":3129,"path":3130,"stem":3131},"Pod — атомарна одиниця Kubernetes","\u002Ftools\u002Fkubernetes\u002Fpods-and-containers","07.tools\u002F02.kubernetes\u002F04.pods-and-containers",{"title":3133,"path":3134,"stem":3135},"Патерни використання Pod","\u002Ftools\u002Fkubernetes\u002Fpod-patterns","07.tools\u002F02.kubernetes\u002F05.pod-patterns",{"title":3137,"path":3138,"stem":3139},"Deployment — декларативне управління Pod","\u002Ftools\u002Fkubernetes\u002Fdeployment-basics","07.tools\u002F02.kubernetes\u002F06.deployment-basics",{"title":3141,"path":3142,"stem":3143},"Rolling Updates та управління життєвим циклом Deployment","\u002Ftools\u002Fkubernetes\u002Fdeployment-rolling-updates","07.tools\u002F02.kubernetes\u002F07.deployment-rolling-updates",{"title":3145,"path":3146,"stem":3147},"Service — мережева абстракція для Pod","\u002Ftools\u002Fkubernetes\u002Fservices-networking","07.tools\u002F02.kubernetes\u002F08.services-networking",{"title":3149,"icon":3150,"path":3151,"stem":3152,"children":3153,"page":59},"Software Engineering","i-lucide-code-2","\u002Fsoftware-engineering","09.software-engineering",[3154,3158,3162,3166,3170,3174,3178,3182,3186,3190,3194],{"title":3155,"path":3156,"stem":3157},"1. Аналіз предметної області. Експертні знання та складність","\u002Fsoftware-engineering\u002Fintro-subdomains","09.software-engineering\u002F01.intro-subdomains",{"title":3159,"path":3160,"stem":3161},"2. Обмежені контексти. Інтеграція обмежених контекстів","\u002Fsoftware-engineering\u002Fintegrating-limited-contexts","09.software-engineering\u002F02.integrating-limited-contexts",{"title":3163,"path":3164,"stem":3165},"3. Реалізація простої бізнес-логіки","\u002Fsoftware-engineering\u002Fsimple","09.software-engineering\u002F03.simple",{"title":3167,"path":3168,"stem":3169},"4. Опрацювання складної бізнес-логіки","\u002Fsoftware-engineering\u002Fcomplex-business-logic","09.software-engineering\u002F04.complex-business-logic",{"title":3171,"path":3172,"stem":3173},"5. Моделювання фактора часу. Подієво-орієнтована архітектура.","\u002Fsoftware-engineering\u002Fmodelling-the-time-factor","09.software-engineering\u002F05.modelling-the-time-factor",{"title":3175,"path":3176,"stem":3177},"6. Архітектурні патерни","\u002Fsoftware-engineering\u002Farchitectural-patterns","09.software-engineering\u002F06.architectural-patterns",{"title":3179,"path":3180,"stem":3181},"Паттерни взаємодії","\u002Fsoftware-engineering\u002Fpatterns-of-interaction","09.software-engineering\u002F07.patterns-of-interaction",{"title":3183,"path":3184,"stem":3185},"Евристика проєктування","\u002Fsoftware-engineering\u002Fdesign-heuristics","09.software-engineering\u002F08.design-heuristics",{"title":3187,"path":3188,"stem":3189},"Еволюція проєктних рішень","\u002Fsoftware-engineering\u002Fevolution-of-design-solutions","09.software-engineering\u002F09.evolution-of-design-solutions",{"title":3191,"path":3192,"stem":3193},"EventStorming","\u002Fsoftware-engineering\u002Feventstorming","09.software-engineering\u002F10.eventstorming",{"title":3195,"path":3196,"stem":3197},"DDD на практиці","\u002Fsoftware-engineering\u002Fddd-in-practice","09.software-engineering\u002F11.ddd-in-practice",{"title":3199,"icon":943,"path":3200,"stem":3201,"children":3202,"page":59},"DDD","\u002Fddd","10.ddd",[3203,3207,3211,3215,3219,3223,3227,3231,3235,3239,3243,3247,3251],{"title":3204,"path":3205,"stem":3206},"Аналіз предметної області","\u002Fddd\u002Fdomain-analysis","10.ddd\u002F01.domain-analysis",{"title":3208,"path":3209,"stem":3210},"Експертні знання про предметну область","\u002Fddd\u002Fdomain-expert-knowledge","10.ddd\u002F02.domain-expert-knowledge",{"title":3212,"path":3213,"stem":3214},"Як осмислити складність предметної області","\u002Fddd\u002Fmanaging-domain-complexity","10.ddd\u002F03.managing-domain-complexity",{"title":3216,"path":3217,"stem":3218},"Інтеграція обмежених контекстів","\u002Fddd\u002Fbounded-context-integration","10.ddd\u002F04.bounded-context-integration",{"title":3220,"path":3221,"stem":3222},"Реалізація простої бізнес-логіки","\u002Fddd\u002Fsimple-business-logic","10.ddd\u002F05.simple-business-logic",{"title":3224,"path":3225,"stem":3226},"Обробка складної бізнес-логіки","\u002Fddd\u002Fcomplex-business-logic","10.ddd\u002F06.complex-business-logic",{"title":3228,"path":3229,"stem":3230},"Моделювання фактора часу","\u002Fddd\u002Ftime-modeling","10.ddd\u002F07.time-modeling",{"title":3232,"path":3233,"stem":3234},"Глава 8. Архітектурні Патерни","\u002Fddd\u002Farchitectural-patterns","10.ddd\u002F08.architectural-patterns",{"title":3236,"path":3237,"stem":3238},"Глава 9. Патерни Взаємодії","\u002Fddd\u002Finteraction-patterns","10.ddd\u002F09.interaction-patterns",{"title":3240,"path":3241,"stem":3242},"Глава 10. Проектні Евристики","\u002Fddd\u002Fdesign-heuristics","10.ddd\u002F10.design-heuristics",{"title":3244,"path":3245,"stem":3246},"Глава 11. Еволюція Проектних Рішень","\u002Fddd\u002Fevolution-of-design-decisions","10.ddd\u002F11.evolution-of-design-decisions",{"title":3248,"path":3249,"stem":3250},"Глава 12. EventStorming","\u002Fddd\u002Fevent-storming","10.ddd\u002F12.event-storming",{"title":3252,"path":3253,"stem":3254},"Глава 13. DDD на Практиці","\u002Fddd\u002Fddd-in-practice","10.ddd\u002F13.ddd-in-practice",{"title":3256,"icon":3257,"path":3258,"stem":3259,"children":3260,"page":59},"Media Streaming","i-lucide-video","\u002Fmedia-streaming","11.media-streaming",[3261,3265,3269,3273,3277,3281,3285],{"title":3262,"path":3263,"stem":3264},"01. Магія Стрімінгу: Що відбувається, коли ви натискаєте \"Play\"","\u002Fmedia-streaming\u002Fintroduction","11.media-streaming\u002F01.introduction",{"title":3266,"path":3267,"stem":3268},"02. Анатомія Медіа: Кодеки, Контейнери та Стиснення","\u002Fmedia-streaming\u002Faudio-video-anatomy","11.media-streaming\u002F02.audio-video-anatomy",{"title":3270,"path":3271,"stem":3272},"03. The Gym: FFmpeg Deep Dive","\u002Fmedia-streaming\u002Fffmpeg-gym","11.media-streaming\u002F03.ffmpeg-gym",{"title":3274,"path":3275,"stem":3276},"04. HLS Protocol: HTTP Live Streaming у Деталях","\u002Fmedia-streaming\u002Fhls-protocol","11.media-streaming\u002F04.hls-protocol",{"title":3278,"path":3279,"stem":3280},"05. DASH Protocol: Відкритий Стандарт","\u002Fmedia-streaming\u002Fdash-protocol","11.media-streaming\u002F05.dash-protocol",{"title":3282,"path":3283,"stem":3284},"06. Масштабування: CDN та Adaptive Bitrate","\u002Fmedia-streaming\u002Fcdn-and-adaptive-bitrate","11.media-streaming\u002F06.cdn-and-adaptive-bitrate",{"title":3286,"path":3287,"stem":3288},"07. Війна із Затримкою (Latency)","\u002Fmedia-streaming\u002Frealtime-latency","11.media-streaming\u002F07.realtime-latency",{"title":3290,"icon":3291,"path":3292,"stem":3293,"children":3294,"page":59},"HTML & CSS","i-devicon-html5","\u002Fhtml-css","12.html-css",[3295,3299,3303,3307,3311,3315,3319,3323,3327,3331,3335,3339,3343,3347,3351,3355,3359,3363,3367,3371,3375,3379,3383,3387,3391,3395,3399,3403,3407,3411],{"title":3296,"path":3297,"stem":3298},"Вступ до HTML. Структура документа","\u002Fhtml-css\u002Fintro-html-structure","12.html-css\u002F01.intro-html-structure",{"title":3300,"path":3301,"stem":3302},"Форматування тексту в HTML","\u002Fhtml-css\u002Fhtml-text-formatting","12.html-css\u002F02.html-text-formatting",{"title":3304,"path":3305,"stem":3306},"Посилання та зображення в HTML","\u002Fhtml-css\u002Fhtml-links-images","12.html-css\u002F03.html-links-images",{"title":3308,"path":3309,"stem":3310},"Списки та таблиці в HTML","\u002Fhtml-css\u002Fhtml-lists-tables","12.html-css\u002F04.html-lists-tables",{"title":3312,"path":3313,"stem":3314},"Форми в HTML","\u002Fhtml-css\u002Fhtml-forms","12.html-css\u002F05.html-forms",{"title":3316,"path":3317,"stem":3318},"Семантичні елементи HTML5","\u002Fhtml-css\u002Fhtml-semantic-elements","12.html-css\u002F06.html-semantic-elements",{"title":3320,"path":3321,"stem":3322},"Мультимедіа та розширені елементи HTML","\u002Fhtml-css\u002Fhtml-multimedia-advanced","12.html-css\u002F07.html-multimedia-advanced",{"title":3324,"path":3325,"stem":3326},"Мікророзмітка та SEO в HTML","\u002Fhtml-css\u002Fhtml-microdata-seo","12.html-css\u002F08.html-microdata-seo",{"title":3328,"path":3329,"stem":3330},"Вступ до CSS. Селектори та специфічність","\u002Fhtml-css\u002Fcss-intro-selectors","12.html-css\u002F09.css-intro-selectors",{"title":3332,"path":3333,"stem":3334},"Блокова модель CSS. Відступи. Box Sizing","\u002Fhtml-css\u002Fcss-box-model","12.html-css\u002F10.css-box-model",{"title":3336,"path":3337,"stem":3338},"Розміри у CSS: повний довідник одиниць і ключових слів","\u002Fhtml-css\u002F10a.css-sizing","12.html-css\u002F10a.css-sizing",{"title":3340,"path":3341,"stem":3342},"Типографіка в CSS. Шрифти та текст","\u002Fhtml-css\u002Fcss-typography","12.html-css\u002F11.css-typography",{"title":3344,"path":3345,"stem":3346},"Кольори та фони в CSS","\u002Fhtml-css\u002Fcss-colors-backgrounds","12.html-css\u002F12.css-colors-backgrounds",{"title":3348,"path":3349,"stem":3350},"Тіні та фільтри в CSS","\u002Fhtml-css\u002F12b.css-shadows-filters","12.html-css\u002F12b.css-shadows-filters",{"title":3352,"path":3353,"stem":3354},"CSS Flexbox: Фундамент гнучких макетів","\u002Fhtml-css\u002Fcss-flexbox-fundamentals","12.html-css\u002F13.css-flexbox-fundamentals",{"title":3356,"path":3357,"stem":3358},"CSS Flexbox: Вирівнювання та Позиціонування","\u002Fhtml-css\u002Fcss-flexbox-alignment-sizing-and-patterns","12.html-css\u002F14.css-flexbox-alignment-sizing-and-patterns",{"title":3360,"path":3361,"stem":3362},"CSS Grid. Двовимірний макет. Частина 1","\u002Fhtml-css\u002Fcss-layout-grid","12.html-css\u002F15.css-layout-grid",{"title":3364,"path":3365,"stem":3366},"CSS Grid. Двовимірний макет. Частина 2","\u002Fhtml-css\u002Fcss-layout-grid-advanced","12.html-css\u002F16.css-layout-grid-advanced",{"title":3368,"path":3369,"stem":3370},"Позиціонування в CSS. Z-index. Stacking Context","\u002Fhtml-css\u002Fcss-positioning","12.html-css\u002F17.css-positioning",{"title":3372,"path":3373,"stem":3374},"CSS Анімації та Переходи","\u002Fhtml-css\u002Fcss-animations-transitions","12.html-css\u002F18.css-animations-transitions",{"title":3376,"path":3377,"stem":3378},"Адаптивний дизайн. Media Queries. Частина 1","\u002Fhtml-css\u002Fcss-responsive-media-queries","12.html-css\u002F19.css-responsive-media-queries",{"title":3380,"path":3381,"stem":3382},"Адаптивний дизайн. Частина 2: clamp(), Container Queries, @layer","\u002Fhtml-css\u002Fcss-responsive-advanced","12.html-css\u002F20.css-responsive-advanced",{"title":3384,"path":3385,"stem":3386},"CSS Custom Properties. Методології. Сучасний CSS","\u002Fhtml-css\u002Fcss-variables-methodologies","12.html-css\u002F21.css-variables-methodologies",{"title":3388,"path":3389,"stem":3390},"Сучасний CSS 2023–2025: Нові можливості","\u002Fhtml-css\u002Fcss-modern-features","12.html-css\u002F22.css-modern-features",{"title":3392,"path":3393,"stem":3394},"CSS Nesting, @layer, @scope та @property: нативний препроцесор","\u002Fhtml-css\u002F22a.css-nesting-modern-syntax","12.html-css\u002F22a.css-nesting-modern-syntax",{"title":3396,"path":3397,"stem":3398},"CSS для форм та інтерактивних станів","\u002Fhtml-css\u002Fcss-forms-interactive-states","12.html-css\u002F23.css-forms-interactive-states",{"title":3400,"path":3401,"stem":3402},"Доступність у CSS (CSS Accessibility)","\u002Fhtml-css\u002Fcss-accessibility","12.html-css\u002F24.css-accessibility",{"title":3404,"path":3405,"stem":3406},"CSS-функції та сучасні sizing primitives","\u002Fhtml-css\u002Fcss-functions-sizing","12.html-css\u002F25.css-functions-sizing",{"title":3408,"path":3409,"stem":3410},"Rendering Pipeline і CSS Performance","\u002Fhtml-css\u002Fcss-rendering-performance","12.html-css\u002F26.css-rendering-performance",{"title":3412,"path":3413,"stem":3414},"CSS Best Practices: типові ситуації та правильні рішення","\u002Fhtml-css\u002Fcss-best-practices","12.html-css\u002F27.css-best-practices",{"title":3416,"path":3417,"stem":3418,"children":3419,"page":59},"AWS","\u002Faws","13.aws",[3420,3424,3428,3432,3436,3440,3444,3448,3452,3456,3460,3464,3468,3472,3476,3480,3484,3488],{"title":3421,"path":3422,"stem":3423},"Реєстрація AWS акаунту та студентські програми","\u002Faws\u002Faccount-registration","13.aws\u002F00.account-registration",{"title":3425,"path":3426,"stem":3427},"Вступ до хмарних обчислень та AWS","\u002Faws\u002Fintroduction-to-cloud","13.aws\u002F01.introduction-to-cloud",{"title":3429,"path":3430,"stem":3431},"AWS IAM — Identity and Access Management","\u002Faws\u002Fiam","13.aws\u002F02.iam",{"title":3433,"path":3434,"stem":3435},"AWS IAM CLI — Довідник команд","\u002Faws\u002F02a.iam-doc","13.aws\u002F02a.iam-doc",{"title":3437,"path":3438,"stem":3439},"Docker та контейнеризація в AWS — ECR, ECS та Fargate","\u002Faws\u002Fdocker-ecs","13.aws\u002F03.docker-ecs",{"title":3441,"path":3442,"stem":3443},"AWS ECR \u002F ECS CLI — Довідник команд","\u002Faws\u002F03a.docker-ecs-doc","13.aws\u002F03a.docker-ecs-doc",{"title":3445,"path":3446,"stem":3447},"Amazon EC2 — Elastic Compute Cloud","\u002Faws\u002Fec2","13.aws\u002F04.ec2",{"title":3449,"path":3450,"stem":3451},"AWS EC2 CLI — Довідник команд","\u002Faws\u002F04a.ec2-doc","13.aws\u002F04a.ec2-doc",{"title":3453,"path":3454,"stem":3455},"Elastic Load Balancing та Auto Scaling","\u002Faws\u002Falb-asg","13.aws\u002F05.alb-asg",{"title":3457,"path":3458,"stem":3459},"Amazon S3 — Simple Storage Service","\u002Faws\u002Fs3","13.aws\u002F06.s3",{"title":3461,"path":3462,"stem":3463},"Amazon CloudFront — Content Delivery Network","\u002Faws\u002Fcloudfront","13.aws\u002F07.cloudfront",{"title":3465,"path":3466,"stem":3467},"Amazon RDS — Relational Database Service","\u002Faws\u002Frds","13.aws\u002F08.rds",{"title":3469,"path":3470,"stem":3471},"Amazon DynamoDB — NoSQL Database","\u002Faws\u002Fdynamodb","13.aws\u002F09.dynamodb",{"title":3473,"path":3474,"stem":3475},"AWS Lambda та Serverless Compute","\u002Faws\u002Flambda","13.aws\u002F10.lambda",{"title":3477,"path":3478,"stem":3479},"Amazon Bedrock - Foundation Models, RAG та Agents","\u002Faws\u002Fbedrock","13.aws\u002F22.bedrock",{"title":3481,"path":3482,"stem":3483},"Amazon Rekognition - Комп'ютерний зір","\u002Faws\u002Frekognition","13.aws\u002F23.rekognition",{"title":3485,"path":3486,"stem":3487},"Amazon Textract - Інтелектуальний аналіз документів","\u002Faws\u002Ftextract","13.aws\u002F24.textract",{"title":3489,"path":3490,"stem":3491},"Amazon Polly, Transcribe, Comprehend та Translate","\u002Faws\u002Faudio-nlp-services","13.aws\u002F25.audio-nlp-services",{"title":3493,"path":3494,"stem":3495,"children":3496,"page":59},"Ai Development","\u002Fai-development","14.ai-development",[3497],{"title":3498,"path":3499,"stem":3500,"children":3501,"page":59},"Foundations","\u002Fai-development\u002Ffoundations","14.ai-development\u002F00.foundations",[3502],{"title":3503,"path":3504,"stem":3505},"Модуль 0.1. Що таке Large Language Model (LLM)","\u002Fai-development\u002Ffoundations\u002Fwhat-is-llm","14.ai-development\u002F00.foundations\u002F01.what-is-llm",{"title":3507,"path":3508,"stem":3509,"children":3510,"page":59},"React Native","\u002Freact-native","15.react-native",[3511,3532],{"title":3512,"path":3513,"stem":3514,"children":3515,"page":59},"Version1","\u002Freact-native\u002Fversion1","15.react-native\u002Fversion1",[3516,3520,3524,3528],{"title":3517,"path":3518,"stem":3519},"Що таке React Native","\u002Freact-native\u002Fversion1\u002Fwhat-is-react-native","15.react-native\u002Fversion1\u002F01.what-is-react-native",{"title":3521,"path":3522,"stem":3523},"Як влаштований React Native «під капотом»","\u002Freact-native\u002Fversion1\u002Freact-native-architecture","15.react-native\u002Fversion1\u002F02.react-native-architecture",{"title":3525,"path":3526,"stem":3527},"Від React до React Native: ключові відмінності","\u002Freact-native\u002Fversion1\u002Freact-native-vs-react","15.react-native\u002Fversion1\u002F03.react-native-vs-react",{"title":3529,"path":3530,"stem":3531},"View та Text — фундаментальні будівельні блоки","\u002Freact-native\u002Fversion1\u002Fview-and-text","15.react-native\u002Fversion1\u002F04.view-and-text",{"title":3533,"path":3534,"stem":3535,"children":3536,"page":59},"Version2","\u002Freact-native\u002Fversion2","15.react-native\u002Fversion2",[3537,3541,3545,3549,3553,3557,3561,3565,3569,3573,3577,3581,3585,3589,3593,3597,3601,3605,3609,3613,3617,3621,3625,3629,3633,3637,3641],{"title":3538,"path":3539,"stem":3540},"React Native — навіщо він потрібен","\u002Freact-native\u002Fversion2\u002Fwhy-react-native","15.react-native\u002Fversion2\u002F01.why-react-native",{"title":3542,"path":3543,"stem":3544},"Налаштування середовища розробки","\u002Freact-native\u002Fversion2\u002Fenvironment-setup","15.react-native\u002Fversion2\u002F02.environment-setup",{"title":3546,"path":3547,"stem":3548},"Expo і класичний React Native — два шляхи одного фреймворку","\u002Freact-native\u002Fversion2\u002Fexpo-vs-cli-landscape","15.react-native\u002Fversion2\u002F03.expo-vs-cli-landscape",{"title":3550,"path":3551,"stem":3552},"Середовище Expo — перший запуск","\u002Freact-native\u002Fversion2\u002Fexpo-setup-and-tooling","15.react-native\u002Fversion2\u002F04.expo-setup-and-tooling",{"title":3554,"path":3555,"stem":3556},"Структура проєкту та перші UI-примітиви","\u002Freact-native\u002Fversion2\u002Fproject-structure-and-conventions","15.react-native\u002Fversion2\u002F05.project-structure-and-conventions",{"title":3558,"path":3559,"stem":3560},"Базові компоненти React Native","\u002Freact-native\u002Fversion2\u002Fcore-components","15.react-native\u002Fversion2\u002F06.core-components",{"title":3562,"path":3563,"stem":3564},"Flexbox і layout у React Native","\u002Freact-native\u002Fversion2\u002Fflexbox-and-layout","15.react-native\u002Fversion2\u002F07.flexbox-and-layout",{"title":3566,"path":3567,"stem":3568},"StyleSheet і теми (light \u002F dark)","\u002Freact-native\u002Fversion2\u002Fstylesheet-and-theming","15.react-native\u002Fversion2\u002F08.stylesheet-and-theming",{"title":3570,"path":3571,"stem":3572},"Списки та віртуалізація","\u002Freact-native\u002Fversion2\u002Flists-and-virtualization","15.react-native\u002Fversion2\u002F09.lists-and-virtualization",{"title":3574,"path":3575,"stem":3576},"Форми, ввід і валідація","\u002Freact-native\u002Fversion2\u002Fforms-input-validation","15.react-native\u002Fversion2\u002F10.forms-input-validation",{"title":3578,"path":3579,"stem":3580},"Основи Expo Router","\u002Freact-native\u002Fversion2\u002Fexpo-router-basics","15.react-native\u002Fversion2\u002F11.expo-router-basics",{"title":3582,"path":3583,"stem":3584},"Вкладені навігатори та модалки","\u002Freact-native\u002Fversion2\u002Fnested-navigators-and-modals","15.react-native\u002Fversion2\u002F12.nested-navigators-and-modals",{"title":3586,"path":3587,"stem":3588},"Deep linking і параметри маршрутів","\u002Freact-native\u002Fversion2\u002Fdeep-linking-and-params","15.react-native\u002Fversion2\u002F13.deep-linking-and-params",{"title":3590,"path":3591,"stem":3592},"Робота з мережею та життєвий цикл застосунку","\u002Freact-native\u002Fversion2\u002Fnetworking-and-app-lifecycle","15.react-native\u002Fversion2\u002F14.networking-and-app-lifecycle",{"title":3594,"path":3595,"stem":3596},"Redux Toolkit на мобільних платформах","\u002Freact-native\u002Fversion2\u002Fredux-toolkit-on-mobile","15.react-native\u002Fversion2\u002F15.redux-toolkit-on-mobile",{"title":3598,"path":3599,"stem":3600},"RTK Query на мобільних платформах","\u002Freact-native\u002Fversion2\u002Frtk-query-mobile","15.react-native\u002Fversion2\u002F16.rtk-query-mobile",{"title":3602,"path":3603,"stem":3604},"Автентифікація та Безпечне Зберігання","\u002Freact-native\u002Fversion2\u002Fauth-secure-storage","15.react-native\u002Fversion2\u002F18.auth-secure-storage",{"title":3606,"path":3607,"stem":3608},"Локальне сховище — AsyncStorage, MMKV, SQLite","\u002Freact-native\u002Fversion2\u002Flocal-storage-mmkv-sqlite","15.react-native\u002Fversion2\u002F19.local-storage-mmkv-sqlite",{"title":3610,"path":3611,"stem":3612},"Офлайн-First і синхронізація","\u002Freact-native\u002Fversion2\u002Foffline-first-sync","15.react-native\u002Fversion2\u002F20.offline-first-sync",{"title":3614,"path":3615,"stem":3616},"Камера, медіа та файлова система","\u002Freact-native\u002Fversion2\u002Fcamera-media-filesystem","15.react-native\u002Fversion2\u002F21.camera-media-filesystem",{"title":3618,"path":3619,"stem":3620},"Карти та геолокація","\u002Freact-native\u002Fversion2\u002Fmaps-and-location","15.react-native\u002Fversion2\u002F22.maps-and-location",{"title":3622,"path":3623,"stem":3624},"Жести та дотики","\u002Freact-native\u002Fversion2\u002Fgestures-and-touch","15.react-native\u002Fversion2\u002F23.gestures-and-touch",{"title":3626,"path":3627,"stem":3628},"Анімації з Reanimated","\u002Freact-native\u002Fversion2\u002Freanimated-animations","15.react-native\u002Fversion2\u002F24.reanimated-animations",{"title":3630,"path":3631,"stem":3632},"Push і локальні сповіщення","\u002Freact-native\u002Fversion2\u002Fpush-and-local-notifications","15.react-native\u002Fversion2\u002F25.push-and-local-notifications",{"title":3634,"path":3635,"stem":3636},"Інтернаціоналізація та доступність","\u002Freact-native\u002Fversion2\u002Faccessibility-and-i18n","15.react-native\u002Fversion2\u002F26.accessibility-and-i18n",{"title":3638,"path":3639,"stem":3640},"EAS Build та профілі збірки","\u002Freact-native\u002Fversion2\u002Feas-build-and-profiles","15.react-native\u002Fversion2\u002F29.eas-build-and-profiles",{"title":3642,"path":3643,"stem":3644},"Публікація в App Store та Google Play","\u002Freact-native\u002Fversion2\u002Fapp-store-play-release","15.react-native\u002Fversion2\u002F30.app-store-play-release",{"title":3646,"path":3647,"stem":3648,"children":3649,"page":59},"Ai Python","\u002Fai-python","16.ai-python",[3650,3659,3668,3677,3706,3732,3757,3790,3807,3820,3829,3842,3851,3872,3885,3906],{"title":3651,"path":3652,"stem":3653,"children":3654,"page":59},"Intro To Ai","\u002Fai-python\u002Fintro-to-ai","16.ai-python\u002F01.intro-to-ai",[3655],{"title":3656,"path":3657,"stem":3658},"Вступ до штучного інтелекту та машинного навчання","\u002Fai-python\u002Fintro-to-ai\u002Fintro-to-ai","16.ai-python\u002F01.intro-to-ai\u002F01.intro-to-ai",{"title":3660,"path":3661,"stem":3662,"children":3663,"page":59},"Jupyter Notebook","\u002Fai-python\u002Fjupyter-notebook","16.ai-python\u002F02.jupyter-notebook",[3664],{"title":3665,"path":3666,"stem":3667},"Знайомство з Jupyter Notebook та JupyterLab","\u002Fai-python\u002Fjupyter-notebook\u002Fjupyter-notebook","16.ai-python\u002F02.jupyter-notebook\u002F02.jupyter-notebook",{"title":3669,"path":3670,"stem":3671,"children":3672,"page":59},"Numpy Pandas","\u002Fai-python\u002Fnumpy-pandas","16.ai-python\u002F03.numpy-pandas",[3673],{"title":3674,"path":3675,"stem":3676},"Робота з даними з використанням NumPy і pandas","\u002Fai-python\u002Fnumpy-pandas\u002Fnumpy-pandas","16.ai-python\u002F03.numpy-pandas\u002F03.numpy-pandas",{"title":3678,"path":3679,"stem":3680,"children":3681,"page":59},"Statistics Basics","\u002Fai-python\u002Fstatistics-basics","16.ai-python\u002F04.statistics-basics",[3682,3686,3690,3694,3698,3702],{"title":3683,"path":3684,"stem":3685},"Випадкові величини та їх типи","\u002Fai-python\u002Fstatistics-basics\u002Frandom-variables","16.ai-python\u002F04.statistics-basics\u002F01.random-variables",{"title":3687,"path":3688,"stem":3689},"Міри центральної тенденції","\u002Fai-python\u002Fstatistics-basics\u002Fcentral-tendency","16.ai-python\u002F04.statistics-basics\u002F02.central-tendency",{"title":3691,"path":3692,"stem":3693},"Міри варіації (розсіювання)","\u002Fai-python\u002Fstatistics-basics\u002Fvariability","16.ai-python\u002F04.statistics-basics\u002F03.variability",{"title":3695,"path":3696,"stem":3697},"Розподіли випадкових величин","\u002Fai-python\u002Fstatistics-basics\u002Fdistributions","16.ai-python\u002F04.statistics-basics\u002F04.distributions",{"title":3699,"path":3700,"stem":3701},"Кореляція та взаємозв'язки","\u002Fai-python\u002Fstatistics-basics\u002Fcorrelation","16.ai-python\u002F04.statistics-basics\u002F05.correlation",{"title":3703,"path":3704,"stem":3705},"Викиди та аномалії","\u002Fai-python\u002Fstatistics-basics\u002Foutliers","16.ai-python\u002F04.statistics-basics\u002F06.outliers",{"title":3707,"path":3708,"stem":3709,"children":3710},"Візуалізація даних за допомогою matplotlib","\u002Fai-python\u002Fdata-visualization","16.ai-python\u002F05.data-visualization\u002Findex",[3711,3712,3716,3720,3724,3728],{"title":3707,"path":3708,"stem":3709},{"title":3713,"path":3714,"stem":3715},"Основи matplotlib — Figure, Axes та перший графік","\u002Fai-python\u002Fdata-visualization\u002Fmatplotlib-basics","16.ai-python\u002F05.data-visualization\u002F01.matplotlib-basics",{"title":3717,"path":3718,"stem":3719},"Візуалізація розподілів — гістограми та box plots","\u002Fai-python\u002Fdata-visualization\u002Fdistributions","16.ai-python\u002F05.data-visualization\u002F02.distributions",{"title":3721,"path":3722,"stem":3723},"Візуалізація залежностей — scatter plots та кореляція","\u002Fai-python\u002Fdata-visualization\u002Frelationships","16.ai-python\u002F05.data-visualization\u002F03.relationships",{"title":3725,"path":3726,"stem":3727},"Візуалізація категоріальних даних — bar charts та pie charts","\u002Fai-python\u002Fdata-visualization\u002Fcategorical","16.ai-python\u002F05.data-visualization\u002F04.categorical",{"title":3729,"path":3730,"stem":3731},"Композиції та стилізація — subplots та багатопанельні дашборди","\u002Fai-python\u002Fdata-visualization\u002Fsubplots-styling","16.ai-python\u002F05.data-visualization\u002F05.subplots-styling",{"title":3733,"path":3734,"stem":3735,"children":3736,"page":59},"Linear Regression","\u002Fai-python\u002Flinear-regression","16.ai-python\u002F06.linear-regression",[3737,3741,3745,3749,3753],{"title":3738,"path":3739,"stem":3740},"Регресійна задача та лінійна регресія з однією змінною","\u002Fai-python\u002Flinear-regression\u002Fregression-task","16.ai-python\u002F06.linear-regression\u002F01.regression-task",{"title":3742,"path":3743,"stem":3744},"Метрики якості регресії — як оцінити модель","\u002Fai-python\u002Flinear-regression\u002Fquality-metrics","16.ai-python\u002F06.linear-regression\u002F02.quality-metrics",{"title":3746,"path":3747,"stem":3748},"Градієнтний спуск — як модель навчається","\u002Fai-python\u002Flinear-regression\u002Fgradient-descent","16.ai-python\u002F06.linear-regression\u002F03.gradient-descent",{"title":3750,"path":3751,"stem":3752},"Множинна лінійна регресія — модель з багатьма ознаками","\u002Fai-python\u002Flinear-regression\u002Fmultiple-regression","16.ai-python\u002F06.linear-regression\u002F04.multiple-regression",{"title":3754,"path":3755,"stem":3756},"Практичні завдання та резюме модуля","\u002Fai-python\u002Flinear-regression\u002Fpractice","16.ai-python\u002F06.linear-regression\u002F05.practice",{"title":3758,"path":3759,"stem":3760,"children":3761,"page":59},"Practical Regression","\u002Fai-python\u002Fpractical-regression","16.ai-python\u002F07.practical-regression",[3762,3766,3770,3774,3778,3782,3786],{"title":3763,"path":3764,"stem":3765},"Знайомство з scikit-learn","\u002Fai-python\u002Fpractical-regression\u002Fintro-scikit-learn","16.ai-python\u002F07.practical-regression\u002F01.intro-scikit-learn",{"title":3767,"path":3768,"stem":3769},"Exploratory Data Analysis датасету Housing Prices","\u002Fai-python\u002Fpractical-regression\u002Fhousing-dataset-eda","16.ai-python\u002F07.practical-regression\u002F02.housing-dataset-eda",{"title":3771,"path":3772,"stem":3773},"Попередня обробка даних (Data Preprocessing)","\u002Fai-python\u002Fpractical-regression\u002Fdata-preprocessing","16.ai-python\u002F07.practical-regression\u002F03.data-preprocessing",{"title":3775,"path":3776,"stem":3777},"Train\u002FTest Split та перша повноцінна модель","\u002Fai-python\u002Fpractical-regression\u002Ftrain-test-split","16.ai-python\u002F07.practical-regression\u002F04.train-test-split",{"title":3779,"path":3780,"stem":3781},"Перенавчання та недонавчання","\u002Fai-python\u002Fpractical-regression\u002Foverfitting-underfitting","16.ai-python\u002F07.practical-regression\u002F05.overfitting-underfitting",{"title":3783,"path":3784,"stem":3785},"Крос-валідація (Cross-Validation)","\u002Fai-python\u002Fpractical-regression\u002Fcross-validation","16.ai-python\u002F07.practical-regression\u002F06.cross-validation",{"title":3787,"path":3788,"stem":3789},"Гіперпараметри та їх оптимізація","\u002Fai-python\u002Fpractical-regression\u002Fhyperparameter-tuning","16.ai-python\u002F07.practical-regression\u002F07.hyperparameter-tuning",{"title":3791,"path":3792,"stem":3793,"children":3794,"page":59},"Polynomial Regression","\u002Fai-python\u002Fpolynomial-regression","16.ai-python\u002F08.polynomial-regression",[3795,3799,3803],{"title":3796,"path":3797,"stem":3798},"Нелінійні залежності та поліноміальна регресія","\u002Fai-python\u002Fpolynomial-regression\u002Fnonlinear-patterns","16.ai-python\u002F08.polynomial-regression\u002F01.nonlinear-patterns",{"title":3800,"path":3801,"stem":3802},"Регуляризація в регресії — Ridge та Lasso","\u002Fai-python\u002Fpolynomial-regression\u002Fregularization-methods","16.ai-python\u002F08.polynomial-regression\u002F02.regularization-methods",{"title":3804,"path":3805,"stem":3806},"Практика — поліноміальна регресія та регуляризація на Housing Prices","\u002Fai-python\u002Fpolynomial-regression\u002Fhousing-prices-practice","16.ai-python\u002F08.polynomial-regression\u002F03.housing-prices-practice",{"title":3808,"path":3809,"stem":3810,"children":3811,"page":59},"Decision Trees Random Forest","\u002Fai-python\u002Fdecision-trees-random-forest","16.ai-python\u002F09.decision-trees-random-forest",[3812,3816],{"title":3813,"path":3814,"stem":3815},"Дерева рішень — інтерпретабельні моделі машинного навчання","\u002Fai-python\u002Fdecision-trees-random-forest\u002Fdecision-trees","16.ai-python\u002F09.decision-trees-random-forest\u002F01.decision-trees",{"title":3817,"path":3818,"stem":3819},"Random Forest — ансамблі дерев рішень","\u002Fai-python\u002Fdecision-trees-random-forest\u002Frandom-forest","16.ai-python\u002F09.decision-trees-random-forest\u002F02.random-forest",{"title":3821,"path":3822,"stem":3823,"children":3824,"page":59},"Gradient Boosting","\u002Fai-python\u002Fgradient-boosting","16.ai-python\u002F10.gradient-boosting",[3825],{"title":3826,"path":3827,"stem":3828},"Градієнтний бустинг та XGBoost — послідовне виправлення помилок","\u002Fai-python\u002Fgradient-boosting\u002Fgradient-boosting-xgboost","16.ai-python\u002F10.gradient-boosting\u002F01.gradient-boosting-xgboost",{"title":3830,"path":3831,"stem":3832,"children":3833,"page":59},"Logistic Regression Classification","\u002Fai-python\u002Flogistic-regression-classification","16.ai-python\u002F11.logistic-regression-classification",[3834,3838],{"title":3835,"path":3836,"stem":3837},"Вступ до класифікації та логістична регресія","\u002Fai-python\u002Flogistic-regression-classification\u002Fclassification-basics","16.ai-python\u002F11.logistic-regression-classification\u002F01.classification-basics",{"title":3839,"path":3840,"stem":3841},"Метрики якості класифікації — Confusion Matrix, Precision, Recall, F1","\u002Fai-python\u002Flogistic-regression-classification\u002Fclassification-metrics","16.ai-python\u002F11.logistic-regression-classification\u002F02.classification-metrics",{"title":3843,"path":3844,"stem":3845,"children":3846,"page":59},"Neural Networks Basics","\u002Fai-python\u002Fneural-networks-basics","16.ai-python\u002F12.neural-networks-basics",[3847],{"title":3848,"path":3849,"stem":3850},"Вступ до нейронних мереж — від персептрона до багатошарових мереж","\u002Fai-python\u002Fneural-networks-basics\u002Fperceptron-to-mlp","16.ai-python\u002F12.neural-networks-basics\u002F01.perceptron-to-mlp",{"title":3852,"path":3853,"stem":3854,"children":3855,"page":59},"Pytorch Intro","\u002Fai-python\u002Fpytorch-intro","16.ai-python\u002F13.pytorch-intro",[3856,3860,3864,3868],{"title":3857,"path":3858,"stem":3859},"Тензори та автоматичне диференціювання в PyTorch","\u002Fai-python\u002Fpytorch-intro\u002Ftensors-and-autograd","16.ai-python\u002F13.pytorch-intro\u002F01.tensors-and-autograd",{"title":3861,"path":3862,"stem":3863},"Побудова та тренування нейронних мереж у PyTorch","\u002Fai-python\u002Fpytorch-intro\u002Fbuilding-and-training","16.ai-python\u002F13.pytorch-intro\u002F02.building-and-training",{"title":3865,"path":3866,"stem":3867},"Робота з даними та регуляризація в PyTorch","\u002Fai-python\u002Fpytorch-intro\u002Fdata-and-regularization","16.ai-python\u002F13.pytorch-intro\u002F03.data-and-regularization",{"title":3869,"path":3870,"stem":3871},"Просунуті теми PyTorch — GPU, оптимізація та deployment","\u002Fai-python\u002Fpytorch-intro\u002Fadvanced-topics","16.ai-python\u002F13.pytorch-intro\u002F04.advanced-topics",{"title":3873,"path":3874,"stem":3875,"children":3876,"page":59},"Activation Functions Heart Disease","\u002Fai-python\u002Factivation-functions-heart-disease","16.ai-python\u002F14.activation-functions-heart-disease",[3877,3881],{"title":3878,"path":3879,"stem":3880},"Функції активації та побудова нейронної мережі для передбачення вад серця","\u002Fai-python\u002Factivation-functions-heart-disease\u002Fheart-disease-prediction","16.ai-python\u002F14.activation-functions-heart-disease\u002F01.heart-disease-prediction",{"title":3882,"path":3883,"stem":3884},"Бонус — Повний приклад Student Performance Prediction","\u002Fai-python\u002Factivation-functions-heart-disease\u002Fstudent-performance-bonus","16.ai-python\u002F14.activation-functions-heart-disease\u002F02.student-performance-bonus",{"title":3886,"path":3887,"stem":3888,"children":3889,"page":59},"Cnn Image Classification","\u002Fai-python\u002Fcnn-image-classification","16.ai-python\u002F15.cnn-image-classification",[3890,3894,3898,3902],{"title":3891,"path":3892,"stem":3893},"Основи згорткових нейронних мереж — чому не MLP?","\u002Fai-python\u002Fcnn-image-classification\u002Fcnn-fundamentals","16.ai-python\u002F15.cnn-image-classification\u002F01.cnn-fundamentals",{"title":3895,"path":3896,"stem":3897},"Практична імплементація CNN для класифікації MNIST","\u002Fai-python\u002Fcnn-image-classification\u002Fmnist-classification","16.ai-python\u002F15.cnn-image-classification\u002F02.mnist-classification",{"title":3899,"path":3900,"stem":3901},"Глибші CNN та аугментація даних для CIFAR-10","\u002Fai-python\u002Fcnn-image-classification\u002Fcifar10-and-data-augmentation","16.ai-python\u002F15.cnn-image-classification\u002F03.cifar10-and-data-augmentation",{"title":3903,"path":3904,"stem":3905},"Transfer Learning та візуалізація внутрішніх представлень CNN","\u002Fai-python\u002Fcnn-image-classification\u002Ftransfer-learning-and-visualization","16.ai-python\u002F15.cnn-image-classification\u002F04.transfer-learning-and-visualization",{"title":3907,"path":3908,"stem":3909,"children":3910,"page":59},"Batching Normalization Deep Networks","\u002Fai-python\u002Fbatching-normalization-deep-networks","16.ai-python\u002F16.batching-normalization-deep-networks",[3911,3915,3919,3923,3927,3931,3935],{"title":3912,"path":3913,"stem":3914},"Mini-batch Gradient Descent та ефективна обробка даних","\u002Fai-python\u002Fbatching-normalization-deep-networks\u002Fmini-batch-gradient-descent","16.ai-python\u002F16.batching-normalization-deep-networks\u002F01.mini-batch-gradient-descent",{"title":3916,"path":3917,"stem":3918},"Ініціалізація ваг — фундамент стабільного навчання","\u002Fai-python\u002Fbatching-normalization-deep-networks\u002Fweight-initialization","16.ai-python\u002F16.batching-normalization-deep-networks\u002F02.weight-initialization",{"title":3920,"path":3921,"stem":3922},"Batch Normalization — прорив у глибокому навчанні","\u002Fai-python\u002Fbatching-normalization-deep-networks\u002Fbatch-normalization","16.ai-python\u002F16.batching-normalization-deep-networks\u002F03.batch-normalization",{"title":3924,"path":3925,"stem":3926},"Dropout та комбіновані стратегії регуляризації","\u002Fai-python\u002Fbatching-normalization-deep-networks\u002Fdropout-and-regularization","16.ai-python\u002F16.batching-normalization-deep-networks\u002F04.dropout-and-regularization",{"title":3928,"path":3929,"stem":3930},"Проєктування глибоких мереж — від теорії до практики","\u002Fai-python\u002Fbatching-normalization-deep-networks\u002Fdeep-networks-architectures","16.ai-python\u002F16.batching-normalization-deep-networks\u002F05.deep-networks-architectures",{"title":3932,"path":3933,"stem":3934},"Планування learning rate — коли та як зменшувати крок навчання","\u002Fai-python\u002Fbatching-normalization-deep-networks\u002Flearning-rate-scheduling","16.ai-python\u002F16.batching-normalization-deep-networks\u002F06.learning-rate-scheduling",{"title":3936,"path":3937,"stem":3938},"Підсумковий проєкт — побудова сучасного класифікатора зображень","\u002Fai-python\u002Fbatching-normalization-deep-networks\u002Ffinal-project-deep-classifier","16.ai-python\u002F16.batching-normalization-deep-networks\u002F07.final-project-deep-classifier",{"title":3940,"icon":658,"path":3941,"stem":3942,"children":3943,"page":59},"Клієнт-серверна архітектура","\u002Fclient-server-architecture","17.client-server-architecture",[3944,3948,3952,3956,3960,3964,3968,3972,3976,3980,3984,3988,3992,3996,4000,4004,4008,4012],{"title":3945,"path":3946,"stem":3947},"Архітектура клієнт-серверних систем та основи Go","\u002Fclient-server-architecture\u002Fclient-server-architecture-and-go","17.client-server-architecture\u002F01.client-server-architecture-and-go",{"title":3949,"path":3950,"stem":3951},"Система типів та модель пам’яті мови Go","\u002Fclient-server-architecture\u002Ftype-system-and-memory-model","17.client-server-architecture\u002F02.type-system-and-memory-model",{"title":3953,"path":3954,"stem":3955},"Інтерфейси та модель конкурентності мови Go","\u002Fclient-server-architecture\u002Finterfaces-and-concurrency-model","17.client-server-architecture\u002F03.interfaces-and-concurrency-model",{"title":3957,"path":3958,"stem":3959},"Сокетне програмування та протокол TCP у Go","\u002Fclient-server-architecture\u002Ftcp-socket-programming","17.client-server-architecture\u002F04.tcp-socket-programming",{"title":3961,"path":3962,"stem":3963},"Протокол UDP та датаграмна передача даних","\u002Fclient-server-architecture\u002Fudp-datagram-transmission","17.client-server-architecture\u002F05.udp-datagram-transmission",{"title":3965,"path":3966,"stem":3967},"Криптографічний захист каналів зв","\u002Fclient-server-architecture\u002Ftls-cryptographic-channel-protection","17.client-server-architecture\u002F06.tls-cryptographic-channel-protection",{"title":3969,"path":3970,"stem":3971},"Прикладні мережеві протоколи DNS та SMTP","\u002Fclient-server-architecture\u002Fdns-smtp-application-protocols","17.client-server-architecture\u002F07.dns-smtp-application-protocols",{"title":3973,"path":3974,"stem":3975},"Протокол HTTP та стандартний вебсервер Go","\u002Fclient-server-architecture\u002Fhttp-protocol-and-web-server","17.client-server-architecture\u002F08.http-protocol-and-web-server",{"title":3977,"path":3978,"stem":3979},"Проєктування REST API та маршрутизація засобами Chi","\u002Fclient-server-architecture\u002Frest-api-design-and-chi-routing","17.client-server-architecture\u002F09.rest-api-design-and-chi-routing",{"title":3981,"path":3982,"stem":3983},"Лекція 10. Шарувата архітектура та конвеєри обробки запитів","\u002Fclient-server-architecture\u002Flayered-architecture-and-middleware-pipelines","17.client-server-architecture\u002F10.layered-architecture-and-middleware-pipelines",{"title":3985,"path":3986,"stem":3987},"Лекція 11. Архітектура та маршрутизація фреймворку Gin","\u002Fclient-server-architecture\u002Fgin-framework-architecture-and-routing","17.client-server-architecture\u002F11.gin-framework-architecture-and-routing",{"title":3989,"path":3990,"stem":3991},"Лекція 12. Валідація даних, безпека та документування у Gin","\u002Fclient-server-architecture\u002Fdata-validation-security-and-documentation-in-gin","17.client-server-architecture\u002F12.data-validation-security-and-documentation-in-gin",{"title":3993,"path":3994,"stem":3995},"Лекція 13. Взаємодія з реляційними СУБД та технології ORM","\u002Fclient-server-architecture\u002Frelational-databases-and-orm","17.client-server-architecture\u002F13.relational-databases-and-orm",{"title":3997,"path":3998,"stem":3999},"Потокова передача даних та протокол WebSocket","\u002Fclient-server-architecture\u002Fstreaming-data-and-websockets","17.client-server-architecture\u002F14.streaming-data-and-websockets",{"title":4001,"path":4002,"stem":4003},"Міжсервісна взаємодія на основі протоколу gRPC","\u002Fclient-server-architecture\u002Fgrpc-inter-service-communication","17.client-server-architecture\u002F15.grpc-inter-service-communication",{"title":4005,"path":4006,"stem":4007},"Асинхронний обмін повідомленнями та протоколи черг","\u002Fclient-server-architecture\u002Fasynchronous-message-exchange-and-queue-protocols","17.client-server-architecture\u002F16.asynchronous-message-exchange-and-queue-protocols",{"title":4009,"path":4010,"stem":4011},"Протоколи потокового медіа та технологія WebRTC","\u002Fclient-server-architecture\u002Fstreaming-media-protocols-and-webrtc","17.client-server-architecture\u002F17.streaming-media-protocols-and-webrtc",{"title":4013,"path":4014,"stem":4015},"Моніторинг, безпека та контейнеризація застосунків","\u002Fclient-server-architecture\u002Fmonitoring-security-and-containerization","17.client-server-architecture\u002F18.monitoring-security-and-containerization",{"title":4017,"icon":2074,"path":4018,"stem":4019,"children":4020,"page":59},"Розробка WEB-застосувань","\u002Fweb-development","18.web-development",[4021,4066,4104,4133,4162,4219,4269,4363,4429,4511,4544,4572,4614,4660,4703],{"title":4022,"icon":658,"path":4023,"stem":4024,"children":4025,"page":59},"Архітектура комп'ютерних мереж та мережеві протоколи","\u002Fweb-development\u002Fnetwork-architecture-and-protocols","18.web-development\u002F01.network-architecture-and-protocols",[4026,4030,4034,4038,4042,4046,4050,4054,4058,4062],{"title":4027,"path":4028,"stem":4029},"Еволюція веброзробки та модель клієнт-сервер","\u002Fweb-development\u002Fnetwork-architecture-and-protocols\u002Fweb-evolution-client-server","18.web-development\u002F01.network-architecture-and-protocols\u002F01.web-evolution-client-server",{"title":4031,"path":4032,"stem":4033},"Еталонна модель OSI","\u002Fweb-development\u002Fnetwork-architecture-and-protocols\u002Fosi-model","18.web-development\u002F01.network-architecture-and-protocols\u002F02.osi-model",{"title":4035,"path":4036,"stem":4037},"Стек протоколів TCP\u002FIP","\u002Fweb-development\u002Fnetwork-architecture-and-protocols\u002Ftcp-ip-stack","18.web-development\u002F01.network-architecture-and-protocols\u002F03.tcp-ip-stack",{"title":4039,"path":4040,"stem":4041},"IP-адресація, порти та сокети","\u002Fweb-development\u002Fnetwork-architecture-and-protocols\u002Fip-addressing-ports-sockets","18.web-development\u002F01.network-architecture-and-protocols\u002F04.ip-addressing-ports-sockets",{"title":4043,"path":4044,"stem":4045},"Протоколи TCP та UDP","\u002Fweb-development\u002Fnetwork-architecture-and-protocols\u002Ftcp-udp-protocols","18.web-development\u002F01.network-architecture-and-protocols\u002F05.tcp-udp-protocols",{"title":4047,"path":4048,"stem":4049},"Система доменних імен DNS","\u002Fweb-development\u002Fnetwork-architecture-and-protocols\u002Fdns-system","18.web-development\u002F01.network-architecture-and-protocols\u002F06.dns-system",{"title":4051,"path":4052,"stem":4053},"Протокол SMTP та поштові протоколи","\u002Fweb-development\u002Fnetwork-architecture-and-protocols\u002Fsmtp-email-protocols","18.web-development\u002F01.network-architecture-and-protocols\u002F07.smtp-email-protocols",{"title":4055,"path":4056,"stem":4057},"Протокол WebSocket","\u002Fweb-development\u002Fnetwork-architecture-and-protocols\u002Fwebsocket-protocol","18.web-development\u002F01.network-architecture-and-protocols\u002F08.websocket-protocol",{"title":4059,"path":4060,"stem":4061},"Протокол SSH","\u002Fweb-development\u002Fnetwork-architecture-and-protocols\u002Fssh-protocol","18.web-development\u002F01.network-architecture-and-protocols\u002F09.ssh-protocol",{"title":4063,"path":4064,"stem":4065},"Огляд сучасних серверних технологій","\u002Fweb-development\u002Fnetwork-architecture-and-protocols\u002Fmodern-backend-technologies","18.web-development\u002F01.network-architecture-and-protocols\u002F10.modern-backend-technologies",{"title":4067,"icon":4068,"path":4069,"stem":4070,"children":4071,"page":59},"Протокол HTTP та захищена передача даних HTTPS","i-lucide-lock","\u002Fweb-development\u002Fhttp-and-https","18.web-development\u002F02.http-and-https",[4072,4076,4080,4084,4088,4092,4096,4100],{"title":4073,"path":4074,"stem":4075},"Вступ до протоколу HTTP та еволюція веб-технологій","\u002Fweb-development\u002Fhttp-and-https\u002Fintroduction-and-evolution","18.web-development\u002F02.http-and-https\u002F01.introduction-and-evolution",{"title":4077,"path":4078,"stem":4079},"Анатомія HTTP-запиту","\u002Fweb-development\u002Fhttp-and-https\u002Fhttp-request-structure","18.web-development\u002F02.http-and-https\u002F02.http-request-structure",{"title":4081,"path":4082,"stem":4083},"Анатомія HTTP-відповіді","\u002Fweb-development\u002Fhttp-and-https\u002Fhttp-response-structure","18.web-development\u002F02.http-and-https\u002F03.http-response-structure",{"title":4085,"path":4086,"stem":4087},"Методи HTTP та їхня семантика","\u002Fweb-development\u002Fhttp-and-https\u002Fhttp-methods","18.web-development\u002F02.http-and-https\u002F04.http-methods",{"title":4089,"path":4090,"stem":4091},"Коди статусу HTTP та їхнє призначення","\u002Fweb-development\u002Fhttp-and-https\u002Fhttp-status-codes","18.web-development\u002F02.http-and-https\u002F05.http-status-codes",{"title":4093,"path":4094,"stem":4095},"HTTP-заголовки та їхнє призначення","\u002Fweb-development\u002Fhttp-and-https\u002Fhttp-headers","18.web-development\u002F02.http-and-https\u002F06.http-headers",{"title":4097,"path":4098,"stem":4099},"CORS та механізми безпеки HTTP","\u002Fweb-development\u002Fhttp-and-https\u002Fcors-and-security","18.web-development\u002F02.http-and-https\u002F07.cors-and-security",{"title":4101,"path":4102,"stem":4103},"HTTPS та криптографічні основи TLS","\u002Fweb-development\u002Fhttp-and-https\u002Fhttps-and-tls","18.web-development\u002F02.http-and-https\u002F08.https-and-tls",{"title":4105,"icon":2454,"path":4106,"stem":4107,"children":4108,"page":59},"Система типів мови TypeScript","\u002Fweb-development\u002Ftypescript-type-system","18.web-development\u002F03.typescript-type-system",[4109,4113,4117,4121,4125,4129],{"title":4110,"path":4111,"stem":4112},"Знайомство з TypeScript Compiler","\u002Fweb-development\u002Ftypescript-type-system\u002Fintroduction-compiler-config","18.web-development\u002F03.typescript-type-system\u002F01.introduction-compiler-config",{"title":4114,"path":4115,"stem":4116},"Примітивні типи даних: string, number, boolean","\u002Fweb-development\u002Ftypescript-type-system\u002Fprimitive-special-types","18.web-development\u002F03.typescript-type-system\u002F02.primitive-special-types",{"title":4118,"path":4119,"stem":4120},"Масиви, кортежі та об","\u002Fweb-development\u002Ftypescript-type-system\u002Farrays-tuples-objects","18.web-development\u002F03.typescript-type-system\u002F03.arrays-tuples-objects",{"title":4122,"path":4123,"stem":4124},"Псевдоніми типів, Union типи, Intersection типи та літеральні типи","\u002Fweb-development\u002Ftypescript-type-system\u002Funion-intersection-literals","18.web-development\u002F03.typescript-type-system\u002F04.union-intersection-literals",{"title":4126,"path":4127,"stem":4128},"Type Narrowing (звуження типів)","\u002Fweb-development\u002Ftypescript-type-system\u002Ftype-narrowing","18.web-development\u002F03.typescript-type-system\u002F05.type-narrowing",{"title":4130,"path":4131,"stem":4132},"Типізація функцій","\u002Fweb-development\u002Ftypescript-type-system\u002Ffunctions-typing","18.web-development\u002F03.typescript-type-system\u002F06.functions-typing",{"title":4134,"icon":2454,"path":4135,"stem":4136,"children":4137,"page":59},"Розширені засоби мови TypeScript","\u002Fweb-development\u002Ftypescript-advanced","18.web-development\u002F04.typescript-advanced",[4138,4142,4146,4150,4154,4158],{"title":4139,"path":4140,"stem":4141},"Interfaces та Classes","\u002Fweb-development\u002Ftypescript-advanced\u002Finterfaces-classes","18.web-development\u002F04.typescript-advanced\u002F01.interfaces-classes",{"title":4143,"path":4144,"stem":4145},"Основи Generic типів","\u002Fweb-development\u002Ftypescript-advanced\u002Fgenerics-fundamentals","18.web-development\u002F04.typescript-advanced\u002F02.generics-fundamentals",{"title":4147,"path":4148,"stem":4149},"Enums та Decorators","\u002Fweb-development\u002Ftypescript-advanced\u002Fenums-decorators","18.web-development\u002F04.typescript-advanced\u002F03.enums-decorators",{"title":4151,"path":4152,"stem":4153},"Utility Types","\u002Fweb-development\u002Ftypescript-advanced\u002Futility-types","18.web-development\u002F04.typescript-advanced\u002F04.utility-types",{"title":4155,"path":4156,"stem":4157},"Mapped та Conditional Types","\u002Fweb-development\u002Ftypescript-advanced\u002Fmapped-conditional-types","18.web-development\u002F04.typescript-advanced\u002F05.mapped-conditional-types",{"title":4159,"path":4160,"stem":4161},"Модулі та Strict Mode","\u002Fweb-development\u002Ftypescript-advanced\u002Fmodules-strict-mode","18.web-development\u002F04.typescript-advanced\u002F06.modules-strict-mode",{"title":4163,"icon":4164,"path":4165,"stem":4166,"children":4167,"page":59},"Платформа Node.js як середовище виконання","i-devicon-nodejs","\u002Fweb-development\u002Fnodejs-platform","18.web-development\u002F05.nodejs-platform",[4168,4172,4176,4180,4184,4188,4192,4196,4200,4204,4208,4211,4215],{"title":4169,"path":4170,"stem":4171},"Що таке Node.js та його архітектура","\u002Fweb-development\u002Fnodejs-platform\u002Fwhat-is-nodejs","18.web-development\u002F05.nodejs-platform\u002F01.what-is-nodejs",{"title":4173,"path":4174,"stem":4175},"Event Loop — серце Node.js","\u002Fweb-development\u002Fnodejs-platform\u002Fevent-loop","18.web-development\u002F05.nodejs-platform\u002F02.event-loop",{"title":4177,"path":4178,"stem":4179},"Модульні системи: CommonJS vs ES Modules","\u002Fweb-development\u002Fnodejs-platform\u002Fmodule-systems","18.web-development\u002F05.nodejs-platform\u002F03.module-systems",{"title":4181,"path":4182,"stem":4183},"npm та package.json","\u002Fweb-development\u002Fnodejs-platform\u002Fnpm-package-json","18.web-development\u002F05.nodejs-platform\u002F04.npm-package-json",{"title":4185,"path":4186,"stem":4187},"Глобальні об\\","\u002Fweb-development\u002Fnodejs-platform\u002Fglobal-objects","18.web-development\u002F05.nodejs-platform\u002F05.global-objects",{"title":4189,"path":4190,"stem":4191},"Вбудований модуль fs (File System)","\u002Fweb-development\u002Fnodejs-platform\u002Fbuilt-in-modules-fs","18.web-development\u002F05.nodejs-platform\u002F06.built-in-modules-fs",{"title":4193,"path":4194,"stem":4195},"Вбудований модуль path","\u002Fweb-development\u002Fnodejs-platform\u002Fbuilt-in-modules-path","18.web-development\u002F05.nodejs-platform\u002F07.built-in-modules-path",{"title":4197,"path":4198,"stem":4199},"Вбудований модуль http","\u002Fweb-development\u002Fnodejs-platform\u002Fbuilt-in-modules-http","18.web-development\u002F05.nodejs-platform\u002F08.built-in-modules-http",{"title":4201,"path":4202,"stem":4203},"Streams — потоки даних у Node.js","\u002Fweb-development\u002Fnodejs-platform\u002Fstreams","18.web-development\u002F05.nodejs-platform\u002F09.streams",{"title":4205,"path":4206,"stem":4207},"Вбудований модуль util (Utilities)","\u002Fweb-development\u002Fnodejs-platform\u002Fbuilt-in-modules-util","18.web-development\u002F05.nodejs-platform\u002F10.built-in-modules-util",{"title":3097,"path":4209,"stem":4210},"\u002Fweb-development\u002Fnodejs-platform\u002Fenvironment-variables","18.web-development\u002F05.nodejs-platform\u002F11.environment-variables",{"title":4212,"path":4213,"stem":4214},"Інструменти налагодження та розробки","\u002Fweb-development\u002Fnodejs-platform\u002Fdebugging-tools","18.web-development\u002F05.nodejs-platform\u002F12.debugging-tools",{"title":4216,"path":4217,"stem":4218},"Продуктивність та Best Practices","\u002Fweb-development\u002Fnodejs-platform\u002Fperformance-best-practices","18.web-development\u002F05.nodejs-platform\u002F13.performance-best-practices",{"title":4220,"icon":4221,"path":4222,"stem":4223,"children":4224,"page":59},"Асинхронне програмування у Node.js","i-lucide-clock","\u002Fweb-development\u002Fasync-programming-nodejs","18.web-development\u002F06.async-programming-nodejs",[4225,4229,4233,4237,4241,4245,4249,4253,4257,4261,4265],{"title":4226,"path":4227,"stem":4228},"Синхронний vs асинхронний код","\u002Fweb-development\u002Fasync-programming-nodejs\u002Fsync-vs-async","18.web-development\u002F06.async-programming-nodejs\u002F01.sync-vs-async",{"title":4230,"path":4231,"stem":4232},"Callbacks — перша парадигма асинхронності","\u002Fweb-development\u002Fasync-programming-nodejs\u002Fcallbacks","18.web-development\u002F06.async-programming-nodejs\u002F02.callbacks",{"title":4234,"path":4235,"stem":4236},"Promises — стандарт асинхронності","\u002Fweb-development\u002Fasync-programming-nodejs\u002Fpromises","18.web-development\u002F06.async-programming-nodejs\u002F03.promises",{"title":4238,"path":4239,"stem":4240},"Promise комбінатори","\u002Fweb-development\u002Fasync-programming-nodejs\u002Fpromise-combinators","18.web-development\u002F06.async-programming-nodejs\u002F04.promise-combinators",{"title":4242,"path":4243,"stem":4244},"Async\u002FAwait — синтаксичний цукор над Promises","\u002Fweb-development\u002Fasync-programming-nodejs\u002Fasync-await","18.web-development\u002F06.async-programming-nodejs\u002F05.async-await",{"title":4246,"path":4247,"stem":4248},"Паралельне vs послідовне виконання","\u002Fweb-development\u002Fasync-programming-nodejs\u002Fparallel-vs-sequential","18.web-development\u002F06.async-programming-nodejs\u002F06.parallel-vs-sequential",{"title":4250,"path":4251,"stem":4252},"EventEmitter — подієва модель Node.js","\u002Fweb-development\u002Fasync-programming-nodejs\u002Fevent-emitter","18.web-development\u002F06.async-programming-nodejs\u002F07.event-emitter",{"title":4254,"path":4255,"stem":4256},"Worker Threads — багатопотоковість у Node.js","\u002Fweb-development\u002Fasync-programming-nodejs\u002Fworker-threads","18.web-development\u002F06.async-programming-nodejs\u002F08.worker-threads",{"title":4258,"path":4259,"stem":4260},"Child Processes — запуск зовнішніх процесів","\u002Fweb-development\u002Fasync-programming-nodejs\u002Fchild-processes","18.web-development\u002F06.async-programming-nodejs\u002F09.child-processes",{"title":4262,"path":4263,"stem":4264},"Обробка помилок у асинхронному коді","\u002Fweb-development\u002Fasync-programming-nodejs\u002Ferror-handling","18.web-development\u002F06.async-programming-nodejs\u002F10.error-handling",{"title":4266,"path":4267,"stem":4268},"Порівняння Node.js фреймворків","\u002Fweb-development\u002Fasync-programming-nodejs\u002Fnodejs-frameworks","18.web-development\u002F06.async-programming-nodejs\u002F11.nodejs-frameworks",{"title":4270,"icon":4271,"path":4272,"stem":4273,"children":4274,"page":59},"Тема 7. Фреймворк NestJS. Контролери та маршрутизація","i-lucide-route","\u002Fweb-development\u002Fnestjs-controllers-routing","18.web-development\u002F07.nestjs-controllers-routing",[4275,4279,4283,4287,4291,4295,4299,4303,4307,4311,4315,4319,4323,4327,4331,4335,4339,4343,4347,4351,4355,4359],{"title":4276,"path":4277,"stem":4278},"Філософія та архітектура NestJS","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Fnestjs-philosophy","18.web-development\u002F07.nestjs-controllers-routing\u002F01.nestjs-philosophy",{"title":4280,"path":4281,"stem":4282},"Три стовпи архітектури: модулі, контролери, провайдери","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Fthree-pillars","18.web-development\u002F07.nestjs-controllers-routing\u002F02.three-pillars",{"title":4284,"path":4285,"stem":4286},"Інверсія керування (IoC) та ін","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Fioc-and-di","18.web-development\u002F07.nestjs-controllers-routing\u002F03.ioc-and-di",{"title":4288,"path":4289,"stem":4290},"Встановлення NestJS CLI та створення проєкту","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Fnestjs-cli-setup","18.web-development\u002F07.nestjs-controllers-routing\u002F04.nestjs-cli-setup",{"title":4292,"path":4293,"stem":4294},"Структура проєкту NestJS та конвенції","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Fproject-structure","18.web-development\u002F07.nestjs-controllers-routing\u002F05.project-structure",{"title":4296,"path":4297,"stem":4298},"Контролери: основи маршрутизації","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Fcontrollers-basics","18.web-development\u002F07.nestjs-controllers-routing\u002F06.controllers-basics",{"title":4300,"path":4301,"stem":4302},"Декоратори маршрутизації HTTP-методів","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Froute-decorators","18.web-development\u002F07.nestjs-controllers-routing\u002F07.route-decorators",{"title":4304,"path":4305,"stem":4306},"Параметри маршруту та декоратор @Param","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Froute-parameters","18.web-development\u002F07.nestjs-controllers-routing\u002F08.route-parameters",{"title":4308,"path":4309,"stem":4310},"Query-параметри та декоратор @Query","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Fquery-parameters","18.web-development\u002F07.nestjs-controllers-routing\u002F09.query-parameters",{"title":4312,"path":4313,"stem":4314},"Тіло запиту та декоратор @Body","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Frequest-body","18.web-development\u002F07.nestjs-controllers-routing\u002F10.request-body",{"title":4316,"path":4317,"stem":4318},"Робота з заголовками запиту та відповіді","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Fheaders-decorators","18.web-development\u002F07.nestjs-controllers-routing\u002F11.headers-decorators",{"title":4320,"path":4321,"stem":4322},"Data Transfer Objects (DTO): визначення та призначення","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Fdto-introduction","18.web-development\u002F07.nestjs-controllers-routing\u002F12.dto-introduction",{"title":4324,"path":4325,"stem":4326},"Об'єкти Request та Response","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Frequest-response-objects","18.web-development\u002F07.nestjs-controllers-routing\u002F13.request-response-objects",{"title":4328,"path":4329,"stem":4330},"HTTP-статус коди та декоратор @HttpCode","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Fhttp-status-codes","18.web-development\u002F07.nestjs-controllers-routing\u002F14.http-status-codes",{"title":4332,"path":4333,"stem":4334},"Перенаправлення (Redirects) та декоратор @Redirect","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Fredirects","18.web-development\u002F07.nestjs-controllers-routing\u002F15.redirects",{"title":4336,"path":4337,"stem":4338},"Асинхронні обробники та Observables","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Fasync-handlers","18.web-development\u002F07.nestjs-controllers-routing\u002F16.async-handlers",{"title":4340,"path":4341,"stem":4342},"Маршрутизація за піддоменами та версіонування API","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Fsub-domain-routing","18.web-development\u002F07.nestjs-controllers-routing\u002F17.sub-domain-routing",{"title":4344,"path":4345,"stem":4346},"Області видимості контролерів (Scopes)","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Fcontroller-scopes","18.web-development\u002F07.nestjs-controllers-routing\u002F18.controller-scopes",{"title":4348,"path":4349,"stem":4350},"Основні команди NestJS CLI","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Fnestjs-cli-commands","18.web-development\u002F07.nestjs-controllers-routing\u002F19.nestjs-cli-commands",{"title":4352,"path":4353,"stem":4354},"Практичний приклад: створення CRUD-контролера","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Fcrud-example","18.web-development\u002F07.nestjs-controllers-routing\u002F20.crud-example",{"title":4356,"path":4357,"stem":4358},"Тестування контролерів","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Ftesting-controllers","18.web-development\u002F07.nestjs-controllers-routing\u002F21.testing-controllers",{"title":4360,"path":4361,"stem":4362},"Best Practices для контролерів та маршрутизації","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Fbest-practices","18.web-development\u002F07.nestjs-controllers-routing\u002F22.best-practices",{"title":4364,"icon":4365,"path":4366,"stem":4367,"children":4368,"page":59},"Тема 8. Провайдери, сервіси та модульна система NestJS","i-lucide-package","\u002Fweb-development\u002Fnestjs-providers-modules","18.web-development\u002F08.nestjs-providers-modules",[4369,4373,4377,4381,4385,4389,4393,4397,4401,4405,4409,4413,4417,4421,4425],{"title":4370,"path":4371,"stem":4372},"Концепція провайдерів у NestJS","\u002Fweb-development\u002Fnestjs-providers-modules\u002Fproviders-concept","18.web-development\u002F08.nestjs-providers-modules\u002F01.providers-concept",{"title":4374,"path":4375,"stem":4376},"Сервіси: інкапсуляція бізнес-логіки","\u002Fweb-development\u002Fnestjs-providers-modules\u002Fservices-business-logic","18.web-development\u002F08.nestjs-providers-modules\u002F02.services-business-logic",{"title":4378,"path":4379,"stem":4380},"Практика ін","\u002Fweb-development\u002Fnestjs-providers-modules\u002Fdependency-injection-practice","18.web-development\u002F08.nestjs-providers-modules\u002F03.dependency-injection-practice",{"title":4382,"path":4383,"stem":4384},"Custom Providers: useValue, useClass, useFactory, useExisting","\u002Fweb-development\u002Fnestjs-providers-modules\u002Fcustom-providers","18.web-development\u002F08.nestjs-providers-modules\u002F04.custom-providers",{"title":4386,"path":4387,"stem":4388},"Injection Tokens: рядкові та символьні токени","\u002Fweb-development\u002Fnestjs-providers-modules\u002Finjection-tokens","18.web-development\u002F08.nestjs-providers-modules\u002F05.injection-tokens",{"title":4390,"path":4391,"stem":4392},"Опціональні провайдери та декоратор @Optional","\u002Fweb-development\u002Fnestjs-providers-modules\u002Foptional-providers","18.web-development\u002F08.nestjs-providers-modules\u002F07.optional-providers",{"title":4394,"path":4395,"stem":4396},"Структура модулів: imports, controllers, providers, exports","\u002Fweb-development\u002Fnestjs-providers-modules\u002Fmodules-structure","18.web-development\u002F08.nestjs-providers-modules\u002F08.modules-structure",{"title":4398,"path":4399,"stem":4400},"Feature Modules: ізоляція функціональності","\u002Fweb-development\u002Fnestjs-providers-modules\u002Ffeature-modules","18.web-development\u002F08.nestjs-providers-modules\u002F09.feature-modules",{"title":4402,"path":4403,"stem":4404},"Shared Modules: перевикористання провайдерів","\u002Fweb-development\u002Fnestjs-providers-modules\u002Fshared-modules","18.web-development\u002F08.nestjs-providers-modules\u002F10.shared-modules",{"title":4406,"path":4407,"stem":4408},"Global Modules та декоратор @Global","\u002Fweb-development\u002Fnestjs-providers-modules\u002Fglobal-modules","18.web-development\u002F08.nestjs-providers-modules\u002F11.global-modules",{"title":4410,"path":4411,"stem":4412},"Dynamic Modules: конфігурація під час виконання","\u002Fweb-development\u002Fnestjs-providers-modules\u002Fdynamic-modules","18.web-development\u002F08.nestjs-providers-modules\u002F12.dynamic-modules",{"title":4414,"path":4415,"stem":4416},"Циклічні залежності та forwardRef","\u002Fweb-development\u002Fnestjs-providers-modules\u002Fcircular-dependencies","18.web-development\u002F08.nestjs-providers-modules\u002F13.circular-dependencies",{"title":4418,"path":4419,"stem":4420},"Lifecycle Hooks: хуки життєвого циклу","\u002Fweb-development\u002Fnestjs-providers-modules\u002Flifecycle-hooks","18.web-development\u002F08.nestjs-providers-modules\u002F14.lifecycle-hooks",{"title":4422,"path":4423,"stem":4424},"ModuleRef: динамічний доступ до провайдерів","\u002Fweb-development\u002Fnestjs-providers-modules\u002Fmodule-reference","18.web-development\u002F08.nestjs-providers-modules\u002F15.module-reference",{"title":4426,"path":4427,"stem":4428},"Тестування сервісів та провайдерів","\u002Fweb-development\u002Fnestjs-providers-modules\u002Ftesting-services","18.web-development\u002F08.nestjs-providers-modules\u002F16.testing-services",{"title":4430,"icon":4431,"path":4432,"stem":4433,"children":4434,"page":59},"Тема 9. Валідація даних та конвеєр обробки запитів у NestJS","i-lucide-filter","\u002Fweb-development\u002Fnestjs-validation-pipeline","18.web-development\u002F09.nestjs-validation-pipeline",[4435,4439,4443,4447,4451,4455,4459,4463,4467,4471,4475,4479,4483,4487,4491,4495,4499,4503,4507],{"title":4436,"path":4437,"stem":4438},"Request Pipeline: конвеєр обробки запитів","\u002Fweb-development\u002Fnestjs-validation-pipeline\u002Frequest-pipeline","18.web-development\u002F09.nestjs-validation-pipeline\u002F01.request-pipeline",{"title":4440,"path":4441,"stem":4442},"Pipes: концепція трансформації та валідації","\u002Fweb-development\u002Fnestjs-validation-pipeline\u002Fpipes-concept","18.web-development\u002F09.nestjs-validation-pipeline\u002F02.pipes-concept",{"title":4444,"path":4445,"stem":4446},"Вбудовані pipes: ParseIntPipe, ParseBoolPipe, ParseUUIDPipe","\u002Fweb-development\u002Fnestjs-validation-pipeline\u002Fbuilt-in-pipes","18.web-development\u002F09.nestjs-validation-pipeline\u002F03.built-in-pipes",{"title":4448,"path":4449,"stem":4450},"ValidationPipe: автоматична валідація DTO","\u002Fweb-development\u002Fnestjs-validation-pipeline\u002Fvalidation-pipe","18.web-development\u002F09.nestjs-validation-pipeline\u002F04.validation-pipe",{"title":4452,"path":4453,"stem":4454},"Декоратори class-validator для DTO","\u002Fweb-development\u002Fnestjs-validation-pipeline\u002Fclass-validator-decorators","18.web-development\u002F09.nestjs-validation-pipeline\u002F05.class-validator-decorators",{"title":4456,"path":4457,"stem":4458},"Створення кастомних валідаторів","\u002Fweb-development\u002Fnestjs-validation-pipeline\u002Fcustom-validators","18.web-development\u002F09.nestjs-validation-pipeline\u002F06.custom-validators",{"title":4460,"path":4461,"stem":4462},"Створення власних Pipes","\u002Fweb-development\u002Fnestjs-validation-pipeline\u002Fcustom-pipes","18.web-development\u002F09.nestjs-validation-pipeline\u002F07.custom-pipes",{"title":4464,"path":4465,"stem":4466},"Основи Middleware","\u002Fweb-development\u002Fnestjs-validation-pipeline\u002Fmiddleware-basics","18.web-development\u002F09.nestjs-validation-pipeline\u002F08.middleware-basics",{"title":4468,"path":4469,"stem":4470},"Практичні приклади Middleware","\u002Fweb-development\u002Fnestjs-validation-pipeline\u002Fmiddleware-examples","18.web-development\u002F09.nestjs-validation-pipeline\u002F09.middleware-examples",{"title":4472,"path":4473,"stem":4474},"Guards: захисні перехоплювачі доступу","\u002Fweb-development\u002Fnestjs-validation-pipeline\u002Fguards-concept","18.web-development\u002F09.nestjs-validation-pipeline\u002F10.guards-concept",{"title":4476,"path":4477,"stem":4478},"Створення кастомних Guards","\u002Fweb-development\u002Fnestjs-validation-pipeline\u002Fcustom-guards","18.web-development\u002F09.nestjs-validation-pipeline\u002F11.custom-guards",{"title":4480,"path":4481,"stem":4482},"Interceptors: перехоплення та трансформація відповідей","\u002Fweb-development\u002Fnestjs-validation-pipeline\u002Finterceptors-concept","18.web-development\u002F09.nestjs-validation-pipeline\u002F12.interceptors-concept",{"title":4484,"path":4485,"stem":4486},"Практичні приклади Interceptors","\u002Fweb-development\u002Fnestjs-validation-pipeline\u002Finterceptor-examples","18.web-development\u002F09.nestjs-validation-pipeline\u002F13.interceptor-examples",{"title":4488,"path":4489,"stem":4490},"Exception Filters: обробка виключень","\u002Fweb-development\u002Fnestjs-validation-pipeline\u002Fexception-filters","18.web-development\u002F09.nestjs-validation-pipeline\u002F14.exception-filters",{"title":4492,"path":4493,"stem":4494},"Custom Exception Filters: спеціалізовані фільтри","\u002Fweb-development\u002Fnestjs-validation-pipeline\u002Fcustom-exception-filters","18.web-development\u002F09.nestjs-validation-pipeline\u002F15.custom-exception-filters",{"title":4496,"path":4497,"stem":4498},"Execution Context: контекст виконання","\u002Fweb-development\u002Fnestjs-validation-pipeline\u002Fexecution-context","18.web-development\u002F09.nestjs-validation-pipeline\u002F16.execution-context",{"title":4500,"path":4501,"stem":4502},"Custom Decorators: власні декоратори","\u002Fweb-development\u002Fnestjs-validation-pipeline\u002Fcustom-decorators","18.web-development\u002F09.nestjs-validation-pipeline\u002F17.custom-decorators",{"title":4504,"path":4505,"stem":4506},"Global Components: глобальні компоненти","\u002Fweb-development\u002Fnestjs-validation-pipeline\u002Fglobal-components","18.web-development\u002F09.nestjs-validation-pipeline\u002F18.global-components",{"title":4508,"path":4509,"stem":4510},"Pipeline Best Practices: найкращі практики","\u002Fweb-development\u002Fnestjs-validation-pipeline\u002Fpipeline-best-practices","18.web-development\u002F09.nestjs-validation-pipeline\u002F19.pipeline-best-practices",{"title":4512,"icon":2821,"path":4513,"stem":4514,"children":4515,"page":59},"Інтеграція реляційних баз даних засобами TypeORM","\u002Fweb-development\u002Ftypeorm-integration","18.web-development\u002F10.typeorm-integration",[4516,4520,4524,4528,4532,4536,4540],{"title":4517,"path":4518,"stem":4519},"Робота з базами даних у Node.js через нативні драйвери","\u002Fweb-development\u002Ftypeorm-integration\u002F01-nodejs-database-drivers","18.web-development\u002F10.typeorm-integration\u002F01-nodejs-database-drivers",{"title":4521,"path":4522,"stem":4523},"ORM та знайомство з TypeORM","\u002Fweb-development\u002Ftypeorm-integration\u002F02-orm-and-typeorm-intro","18.web-development\u002F10.typeorm-integration\u002F02-orm-and-typeorm-intro",{"title":4525,"path":4526,"stem":4527},"Налаштування TypeORM у NestJS проєкті","\u002Fweb-development\u002Ftypeorm-integration\u002F03-typeorm-setup-nestjs","18.web-development\u002F10.typeorm-integration\u002F03-typeorm-setup-nestjs",{"title":4529,"path":4530,"stem":4531},"Entity класи та декоратори TypeORM","\u002Fweb-development\u002Ftypeorm-integration\u002F04-entities-and-decorators","18.web-development\u002F10.typeorm-integration\u002F04-entities-and-decorators",{"title":4533,"path":4534,"stem":4535},"Repository Pattern та CRUD операції","\u002Fweb-development\u002Ftypeorm-integration\u002F05-repository-pattern-crud","18.web-development\u002F10.typeorm-integration\u002F05-repository-pattern-crud",{"title":4537,"path":4538,"stem":4539},"Пагінація та фільтрація даних","\u002Fweb-development\u002Ftypeorm-integration\u002F06-pagination-filtering","18.web-development\u002F10.typeorm-integration\u002F06-pagination-filtering",{"title":4541,"path":4542,"stem":4543},"QueryBuilder для складних запитів","\u002Fweb-development\u002Ftypeorm-integration\u002F07-query-builder","18.web-development\u002F10.typeorm-integration\u002F07-query-builder",{"title":4545,"icon":4546,"path":4547,"stem":4548,"children":4549,"page":59},"Зв'язки між сутностями, міграції та оптимізація запитів","i-lucide-git-branch","\u002Fweb-development\u002Fentity-relations-migrations","18.web-development\u002F11.entity-relations-migrations",[4550,4554,4558,4562,4566],{"title":4551,"path":4552,"stem":4553},"Зв'язки One-to-Many та Many-to-One","\u002Fweb-development\u002Fentity-relations-migrations\u002F01-one-to-many-many-to-one","18.web-development\u002F11.entity-relations-migrations\u002F01-one-to-many-many-to-one",{"title":4555,"path":4556,"stem":4557},"Зв'язки One-to-One та Many-to-Many","\u002Fweb-development\u002Fentity-relations-migrations\u002F02-one-to-one-many-to-many","18.web-development\u002F11.entity-relations-migrations\u002F02-one-to-one-many-to-many",{"title":4559,"path":4560,"stem":4561},"Керування міграціями бази даних","\u002Fweb-development\u002Fentity-relations-migrations\u002F03-migrations-management","18.web-development\u002F11.entity-relations-migrations\u002F03-migrations-management",{"title":4563,"path":4564,"stem":4565},"Транзакції у TypeORM","\u002Fweb-development\u002Fentity-relations-migrations\u002F04-transactions","18.web-development\u002F11.entity-relations-migrations\u002F04-transactions",{"title":4567,"path":4568,"stem":4569,"children":4570},"Індекси та оптимізація запитів","\u002Fweb-development\u002Fentity-relations-migrations\u002F05-indexes-optimization","18.web-development\u002F11.entity-relations-migrations\u002F05-indexes-optimization",[4571],{"title":4567,"path":4568,"stem":4569},{"title":4573,"icon":4574,"path":4575,"stem":4576,"children":4577,"page":59},"Механізми автентифікації у вебзастосунках","i-lucide-key","\u002Fweb-development\u002Fauthentication","18.web-development\u002F12.authentication",[4578,4582,4586,4590,4594,4598,4602,4606,4610],{"title":4579,"path":4580,"stem":4581},"Автентифікація vs Авторизація","\u002Fweb-development\u002Fauthentication\u002F01-authentication-vs-authorization","18.web-development\u002F12.authentication\u002F01-authentication-vs-authorization",{"title":4583,"path":4584,"stem":4585},"Стратегії автентифікації","\u002Fweb-development\u002Fauthentication\u002F02-authentication-strategies","18.web-development\u002F12.authentication\u002F02-authentication-strategies",{"title":4587,"path":4588,"stem":4589},"Автентифікація на основі сесій","\u002Fweb-development\u002Fauthentication\u002F03-session-based-authentication","18.web-development\u002F12.authentication\u002F03-session-based-authentication",{"title":4591,"path":4592,"stem":4593},"Структура JSON Web Token (JWT)","\u002Fweb-development\u002Fauthentication\u002F04-jwt-structure","18.web-development\u002F12.authentication\u002F04-jwt-structure",{"title":4595,"path":4596,"stem":4597},"Інтеграція JWT у NestJS","\u002Fweb-development\u002Fauthentication\u002F05-nestjs-jwt-integration","18.web-development\u002F12.authentication\u002F05-nestjs-jwt-integration",{"title":4599,"path":4600,"stem":4601},"Passport.js стратегії у NestJS","\u002Fweb-development\u002Fauthentication\u002F06-passport-strategies","18.web-development\u002F12.authentication\u002F06-passport-strategies",{"title":4603,"path":4604,"stem":4605},"Безпечне зберігання паролів: хешування та криптографічний захист","\u002Fweb-development\u002Fauthentication\u002F07-password-hashing","18.web-development\u002F12.authentication\u002F07-password-hashing",{"title":4607,"path":4608,"stem":4609},"Refresh Token та безпечне оновлення доступу","\u002Fweb-development\u002Fauthentication\u002F08-refresh-tokens","18.web-development\u002F12.authentication\u002F08-refresh-tokens",{"title":4611,"path":4612,"stem":4613},"OAuth 2.0 та OpenID Connect: делегована автентифікація","\u002Fweb-development\u002Fauthentication\u002F09-oauth2-openid","18.web-development\u002F12.authentication\u002F09-oauth2-openid",{"title":4615,"icon":4616,"path":4617,"stem":4618,"children":4619,"page":59},"Авторизація та безпека серверних застосунків","i-lucide-shield","\u002Fweb-development\u002Fauthorization-security","18.web-development\u002F13.authorization-security",[4620,4624,4628,4632,4636,4640,4644,4648,4652,4656],{"title":4621,"path":4622,"stem":4623},"Моделі авторизації","\u002Fweb-development\u002Fauthorization-security\u002F01-authorization-models","18.web-development\u002F13.authorization-security\u002F01-authorization-models",{"title":4625,"path":4626,"stem":4627},"Реалізація RBAC у NestJS","\u002Fweb-development\u002Fauthorization-security\u002F02-rbac-implementation","18.web-development\u002F13.authorization-security\u002F02-rbac-implementation",{"title":4629,"path":4630,"stem":4631},"Permission-Based Access Control","\u002Fweb-development\u002Fauthorization-security\u002F03-permission-based-access","18.web-development\u002F13.authorization-security\u002F03-permission-based-access",{"title":4633,"path":4634,"stem":4635},"ABAC та бібліотека CASL","\u002Fweb-development\u002Fauthorization-security\u002F04-abac-casl","18.web-development\u002F13.authorization-security\u002F04-abac-casl",{"title":4637,"path":4638,"stem":4639},"Кастомні декоратори та метадані","\u002Fweb-development\u002Fauthorization-security\u002F05-custom-decorators-metadata","18.web-development\u002F13.authorization-security\u002F05-custom-decorators-metadata",{"title":4641,"path":4642,"stem":4643},"Конфігурація CORS","\u002Fweb-development\u002Fauthorization-security\u002F06-cors-configuration","18.web-development\u002F13.authorization-security\u002F06-cors-configuration",{"title":4645,"path":4646,"stem":4647},"Захист через HTTP Security Headers (Helmet)","\u002Fweb-development\u002Fauthorization-security\u002F07-helmet-security-headers","18.web-development\u002F13.authorization-security\u002F07-helmet-security-headers",{"title":4649,"path":4650,"stem":4651},"Rate Limiting та Throttling","\u002Fweb-development\u002Fauthorization-security\u002F08-rate-limiting-throttling","18.web-development\u002F13.authorization-security\u002F08-rate-limiting-throttling",{"title":4653,"path":4654,"stem":4655},"Захист від поширених атак","\u002Fweb-development\u002Fauthorization-security\u002F09-common-attacks-protection","18.web-development\u002F13.authorization-security\u002F09-common-attacks-protection",{"title":4657,"path":4658,"stem":4659},"HTTPS у продакшені","\u002Fweb-development\u002Fauthorization-security\u002F10-https-production","18.web-development\u002F13.authorization-security\u002F10-https-production",{"title":4661,"icon":871,"path":4662,"stem":4663,"children":4664,"page":59},"Комунікація у реальному часі, нотифікації та фонові задачі","\u002Fweb-development\u002Frealtime-notifications-jobs","18.web-development\u002F14.realtime-notifications-jobs",[4665,4669,4673,4677,4681,4685,4688,4691,4695,4699],{"title":4666,"path":4667,"stem":4668},"Методи комунікації у реальному часі","\u002Fweb-development\u002Frealtime-notifications-jobs\u002F01-realtime-communication-methods","18.web-development\u002F14.realtime-notifications-jobs\u002F01-realtime-communication-methods",{"title":4670,"path":4671,"stem":4672},"WebSocket Gateway у NestJS","\u002Fweb-development\u002Frealtime-notifications-jobs\u002F02-websocket-gateway","18.web-development\u002F14.realtime-notifications-jobs\u002F02-websocket-gateway",{"title":4674,"path":4675,"stem":4676},"Lifecycle та Rooms у WebSocket","\u002Fweb-development\u002Frealtime-notifications-jobs\u002F03-websocket-lifecycle-rooms","18.web-development\u002F14.realtime-notifications-jobs\u002F03-websocket-lifecycle-rooms",{"title":4678,"path":4679,"stem":4680},"Server-Sent Events (SSE)","\u002Fweb-development\u002Frealtime-notifications-jobs\u002F04-server-sent-events","18.web-development\u002F14.realtime-notifications-jobs\u002F04-server-sent-events",{"title":4682,"path":4683,"stem":4684},"Архітектура системи нотифікацій","\u002Fweb-development\u002Frealtime-notifications-jobs\u002F05-notification-system","18.web-development\u002F14.realtime-notifications-jobs\u002F05-notification-system",{"title":904,"path":4686,"stem":4687},"\u002Fweb-development\u002Frealtime-notifications-jobs\u002F06-email-notifications","18.web-development\u002F14.realtime-notifications-jobs\u002F06-email-notifications",{"title":900,"path":4689,"stem":4690},"\u002Fweb-development\u002Frealtime-notifications-jobs\u002F07-web-push-notifications","18.web-development\u002F14.realtime-notifications-jobs\u002F07-web-push-notifications",{"title":4692,"path":4693,"stem":4694},"Фонові задачі та черги","\u002Fweb-development\u002Frealtime-notifications-jobs\u002F08-background-jobs-queues","18.web-development\u002F14.realtime-notifications-jobs\u002F08-background-jobs-queues",{"title":4696,"path":4697,"stem":4698},"Планувальники та Cron jobs","\u002Fweb-development\u002Frealtime-notifications-jobs\u002F09-schedulers-cron","18.web-development\u002F14.realtime-notifications-jobs\u002F09-schedulers-cron",{"title":4700,"path":4701,"stem":4702},"Практичні сценарії застосування","\u002Fweb-development\u002Frealtime-notifications-jobs\u002F10-practical-scenarios","18.web-development\u002F14.realtime-notifications-jobs\u002F10-practical-scenarios",{"title":4704,"icon":4705,"path":4706,"stem":4707,"children":4708,"page":59},"Тестування, документування та розгортання застосунків","i-lucide-rocket","\u002Fweb-development\u002Ftesting-documentation-deployment","18.web-development\u002F15.testing-documentation-deployment",[4709,4713,4717,4721,4725,4729,4733,4737,4741,4745],{"title":4710,"path":4711,"stem":4712},"Піраміда тестування та стратегія","\u002Fweb-development\u002Ftesting-documentation-deployment\u002F01-testing-pyramid-strategy","18.web-development\u002F15.testing-documentation-deployment\u002F01-testing-pyramid-strategy",{"title":4714,"path":4715,"stem":4716},"Основи Jest у NestJS","\u002Fweb-development\u002Ftesting-documentation-deployment\u002F02-jest-nestjs-basics","18.web-development\u002F15.testing-documentation-deployment\u002F02-jest-nestjs-basics",{"title":4718,"path":4719,"stem":4720},"Unit-тестування сервісів","\u002Fweb-development\u002Ftesting-documentation-deployment\u002F03-unit-testing-services","18.web-development\u002F15.testing-documentation-deployment\u002F03-unit-testing-services",{"title":4722,"path":4723,"stem":4724},"E2E тестування та покриття коду","\u002Fweb-development\u002Ftesting-documentation-deployment\u002F04-e2e-testing-coverage","18.web-development\u002F15.testing-documentation-deployment\u002F04-e2e-testing-coverage",{"title":4726,"path":4727,"stem":4728},"Swagger, OpenAPI, Scalar та Arazzo","\u002Fweb-development\u002Ftesting-documentation-deployment\u002F05-swagger-openapi","18.web-development\u002F15.testing-documentation-deployment\u002F05-swagger-openapi",{"title":4730,"path":4731,"stem":4732},"Керування конфігурацією","\u002Fweb-development\u002Ftesting-documentation-deployment\u002F06-configuration-management","18.web-development\u002F15.testing-documentation-deployment\u002F06-configuration-management",{"title":4734,"path":4735,"stem":4736},"Docker та контейнеризація","\u002Fweb-development\u002Ftesting-documentation-deployment\u002F07-docker-containerization","18.web-development\u002F15.testing-documentation-deployment\u002F07-docker-containerization",{"title":4738,"path":4739,"stem":4740},"CI\u002FCD Pipelines","\u002Fweb-development\u002Ftesting-documentation-deployment\u002F08-cicd-pipelines","18.web-development\u002F15.testing-documentation-deployment\u002F08-cicd-pipelines",{"title":4742,"path":4743,"stem":4744},"Логування та моніторинг","\u002Fweb-development\u002Ftesting-documentation-deployment\u002F09-logging-monitoring","18.web-development\u002F15.testing-documentation-deployment\u002F09-logging-monitoring",{"title":4746,"path":4747,"stem":4748},"Серіалізація та компресія","\u002Fweb-development\u002Ftesting-documentation-deployment\u002F10-serialization-compression","18.web-development\u002F15.testing-documentation-deployment\u002F10-serialization-compression",{"title":4750,"icon":4751,"path":4752,"stem":4753,"children":4754,"page":59},"AI Core: мислення AI","i-lucide-brain","\u002Fai-core","19.ai-core",[4755,4796,4845,4883],{"title":4756,"icon":4705,"path":4757,"stem":4758,"children":4759,"page":59},"Вхід у професію AI Native Developer","\u002Fai-core\u002Fai-native-developer","19.ai-core\u002F01.ai-native-developer",[4760,4764,4768,4772,4776,4780,4784,4788,4792],{"title":4761,"path":4762,"stem":4763},"Вступ до професії розробника цифрових продуктів","\u002Fai-core\u002Fai-native-developer\u002Fdigital-products","19.ai-core\u002F01.ai-native-developer\u002F01.digital-products",{"title":4765,"path":4766,"stem":4767},"Професія розробника програмного забезпечення","\u002Fai-core\u002Fai-native-developer\u002Fsoftware-developer","19.ai-core\u002F01.ai-native-developer\u002F02.software-developer",{"title":4769,"path":4770,"stem":4771},"Професія AI Native Developer","\u002Fai-core\u002Fai-native-developer\u002Fai-native-developer","19.ai-core\u002F01.ai-native-developer\u002F03.ai-native-developer",{"title":4773,"path":4774,"stem":4775},"Вплив AI на розробку програмного забезпечення","\u002Fai-core\u002Fai-native-developer\u002Fai-impact-on-development","19.ai-core\u002F01.ai-native-developer\u002F04.ai-impact-on-development",{"title":4777,"path":4778,"stem":4779},"Життєвий цикл цифрового продукту","\u002Fai-core\u002Fai-native-developer\u002Fproduct-lifecycle","19.ai-core\u002F01.ai-native-developer\u002F05.product-lifecycle",{"title":4781,"path":4782,"stem":4783},"Застосування AI на етапах створення цифрового продукту","\u002Fai-core\u002Fai-native-developer\u002Fai-in-product-creation","19.ai-core\u002F01.ai-native-developer\u002F06.ai-in-product-creation",{"title":4785,"path":4786,"stem":4787},"Розподіл завдань між людиною та AI","\u002Fai-core\u002Fai-native-developer\u002Fhuman-vs-ai-tasks","19.ai-core\u002F01.ai-native-developer\u002F07.human-vs-ai-tasks",{"title":4789,"path":4790,"stem":4791},"Компетенції AI Native Developer","\u002Fai-core\u002Fai-native-developer\u002Fcompetencies","19.ai-core\u002F01.ai-native-developer\u002F08.competencies",{"title":4793,"path":4794,"stem":4795},"Карта професійного розвитку AI Native Developer","\u002Fai-core\u002Fai-native-developer\u002Fcareer-roadmap","19.ai-core\u002F01.ai-native-developer\u002F09.career-roadmap",{"title":4797,"icon":255,"path":4798,"stem":4799,"children":4800,"page":59},"Штучний інтелект і машинне навчання","\u002Fai-core\u002Fai-and-machine-learning","19.ai-core\u002F02.ai-and-machine-learning",[4801,4805,4809,4813,4817,4821,4825,4829,4833,4837,4841],{"title":4802,"path":4803,"stem":4804},"Штучний інтелект","\u002Fai-core\u002Fai-and-machine-learning\u002Fwhat-is-ai","19.ai-core\u002F02.ai-and-machine-learning\u002F01.what-is-ai",{"title":4806,"path":4807,"stem":4808},"Відмінність AI від інших програмних систем","\u002Fai-core\u002Fai-and-machine-learning\u002Fai-vs-automation","19.ai-core\u002F02.ai-and-machine-learning\u002F02.ai-vs-automation",{"title":4810,"path":4811,"stem":4812},"Завдання штучного інтелекту","\u002Fai-core\u002Fai-and-machine-learning\u002Fai-tasks","19.ai-core\u002F02.ai-and-machine-learning\u002F03.ai-tasks",{"title":4814,"path":4815,"stem":4816},"Machine Learning","\u002Fai-core\u002Fai-and-machine-learning\u002Fmachine-learning","19.ai-core\u002F02.ai-and-machine-learning\u002F04.machine-learning",{"title":4818,"path":4819,"stem":4820},"Machine Learning Workflow","\u002Fai-core\u002Fai-and-machine-learning\u002Fml-workflow","19.ai-core\u002F02.ai-and-machine-learning\u002F05.ml-workflow",{"title":4822,"path":4823,"stem":4824},"Типи машинного навчання","\u002Fai-core\u002Fai-and-machine-learning\u002Fml-types","19.ai-core\u002F02.ai-and-machine-learning\u002F06.ml-types",{"title":4826,"path":4827,"stem":4828},"Generative AI","\u002Fai-core\u002Fai-and-machine-learning\u002Fgenerative-ai","19.ai-core\u002F02.ai-and-machine-learning\u002F07.generative-ai",{"title":4830,"path":4831,"stem":4832},"Типи контенту, створюваного AI","\u002Fai-core\u002Fai-and-machine-learning\u002Fai-content-types","19.ai-core\u002F02.ai-and-machine-learning\u002F08.ai-content-types",{"title":4834,"path":4835,"stem":4836},"Великі мовні моделі","\u002Fai-core\u002Fai-and-machine-learning\u002Flarge-language-models","19.ai-core\u002F02.ai-and-machine-learning\u002F09.large-language-models",{"title":4838,"path":4839,"stem":4840},"Можливості та обмеження Generative AI","\u002Fai-core\u002Fai-and-machine-learning\u002Fcapabilities-and-limitations","19.ai-core\u002F02.ai-and-machine-learning\u002F10.capabilities-and-limitations",{"title":4842,"path":4843,"stem":4844},"Сучасні AI-асистенти","\u002Fai-core\u002Fai-and-machine-learning\u002Fmodern-ai-assistants","19.ai-core\u002F02.ai-and-machine-learning\u002F11.modern-ai-assistants",{"title":4846,"icon":4847,"path":4848,"stem":4849,"children":4850,"page":59},"Постановка завдань для AI: принципи створення промптів","i-lucide-message-square-text","\u002Fai-core\u002Fprompt-engineering","19.ai-core\u002F03.prompt-engineering",[4851,4855,4859,4863,4867,4871,4875,4879],{"title":4852,"path":4853,"stem":4854},"Промпт як інструмент","\u002Fai-core\u002Fprompt-engineering\u002Fprompt-as-tool","19.ai-core\u002F03.prompt-engineering\u002F01.prompt-as-tool",{"title":4856,"path":4857,"stem":4858},"Принципи створення промптів","\u002Fai-core\u002Fprompt-engineering\u002Fprompt-principles","19.ai-core\u002F03.prompt-engineering\u002F02.prompt-principles",{"title":4860,"path":4861,"stem":4862},"Структура промпта","\u002Fai-core\u002Fprompt-engineering\u002Fprompt-structure","19.ai-core\u002F03.prompt-engineering\u002F03.prompt-structure",{"title":4864,"path":4865,"stem":4866},"Види промптів за типом задачі","\u002Fai-core\u002Fprompt-engineering\u002Fprompt-types","19.ai-core\u002F03.prompt-engineering\u002F04.prompt-types",{"title":4868,"path":4869,"stem":4870},"Прийоми посилення промпта","\u002Fai-core\u002Fprompt-engineering\u002Fprompt-enhancement","19.ai-core\u002F03.prompt-engineering\u002F05.prompt-enhancement",{"title":4872,"path":4873,"stem":4874},"Розбір і покращення промпта","\u002Fai-core\u002Fprompt-engineering\u002Fprompt-analysis","19.ai-core\u002F03.prompt-engineering\u002F06.prompt-analysis",{"title":4876,"path":4877,"stem":4878},"Типові помилки під час створення промптів","\u002Fai-core\u002Fprompt-engineering\u002Fcommon-mistakes","19.ai-core\u002F03.prompt-engineering\u002F07.common-mistakes",{"title":4880,"path":4881,"stem":4882},"Практикум: особиста бібліотека промптів","\u002Fai-core\u002Fprompt-engineering\u002Fprompt-library","19.ai-core\u002F03.prompt-engineering\u002F08.prompt-library",{"title":4884,"icon":793,"path":4885,"stem":4886,"children":4887,"page":59},"Перевірка результатів, безпека та AI Native Workflow","\u002Fai-core\u002Fverification-security-workflow","19.ai-core\u002F04.verification-security-workflow",[4888,4892,4896,4900,4904,4908,4912,4916,4920],{"title":4889,"path":4890,"stem":4891},"Межі можливостей AI","\u002Fai-core\u002Fverification-security-workflow\u002Fai-limitations","19.ai-core\u002F04.verification-security-workflow\u002F01.ai-limitations",{"title":4893,"path":4894,"stem":4895},"Галюцинації AI","\u002Fai-core\u002Fverification-security-workflow\u002Fai-hallucinations","19.ai-core\u002F04.verification-security-workflow\u002F02.ai-hallucinations",{"title":4897,"path":4898,"stem":4899},"Критичне мислення під час роботи з AI","\u002Fai-core\u002Fverification-security-workflow\u002Fcritical-thinking","19.ai-core\u002F04.verification-security-workflow\u002F03.critical-thinking",{"title":4901,"path":4902,"stem":4903},"Перевірка результатів роботи AI","\u002Fai-core\u002Fverification-security-workflow\u002Fresult-verification","19.ai-core\u002F04.verification-security-workflow\u002F04.result-verification",{"title":4905,"path":4906,"stem":4907},"Захист даних під час роботи з AI","\u002Fai-core\u002Fverification-security-workflow\u002Fdata-protection","19.ai-core\u002F04.verification-security-workflow\u002F05.data-protection",{"title":4909,"path":4910,"stem":4911},"Авторське право та навчальна доброчесність","\u002Fai-core\u002Fverification-security-workflow\u002Fcopyright-integrity","19.ai-core\u002F04.verification-security-workflow\u002F06.copyright-integrity",{"title":4913,"path":4914,"stem":4915},"Відповідальне використання AI","\u002Fai-core\u002Fverification-security-workflow\u002Fresponsible-use","19.ai-core\u002F04.verification-security-workflow\u002F07.responsible-use",{"title":4917,"path":4918,"stem":4919},"AI Native Workflow","\u002Fai-core\u002Fverification-security-workflow\u002Fai-native-workflow","19.ai-core\u002F04.verification-security-workflow\u002F08.ai-native-workflow",{"title":4921,"path":4922,"stem":4923},"Застосування AI Native Workflow під час створення цифрового продукту","\u002Fai-core\u002Fverification-security-workflow\u002Fworkflow-in-practice","19.ai-core\u002F04.verification-security-workflow\u002F09.workflow-in-practice",{"title":4925,"icon":4926,"path":4927,"stem":4928,"children":4929,"page":59},"AI Developer: Vibe Coding Sprint","i-lucide-code-xml","\u002Fai-developer","20.ai-developer",[4930,4956,4978,4999],{"title":4931,"icon":4932,"path":4933,"stem":4934,"children":4935,"page":59},"Проєктування і чорнова версія","i-lucide-pencil-ruler","\u002Fai-developer\u002Fdraft-creation","20.ai-developer\u002F01.draft-creation",[4936,4940,4944,4948,4952],{"title":4937,"path":4938,"stem":4939},"Парадигма Vibe Coding Workflow","\u002Fai-developer\u002Fdraft-creation\u002Fvibe-coding-workflow","20.ai-developer\u002F01.draft-creation\u002F01.vibe-coding-workflow",{"title":4941,"path":4942,"stem":4943},"AI-середовище швидкої розробки","\u002Fai-developer\u002Fdraft-creation\u002Fai-ide-environment","20.ai-developer\u002F01.draft-creation\u002F02.ai-ide-environment",{"title":4945,"path":4946,"stem":4947},"Користувацьке завдання та межі першої версії","\u002Fai-developer\u002Fdraft-creation\u002Fproduct-scope-and-mvp","20.ai-developer\u002F01.draft-creation\u002F03.product-scope-and-mvp",{"title":4949,"path":4950,"stem":4951},"Архітектура інтерфейсу та стани застосунку","\u002Fai-developer\u002Fdraft-creation\u002Fapplication-architecture-and-states","20.ai-developer\u002F01.draft-creation\u002F04.application-architecture-and-states",{"title":4953,"path":4954,"stem":4955},"Генерація та фіксація чорнової версії","\u002Fai-developer\u002Fdraft-creation\u002Fgenerating-draft-version","20.ai-developer\u002F01.draft-creation\u002F05.generating-draft-version",{"title":4957,"icon":4958,"path":4959,"stem":4960,"children":4961,"page":59},"Перша робоча версія","i-lucide-play-circle","\u002Fai-developer\u002Ffirst-working-version","20.ai-developer\u002F02.first-working-version",[4962,4966,4970,4974],{"title":4963,"path":4964,"stem":4965},"Уточнення контексту проєкту для AI","\u002Fai-developer\u002Ffirst-working-version\u002Frefining-project-context","20.ai-developer\u002F02.first-working-version\u002F01.refining-project-context",{"title":4967,"path":4968,"stem":4969},"Стратегія завдань для AI-розробки","\u002Fai-developer\u002Ffirst-working-version\u002Fai-prompting-strategy","20.ai-developer\u002F02.first-working-version\u002F02.ai-prompting-strategy",{"title":4971,"path":4972,"stem":4973},"Доопрацювання до першої робочої версії","\u002Fai-developer\u002Ffirst-working-version\u002Frefining-to-working-version","20.ai-developer\u002F02.first-working-version\u002F03.refining-to-working-version",{"title":4975,"path":4976,"stem":4977},"Перевірка першої робочої версії","\u002Fai-developer\u002Ffirst-working-version\u002Ftesting-first-working-version","20.ai-developer\u002F02.first-working-version\u002F04.testing-first-working-version",{"title":4979,"icon":3028,"path":4980,"stem":4981,"children":4982,"page":59},"Ітерації, дебагінг та відновлення","\u002Fai-developer\u002Fiteration-debugging-recovery","20.ai-developer\u002F03.iteration-debugging-recovery",[4983,4987,4991,4995],{"title":4984,"path":4985,"stem":4986},"Управління точковими змінами продукту","\u002Fai-developer\u002Fiteration-debugging-recovery\u002Fchange-management","20.ai-developer\u002F03.iteration-debugging-recovery\u002F01.change-management",{"title":4988,"path":4989,"stem":4990},"Надійність інтерфейсу та обробка помилок","\u002Fai-developer\u002Fiteration-debugging-recovery\u002Fscenario-reliability-and-errors","20.ai-developer\u002F03.iteration-debugging-recovery\u002F02.scenario-reliability-and-errors",{"title":4992,"path":4993,"stem":4994},"Керування контекстом і збереження версій","\u002Fai-developer\u002Fiteration-debugging-recovery\u002Fproject-context-and-versions","20.ai-developer\u002F03.iteration-debugging-recovery\u002F03.project-context-and-versions",{"title":4996,"path":4997,"stem":4998},"Збої AI-розробки, ліміти та аварійне відновлення","\u002Fai-developer\u002Fiteration-debugging-recovery\u002Fai-breakdowns-and-recovery","20.ai-developer\u002F03.iteration-debugging-recovery\u002F04.ai-breakdowns-and-recovery",{"title":5000,"icon":5001,"path":5002,"stem":5003,"children":5004,"page":59},"Перевірка та завершення продукту","i-lucide-check-check","\u002Fai-developer\u002Fverification-and-completion","20.ai-developer\u002F04.verification-and-completion",[5005,5009,5013],{"title":5006,"path":5007,"stem":5008},"Комплексний аудит та підсумкове тестування","\u002Fai-developer\u002Fverification-and-completion\u002Ffinal-product-testing","20.ai-developer\u002F04.verification-and-completion\u002F01.final-product-testing",{"title":5010,"path":5011,"stem":5012},"Підготовка підсумкової версії коду","\u002Fai-developer\u002Fverification-and-completion\u002Fpreparing-release-version","20.ai-developer\u002F04.verification-and-completion\u002F02.preparing-release-version",{"title":5014,"path":5015,"stem":5016},"Демонстрація, публікація та критерії оцінювання","\u002Fai-developer\u002Fverification-and-completion\u002Fproduct-showcase-and-publishing","20.ai-developer\u002F04.verification-and-completion\u002F03.product-showcase-and-publishing",{"title":5018,"path":5019,"stem":5020,"children":5021,"page":59},"Tailwind","\u002Ftailwind","21.tailwind",[5022,5026,5030,5034,5038,5042,5046,5050,5054,5058,5062,5066],{"title":5023,"path":5024,"stem":5025},"Що таке Tailwind CSS і навіщо він потрібен","\u002Ftailwind\u002Ftailwind-intro-philosophy","21.tailwind\u002F01.tailwind-intro-philosophy",{"title":5027,"path":5028,"stem":5029},"Встановлення та налаштування Tailwind CSS v4","\u002Ftailwind\u002Ftailwind-installation-setup","21.tailwind\u002F02.tailwind-installation-setup",{"title":5031,"path":5032,"stem":5033},"Utility-класи: основи та система Tailwind","\u002Ftailwind\u002Ftailwind-utility-classes-core","21.tailwind\u002F03.tailwind-utility-classes-core",{"title":5035,"path":5036,"stem":5037},"Layout: Flexbox та Grid через Tailwind","\u002Ftailwind\u002Ftailwind-flexbox-grid","21.tailwind\u002F04.tailwind-flexbox-grid",{"title":5039,"path":5040,"stem":5041},"Кастомізація теми через @theme у Tailwind v4","\u002Ftailwind\u002Ftailwind-theme-customization","21.tailwind\u002F05.tailwind-theme-customization",{"title":5043,"path":5044,"stem":5045},"Варіанти: hover, focus, responsive, dark mode та нові v4","\u002Ftailwind\u002Ftailwind-variants-states","21.tailwind\u002F06.tailwind-variants-states",{"title":5047,"path":5048,"stem":5049},"Типографіка та система кольорів у Tailwind v4","\u002Ftailwind\u002Ftailwind-typography-colors","21.tailwind\u002F07.tailwind-typography-colors",{"title":5051,"path":5052,"stem":5053},"Компоненти та повторюваність: @apply, @utility та патерни","\u002Ftailwind\u002Ftailwind-components-patterns","21.tailwind\u002F08.tailwind-components-patterns",{"title":5055,"path":5056,"stem":5057},"Темна тема та система дизайн-токенів у Tailwind v4","\u002Ftailwind\u002Ftailwind-dark-mode-theming","21.tailwind\u002F09.tailwind-dark-mode-theming",{"title":5059,"path":5060,"stem":5061},"Довільні значення та контейнерні запити у Tailwind v4","\u002Ftailwind\u002Ftailwind-arbitrary-container-queries","21.tailwind\u002F10.tailwind-arbitrary-container-queries",{"title":5063,"path":5064,"stem":5065},"Анімації, трансформації та 3D у Tailwind v4","\u002Ftailwind\u002Ftailwind-animations-transforms","21.tailwind\u002F11.tailwind-animations-transforms",{"title":5067,"path":5068,"stem":5069},"Tailwind CLI, PostCSS та інтеграція з фреймворками","\u002Ftailwind\u002Ftailwind-cli-tooling","21.tailwind\u002F12.tailwind-cli-tooling",{"title":5071,"path":5072,"stem":5073},"Тестування компонентів діаграм","\u002Ftest-components","98.test-components",{"id":5075,"title":4738,"body":5076,"description":5245,"extension":21522,"links":21523,"meta":21524,"navigation":5305,"path":4739,"seo":21525,"stem":4740,"__hash__":21526},"docs\u002F18.web-development\u002F15.testing-documentation-deployment\u002F08-cicd-pipelines.md",{"type":5077,"value":5078,"toc":21452},"minimark",[5079,5083,5146,5149,5154,5158,5223,5226,5228,5232,5239,5461,5466,5499,5505,5552,5559,5843,5845,5848,5853,5867,5872,5914,5919,5945,5949,5955,5960,5982,5987,6013,6018,6044,6046,6050,6055,6059,6097,6101,6110,6116,6122,6128,6134,6139,6375,6379,6387,6441,6449,6499,6507,6541,6549,6611,6616,6682,6684,6688,6691,7729,7733,7748,7771,7779,7832,7839,7847,7924,7932,7969,7977,8006,8014,8054,8202,8204,8208,8211,8616,8620,8625,8631,8634,8648,8651,8669,8674,8738,8743,8909,8913,8918,8947,8952,8970,8975,8993,8998,9016,9021,9048,9055,9072,9074,9078,9081,9085,9480,9484,9489,9517,9522,9546,9551,9572,9577,9597,9604,9608,9881,9883,9887,9890,9894,9900,10328,10333,10353,10358,10372,10376,10381,10637,10641,10660,10664,10678,10682,10691,11106,11110,11130,11134,11148,11150,11154,11157,11161,11450,11454,11783,11787,11792,11799,11822,11825,11836,11843,11845,11848,11852,11855,11860,11879,11884,11889,11894,11902,11906,12060,12064,12067,12072,12091,12096,12112,12115,12189,12265,12267,12271,12274,12278,12994,12999,13024,13028,13144,13148,13151,13303,13307,13310,13502,13504,13508,13511,13515,13886,13890,14436,14439,14463,14465,14469,14472,14476,14662,14666,14766,14770,14949,14953,15008,15012,15053,15055,15059,15063,15066,15233,15236,15240,15291,15295,15336,15340,15374,15377,15381,15460,15464,15467,15497,15501,15504,15655,15761,15765,15770,15967,15969,15973,15976,18651,18839,18841,18845,18942,18947,19021,19026,19076,19083,19085,19089,21413,21418,21448],[5080,5081,4738],"h1",{"id":5082},"cicd-pipelines",[5084,5085,5086,5110],"card-group",{},[5087,5088,5091],"card",{"icon":5089,"title":5090},"i-lucide-target","🎯 Мета лекції",[5092,5093,5094,5098,5101,5104,5107],"ul",{},[5095,5096,5097],"li",{},"Опанувати концепції Continuous Integration та Continuous Deployment для автоматизації delivery процесу.",[5095,5099,5100],{},"Навчитися створювати GitHub Actions workflows для автоматичного тестування, побудови та розгортання NestJS застосунків.",[5095,5102,5103],{},"Освоїти налаштування pipeline stages: lint, unit tests, E2E tests, Docker build, deployment.",[5095,5105,5106],{},"Зрозуміти стратегії deployment: Blue-Green, Rolling Updates, Canary Releases для zero-downtime deploys.",[5095,5108,5109],{},"Впровадити best practices для безпечного управління secrets та notifications при збоях.",[5087,5111,5113],{"icon":4574,"title":5112},"🔑 Ключові терміни",[5092,5114,5115,5122,5128,5134,5140],{},[5095,5116,5117,5121],{},[5118,5119,5120],"strong",{},"CI (Continuous Integration):"," практика автоматичного запуску тестів при кожному commit для раннього виявлення проблем.",[5095,5123,5124,5127],{},[5118,5125,5126],{},"CD (Continuous Deployment):"," автоматичне розгортання застосунку у production після успішного проходження всіх тестів.",[5095,5129,5130,5133],{},[5118,5131,5132],{},"Pipeline:"," послідовність автоматизованих етапів (stages) від commit до production deploy.",[5095,5135,5136,5139],{},[5118,5137,5138],{},"Workflow:"," YAML файл, що описує CI\u002FCD процес у GitHub Actions.",[5095,5141,5142,5145],{},[5118,5143,5144],{},"Artifact:"," результат побудови (Docker image, compiled code), що зберігається для наступних stages або deployment.",[5147,5148],"hr",{},[5150,5151,5153],"h2",{"id":5152},"короткий-зміст","Короткий зміст",[5155,5156,5157],"p",{},"У цій лекції вивчається автоматизація процесів testing, building та deployment через CI\u002FCD:",[5092,5159,5160,5166,5177,5183,5189,5195,5201,5207,5217],{},[5095,5161,5162,5165],{},[5118,5163,5164],{},"Концепція CI\u002FCD"," — Continuous Integration: автоматичний запуск тестів при кожному commit, early detection проблем; Continuous Deployment: автоматичний deploy після успішних тестів, швидкий delivery features",[5095,5167,5168,5171,5172,5176],{},[5118,5169,5170],{},"GitHub Actions"," — CI\u002FCD платформа інтегрована у GitHub, workflow files у ",[5173,5174,5175],"code",{},".github\u002Fworkflows\u002F",", тригери: push, pull_request, schedule, manual dispatch",[5095,5178,5179,5182],{},[5118,5180,5181],{},"Workflow синтаксис YAML"," — структура: name, on (triggers), jobs (stages), steps (commands), використання actions з marketplace, environment variables та secrets",[5095,5184,5185,5188],{},[5118,5186,5187],{},"Pipeline stages"," — типовий flow: 1) Checkout code, 2) Setup Node.js, 3) Install dependencies, 4) Lint code (ESLint), 5) Run unit tests, 6) Run E2E tests, 7) Build production, 8) Deploy, кожен stage може блокувати наступний при failure",[5095,5190,5191,5194],{},[5118,5192,5193],{},"Running tests у CI"," — setup test database через Docker services, environment variables для test config, parallel test execution через matrix strategy (Node 18\u002F20), artifacts для test reports та coverage",[5095,5196,5197,5200],{},[5118,5198,5199],{},"Docker build та push"," — build Docker image у CI, login до Docker registry (Docker Hub, GitHub Container Registry, AWS ECR), tagging з version\u002Fcommit SHA, push до registry для deployment",[5095,5202,5203,5206],{},[5118,5204,5205],{},"Deployment strategies"," — Blue-Green deployment: два identичні environments, switch traffic; Rolling updates: поступова заміна instances; Canary releases: поступовий rollout до частини users",[5095,5208,5209,5212,5213,5216],{},[5118,5210,5211],{},"Environment secrets"," — зберігання sensitive data у GitHub Secrets, доступ через ",[5173,5214,5215],{},"${{ secrets.DATABASE_URL }}",", різні secrets для staging\u002Fproduction environments",[5095,5218,5219,5222],{},[5118,5220,5221],{},"Notifications"," — Slack\u002FDiscord\u002FEmail notifications при failed builds, integration з issue tracking для auto-creating bugs",[5155,5224,5225],{},"Розглядаються практичні приклади: GitHub Actions workflow для NestJS (lint → test → build → deploy), setup PostgreSQL service для E2E тестів, deploy до AWS\u002FDigitalOcean, rollback strategies.",[5147,5227],{},[5150,5229,5231],{"id":5230},"проблема-ручного-deployment","Проблема ручного deployment",[5155,5233,5234,5235,5238],{},"На попередніх лекціях ми розробили NestJS застосунок, покрили його тестами, контейнеризували через Docker. Проте ",[5118,5236,5237],{},"процес від commit до production"," залишається ручним:",[5240,5241,5246],"pre",{"className":5242,"code":5243,"language":5244,"meta":5245,"style":5245},"language-bash shiki shiki-themes light-plus dark-plus dark-plus","# Типовий ручний workflow\ngit add .\ngit commit -m \"feat: add user profile endpoint\"\ngit push origin main\n\n# SSH на production сервер\nssh user@prod-server\n\n# Pull останніх змін\ncd \u002Fapp\ngit pull origin main\n\n# Встановлення залежностей (якщо package.json змінився)\nnpm install\n\n# Запуск тестів (вручну перевіряємо, що нічого не зламалося)\nnpm run test\n\n# Компіляція\nnpm run build\n\n# Перезапуск застосунку\npm2 restart app\n\n# Перевірка логів\npm2 logs app\n","bash","",[5173,5247,5248,5257,5271,5286,5300,5307,5313,5322,5327,5333,5342,5354,5359,5365,5374,5379,5385,5396,5401,5407,5417,5422,5428,5440,5445,5451],{"__ignoreMap":5245},[5249,5250,5253],"span",{"class":5251,"line":5252},"line",1,[5249,5254,5256],{"class":5255},"spJ8K","# Типовий ручний workflow\n",[5249,5258,5260,5264,5268],{"class":5251,"line":5259},2,[5249,5261,5263],{"class":5262},"s8Opu","git",[5249,5265,5267],{"class":5266},"sbdoH"," add",[5249,5269,5270],{"class":5266}," .\n",[5249,5272,5274,5276,5279,5283],{"class":5251,"line":5273},3,[5249,5275,5263],{"class":5262},[5249,5277,5278],{"class":5266}," commit",[5249,5280,5282],{"class":5281},"su1O8"," -m",[5249,5284,5285],{"class":5266}," \"feat: add user profile endpoint\"\n",[5249,5287,5289,5291,5294,5297],{"class":5251,"line":5288},4,[5249,5290,5263],{"class":5262},[5249,5292,5293],{"class":5266}," push",[5249,5295,5296],{"class":5266}," origin",[5249,5298,5299],{"class":5266}," main\n",[5249,5301,5303],{"class":5251,"line":5302},5,[5249,5304,5306],{"emptyLinePlaceholder":5305},true,"\n",[5249,5308,5310],{"class":5251,"line":5309},6,[5249,5311,5312],{"class":5255},"# SSH на production сервер\n",[5249,5314,5316,5319],{"class":5251,"line":5315},7,[5249,5317,5318],{"class":5262},"ssh",[5249,5320,5321],{"class":5266}," user@prod-server\n",[5249,5323,5325],{"class":5251,"line":5324},8,[5249,5326,5306],{"emptyLinePlaceholder":5305},[5249,5328,5330],{"class":5251,"line":5329},9,[5249,5331,5332],{"class":5255},"# Pull останніх змін\n",[5249,5334,5336,5339],{"class":5251,"line":5335},10,[5249,5337,5338],{"class":5262},"cd",[5249,5340,5341],{"class":5266}," \u002Fapp\n",[5249,5343,5345,5347,5350,5352],{"class":5251,"line":5344},11,[5249,5346,5263],{"class":5262},[5249,5348,5349],{"class":5266}," pull",[5249,5351,5296],{"class":5266},[5249,5353,5299],{"class":5266},[5249,5355,5357],{"class":5251,"line":5356},12,[5249,5358,5306],{"emptyLinePlaceholder":5305},[5249,5360,5362],{"class":5251,"line":5361},13,[5249,5363,5364],{"class":5255},"# Встановлення залежностей (якщо package.json змінився)\n",[5249,5366,5368,5371],{"class":5251,"line":5367},14,[5249,5369,5370],{"class":5262},"npm",[5249,5372,5373],{"class":5266}," install\n",[5249,5375,5377],{"class":5251,"line":5376},15,[5249,5378,5306],{"emptyLinePlaceholder":5305},[5249,5380,5382],{"class":5251,"line":5381},16,[5249,5383,5384],{"class":5255},"# Запуск тестів (вручну перевіряємо, що нічого не зламалося)\n",[5249,5386,5388,5390,5393],{"class":5251,"line":5387},17,[5249,5389,5370],{"class":5262},[5249,5391,5392],{"class":5266}," run",[5249,5394,5395],{"class":5266}," test\n",[5249,5397,5399],{"class":5251,"line":5398},18,[5249,5400,5306],{"emptyLinePlaceholder":5305},[5249,5402,5404],{"class":5251,"line":5403},19,[5249,5405,5406],{"class":5255},"# Компіляція\n",[5249,5408,5410,5412,5414],{"class":5251,"line":5409},20,[5249,5411,5370],{"class":5262},[5249,5413,5392],{"class":5266},[5249,5415,5416],{"class":5266}," build\n",[5249,5418,5420],{"class":5251,"line":5419},21,[5249,5421,5306],{"emptyLinePlaceholder":5305},[5249,5423,5425],{"class":5251,"line":5424},22,[5249,5426,5427],{"class":5255},"# Перезапуск застосунку\n",[5249,5429,5431,5434,5437],{"class":5251,"line":5430},23,[5249,5432,5433],{"class":5262},"pm2",[5249,5435,5436],{"class":5266}," restart",[5249,5438,5439],{"class":5266}," app\n",[5249,5441,5443],{"class":5251,"line":5442},24,[5249,5444,5306],{"emptyLinePlaceholder":5305},[5249,5446,5448],{"class":5251,"line":5447},25,[5249,5449,5450],{"class":5255},"# Перевірка логів\n",[5249,5452,5454,5456,5459],{"class":5251,"line":5453},26,[5249,5455,5433],{"class":5262},[5249,5457,5458],{"class":5266}," logs",[5249,5460,5439],{"class":5266},[5155,5462,5463],{},[5118,5464,5465],{},"Проблеми ручного підходу:",[5467,5468,5469,5475,5481,5487,5493],"ol",{},[5095,5470,5471,5474],{},[5118,5472,5473],{},"Людські помилки:"," розробник забув запустити тести перед push → баг потрапив у production.",[5095,5476,5477,5480],{},[5118,5478,5479],{},"Втрата часу:"," кожен deploy займає 10-15 хвилин ручної роботи → розробники уникають частих deploys.",[5095,5482,5483,5486],{},[5118,5484,5485],{},"Inconsistency:"," різні розробники виконують deploy по-різному → непередбачувані результати.",[5095,5488,5489,5492],{},[5118,5490,5491],{},"Відсутність rollback:"," якщо новий deploy зламав production, повернення до попередньої версії — ще 15 хвилин ручної роботи.",[5095,5494,5495,5498],{},[5118,5496,5497],{},"Блокування розробників:"," тільки senior розробники мають SSH доступ до production → bottleneck.",[5155,5500,5501,5504],{},[5118,5502,5503],{},"Рішення — CI\u002FCD автоматизація."," При кожному commit у main гілку автоматично:",[5467,5506,5507,5514,5525,5531,5538,5545],{},[5095,5508,5509,5510,5513],{},"Запускаються ",[5118,5511,5512],{},"всі тести"," (unit, integration, E2E).",[5095,5515,5516,5517,5520,5521,5524],{},"Виконується ",[5118,5518,5519],{},"linting"," та ",[5118,5522,5523],{},"type checking",".",[5095,5526,5527,5528,5524],{},"Будується ",[5118,5529,5530],{},"Docker image",[5095,5532,5533,5534,5537],{},"Image ",[5118,5535,5536],{},"публікується"," у Docker registry.",[5095,5539,5540,5541,5544],{},"Застосунок ",[5118,5542,5543],{},"автоматично деплоїться"," на production сервер.",[5095,5546,5547,5548,5551],{},"У разі проблем — ",[5118,5549,5550],{},"automatic rollback"," до попередньої версії.",[5155,5553,5554,5555,5558],{},"Весь процес займає ",[5118,5556,5557],{},"3-5 хвилин"," без участі розробника.",[5560,5561,5563],"plant-uml",{"alt":5562},"CI\u002FCD Pipeline Flow",[5240,5564,5568],{"className":5565,"code":5566,"language":5567,"meta":5245,"style":5245},"language-plantuml shiki shiki-themes light-plus dark-plus dark-plus","@startuml\nskinparam style plain\nskinparam backgroundColor #FFFFFF\n\nactor Developer as Dev\nparticipant \"GitHub\" as GH\nparticipant \"GitHub Actions\" as GA\nparticipant \"Docker Registry\" as DR\nparticipant \"Production Server\" as Prod\n\nDev -> GH : git push\nactivate GH\nGH -> GA : Trigger workflow\nactivate GA\n\nGA -> GA : Checkout code\nGA -> GA : Install dependencies\nGA -> GA : Run ESLint\nGA -> GA : Run unit tests\nGA -> GA : Run E2E tests\n\nalt Tests Failed\n  GA -> Dev : ❌ Build Failed\\n(Email notification)\n  Dev -> Dev : Fix bugs\nelse Tests Passed\n  GA -> GA : Build Docker image\n  GA -> DR : Push image\n  activate DR\n  DR --> GA : Image stored\n  deactivate DR\n  \n  GA -> Prod : Deploy new version\n  activate Prod\n  Prod -> Prod : Pull image\n  Prod -> Prod : Rolling update\n  Prod -> Prod : Health check\n  \n  alt Health Check Failed\n    Prod -> Prod : Rollback\n    GA -> Dev : ⚠️ Deployment Failed\n  else Health Check Passed\n    Prod --> GA : ✅ Deployed\n    GA -> Dev : ✅ Deploy Success\n  end\n  deactivate Prod\nend\n\ndeactivate GA\ndeactivate GH\n\n@enduml\n","plantuml",[5173,5569,5570,5575,5580,5585,5589,5594,5599,5604,5609,5614,5618,5623,5628,5633,5638,5642,5647,5652,5657,5662,5667,5671,5676,5681,5686,5691,5696,5702,5708,5714,5720,5726,5732,5738,5744,5750,5756,5761,5767,5773,5779,5785,5791,5797,5803,5809,5815,5820,5826,5832,5837],{"__ignoreMap":5245},[5249,5571,5572],{"class":5251,"line":5252},[5249,5573,5574],{},"@startuml\n",[5249,5576,5577],{"class":5251,"line":5259},[5249,5578,5579],{},"skinparam style plain\n",[5249,5581,5582],{"class":5251,"line":5273},[5249,5583,5584],{},"skinparam backgroundColor #FFFFFF\n",[5249,5586,5587],{"class":5251,"line":5288},[5249,5588,5306],{"emptyLinePlaceholder":5305},[5249,5590,5591],{"class":5251,"line":5302},[5249,5592,5593],{},"actor Developer as Dev\n",[5249,5595,5596],{"class":5251,"line":5309},[5249,5597,5598],{},"participant \"GitHub\" as GH\n",[5249,5600,5601],{"class":5251,"line":5315},[5249,5602,5603],{},"participant \"GitHub Actions\" as GA\n",[5249,5605,5606],{"class":5251,"line":5324},[5249,5607,5608],{},"participant \"Docker Registry\" as DR\n",[5249,5610,5611],{"class":5251,"line":5329},[5249,5612,5613],{},"participant \"Production Server\" as Prod\n",[5249,5615,5616],{"class":5251,"line":5335},[5249,5617,5306],{"emptyLinePlaceholder":5305},[5249,5619,5620],{"class":5251,"line":5344},[5249,5621,5622],{},"Dev -> GH : git push\n",[5249,5624,5625],{"class":5251,"line":5356},[5249,5626,5627],{},"activate GH\n",[5249,5629,5630],{"class":5251,"line":5361},[5249,5631,5632],{},"GH -> GA : Trigger workflow\n",[5249,5634,5635],{"class":5251,"line":5367},[5249,5636,5637],{},"activate GA\n",[5249,5639,5640],{"class":5251,"line":5376},[5249,5641,5306],{"emptyLinePlaceholder":5305},[5249,5643,5644],{"class":5251,"line":5381},[5249,5645,5646],{},"GA -> GA : Checkout code\n",[5249,5648,5649],{"class":5251,"line":5387},[5249,5650,5651],{},"GA -> GA : Install dependencies\n",[5249,5653,5654],{"class":5251,"line":5398},[5249,5655,5656],{},"GA -> GA : Run ESLint\n",[5249,5658,5659],{"class":5251,"line":5403},[5249,5660,5661],{},"GA -> GA : Run unit tests\n",[5249,5663,5664],{"class":5251,"line":5409},[5249,5665,5666],{},"GA -> GA : Run E2E tests\n",[5249,5668,5669],{"class":5251,"line":5419},[5249,5670,5306],{"emptyLinePlaceholder":5305},[5249,5672,5673],{"class":5251,"line":5424},[5249,5674,5675],{},"alt Tests Failed\n",[5249,5677,5678],{"class":5251,"line":5430},[5249,5679,5680],{},"  GA -> Dev : ❌ Build Failed\\n(Email notification)\n",[5249,5682,5683],{"class":5251,"line":5442},[5249,5684,5685],{},"  Dev -> Dev : Fix bugs\n",[5249,5687,5688],{"class":5251,"line":5447},[5249,5689,5690],{},"else Tests Passed\n",[5249,5692,5693],{"class":5251,"line":5453},[5249,5694,5695],{},"  GA -> GA : Build Docker image\n",[5249,5697,5699],{"class":5251,"line":5698},27,[5249,5700,5701],{},"  GA -> DR : Push image\n",[5249,5703,5705],{"class":5251,"line":5704},28,[5249,5706,5707],{},"  activate DR\n",[5249,5709,5711],{"class":5251,"line":5710},29,[5249,5712,5713],{},"  DR --> GA : Image stored\n",[5249,5715,5717],{"class":5251,"line":5716},30,[5249,5718,5719],{},"  deactivate DR\n",[5249,5721,5723],{"class":5251,"line":5722},31,[5249,5724,5725],{},"  \n",[5249,5727,5729],{"class":5251,"line":5728},32,[5249,5730,5731],{},"  GA -> Prod : Deploy new version\n",[5249,5733,5735],{"class":5251,"line":5734},33,[5249,5736,5737],{},"  activate Prod\n",[5249,5739,5741],{"class":5251,"line":5740},34,[5249,5742,5743],{},"  Prod -> Prod : Pull image\n",[5249,5745,5747],{"class":5251,"line":5746},35,[5249,5748,5749],{},"  Prod -> Prod : Rolling update\n",[5249,5751,5753],{"class":5251,"line":5752},36,[5249,5754,5755],{},"  Prod -> Prod : Health check\n",[5249,5757,5759],{"class":5251,"line":5758},37,[5249,5760,5725],{},[5249,5762,5764],{"class":5251,"line":5763},38,[5249,5765,5766],{},"  alt Health Check Failed\n",[5249,5768,5770],{"class":5251,"line":5769},39,[5249,5771,5772],{},"    Prod -> Prod : Rollback\n",[5249,5774,5776],{"class":5251,"line":5775},40,[5249,5777,5778],{},"    GA -> Dev : ⚠️ Deployment Failed\n",[5249,5780,5782],{"class":5251,"line":5781},41,[5249,5783,5784],{},"  else Health Check Passed\n",[5249,5786,5788],{"class":5251,"line":5787},42,[5249,5789,5790],{},"    Prod --> GA : ✅ Deployed\n",[5249,5792,5794],{"class":5251,"line":5793},43,[5249,5795,5796],{},"    GA -> Dev : ✅ Deploy Success\n",[5249,5798,5800],{"class":5251,"line":5799},44,[5249,5801,5802],{},"  end\n",[5249,5804,5806],{"class":5251,"line":5805},45,[5249,5807,5808],{},"  deactivate Prod\n",[5249,5810,5812],{"class":5251,"line":5811},46,[5249,5813,5814],{},"end\n",[5249,5816,5818],{"class":5251,"line":5817},47,[5249,5819,5306],{"emptyLinePlaceholder":5305},[5249,5821,5823],{"class":5251,"line":5822},48,[5249,5824,5825],{},"deactivate GA\n",[5249,5827,5829],{"class":5251,"line":5828},49,[5249,5830,5831],{},"deactivate GH\n",[5249,5833,5835],{"class":5251,"line":5834},50,[5249,5836,5306],{"emptyLinePlaceholder":5305},[5249,5838,5840],{"class":5251,"line":5839},51,[5249,5841,5842],{},"@enduml\n",[5147,5844],{},[5150,5846,5164],{"id":5847},"концепція-cicd",[5849,5850,5852],"h3",{"id":5851},"continuous-integration-ci","Continuous Integration (CI)",[5155,5854,5855,5858,5859,5862,5863,5866],{},[5118,5856,5857],{},"Continuous Integration"," — практика, при якій розробники ",[5118,5860,5861],{},"часто інтегрують"," код у спільний репозиторій (кілька разів на день), а ",[5118,5864,5865],{},"автоматичні тести"," перевіряють кожну інтеграцію.",[5155,5868,5869],{},[5118,5870,5871],{},"Основні принципи CI:",[5467,5873,5874,5880,5886,5892,5898,5904],{},[5095,5875,5876,5879],{},[5118,5877,5878],{},"Maintain a Single Source Repository"," — один Git репозиторій як single source of truth.",[5095,5881,5882,5885],{},[5118,5883,5884],{},"Automate the Build"," — автоматична компіляція при кожному commit.",[5095,5887,5888,5891],{},[5118,5889,5890],{},"Make Your Build Self-Testing"," — тести запускаються автоматично як частина build процесу.",[5095,5893,5894,5897],{},[5118,5895,5896],{},"Everyone Commits to Mainline Every Day"," — розробники комітять у main гілку щодня, уникаючи довгоживучих feature branches.",[5095,5899,5900,5903],{},[5118,5901,5902],{},"Every Commit Should Build Mainline on an Integration Machine"," — кожен commit тригерить build на CI сервері (не лише локально).",[5095,5905,5906,5909,5910,5913],{},[5118,5907,5908],{},"Fix Broken Builds Immediately"," — якщо build падає, це ",[5118,5911,5912],{},"highest priority"," — фіксити одразу.",[5155,5915,5916],{},[5118,5917,5918],{},"Переваги CI:",[5092,5920,5921,5927,5933,5939],{},[5095,5922,5923,5926],{},[5118,5924,5925],{},"Early Bug Detection:"," баги виявляються через години після commit, а не через тижні.",[5095,5928,5929,5932],{},[5118,5930,5931],{},"Reduce Integration Risk:"," постійна інтеграція замість \"big bang merge\" раз на тиждень.",[5095,5934,5935,5938],{},[5118,5936,5937],{},"Better Code Quality:"," automated linting та tests забезпечують дотримання стандартів.",[5095,5940,5941,5944],{},[5118,5942,5943],{},"Faster Development:"," розробники отримують швидкий feedback про проблеми.",[5849,5946,5948],{"id":5947},"continuous-deployment-cd","Continuous Deployment (CD)",[5155,5950,5951,5954],{},[5118,5952,5953],{},"Continuous Deployment"," — автоматичне розгортання кожного успішного build у production без ручного втручання.",[5155,5956,5957],{},[5118,5958,5959],{},"Відрізнити від Continuous Delivery:",[5092,5961,5962,5972],{},[5095,5963,5964,5967,5968,5971],{},[5118,5965,5966],{},"Continuous Delivery:"," автоматична підготовка до deploy, але ",[5118,5969,5970],{},"фінальний deploy — ручний"," (натискання кнопки).",[5095,5973,5974,5977,5978,5981],{},[5118,5975,5976],{},"Continuous Deployment:"," ",[5118,5979,5980],{},"повністю автоматичний"," deploy у production після успішних тестів.",[5155,5983,5984],{},[5118,5985,5986],{},"Переваги CD:",[5092,5988,5989,5995,6001,6007],{},[5095,5990,5991,5994],{},[5118,5992,5993],{},"Faster Time to Market:"," features потрапляють до користувачів через хвилини після merge, а не через дні\u002Fтижні.",[5095,5996,5997,6000],{},[5118,5998,5999],{},"Reduced Risk:"," маленькі, часті deploys легше відкотити у разі проблем, ніж великі release.",[5095,6002,6003,6006],{},[5118,6004,6005],{},"Immediate Feedback:"," користувачі одразу бачать нові features → швидкий feedback loop.",[5095,6008,6009,6012],{},[5118,6010,6011],{},"Less Manual Work:"," розробники фокусуються на коді, а не на deployment процесі.",[5155,6014,6015],{},[5118,6016,6017],{},"Вимоги для CD:",[5467,6019,6020,6026,6032,6038],{},[5095,6021,6022,6025],{},[5118,6023,6024],{},"Comprehensive Test Suite"," — висока test coverage (80%+) для впевненості, що зміни не зламають production.",[5095,6027,6028,6031],{},[5118,6029,6030],{},"Fast Tests"," — тести мають виконуватися за 5-10 хвилин, інакше блокується delivery.",[5095,6033,6034,6037],{},[5118,6035,6036],{},"Automated Rollback"," — здатність автоматично відкотити deploy при виявленні проблем.",[5095,6039,6040,6043],{},[5118,6041,6042],{},"Monitoring and Alerts"," — моніторинг production для швидкого виявлення проблем після deploy.",[5147,6045],{},[5150,6047,6049],{"id":6048},"github-actions-вступ","GitHub Actions — вступ",[5155,6051,6052,6054],{},[5118,6053,5170],{}," — вбудована CI\u002FCD платформа у GitHub, що дозволяє автоматизувати workflows безпосередньо у репозиторії.",[5849,6056,6058],{"id":6057},"переваги-github-actions","Переваги GitHub Actions",[5467,6060,6061,6067,6073,6079,6085,6091],{},[5095,6062,6063,6066],{},[5118,6064,6065],{},"Native GitHub Integration"," — не потрібно налаштовувати зовнішні сервіси (Jenkins, CircleCI).",[5095,6068,6069,6072],{},[5118,6070,6071],{},"Free for Public Repos"," — необмежені build хвилини для open-source проєктів.",[5095,6074,6075,6078],{},[5118,6076,6077],{},"Generous Free Tier"," — 2000 хвилин\u002Fмісяць для private repos.",[5095,6080,6081,6084],{},[5118,6082,6083],{},"Matrix Builds"," — тестування на кількох версіях Node.js \u002F ОС паралельно.",[5095,6086,6087,6090],{},[5118,6088,6089],{},"Marketplace"," — тисячі готових actions для типових завдань (Docker build, AWS deploy).",[5095,6092,6093,6096],{},[5118,6094,6095],{},"Secrets Management"," — безпечне зберігання API keys, passwords.",[5849,6098,6100],{"id":6099},"структура-github-actions","Структура GitHub Actions",[5155,6102,6103,6106,6107,6109],{},[5118,6104,6105],{},"Workflow"," — YAML файл у ",[5173,6108,5175],{}," директорії, що описує автоматизований процес.",[5155,6111,6112,6115],{},[5118,6113,6114],{},"Job"," — група пов'язаних steps, що виконуються на одному runner (віртуальній машині).",[5155,6117,6118,6121],{},[5118,6119,6120],{},"Step"," — окрема команда або action (наприклад, \"checkout code\", \"run tests\").",[5155,6123,6124,6127],{},[5118,6125,6126],{},"Runner"," — віртуальна машина (Ubuntu, macOS, Windows), де виконується job.",[5155,6129,6130,6133],{},[5118,6131,6132],{},"Action"," — повторно використовуваний блок коду (з Marketplace або custom).",[5155,6135,6136],{},[5118,6137,6138],{},"Основна структура:",[5240,6140,6144],{"className":6141,"code":6142,"language":6143,"meta":5245,"style":5245},"language-yaml shiki shiki-themes light-plus dark-plus dark-plus","name: CI\u002FCD Pipeline  # Назва workflow\n\non:  # Тригери\n  push:\n    branches: [main]\n  pull_request:\n    branches: [main]\n\njobs:  # Jobs для виконання\n  test:\n    runs-on: ubuntu-latest  # Runner\n    steps:  # Steps у job\n      - name: Checkout code\n        uses: actions\u002Fcheckout@v3\n      \n      - name: Setup Node.js\n        uses: actions\u002Fsetup-node@v3\n        with:\n          node-version: 18\n      \n      - name: Install dependencies\n        run: npm ci\n      \n      - name: Run tests\n        run: npm test\n","yaml",[5173,6145,6146,6163,6167,6178,6186,6200,6207,6217,6221,6231,6238,6251,6261,6273,6283,6288,6299,6308,6315,6326,6330,6341,6351,6355,6366],{"__ignoreMap":5245},[5249,6147,6148,6152,6156,6160],{"class":5251,"line":5252},[5249,6149,6151],{"class":6150},"sKtos","name",[5249,6153,6155],{"class":6154},"sHH4Y",": ",[5249,6157,6159],{"class":6158},"su9tN","CI\u002FCD Pipeline",[5249,6161,6162],{"class":5255},"  # Назва workflow\n",[5249,6164,6165],{"class":5251,"line":5259},[5249,6166,5306],{"emptyLinePlaceholder":5305},[5249,6168,6169,6172,6175],{"class":5251,"line":5273},[5249,6170,6171],{"class":5281},"on",[5249,6173,6174],{"class":6154},":  ",[5249,6176,6177],{"class":5255},"# Тригери\n",[5249,6179,6180,6183],{"class":5251,"line":5288},[5249,6181,6182],{"class":6150},"  push",[5249,6184,6185],{"class":6154},":\n",[5249,6187,6188,6191,6194,6197],{"class":5251,"line":5302},[5249,6189,6190],{"class":6150},"    branches",[5249,6192,6193],{"class":6154},": [",[5249,6195,6196],{"class":6158},"main",[5249,6198,6199],{"class":6154},"]\n",[5249,6201,6202,6205],{"class":5251,"line":5309},[5249,6203,6204],{"class":6150},"  pull_request",[5249,6206,6185],{"class":6154},[5249,6208,6209,6211,6213,6215],{"class":5251,"line":5315},[5249,6210,6190],{"class":6150},[5249,6212,6193],{"class":6154},[5249,6214,6196],{"class":6158},[5249,6216,6199],{"class":6154},[5249,6218,6219],{"class":5251,"line":5324},[5249,6220,5306],{"emptyLinePlaceholder":5305},[5249,6222,6223,6226,6228],{"class":5251,"line":5329},[5249,6224,6225],{"class":6150},"jobs",[5249,6227,6174],{"class":6154},[5249,6229,6230],{"class":5255},"# Jobs для виконання\n",[5249,6232,6233,6236],{"class":5251,"line":5335},[5249,6234,6235],{"class":6150},"  test",[5249,6237,6185],{"class":6154},[5249,6239,6240,6243,6245,6248],{"class":5251,"line":5344},[5249,6241,6242],{"class":6150},"    runs-on",[5249,6244,6155],{"class":6154},[5249,6246,6247],{"class":6158},"ubuntu-latest",[5249,6249,6250],{"class":5255},"  # Runner\n",[5249,6252,6253,6256,6258],{"class":5251,"line":5356},[5249,6254,6255],{"class":6150},"    steps",[5249,6257,6174],{"class":6154},[5249,6259,6260],{"class":5255},"# Steps у job\n",[5249,6262,6263,6266,6268,6270],{"class":5251,"line":5361},[5249,6264,6265],{"class":6154},"      - ",[5249,6267,6151],{"class":6150},[5249,6269,6155],{"class":6154},[5249,6271,6272],{"class":6158},"Checkout code\n",[5249,6274,6275,6278,6280],{"class":5251,"line":5367},[5249,6276,6277],{"class":6150},"        uses",[5249,6279,6155],{"class":6154},[5249,6281,6282],{"class":6158},"actions\u002Fcheckout@v3\n",[5249,6284,6285],{"class":5251,"line":5376},[5249,6286,6287],{"class":6154},"      \n",[5249,6289,6290,6292,6294,6296],{"class":5251,"line":5381},[5249,6291,6265],{"class":6154},[5249,6293,6151],{"class":6150},[5249,6295,6155],{"class":6154},[5249,6297,6298],{"class":6158},"Setup Node.js\n",[5249,6300,6301,6303,6305],{"class":5251,"line":5387},[5249,6302,6277],{"class":6150},[5249,6304,6155],{"class":6154},[5249,6306,6307],{"class":6158},"actions\u002Fsetup-node@v3\n",[5249,6309,6310,6313],{"class":5251,"line":5398},[5249,6311,6312],{"class":6150},"        with",[5249,6314,6185],{"class":6154},[5249,6316,6317,6320,6322],{"class":5251,"line":5403},[5249,6318,6319],{"class":6150},"          node-version",[5249,6321,6155],{"class":6154},[5249,6323,6325],{"class":6324},"sJj4R","18\n",[5249,6327,6328],{"class":5251,"line":5409},[5249,6329,6287],{"class":6154},[5249,6331,6332,6334,6336,6338],{"class":5251,"line":5419},[5249,6333,6265],{"class":6154},[5249,6335,6151],{"class":6150},[5249,6337,6155],{"class":6154},[5249,6339,6340],{"class":6158},"Install dependencies\n",[5249,6342,6343,6346,6348],{"class":5251,"line":5424},[5249,6344,6345],{"class":6150},"        run",[5249,6347,6155],{"class":6154},[5249,6349,6350],{"class":6158},"npm ci\n",[5249,6352,6353],{"class":5251,"line":5430},[5249,6354,6287],{"class":6154},[5249,6356,6357,6359,6361,6363],{"class":5251,"line":5442},[5249,6358,6265],{"class":6154},[5249,6360,6151],{"class":6150},[5249,6362,6155],{"class":6154},[5249,6364,6365],{"class":6158},"Run tests\n",[5249,6367,6368,6370,6372],{"class":5251,"line":5447},[5249,6369,6345],{"class":6150},[5249,6371,6155],{"class":6154},[5249,6373,6374],{"class":6158},"npm test\n",[5849,6376,6378],{"id":6377},"тригери-on","Тригери (on)",[5155,6380,6381,6386],{},[5118,6382,6383],{},[5173,6384,6385],{},"push"," — тригер при push у певні гілки:",[5240,6388,6390],{"className":6141,"code":6389,"language":6143,"meta":5245,"style":5245},"on:\n  push:\n    branches:\n      - main\n      - develop\n    paths-ignore:\n      - '**.md'  # Ігнорувати зміни у markdown файлах\n",[5173,6391,6392,6398,6404,6410,6417,6424,6431],{"__ignoreMap":5245},[5249,6393,6394,6396],{"class":5251,"line":5252},[5249,6395,6171],{"class":5281},[5249,6397,6185],{"class":6154},[5249,6399,6400,6402],{"class":5251,"line":5259},[5249,6401,6182],{"class":6150},[5249,6403,6185],{"class":6154},[5249,6405,6406,6408],{"class":5251,"line":5273},[5249,6407,6190],{"class":6150},[5249,6409,6185],{"class":6154},[5249,6411,6412,6414],{"class":5251,"line":5288},[5249,6413,6265],{"class":6154},[5249,6415,6416],{"class":6158},"main\n",[5249,6418,6419,6421],{"class":5251,"line":5302},[5249,6420,6265],{"class":6154},[5249,6422,6423],{"class":6158},"develop\n",[5249,6425,6426,6429],{"class":5251,"line":5309},[5249,6427,6428],{"class":6150},"    paths-ignore",[5249,6430,6185],{"class":6154},[5249,6432,6433,6435,6438],{"class":5251,"line":5315},[5249,6434,6265],{"class":6154},[5249,6436,6437],{"class":6158},"'**.md'",[5249,6439,6440],{"class":5255},"  # Ігнорувати зміни у markdown файлах\n",[5155,6442,6443,6448],{},[5118,6444,6445],{},[5173,6446,6447],{},"pull_request"," — тригер при створенні або оновленні PR:",[5240,6450,6452],{"className":6141,"code":6451,"language":6143,"meta":5245,"style":5245},"on:\n  pull_request:\n    branches: [main]\n    types: [opened, synchronize, reopened]\n",[5173,6453,6454,6460,6466,6476],{"__ignoreMap":5245},[5249,6455,6456,6458],{"class":5251,"line":5252},[5249,6457,6171],{"class":5281},[5249,6459,6185],{"class":6154},[5249,6461,6462,6464],{"class":5251,"line":5259},[5249,6463,6204],{"class":6150},[5249,6465,6185],{"class":6154},[5249,6467,6468,6470,6472,6474],{"class":5251,"line":5273},[5249,6469,6190],{"class":6150},[5249,6471,6193],{"class":6154},[5249,6473,6196],{"class":6158},[5249,6475,6199],{"class":6154},[5249,6477,6478,6481,6483,6486,6489,6492,6494,6497],{"class":5251,"line":5288},[5249,6479,6480],{"class":6150},"    types",[5249,6482,6193],{"class":6154},[5249,6484,6485],{"class":6158},"opened",[5249,6487,6488],{"class":6154},", ",[5249,6490,6491],{"class":6158},"synchronize",[5249,6493,6488],{"class":6154},[5249,6495,6496],{"class":6158},"reopened",[5249,6498,6199],{"class":6154},[5155,6500,6501,6506],{},[5118,6502,6503],{},[5173,6504,6505],{},"schedule"," — запуск за розкладом (cron syntax):",[5240,6508,6510],{"className":6141,"code":6509,"language":6143,"meta":5245,"style":5245},"on:\n  schedule:\n    - cron: '0 2 * * *'  # Кожного дня о 2:00 UTC\n",[5173,6511,6512,6518,6525],{"__ignoreMap":5245},[5249,6513,6514,6516],{"class":5251,"line":5252},[5249,6515,6171],{"class":5281},[5249,6517,6185],{"class":6154},[5249,6519,6520,6523],{"class":5251,"line":5259},[5249,6521,6522],{"class":6150},"  schedule",[5249,6524,6185],{"class":6154},[5249,6526,6527,6530,6533,6535,6538],{"class":5251,"line":5273},[5249,6528,6529],{"class":6154},"    - ",[5249,6531,6532],{"class":6150},"cron",[5249,6534,6155],{"class":6154},[5249,6536,6537],{"class":6158},"'0 2 * * *'",[5249,6539,6540],{"class":5255},"  # Кожного дня о 2:00 UTC\n",[5155,6542,6543,6548],{},[5118,6544,6545],{},[5173,6546,6547],{},"workflow_dispatch"," — ручний запуск через GitHub UI:",[5240,6550,6552],{"className":6141,"code":6551,"language":6143,"meta":5245,"style":5245},"on:\n  workflow_dispatch:\n    inputs:\n      environment:\n        description: 'Environment to deploy'\n        required: true\n        default: 'staging'\n",[5173,6553,6554,6560,6567,6574,6581,6591,6601],{"__ignoreMap":5245},[5249,6555,6556,6558],{"class":5251,"line":5252},[5249,6557,6171],{"class":5281},[5249,6559,6185],{"class":6154},[5249,6561,6562,6565],{"class":5251,"line":5259},[5249,6563,6564],{"class":6150},"  workflow_dispatch",[5249,6566,6185],{"class":6154},[5249,6568,6569,6572],{"class":5251,"line":5273},[5249,6570,6571],{"class":6150},"    inputs",[5249,6573,6185],{"class":6154},[5249,6575,6576,6579],{"class":5251,"line":5288},[5249,6577,6578],{"class":6150},"      environment",[5249,6580,6185],{"class":6154},[5249,6582,6583,6586,6588],{"class":5251,"line":5302},[5249,6584,6585],{"class":6150},"        description",[5249,6587,6155],{"class":6154},[5249,6589,6590],{"class":6158},"'Environment to deploy'\n",[5249,6592,6593,6596,6598],{"class":5251,"line":5309},[5249,6594,6595],{"class":6150},"        required",[5249,6597,6155],{"class":6154},[5249,6599,6600],{"class":5281},"true\n",[5249,6602,6603,6606,6608],{"class":5251,"line":5315},[5249,6604,6605],{"class":6150},"        default",[5249,6607,6155],{"class":6154},[5249,6609,6610],{"class":6158},"'staging'\n",[5155,6612,6613],{},[5118,6614,6615],{},"Комбінація тригерів:",[5240,6617,6619],{"className":6141,"code":6618,"language":6143,"meta":5245,"style":5245},"on:\n  push:\n    branches: [main]\n  pull_request:\n    branches: [main]\n  schedule:\n    - cron: '0 2 * * *'\n  workflow_dispatch:\n",[5173,6620,6621,6627,6633,6643,6649,6659,6665,6676],{"__ignoreMap":5245},[5249,6622,6623,6625],{"class":5251,"line":5252},[5249,6624,6171],{"class":5281},[5249,6626,6185],{"class":6154},[5249,6628,6629,6631],{"class":5251,"line":5259},[5249,6630,6182],{"class":6150},[5249,6632,6185],{"class":6154},[5249,6634,6635,6637,6639,6641],{"class":5251,"line":5273},[5249,6636,6190],{"class":6150},[5249,6638,6193],{"class":6154},[5249,6640,6196],{"class":6158},[5249,6642,6199],{"class":6154},[5249,6644,6645,6647],{"class":5251,"line":5288},[5249,6646,6204],{"class":6150},[5249,6648,6185],{"class":6154},[5249,6650,6651,6653,6655,6657],{"class":5251,"line":5302},[5249,6652,6190],{"class":6150},[5249,6654,6193],{"class":6154},[5249,6656,6196],{"class":6158},[5249,6658,6199],{"class":6154},[5249,6660,6661,6663],{"class":5251,"line":5309},[5249,6662,6522],{"class":6150},[5249,6664,6185],{"class":6154},[5249,6666,6667,6669,6671,6673],{"class":5251,"line":5315},[5249,6668,6529],{"class":6154},[5249,6670,6532],{"class":6150},[5249,6672,6155],{"class":6154},[5249,6674,6675],{"class":6158},"'0 2 * * *'\n",[5249,6677,6678,6680],{"class":5251,"line":5324},[5249,6679,6564],{"class":6150},[5249,6681,6185],{"class":6154},[5147,6683],{},[5150,6685,6687],{"id":6686},"базовий-ci-workflow-для-nestjs","Базовий CI Workflow для NestJS",[5155,6689,6690],{},"Створимо простий CI workflow для автоматичного тестування NestJS застосунку:",[5240,6692,6694],{"className":6141,"code":6693,"language":6143,"meta":5245,"style":5245},"# .github\u002Fworkflows\u002Fci.yml\nname: CI\n\non:\n  push:\n    branches: [main, develop]\n  pull_request:\n    branches: [main]\n\njobs:\n  lint:\n    name: Lint Code\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Checkout code\n        uses: actions\u002Fcheckout@v3\n\n      - name: Setup Node.js\n        uses: actions\u002Fsetup-node@v3\n        with:\n          node-version: 18\n          cache: 'npm'\n\n      - name: Install dependencies\n        run: npm ci\n\n      - name: Run ESLint\n        run: npm run lint\n\n      - name: Check TypeScript types\n        run: npm run build\n\n  test:\n    name: Run Tests\n    runs-on: ubuntu-latest\n    needs: lint  # Запускається після успішного lint\n\n    strategy:\n      matrix:\n        node-version: [18, 20]  # Тестуємо на Node 18 та 20\n\n    steps:\n      - name: Checkout code\n        uses: actions\u002Fcheckout@v3\n\n      - name: Setup Node.js ${{ matrix.node-version }}\n        uses: actions\u002Fsetup-node@v3\n        with:\n          node-version: ${{ matrix.node-version }}\n          cache: 'npm'\n\n      - name: Install dependencies\n        run: npm ci\n\n      - name: Run unit tests\n        run: npm run test:cov\n\n      - name: Upload coverage to Codecov\n        uses: codecov\u002Fcodecov-action@v3\n        with:\n          files: .\u002Fcoverage\u002Flcov.info\n          flags: unittests\n          name: codecov-umbrella\n\n  e2e:\n    name: E2E Tests\n    runs-on: ubuntu-latest\n    needs: lint\n\n    services:\n      postgres:\n        image: postgres:15\n        env:\n          POSTGRES_USER: postgres\n          POSTGRES_PASSWORD: postgres\n          POSTGRES_DB: test_db\n        options: >-\n          --health-cmd pg_isready\n          --health-interval 10s\n          --health-timeout 5s\n          --health-retries 5\n        ports:\n          - 5432:5432\n\n      redis:\n        image: redis:7\n        options: >-\n          --health-cmd \"redis-cli ping\"\n          --health-interval 10s\n          --health-timeout 5s\n          --health-retries 5\n        ports:\n          - 6379:6379\n\n    steps:\n      - name: Checkout code\n        uses: actions\u002Fcheckout@v3\n\n      - name: Setup Node.js\n        uses: actions\u002Fsetup-node@v3\n        with:\n          node-version: 18\n          cache: 'npm'\n\n      - name: Install dependencies\n        run: npm ci\n\n      - name: Run E2E tests\n        run: npm run test:e2e\n        env:\n          DATABASE_HOST: localhost\n          DATABASE_PORT: 5432\n          DATABASE_USER: postgres\n          DATABASE_PASSWORD: postgres\n          DATABASE_NAME: test_db\n          REDIS_HOST: localhost\n          REDIS_PORT: 6379\n\n      - name: Upload E2E test results\n        if: always()\n        uses: actions\u002Fupload-artifact@v3\n        with:\n          name: e2e-test-results\n          path: test-results\u002F\n",[5173,6695,6696,6701,6710,6714,6720,6726,6741,6747,6757,6761,6767,6774,6784,6793,6797,6803,6813,6821,6825,6835,6843,6849,6857,6867,6871,6881,6889,6893,6904,6913,6917,6928,6937,6941,6947,6956,6964,6977,6981,6988,6995,7016,7020,7026,7036,7044,7048,7059,7067,7073,7082,7090,7095,7106,7115,7120,7132,7142,7147,7159,7169,7176,7187,7198,7209,7214,7222,7232,7241,7251,7256,7264,7272,7283,7291,7302,7312,7323,7338,7344,7350,7356,7362,7370,7379,7384,7392,7402,7413,7419,7424,7429,7434,7441,7449,7454,7461,7472,7481,7486,7497,7506,7513,7522,7531,7536,7547,7556,7561,7573,7583,7590,7601,7612,7622,7632,7642,7652,7663,7668,7680,7691,7701,7708,7718],{"__ignoreMap":5245},[5249,6697,6698],{"class":5251,"line":5252},[5249,6699,6700],{"class":5255},"# .github\u002Fworkflows\u002Fci.yml\n",[5249,6702,6703,6705,6707],{"class":5251,"line":5259},[5249,6704,6151],{"class":6150},[5249,6706,6155],{"class":6154},[5249,6708,6709],{"class":6158},"CI\n",[5249,6711,6712],{"class":5251,"line":5273},[5249,6713,5306],{"emptyLinePlaceholder":5305},[5249,6715,6716,6718],{"class":5251,"line":5288},[5249,6717,6171],{"class":5281},[5249,6719,6185],{"class":6154},[5249,6721,6722,6724],{"class":5251,"line":5302},[5249,6723,6182],{"class":6150},[5249,6725,6185],{"class":6154},[5249,6727,6728,6730,6732,6734,6736,6739],{"class":5251,"line":5309},[5249,6729,6190],{"class":6150},[5249,6731,6193],{"class":6154},[5249,6733,6196],{"class":6158},[5249,6735,6488],{"class":6154},[5249,6737,6738],{"class":6158},"develop",[5249,6740,6199],{"class":6154},[5249,6742,6743,6745],{"class":5251,"line":5315},[5249,6744,6204],{"class":6150},[5249,6746,6185],{"class":6154},[5249,6748,6749,6751,6753,6755],{"class":5251,"line":5324},[5249,6750,6190],{"class":6150},[5249,6752,6193],{"class":6154},[5249,6754,6196],{"class":6158},[5249,6756,6199],{"class":6154},[5249,6758,6759],{"class":5251,"line":5329},[5249,6760,5306],{"emptyLinePlaceholder":5305},[5249,6762,6763,6765],{"class":5251,"line":5335},[5249,6764,6225],{"class":6150},[5249,6766,6185],{"class":6154},[5249,6768,6769,6772],{"class":5251,"line":5344},[5249,6770,6771],{"class":6150},"  lint",[5249,6773,6185],{"class":6154},[5249,6775,6776,6779,6781],{"class":5251,"line":5356},[5249,6777,6778],{"class":6150},"    name",[5249,6780,6155],{"class":6154},[5249,6782,6783],{"class":6158},"Lint Code\n",[5249,6785,6786,6788,6790],{"class":5251,"line":5361},[5249,6787,6242],{"class":6150},[5249,6789,6155],{"class":6154},[5249,6791,6792],{"class":6158},"ubuntu-latest\n",[5249,6794,6795],{"class":5251,"line":5367},[5249,6796,5306],{"emptyLinePlaceholder":5305},[5249,6798,6799,6801],{"class":5251,"line":5376},[5249,6800,6255],{"class":6150},[5249,6802,6185],{"class":6154},[5249,6804,6805,6807,6809,6811],{"class":5251,"line":5381},[5249,6806,6265],{"class":6154},[5249,6808,6151],{"class":6150},[5249,6810,6155],{"class":6154},[5249,6812,6272],{"class":6158},[5249,6814,6815,6817,6819],{"class":5251,"line":5387},[5249,6816,6277],{"class":6150},[5249,6818,6155],{"class":6154},[5249,6820,6282],{"class":6158},[5249,6822,6823],{"class":5251,"line":5398},[5249,6824,5306],{"emptyLinePlaceholder":5305},[5249,6826,6827,6829,6831,6833],{"class":5251,"line":5403},[5249,6828,6265],{"class":6154},[5249,6830,6151],{"class":6150},[5249,6832,6155],{"class":6154},[5249,6834,6298],{"class":6158},[5249,6836,6837,6839,6841],{"class":5251,"line":5409},[5249,6838,6277],{"class":6150},[5249,6840,6155],{"class":6154},[5249,6842,6307],{"class":6158},[5249,6844,6845,6847],{"class":5251,"line":5419},[5249,6846,6312],{"class":6150},[5249,6848,6185],{"class":6154},[5249,6850,6851,6853,6855],{"class":5251,"line":5424},[5249,6852,6319],{"class":6150},[5249,6854,6155],{"class":6154},[5249,6856,6325],{"class":6324},[5249,6858,6859,6862,6864],{"class":5251,"line":5430},[5249,6860,6861],{"class":6150},"          cache",[5249,6863,6155],{"class":6154},[5249,6865,6866],{"class":6158},"'npm'\n",[5249,6868,6869],{"class":5251,"line":5442},[5249,6870,5306],{"emptyLinePlaceholder":5305},[5249,6872,6873,6875,6877,6879],{"class":5251,"line":5447},[5249,6874,6265],{"class":6154},[5249,6876,6151],{"class":6150},[5249,6878,6155],{"class":6154},[5249,6880,6340],{"class":6158},[5249,6882,6883,6885,6887],{"class":5251,"line":5453},[5249,6884,6345],{"class":6150},[5249,6886,6155],{"class":6154},[5249,6888,6350],{"class":6158},[5249,6890,6891],{"class":5251,"line":5698},[5249,6892,5306],{"emptyLinePlaceholder":5305},[5249,6894,6895,6897,6899,6901],{"class":5251,"line":5704},[5249,6896,6265],{"class":6154},[5249,6898,6151],{"class":6150},[5249,6900,6155],{"class":6154},[5249,6902,6903],{"class":6158},"Run ESLint\n",[5249,6905,6906,6908,6910],{"class":5251,"line":5710},[5249,6907,6345],{"class":6150},[5249,6909,6155],{"class":6154},[5249,6911,6912],{"class":6158},"npm run lint\n",[5249,6914,6915],{"class":5251,"line":5716},[5249,6916,5306],{"emptyLinePlaceholder":5305},[5249,6918,6919,6921,6923,6925],{"class":5251,"line":5722},[5249,6920,6265],{"class":6154},[5249,6922,6151],{"class":6150},[5249,6924,6155],{"class":6154},[5249,6926,6927],{"class":6158},"Check TypeScript types\n",[5249,6929,6930,6932,6934],{"class":5251,"line":5728},[5249,6931,6345],{"class":6150},[5249,6933,6155],{"class":6154},[5249,6935,6936],{"class":6158},"npm run build\n",[5249,6938,6939],{"class":5251,"line":5734},[5249,6940,5306],{"emptyLinePlaceholder":5305},[5249,6942,6943,6945],{"class":5251,"line":5740},[5249,6944,6235],{"class":6150},[5249,6946,6185],{"class":6154},[5249,6948,6949,6951,6953],{"class":5251,"line":5746},[5249,6950,6778],{"class":6150},[5249,6952,6155],{"class":6154},[5249,6954,6955],{"class":6158},"Run Tests\n",[5249,6957,6958,6960,6962],{"class":5251,"line":5752},[5249,6959,6242],{"class":6150},[5249,6961,6155],{"class":6154},[5249,6963,6792],{"class":6158},[5249,6965,6966,6969,6971,6974],{"class":5251,"line":5758},[5249,6967,6968],{"class":6150},"    needs",[5249,6970,6155],{"class":6154},[5249,6972,6973],{"class":6158},"lint",[5249,6975,6976],{"class":5255},"  # Запускається після успішного lint\n",[5249,6978,6979],{"class":5251,"line":5763},[5249,6980,5306],{"emptyLinePlaceholder":5305},[5249,6982,6983,6986],{"class":5251,"line":5769},[5249,6984,6985],{"class":6150},"    strategy",[5249,6987,6185],{"class":6154},[5249,6989,6990,6993],{"class":5251,"line":5775},[5249,6991,6992],{"class":6150},"      matrix",[5249,6994,6185],{"class":6154},[5249,6996,6997,7000,7002,7005,7007,7010,7013],{"class":5251,"line":5781},[5249,6998,6999],{"class":6150},"        node-version",[5249,7001,6193],{"class":6154},[5249,7003,7004],{"class":6324},"18",[5249,7006,6488],{"class":6154},[5249,7008,7009],{"class":6324},"20",[5249,7011,7012],{"class":6154},"]  ",[5249,7014,7015],{"class":5255},"# Тестуємо на Node 18 та 20\n",[5249,7017,7018],{"class":5251,"line":5787},[5249,7019,5306],{"emptyLinePlaceholder":5305},[5249,7021,7022,7024],{"class":5251,"line":5793},[5249,7023,6255],{"class":6150},[5249,7025,6185],{"class":6154},[5249,7027,7028,7030,7032,7034],{"class":5251,"line":5799},[5249,7029,6265],{"class":6154},[5249,7031,6151],{"class":6150},[5249,7033,6155],{"class":6154},[5249,7035,6272],{"class":6158},[5249,7037,7038,7040,7042],{"class":5251,"line":5805},[5249,7039,6277],{"class":6150},[5249,7041,6155],{"class":6154},[5249,7043,6282],{"class":6158},[5249,7045,7046],{"class":5251,"line":5811},[5249,7047,5306],{"emptyLinePlaceholder":5305},[5249,7049,7050,7052,7054,7056],{"class":5251,"line":5817},[5249,7051,6265],{"class":6154},[5249,7053,6151],{"class":6150},[5249,7055,6155],{"class":6154},[5249,7057,7058],{"class":6158},"Setup Node.js ${{ matrix.node-version }}\n",[5249,7060,7061,7063,7065],{"class":5251,"line":5822},[5249,7062,6277],{"class":6150},[5249,7064,6155],{"class":6154},[5249,7066,6307],{"class":6158},[5249,7068,7069,7071],{"class":5251,"line":5828},[5249,7070,6312],{"class":6150},[5249,7072,6185],{"class":6154},[5249,7074,7075,7077,7079],{"class":5251,"line":5834},[5249,7076,6319],{"class":6150},[5249,7078,6155],{"class":6154},[5249,7080,7081],{"class":6158},"${{ matrix.node-version }}\n",[5249,7083,7084,7086,7088],{"class":5251,"line":5839},[5249,7085,6861],{"class":6150},[5249,7087,6155],{"class":6154},[5249,7089,6866],{"class":6158},[5249,7091,7093],{"class":5251,"line":7092},52,[5249,7094,5306],{"emptyLinePlaceholder":5305},[5249,7096,7098,7100,7102,7104],{"class":5251,"line":7097},53,[5249,7099,6265],{"class":6154},[5249,7101,6151],{"class":6150},[5249,7103,6155],{"class":6154},[5249,7105,6340],{"class":6158},[5249,7107,7109,7111,7113],{"class":5251,"line":7108},54,[5249,7110,6345],{"class":6150},[5249,7112,6155],{"class":6154},[5249,7114,6350],{"class":6158},[5249,7116,7118],{"class":5251,"line":7117},55,[5249,7119,5306],{"emptyLinePlaceholder":5305},[5249,7121,7123,7125,7127,7129],{"class":5251,"line":7122},56,[5249,7124,6265],{"class":6154},[5249,7126,6151],{"class":6150},[5249,7128,6155],{"class":6154},[5249,7130,7131],{"class":6158},"Run unit tests\n",[5249,7133,7135,7137,7139],{"class":5251,"line":7134},57,[5249,7136,6345],{"class":6150},[5249,7138,6155],{"class":6154},[5249,7140,7141],{"class":6158},"npm run test:cov\n",[5249,7143,7145],{"class":5251,"line":7144},58,[5249,7146,5306],{"emptyLinePlaceholder":5305},[5249,7148,7150,7152,7154,7156],{"class":5251,"line":7149},59,[5249,7151,6265],{"class":6154},[5249,7153,6151],{"class":6150},[5249,7155,6155],{"class":6154},[5249,7157,7158],{"class":6158},"Upload coverage to Codecov\n",[5249,7160,7162,7164,7166],{"class":5251,"line":7161},60,[5249,7163,6277],{"class":6150},[5249,7165,6155],{"class":6154},[5249,7167,7168],{"class":6158},"codecov\u002Fcodecov-action@v3\n",[5249,7170,7172,7174],{"class":5251,"line":7171},61,[5249,7173,6312],{"class":6150},[5249,7175,6185],{"class":6154},[5249,7177,7179,7182,7184],{"class":5251,"line":7178},62,[5249,7180,7181],{"class":6150},"          files",[5249,7183,6155],{"class":6154},[5249,7185,7186],{"class":6158},".\u002Fcoverage\u002Flcov.info\n",[5249,7188,7190,7193,7195],{"class":5251,"line":7189},63,[5249,7191,7192],{"class":6150},"          flags",[5249,7194,6155],{"class":6154},[5249,7196,7197],{"class":6158},"unittests\n",[5249,7199,7201,7204,7206],{"class":5251,"line":7200},64,[5249,7202,7203],{"class":6150},"          name",[5249,7205,6155],{"class":6154},[5249,7207,7208],{"class":6158},"codecov-umbrella\n",[5249,7210,7212],{"class":5251,"line":7211},65,[5249,7213,5306],{"emptyLinePlaceholder":5305},[5249,7215,7217,7220],{"class":5251,"line":7216},66,[5249,7218,7219],{"class":6150},"  e2e",[5249,7221,6185],{"class":6154},[5249,7223,7225,7227,7229],{"class":5251,"line":7224},67,[5249,7226,6778],{"class":6150},[5249,7228,6155],{"class":6154},[5249,7230,7231],{"class":6158},"E2E Tests\n",[5249,7233,7235,7237,7239],{"class":5251,"line":7234},68,[5249,7236,6242],{"class":6150},[5249,7238,6155],{"class":6154},[5249,7240,6792],{"class":6158},[5249,7242,7244,7246,7248],{"class":5251,"line":7243},69,[5249,7245,6968],{"class":6150},[5249,7247,6155],{"class":6154},[5249,7249,7250],{"class":6158},"lint\n",[5249,7252,7254],{"class":5251,"line":7253},70,[5249,7255,5306],{"emptyLinePlaceholder":5305},[5249,7257,7259,7262],{"class":5251,"line":7258},71,[5249,7260,7261],{"class":6150},"    services",[5249,7263,6185],{"class":6154},[5249,7265,7267,7270],{"class":5251,"line":7266},72,[5249,7268,7269],{"class":6150},"      postgres",[5249,7271,6185],{"class":6154},[5249,7273,7275,7278,7280],{"class":5251,"line":7274},73,[5249,7276,7277],{"class":6150},"        image",[5249,7279,6155],{"class":6154},[5249,7281,7282],{"class":6158},"postgres:15\n",[5249,7284,7286,7289],{"class":5251,"line":7285},74,[5249,7287,7288],{"class":6150},"        env",[5249,7290,6185],{"class":6154},[5249,7292,7294,7297,7299],{"class":5251,"line":7293},75,[5249,7295,7296],{"class":6150},"          POSTGRES_USER",[5249,7298,6155],{"class":6154},[5249,7300,7301],{"class":6158},"postgres\n",[5249,7303,7305,7308,7310],{"class":5251,"line":7304},76,[5249,7306,7307],{"class":6150},"          POSTGRES_PASSWORD",[5249,7309,6155],{"class":6154},[5249,7311,7301],{"class":6158},[5249,7313,7315,7318,7320],{"class":5251,"line":7314},77,[5249,7316,7317],{"class":6150},"          POSTGRES_DB",[5249,7319,6155],{"class":6154},[5249,7321,7322],{"class":6158},"test_db\n",[5249,7324,7326,7329,7331,7335],{"class":5251,"line":7325},78,[5249,7327,7328],{"class":6150},"        options",[5249,7330,6155],{"class":6154},[5249,7332,7334],{"class":7333},"s8xlr",">",[5249,7336,7337],{"class":5281},"-\n",[5249,7339,7341],{"class":5251,"line":7340},79,[5249,7342,7343],{"class":6158},"          --health-cmd pg_isready\n",[5249,7345,7347],{"class":5251,"line":7346},80,[5249,7348,7349],{"class":6158},"          --health-interval 10s\n",[5249,7351,7353],{"class":5251,"line":7352},81,[5249,7354,7355],{"class":6158},"          --health-timeout 5s\n",[5249,7357,7359],{"class":5251,"line":7358},82,[5249,7360,7361],{"class":6158},"          --health-retries 5\n",[5249,7363,7365,7368],{"class":5251,"line":7364},83,[5249,7366,7367],{"class":6150},"        ports",[5249,7369,6185],{"class":6154},[5249,7371,7373,7376],{"class":5251,"line":7372},84,[5249,7374,7375],{"class":6154},"          - ",[5249,7377,7378],{"class":6158},"5432:5432\n",[5249,7380,7382],{"class":5251,"line":7381},85,[5249,7383,5306],{"emptyLinePlaceholder":5305},[5249,7385,7387,7390],{"class":5251,"line":7386},86,[5249,7388,7389],{"class":6150},"      redis",[5249,7391,6185],{"class":6154},[5249,7393,7395,7397,7399],{"class":5251,"line":7394},87,[5249,7396,7277],{"class":6150},[5249,7398,6155],{"class":6154},[5249,7400,7401],{"class":6158},"redis:7\n",[5249,7403,7405,7407,7409,7411],{"class":5251,"line":7404},88,[5249,7406,7328],{"class":6150},[5249,7408,6155],{"class":6154},[5249,7410,7334],{"class":7333},[5249,7412,7337],{"class":5281},[5249,7414,7416],{"class":5251,"line":7415},89,[5249,7417,7418],{"class":6158},"          --health-cmd \"redis-cli ping\"\n",[5249,7420,7422],{"class":5251,"line":7421},90,[5249,7423,7349],{"class":6158},[5249,7425,7427],{"class":5251,"line":7426},91,[5249,7428,7355],{"class":6158},[5249,7430,7432],{"class":5251,"line":7431},92,[5249,7433,7361],{"class":6158},[5249,7435,7437,7439],{"class":5251,"line":7436},93,[5249,7438,7367],{"class":6150},[5249,7440,6185],{"class":6154},[5249,7442,7444,7446],{"class":5251,"line":7443},94,[5249,7445,7375],{"class":6154},[5249,7447,7448],{"class":6158},"6379:6379\n",[5249,7450,7452],{"class":5251,"line":7451},95,[5249,7453,5306],{"emptyLinePlaceholder":5305},[5249,7455,7457,7459],{"class":5251,"line":7456},96,[5249,7458,6255],{"class":6150},[5249,7460,6185],{"class":6154},[5249,7462,7464,7466,7468,7470],{"class":5251,"line":7463},97,[5249,7465,6265],{"class":6154},[5249,7467,6151],{"class":6150},[5249,7469,6155],{"class":6154},[5249,7471,6272],{"class":6158},[5249,7473,7475,7477,7479],{"class":5251,"line":7474},98,[5249,7476,6277],{"class":6150},[5249,7478,6155],{"class":6154},[5249,7480,6282],{"class":6158},[5249,7482,7484],{"class":5251,"line":7483},99,[5249,7485,5306],{"emptyLinePlaceholder":5305},[5249,7487,7489,7491,7493,7495],{"class":5251,"line":7488},100,[5249,7490,6265],{"class":6154},[5249,7492,6151],{"class":6150},[5249,7494,6155],{"class":6154},[5249,7496,6298],{"class":6158},[5249,7498,7500,7502,7504],{"class":5251,"line":7499},101,[5249,7501,6277],{"class":6150},[5249,7503,6155],{"class":6154},[5249,7505,6307],{"class":6158},[5249,7507,7509,7511],{"class":5251,"line":7508},102,[5249,7510,6312],{"class":6150},[5249,7512,6185],{"class":6154},[5249,7514,7516,7518,7520],{"class":5251,"line":7515},103,[5249,7517,6319],{"class":6150},[5249,7519,6155],{"class":6154},[5249,7521,6325],{"class":6324},[5249,7523,7525,7527,7529],{"class":5251,"line":7524},104,[5249,7526,6861],{"class":6150},[5249,7528,6155],{"class":6154},[5249,7530,6866],{"class":6158},[5249,7532,7534],{"class":5251,"line":7533},105,[5249,7535,5306],{"emptyLinePlaceholder":5305},[5249,7537,7539,7541,7543,7545],{"class":5251,"line":7538},106,[5249,7540,6265],{"class":6154},[5249,7542,6151],{"class":6150},[5249,7544,6155],{"class":6154},[5249,7546,6340],{"class":6158},[5249,7548,7550,7552,7554],{"class":5251,"line":7549},107,[5249,7551,6345],{"class":6150},[5249,7553,6155],{"class":6154},[5249,7555,6350],{"class":6158},[5249,7557,7559],{"class":5251,"line":7558},108,[5249,7560,5306],{"emptyLinePlaceholder":5305},[5249,7562,7564,7566,7568,7570],{"class":5251,"line":7563},109,[5249,7565,6265],{"class":6154},[5249,7567,6151],{"class":6150},[5249,7569,6155],{"class":6154},[5249,7571,7572],{"class":6158},"Run E2E tests\n",[5249,7574,7576,7578,7580],{"class":5251,"line":7575},110,[5249,7577,6345],{"class":6150},[5249,7579,6155],{"class":6154},[5249,7581,7582],{"class":6158},"npm run test:e2e\n",[5249,7584,7586,7588],{"class":5251,"line":7585},111,[5249,7587,7288],{"class":6150},[5249,7589,6185],{"class":6154},[5249,7591,7593,7596,7598],{"class":5251,"line":7592},112,[5249,7594,7595],{"class":6150},"          DATABASE_HOST",[5249,7597,6155],{"class":6154},[5249,7599,7600],{"class":6158},"localhost\n",[5249,7602,7604,7607,7609],{"class":5251,"line":7603},113,[5249,7605,7606],{"class":6150},"          DATABASE_PORT",[5249,7608,6155],{"class":6154},[5249,7610,7611],{"class":6324},"5432\n",[5249,7613,7615,7618,7620],{"class":5251,"line":7614},114,[5249,7616,7617],{"class":6150},"          DATABASE_USER",[5249,7619,6155],{"class":6154},[5249,7621,7301],{"class":6158},[5249,7623,7625,7628,7630],{"class":5251,"line":7624},115,[5249,7626,7627],{"class":6150},"          DATABASE_PASSWORD",[5249,7629,6155],{"class":6154},[5249,7631,7301],{"class":6158},[5249,7633,7635,7638,7640],{"class":5251,"line":7634},116,[5249,7636,7637],{"class":6150},"          DATABASE_NAME",[5249,7639,6155],{"class":6154},[5249,7641,7322],{"class":6158},[5249,7643,7645,7648,7650],{"class":5251,"line":7644},117,[5249,7646,7647],{"class":6150},"          REDIS_HOST",[5249,7649,6155],{"class":6154},[5249,7651,7600],{"class":6158},[5249,7653,7655,7658,7660],{"class":5251,"line":7654},118,[5249,7656,7657],{"class":6150},"          REDIS_PORT",[5249,7659,6155],{"class":6154},[5249,7661,7662],{"class":6324},"6379\n",[5249,7664,7666],{"class":5251,"line":7665},119,[5249,7667,5306],{"emptyLinePlaceholder":5305},[5249,7669,7671,7673,7675,7677],{"class":5251,"line":7670},120,[5249,7672,6265],{"class":6154},[5249,7674,6151],{"class":6150},[5249,7676,6155],{"class":6154},[5249,7678,7679],{"class":6158},"Upload E2E test results\n",[5249,7681,7683,7686,7688],{"class":5251,"line":7682},121,[5249,7684,7685],{"class":6150},"        if",[5249,7687,6155],{"class":6154},[5249,7689,7690],{"class":6158},"always()\n",[5249,7692,7694,7696,7698],{"class":5251,"line":7693},122,[5249,7695,6277],{"class":6150},[5249,7697,6155],{"class":6154},[5249,7699,7700],{"class":6158},"actions\u002Fupload-artifact@v3\n",[5249,7702,7704,7706],{"class":5251,"line":7703},123,[5249,7705,6312],{"class":6150},[5249,7707,6185],{"class":6154},[5249,7709,7711,7713,7715],{"class":5251,"line":7710},124,[5249,7712,7203],{"class":6150},[5249,7714,6155],{"class":6154},[5249,7716,7717],{"class":6158},"e2e-test-results\n",[5249,7719,7721,7724,7726],{"class":5251,"line":7720},125,[5249,7722,7723],{"class":6150},"          path",[5249,7725,6155],{"class":6154},[5249,7727,7728],{"class":6158},"test-results\u002F\n",[5849,7730,7732],{"id":7731},"пояснення-ключових-елементів","Пояснення ключових елементів",[5155,7734,7735,7740,7741,7744,7745,7747],{},[5118,7736,7737],{},[5173,7738,7739],{},"needs: lint"," — job ",[5173,7742,7743],{},"test"," чекає успішного завершення ",[5173,7746,6973],{},":",[5240,7749,7751],{"className":6141,"code":7750,"language":6143,"meta":5245,"style":5245},"test:\n  needs: lint  # Якщо lint падає, test не запускається\n",[5173,7752,7753,7759],{"__ignoreMap":5245},[5249,7754,7755,7757],{"class":5251,"line":5252},[5249,7756,7743],{"class":6150},[5249,7758,6185],{"class":6154},[5249,7760,7761,7764,7766,7768],{"class":5251,"line":5259},[5249,7762,7763],{"class":6150},"  needs",[5249,7765,6155],{"class":6154},[5249,7767,6973],{"class":6158},[5249,7769,7770],{"class":5255},"  # Якщо lint падає, test не запускається\n",[5155,7772,7773,7778],{},[5118,7774,7775],{},[5173,7776,7777],{},"strategy.matrix"," — паралельне виконання на різних версіях:",[5240,7780,7782],{"className":6141,"code":7781,"language":6143,"meta":5245,"style":5245},"strategy:\n  matrix:\n    node-version: [18, 20]\n    os: [ubuntu-latest, windows-latest]  # Опціонально: різні ОС\n",[5173,7783,7784,7791,7798,7813],{"__ignoreMap":5245},[5249,7785,7786,7789],{"class":5251,"line":5252},[5249,7787,7788],{"class":6150},"strategy",[5249,7790,6185],{"class":6154},[5249,7792,7793,7796],{"class":5251,"line":5259},[5249,7794,7795],{"class":6150},"  matrix",[5249,7797,6185],{"class":6154},[5249,7799,7800,7803,7805,7807,7809,7811],{"class":5251,"line":5273},[5249,7801,7802],{"class":6150},"    node-version",[5249,7804,6193],{"class":6154},[5249,7806,7004],{"class":6324},[5249,7808,6488],{"class":6154},[5249,7810,7009],{"class":6324},[5249,7812,6199],{"class":6154},[5249,7814,7815,7818,7820,7822,7824,7827,7829],{"class":5251,"line":5288},[5249,7816,7817],{"class":6150},"    os",[5249,7819,6193],{"class":6154},[5249,7821,6247],{"class":6158},[5249,7823,6488],{"class":6154},[5249,7825,7826],{"class":6158},"windows-latest",[5249,7828,7012],{"class":6154},[5249,7830,7831],{"class":5255},"# Опціонально: різні ОС\n",[5155,7833,7834,7835,7838],{},"Створює ",[5118,7836,7837],{},"4 jobs"," (Node 18\u002FUbuntu, Node 20\u002FUbuntu, Node 18\u002FWindows, Node 20\u002FWindows).",[5155,7840,7841,7846],{},[5118,7842,7843],{},[5173,7844,7845],{},"services"," — Docker контейнери для PostgreSQL та Redis:",[5240,7848,7850],{"className":6141,"code":7849,"language":6143,"meta":5245,"style":5245},"services:\n  postgres:\n    image: postgres:15\n    env:\n      POSTGRES_PASSWORD: postgres\n    options: >-\n      --health-cmd pg_isready\n      --health-interval 10s\n    ports:\n      - 5432:5432\n",[5173,7851,7852,7858,7865,7874,7881,7890,7901,7906,7911,7918],{"__ignoreMap":5245},[5249,7853,7854,7856],{"class":5251,"line":5252},[5249,7855,7845],{"class":6150},[5249,7857,6185],{"class":6154},[5249,7859,7860,7863],{"class":5251,"line":5259},[5249,7861,7862],{"class":6150},"  postgres",[5249,7864,6185],{"class":6154},[5249,7866,7867,7870,7872],{"class":5251,"line":5273},[5249,7868,7869],{"class":6150},"    image",[5249,7871,6155],{"class":6154},[5249,7873,7282],{"class":6158},[5249,7875,7876,7879],{"class":5251,"line":5288},[5249,7877,7878],{"class":6150},"    env",[5249,7880,6185],{"class":6154},[5249,7882,7883,7886,7888],{"class":5251,"line":5302},[5249,7884,7885],{"class":6150},"      POSTGRES_PASSWORD",[5249,7887,6155],{"class":6154},[5249,7889,7301],{"class":6158},[5249,7891,7892,7895,7897,7899],{"class":5251,"line":5309},[5249,7893,7894],{"class":6150},"    options",[5249,7896,6155],{"class":6154},[5249,7898,7334],{"class":7333},[5249,7900,7337],{"class":5281},[5249,7902,7903],{"class":5251,"line":5315},[5249,7904,7905],{"class":6158},"      --health-cmd pg_isready\n",[5249,7907,7908],{"class":5251,"line":5324},[5249,7909,7910],{"class":6158},"      --health-interval 10s\n",[5249,7912,7913,7916],{"class":5251,"line":5329},[5249,7914,7915],{"class":6150},"    ports",[5249,7917,6185],{"class":6154},[5249,7919,7920,7922],{"class":5251,"line":5335},[5249,7921,6265],{"class":6154},[5249,7923,7378],{"class":6158},[5155,7925,7926,7931],{},[5118,7927,7928],{},[5173,7929,7930],{},"cache: 'npm'"," — кешування node_modules для прискорення:",[5240,7933,7935],{"className":6141,"code":7934,"language":6143,"meta":5245,"style":5245},"- uses: actions\u002Fsetup-node@v3\n  with:\n    cache: 'npm'  # Кешує npm dependencies\n",[5173,7936,7937,7949,7956],{"__ignoreMap":5245},[5249,7938,7939,7942,7945,7947],{"class":5251,"line":5252},[5249,7940,7941],{"class":6154},"- ",[5249,7943,7944],{"class":6150},"uses",[5249,7946,6155],{"class":6154},[5249,7948,6307],{"class":6158},[5249,7950,7951,7954],{"class":5251,"line":5259},[5249,7952,7953],{"class":6150},"  with",[5249,7955,6185],{"class":6154},[5249,7957,7958,7961,7963,7966],{"class":5251,"line":5273},[5249,7959,7960],{"class":6150},"    cache",[5249,7962,6155],{"class":6154},[5249,7964,7965],{"class":6158},"'npm'",[5249,7967,7968],{"class":5255},"  # Кешує npm dependencies\n",[5155,7970,7971,7976],{},[5118,7972,7973],{},[5173,7974,7975],{},"if: always()"," — виконати step навіть якщо попередні failed:",[5240,7978,7980],{"className":6141,"code":7979,"language":6143,"meta":5245,"style":5245},"- name: Upload test results\n  if: always()  # Завантажити результати навіть якщо тести провалилися\n",[5173,7981,7982,7993],{"__ignoreMap":5245},[5249,7983,7984,7986,7988,7990],{"class":5251,"line":5252},[5249,7985,7941],{"class":6154},[5249,7987,6151],{"class":6150},[5249,7989,6155],{"class":6154},[5249,7991,7992],{"class":6158},"Upload test results\n",[5249,7994,7995,7998,8000,8003],{"class":5251,"line":5259},[5249,7996,7997],{"class":6150},"  if",[5249,7999,6155],{"class":6154},[5249,8001,8002],{"class":6158},"always()",[5249,8004,8005],{"class":5255},"  # Завантажити результати навіть якщо тести провалилися\n",[5155,8007,8008,8013],{},[5118,8009,8010],{},[5173,8011,8012],{},"uses: actions\u002Fupload-artifact@v3"," — збереження артефактів (test reports, coverage):",[5240,8015,8017],{"className":6141,"code":8016,"language":6143,"meta":5245,"style":5245},"- uses: actions\u002Fupload-artifact@v3\n  with:\n    name: coverage-report\n    path: coverage\u002F\n",[5173,8018,8019,8029,8035,8044],{"__ignoreMap":5245},[5249,8020,8021,8023,8025,8027],{"class":5251,"line":5252},[5249,8022,7941],{"class":6154},[5249,8024,7944],{"class":6150},[5249,8026,6155],{"class":6154},[5249,8028,7700],{"class":6158},[5249,8030,8031,8033],{"class":5251,"line":5259},[5249,8032,7953],{"class":6150},[5249,8034,6185],{"class":6154},[5249,8036,8037,8039,8041],{"class":5251,"line":5273},[5249,8038,6778],{"class":6150},[5249,8040,6155],{"class":6154},[5249,8042,8043],{"class":6158},"coverage-report\n",[5249,8045,8046,8049,8051],{"class":5251,"line":5288},[5249,8047,8048],{"class":6150},"    path",[5249,8050,6155],{"class":6154},[5249,8052,8053],{"class":6158},"coverage\u002F\n",[8055,8056,8059,8072,8082,8089,8096,8107,8110,8120,8131,8138,8141,8151,8160,8163,8173,8180,8191,8194],"terminal-preview",{":cursor":8057,"title":8058},"false","GitHub Actions — Build Success",[8060,8061,8063,5977,8068,8071],"div",{"className":8062},[5251],[5249,8064,8067],{"className":8065},[8066],"text-emerald-400","✓",[5118,8069,8070],{},"Lint Code"," completed in 1m 23s",[8060,8073,8075,8076,8081],{"className":8074},[5251],"  ",[5249,8077,8080],{"className":8078},[8079],"text-sky-400","→"," Checkout code",[8060,8083,8075,8085,8088],{"className":8084},[5251],[5249,8086,8080],{"className":8087},[8079]," Setup Node.js 18",[8060,8090,8075,8092,8095],{"className":8091},[5251],[5249,8093,8080],{"className":8094},[8079]," Install dependencies (cached)",[8060,8097,8075,8099,8102,8103],{"className":8098},[5251],[5249,8100,8080],{"className":8101},[8079]," Run ESLint — ",[5249,8104,8106],{"className":8105},[8066],"✓ No errors found",[8060,8108],{"className":8109},[5251],[8060,8111,8113,5977,8116,8119],{"className":8112},[5251],[5249,8114,8067],{"className":8115},[8066],[5118,8117,8118],{},"Run Tests (Node 18)"," completed in 2m 45s",[8060,8121,8075,8123,8126,8127],{"className":8122},[5251],[5249,8124,8080],{"className":8125},[8079]," Run unit tests — ",[5249,8128,8130],{"className":8129},[8066],"✓ 156 tests passed",[8060,8132,8075,8134,8137],{"className":8133},[5251],[5249,8135,8080],{"className":8136},[8079]," Coverage: 87.3%",[8060,8139],{"className":8140},[5251],[8060,8142,8144,5977,8147,8150],{"className":8143},[5251],[5249,8145,8067],{"className":8146},[8066],[5118,8148,8149],{},"Run Tests (Node 20)"," completed in 2m 38s",[8060,8152,8075,8154,8126,8157],{"className":8153},[5251],[5249,8155,8080],{"className":8156},[8079],[5249,8158,8130],{"className":8159},[8066],[8060,8161],{"className":8162},[5251],[8060,8164,8166,5977,8169,8172],{"className":8165},[5251],[5249,8167,8067],{"className":8168},[8066],[5118,8170,8171],{},"E2E Tests"," completed in 3m 12s",[8060,8174,8075,8176,8179],{"className":8175},[5251],[5249,8177,8080],{"className":8178},[8079]," Setup PostgreSQL service",[8060,8181,8075,8183,8186,8187],{"className":8182},[5251],[5249,8184,8080],{"className":8185},[8079]," Run E2E tests — ",[5249,8188,8190],{"className":8189},[8066],"✓ 42 tests passed",[8060,8192],{"className":8193},[5251],[8060,8195,8197],{"className":8196},[5251],[5249,8198,8201],{"className":8199},[8066,8200],"font-bold","✓ All checks passed!",[5147,8203],{},[5150,8205,8207],{"id":8206},"docker-build-та-push-до-registry","Docker Build та Push до Registry",[5155,8209,8210],{},"Після успішних тестів побудуємо Docker image та опублікуємо у registry:",[5240,8212,8214],{"className":6141,"code":8213,"language":6143,"meta":5245,"style":5245},"# .github\u002Fworkflows\u002Fci-cd.yml\nname: CI\u002FCD\n\non:\n  push:\n    branches: [main]\n\njobs:\n  # ... lint, test, e2e jobs (як у попередньому прикладі)\n\n  build:\n    name: Build and Push Docker Image\n    runs-on: ubuntu-latest\n    needs: [test, e2e]  # Чекає успішного test та e2e\n\n    steps:\n      - name: Checkout code\n        uses: actions\u002Fcheckout@v3\n\n      - name: Set up Docker Buildx\n        uses: docker\u002Fsetup-buildx-action@v2\n\n      - name: Log in to Docker Hub\n        uses: docker\u002Flogin-action@v2\n        with:\n          username: ${{ secrets.DOCKER_USERNAME }}\n          password: ${{ secrets.DOCKER_PASSWORD }}\n\n      - name: Extract metadata\n        id: meta\n        uses: docker\u002Fmetadata-action@v4\n        with:\n          images: myusername\u002Fmy-nestjs-app\n          tags: |\n            type=ref,event=branch\n            type=sha,prefix={{branch}}-\n            type=semver,pattern={{version}}\n\n      - name: Build and push\n        uses: docker\u002Fbuild-push-action@v4\n        with:\n          context: .\n          file: .\u002FDockerfile\n          push: true\n          tags: ${{ steps.meta.outputs.tags }}\n          labels: ${{ steps.meta.outputs.labels }}\n          cache-from: type=registry,ref=myusername\u002Fmy-nestjs-app:latest\n          cache-to: type=inline\n\n      - name: Image digest\n        run: echo ${{ steps.build.outputs.digest }}\n",[5173,8215,8216,8221,8230,8234,8240,8246,8256,8260,8266,8271,8275,8282,8291,8299,8317,8321,8327,8337,8345,8349,8360,8369,8373,8384,8393,8399,8409,8419,8423,8434,8444,8453,8459,8469,8479,8484,8489,8494,8498,8509,8518,8524,8534,8544,8553,8562,8572,8582,8592,8596,8607],{"__ignoreMap":5245},[5249,8217,8218],{"class":5251,"line":5252},[5249,8219,8220],{"class":5255},"# .github\u002Fworkflows\u002Fci-cd.yml\n",[5249,8222,8223,8225,8227],{"class":5251,"line":5259},[5249,8224,6151],{"class":6150},[5249,8226,6155],{"class":6154},[5249,8228,8229],{"class":6158},"CI\u002FCD\n",[5249,8231,8232],{"class":5251,"line":5273},[5249,8233,5306],{"emptyLinePlaceholder":5305},[5249,8235,8236,8238],{"class":5251,"line":5288},[5249,8237,6171],{"class":5281},[5249,8239,6185],{"class":6154},[5249,8241,8242,8244],{"class":5251,"line":5302},[5249,8243,6182],{"class":6150},[5249,8245,6185],{"class":6154},[5249,8247,8248,8250,8252,8254],{"class":5251,"line":5309},[5249,8249,6190],{"class":6150},[5249,8251,6193],{"class":6154},[5249,8253,6196],{"class":6158},[5249,8255,6199],{"class":6154},[5249,8257,8258],{"class":5251,"line":5315},[5249,8259,5306],{"emptyLinePlaceholder":5305},[5249,8261,8262,8264],{"class":5251,"line":5324},[5249,8263,6225],{"class":6150},[5249,8265,6185],{"class":6154},[5249,8267,8268],{"class":5251,"line":5329},[5249,8269,8270],{"class":5255},"  # ... lint, test, e2e jobs (як у попередньому прикладі)\n",[5249,8272,8273],{"class":5251,"line":5335},[5249,8274,5306],{"emptyLinePlaceholder":5305},[5249,8276,8277,8280],{"class":5251,"line":5344},[5249,8278,8279],{"class":6150},"  build",[5249,8281,6185],{"class":6154},[5249,8283,8284,8286,8288],{"class":5251,"line":5356},[5249,8285,6778],{"class":6150},[5249,8287,6155],{"class":6154},[5249,8289,8290],{"class":6158},"Build and Push Docker Image\n",[5249,8292,8293,8295,8297],{"class":5251,"line":5361},[5249,8294,6242],{"class":6150},[5249,8296,6155],{"class":6154},[5249,8298,6792],{"class":6158},[5249,8300,8301,8303,8305,8307,8309,8312,8314],{"class":5251,"line":5367},[5249,8302,6968],{"class":6150},[5249,8304,6193],{"class":6154},[5249,8306,7743],{"class":6158},[5249,8308,6488],{"class":6154},[5249,8310,8311],{"class":6158},"e2e",[5249,8313,7012],{"class":6154},[5249,8315,8316],{"class":5255},"# Чекає успішного test та e2e\n",[5249,8318,8319],{"class":5251,"line":5376},[5249,8320,5306],{"emptyLinePlaceholder":5305},[5249,8322,8323,8325],{"class":5251,"line":5381},[5249,8324,6255],{"class":6150},[5249,8326,6185],{"class":6154},[5249,8328,8329,8331,8333,8335],{"class":5251,"line":5387},[5249,8330,6265],{"class":6154},[5249,8332,6151],{"class":6150},[5249,8334,6155],{"class":6154},[5249,8336,6272],{"class":6158},[5249,8338,8339,8341,8343],{"class":5251,"line":5398},[5249,8340,6277],{"class":6150},[5249,8342,6155],{"class":6154},[5249,8344,6282],{"class":6158},[5249,8346,8347],{"class":5251,"line":5403},[5249,8348,5306],{"emptyLinePlaceholder":5305},[5249,8350,8351,8353,8355,8357],{"class":5251,"line":5409},[5249,8352,6265],{"class":6154},[5249,8354,6151],{"class":6150},[5249,8356,6155],{"class":6154},[5249,8358,8359],{"class":6158},"Set up Docker Buildx\n",[5249,8361,8362,8364,8366],{"class":5251,"line":5419},[5249,8363,6277],{"class":6150},[5249,8365,6155],{"class":6154},[5249,8367,8368],{"class":6158},"docker\u002Fsetup-buildx-action@v2\n",[5249,8370,8371],{"class":5251,"line":5424},[5249,8372,5306],{"emptyLinePlaceholder":5305},[5249,8374,8375,8377,8379,8381],{"class":5251,"line":5430},[5249,8376,6265],{"class":6154},[5249,8378,6151],{"class":6150},[5249,8380,6155],{"class":6154},[5249,8382,8383],{"class":6158},"Log in to Docker Hub\n",[5249,8385,8386,8388,8390],{"class":5251,"line":5442},[5249,8387,6277],{"class":6150},[5249,8389,6155],{"class":6154},[5249,8391,8392],{"class":6158},"docker\u002Flogin-action@v2\n",[5249,8394,8395,8397],{"class":5251,"line":5447},[5249,8396,6312],{"class":6150},[5249,8398,6185],{"class":6154},[5249,8400,8401,8404,8406],{"class":5251,"line":5453},[5249,8402,8403],{"class":6150},"          username",[5249,8405,6155],{"class":6154},[5249,8407,8408],{"class":6158},"${{ secrets.DOCKER_USERNAME }}\n",[5249,8410,8411,8414,8416],{"class":5251,"line":5698},[5249,8412,8413],{"class":6150},"          password",[5249,8415,6155],{"class":6154},[5249,8417,8418],{"class":6158},"${{ secrets.DOCKER_PASSWORD }}\n",[5249,8420,8421],{"class":5251,"line":5704},[5249,8422,5306],{"emptyLinePlaceholder":5305},[5249,8424,8425,8427,8429,8431],{"class":5251,"line":5710},[5249,8426,6265],{"class":6154},[5249,8428,6151],{"class":6150},[5249,8430,6155],{"class":6154},[5249,8432,8433],{"class":6158},"Extract metadata\n",[5249,8435,8436,8439,8441],{"class":5251,"line":5716},[5249,8437,8438],{"class":6150},"        id",[5249,8440,6155],{"class":6154},[5249,8442,8443],{"class":6158},"meta\n",[5249,8445,8446,8448,8450],{"class":5251,"line":5722},[5249,8447,6277],{"class":6150},[5249,8449,6155],{"class":6154},[5249,8451,8452],{"class":6158},"docker\u002Fmetadata-action@v4\n",[5249,8454,8455,8457],{"class":5251,"line":5728},[5249,8456,6312],{"class":6150},[5249,8458,6185],{"class":6154},[5249,8460,8461,8464,8466],{"class":5251,"line":5734},[5249,8462,8463],{"class":6150},"          images",[5249,8465,6155],{"class":6154},[5249,8467,8468],{"class":6158},"myusername\u002Fmy-nestjs-app\n",[5249,8470,8471,8474,8476],{"class":5251,"line":5740},[5249,8472,8473],{"class":6150},"          tags",[5249,8475,6155],{"class":6154},[5249,8477,8478],{"class":7333},"|\n",[5249,8480,8481],{"class":5251,"line":5746},[5249,8482,8483],{"class":6158},"            type=ref,event=branch\n",[5249,8485,8486],{"class":5251,"line":5752},[5249,8487,8488],{"class":6158},"            type=sha,prefix={{branch}}-\n",[5249,8490,8491],{"class":5251,"line":5758},[5249,8492,8493],{"class":6158},"            type=semver,pattern={{version}}\n",[5249,8495,8496],{"class":5251,"line":5763},[5249,8497,5306],{"emptyLinePlaceholder":5305},[5249,8499,8500,8502,8504,8506],{"class":5251,"line":5769},[5249,8501,6265],{"class":6154},[5249,8503,6151],{"class":6150},[5249,8505,6155],{"class":6154},[5249,8507,8508],{"class":6158},"Build and push\n",[5249,8510,8511,8513,8515],{"class":5251,"line":5775},[5249,8512,6277],{"class":6150},[5249,8514,6155],{"class":6154},[5249,8516,8517],{"class":6158},"docker\u002Fbuild-push-action@v4\n",[5249,8519,8520,8522],{"class":5251,"line":5781},[5249,8521,6312],{"class":6150},[5249,8523,6185],{"class":6154},[5249,8525,8526,8529,8531],{"class":5251,"line":5787},[5249,8527,8528],{"class":6150},"          context",[5249,8530,6155],{"class":6154},[5249,8532,8533],{"class":6324},".\n",[5249,8535,8536,8539,8541],{"class":5251,"line":5793},[5249,8537,8538],{"class":6150},"          file",[5249,8540,6155],{"class":6154},[5249,8542,8543],{"class":6158},".\u002FDockerfile\n",[5249,8545,8546,8549,8551],{"class":5251,"line":5799},[5249,8547,8548],{"class":6150},"          push",[5249,8550,6155],{"class":6154},[5249,8552,6600],{"class":5281},[5249,8554,8555,8557,8559],{"class":5251,"line":5805},[5249,8556,8473],{"class":6150},[5249,8558,6155],{"class":6154},[5249,8560,8561],{"class":6158},"${{ steps.meta.outputs.tags }}\n",[5249,8563,8564,8567,8569],{"class":5251,"line":5811},[5249,8565,8566],{"class":6150},"          labels",[5249,8568,6155],{"class":6154},[5249,8570,8571],{"class":6158},"${{ steps.meta.outputs.labels }}\n",[5249,8573,8574,8577,8579],{"class":5251,"line":5817},[5249,8575,8576],{"class":6150},"          cache-from",[5249,8578,6155],{"class":6154},[5249,8580,8581],{"class":6158},"type=registry,ref=myusername\u002Fmy-nestjs-app:latest\n",[5249,8583,8584,8587,8589],{"class":5251,"line":5822},[5249,8585,8586],{"class":6150},"          cache-to",[5249,8588,6155],{"class":6154},[5249,8590,8591],{"class":6158},"type=inline\n",[5249,8593,8594],{"class":5251,"line":5828},[5249,8595,5306],{"emptyLinePlaceholder":5305},[5249,8597,8598,8600,8602,8604],{"class":5251,"line":5834},[5249,8599,6265],{"class":6154},[5249,8601,6151],{"class":6150},[5249,8603,6155],{"class":6154},[5249,8605,8606],{"class":6158},"Image digest\n",[5249,8608,8609,8611,8613],{"class":5251,"line":5839},[5249,8610,6345],{"class":6150},[5249,8612,6155],{"class":6154},[5249,8614,8615],{"class":6158},"echo ${{ steps.build.outputs.digest }}\n",[5849,8617,8619],{"id":8618},"налаштування-docker-registry-secrets","Налаштування Docker Registry Secrets",[5155,8621,8622],{},[5118,8623,8624],{},"1. Docker Hub:",[5155,8626,8627,8628],{},"У GitHub репозиторії: ",[5118,8629,8630],{},"Settings → Secrets and variables → Actions → New repository secret",[5155,8632,8633],{},"Додайте:",[5092,8635,8636,8642],{},[5095,8637,8638,8641],{},[5173,8639,8640],{},"DOCKER_USERNAME"," — ваш Docker Hub username",[5095,8643,8644,8647],{},[5173,8645,8646],{},"DOCKER_PASSWORD"," — Docker Hub access token (не пароль!)",[5155,8649,8650],{},"Створення Docker Hub access token:",[5467,8652,8653,8663,8666],{},[5095,8654,8655,8656],{},"Увійдіть на ",[8657,8658,8662],"a",{"href":8659,"rel":8660},"https:\u002F\u002Fhub.docker.com",[8661],"nofollow","hub.docker.com",[5095,8664,8665],{},"Account Settings → Security → New Access Token",[5095,8667,8668],{},"Скопіюйте token та додайте до GitHub Secrets",[5155,8670,8671],{},[5118,8672,8673],{},"2. GitHub Container Registry (ghcr.io):",[5240,8675,8677],{"className":6141,"code":8676,"language":6143,"meta":5245,"style":5245},"- name: Log in to GitHub Container Registry\n  uses: docker\u002Flogin-action@v2\n  with:\n    registry: ghcr.io\n    username: ${{ github.actor }}\n    password: ${{ secrets.GITHUB_TOKEN }}  # Автоматично доступний\n",[5173,8678,8679,8690,8699,8705,8715,8725],{"__ignoreMap":5245},[5249,8680,8681,8683,8685,8687],{"class":5251,"line":5252},[5249,8682,7941],{"class":6154},[5249,8684,6151],{"class":6150},[5249,8686,6155],{"class":6154},[5249,8688,8689],{"class":6158},"Log in to GitHub Container Registry\n",[5249,8691,8692,8695,8697],{"class":5251,"line":5259},[5249,8693,8694],{"class":6150},"  uses",[5249,8696,6155],{"class":6154},[5249,8698,8392],{"class":6158},[5249,8700,8701,8703],{"class":5251,"line":5273},[5249,8702,7953],{"class":6150},[5249,8704,6185],{"class":6154},[5249,8706,8707,8710,8712],{"class":5251,"line":5288},[5249,8708,8709],{"class":6150},"    registry",[5249,8711,6155],{"class":6154},[5249,8713,8714],{"class":6158},"ghcr.io\n",[5249,8716,8717,8720,8722],{"class":5251,"line":5302},[5249,8718,8719],{"class":6150},"    username",[5249,8721,6155],{"class":6154},[5249,8723,8724],{"class":6158},"${{ github.actor }}\n",[5249,8726,8727,8730,8732,8735],{"class":5251,"line":5309},[5249,8728,8729],{"class":6150},"    password",[5249,8731,6155],{"class":6154},[5249,8733,8734],{"class":6158},"${{ secrets.GITHUB_TOKEN }}",[5249,8736,8737],{"class":5255},"  # Автоматично доступний\n",[5155,8739,8740],{},[5118,8741,8742],{},"3. AWS ECR:",[5240,8744,8746],{"className":6141,"code":8745,"language":6143,"meta":5245,"style":5245},"- name: Configure AWS credentials\n  uses: aws-actions\u002Fconfigure-aws-credentials@v2\n  with:\n    aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}\n    aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}\n    aws-region: us-east-1\n\n- name: Log in to Amazon ECR\n  id: login-ecr\n  uses: aws-actions\u002Famazon-ecr-login@v1\n\n- name: Build and push to ECR\n  env:\n    ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}\n    ECR_REPOSITORY: my-nestjs-app\n    IMAGE_TAG: ${{ github.sha }}\n  run: |\n    docker build -t $ECR_REGISTRY\u002F$ECR_REPOSITORY:$IMAGE_TAG .\n    docker push $ECR_REGISTRY\u002F$ECR_REPOSITORY:$IMAGE_TAG\n",[5173,8747,8748,8759,8768,8774,8784,8794,8804,8808,8819,8829,8838,8842,8853,8860,8870,8880,8890,8899,8904],{"__ignoreMap":5245},[5249,8749,8750,8752,8754,8756],{"class":5251,"line":5252},[5249,8751,7941],{"class":6154},[5249,8753,6151],{"class":6150},[5249,8755,6155],{"class":6154},[5249,8757,8758],{"class":6158},"Configure AWS credentials\n",[5249,8760,8761,8763,8765],{"class":5251,"line":5259},[5249,8762,8694],{"class":6150},[5249,8764,6155],{"class":6154},[5249,8766,8767],{"class":6158},"aws-actions\u002Fconfigure-aws-credentials@v2\n",[5249,8769,8770,8772],{"class":5251,"line":5273},[5249,8771,7953],{"class":6150},[5249,8773,6185],{"class":6154},[5249,8775,8776,8779,8781],{"class":5251,"line":5288},[5249,8777,8778],{"class":6150},"    aws-access-key-id",[5249,8780,6155],{"class":6154},[5249,8782,8783],{"class":6158},"${{ secrets.AWS_ACCESS_KEY_ID }}\n",[5249,8785,8786,8789,8791],{"class":5251,"line":5302},[5249,8787,8788],{"class":6150},"    aws-secret-access-key",[5249,8790,6155],{"class":6154},[5249,8792,8793],{"class":6158},"${{ secrets.AWS_SECRET_ACCESS_KEY }}\n",[5249,8795,8796,8799,8801],{"class":5251,"line":5309},[5249,8797,8798],{"class":6150},"    aws-region",[5249,8800,6155],{"class":6154},[5249,8802,8803],{"class":6158},"us-east-1\n",[5249,8805,8806],{"class":5251,"line":5315},[5249,8807,5306],{"emptyLinePlaceholder":5305},[5249,8809,8810,8812,8814,8816],{"class":5251,"line":5324},[5249,8811,7941],{"class":6154},[5249,8813,6151],{"class":6150},[5249,8815,6155],{"class":6154},[5249,8817,8818],{"class":6158},"Log in to Amazon ECR\n",[5249,8820,8821,8824,8826],{"class":5251,"line":5329},[5249,8822,8823],{"class":6150},"  id",[5249,8825,6155],{"class":6154},[5249,8827,8828],{"class":6158},"login-ecr\n",[5249,8830,8831,8833,8835],{"class":5251,"line":5335},[5249,8832,8694],{"class":6150},[5249,8834,6155],{"class":6154},[5249,8836,8837],{"class":6158},"aws-actions\u002Famazon-ecr-login@v1\n",[5249,8839,8840],{"class":5251,"line":5344},[5249,8841,5306],{"emptyLinePlaceholder":5305},[5249,8843,8844,8846,8848,8850],{"class":5251,"line":5356},[5249,8845,7941],{"class":6154},[5249,8847,6151],{"class":6150},[5249,8849,6155],{"class":6154},[5249,8851,8852],{"class":6158},"Build and push to ECR\n",[5249,8854,8855,8858],{"class":5251,"line":5361},[5249,8856,8857],{"class":6150},"  env",[5249,8859,6185],{"class":6154},[5249,8861,8862,8865,8867],{"class":5251,"line":5367},[5249,8863,8864],{"class":6150},"    ECR_REGISTRY",[5249,8866,6155],{"class":6154},[5249,8868,8869],{"class":6158},"${{ steps.login-ecr.outputs.registry }}\n",[5249,8871,8872,8875,8877],{"class":5251,"line":5376},[5249,8873,8874],{"class":6150},"    ECR_REPOSITORY",[5249,8876,6155],{"class":6154},[5249,8878,8879],{"class":6158},"my-nestjs-app\n",[5249,8881,8882,8885,8887],{"class":5251,"line":5381},[5249,8883,8884],{"class":6150},"    IMAGE_TAG",[5249,8886,6155],{"class":6154},[5249,8888,8889],{"class":6158},"${{ github.sha }}\n",[5249,8891,8892,8895,8897],{"class":5251,"line":5387},[5249,8893,8894],{"class":6150},"  run",[5249,8896,6155],{"class":6154},[5249,8898,8478],{"class":7333},[5249,8900,8901],{"class":5251,"line":5398},[5249,8902,8903],{"class":6158},"    docker build -t $ECR_REGISTRY\u002F$ECR_REPOSITORY:$IMAGE_TAG .\n",[5249,8905,8906],{"class":5251,"line":5403},[5249,8907,8908],{"class":6158},"    docker push $ECR_REGISTRY\u002F$ECR_REPOSITORY:$IMAGE_TAG\n",[5849,8910,8912],{"id":8911},"tagging-стратегії","Tagging стратегії",[5155,8914,8915],{},[5118,8916,8917],{},"Semantic Versioning:",[5240,8919,8921],{"className":6141,"code":8920,"language":6143,"meta":5245,"style":5245},"tags: |\n  type=semver,pattern={{version}}      # 1.2.3\n  type=semver,pattern={{major}}.{{minor}}  # 1.2\n  type=semver,pattern={{major}}        # 1\n",[5173,8922,8923,8932,8937,8942],{"__ignoreMap":5245},[5249,8924,8925,8928,8930],{"class":5251,"line":5252},[5249,8926,8927],{"class":6150},"tags",[5249,8929,6155],{"class":6154},[5249,8931,8478],{"class":7333},[5249,8933,8934],{"class":5251,"line":5259},[5249,8935,8936],{"class":6158},"  type=semver,pattern={{version}}      # 1.2.3\n",[5249,8938,8939],{"class":5251,"line":5273},[5249,8940,8941],{"class":6158},"  type=semver,pattern={{major}}.{{minor}}  # 1.2\n",[5249,8943,8944],{"class":5251,"line":5288},[5249,8945,8946],{"class":6158},"  type=semver,pattern={{major}}        # 1\n",[5155,8948,8949],{},[5118,8950,8951],{},"Git SHA:",[5240,8953,8955],{"className":6141,"code":8954,"language":6143,"meta":5245,"style":5245},"tags: |\n  type=sha,prefix={{branch}}-  # main-abc123d\n",[5173,8956,8957,8965],{"__ignoreMap":5245},[5249,8958,8959,8961,8963],{"class":5251,"line":5252},[5249,8960,8927],{"class":6150},[5249,8962,6155],{"class":6154},[5249,8964,8478],{"class":7333},[5249,8966,8967],{"class":5251,"line":5259},[5249,8968,8969],{"class":6158},"  type=sha,prefix={{branch}}-  # main-abc123d\n",[5155,8971,8972],{},[5118,8973,8974],{},"Branch name:",[5240,8976,8978],{"className":6141,"code":8977,"language":6143,"meta":5245,"style":5245},"tags: |\n  type=ref,event=branch  # main, develop\n",[5173,8979,8980,8988],{"__ignoreMap":5245},[5249,8981,8982,8984,8986],{"class":5251,"line":5252},[5249,8983,8927],{"class":6150},[5249,8985,6155],{"class":6154},[5249,8987,8478],{"class":7333},[5249,8989,8990],{"class":5251,"line":5259},[5249,8991,8992],{"class":6158},"  type=ref,event=branch  # main, develop\n",[5155,8994,8995],{},[5118,8996,8997],{},"Latest tag для main:",[5240,8999,9001],{"className":6141,"code":9000,"language":6143,"meta":5245,"style":5245},"tags: |\n  type=raw,value=latest,enable={{is_default_branch}}\n",[5173,9002,9003,9011],{"__ignoreMap":5245},[5249,9004,9005,9007,9009],{"class":5251,"line":5252},[5249,9006,8927],{"class":6150},[5249,9008,6155],{"class":6154},[5249,9010,8478],{"class":7333},[5249,9012,9013],{"class":5251,"line":5259},[5249,9014,9015],{"class":6158},"  type=raw,value=latest,enable={{is_default_branch}}\n",[5155,9017,9018],{},[5118,9019,9020],{},"Комбінована стратегія:",[5240,9022,9024],{"className":6141,"code":9023,"language":6143,"meta":5245,"style":5245},"tags: |\n  type=ref,event=branch\n  type=sha,prefix={{branch}}-\n  type=raw,value=latest,enable={{is_default_branch}}\n",[5173,9025,9026,9034,9039,9044],{"__ignoreMap":5245},[5249,9027,9028,9030,9032],{"class":5251,"line":5252},[5249,9029,8927],{"class":6150},[5249,9031,6155],{"class":6154},[5249,9033,8478],{"class":7333},[5249,9035,9036],{"class":5251,"line":5259},[5249,9037,9038],{"class":6158},"  type=ref,event=branch\n",[5249,9040,9041],{"class":5251,"line":5273},[5249,9042,9043],{"class":6158},"  type=sha,prefix={{branch}}-\n",[5249,9045,9046],{"class":5251,"line":5288},[5249,9047,9015],{"class":6158},[5155,9049,9050,9051,9054],{},"Результат для commit ",[5173,9052,9053],{},"abc123d"," у main:",[5092,9056,9057,9062,9067],{},[5095,9058,9059],{},[5173,9060,9061],{},"myusername\u002Fmy-app:main",[5095,9063,9064],{},[5173,9065,9066],{},"myusername\u002Fmy-app:main-abc123d",[5095,9068,9069],{},[5173,9070,9071],{},"myusername\u002Fmy-app:latest",[5147,9073],{},[5150,9075,9077],{"id":9076},"deployment-на-production","Deployment на Production",[5155,9079,9080],{},"Після побудови Docker image автоматично деплоїмо на production сервер:",[5849,9082,9084],{"id":9083},"deployment-через-ssh","Deployment через SSH",[5240,9086,9088],{"className":6141,"code":9087,"language":6143,"meta":5245,"style":5245},"deploy:\n  name: Deploy to Production\n  runs-on: ubuntu-latest\n  needs: build\n  if: github.ref == 'refs\u002Fheads\u002Fmain'  # Лише для main гілки\n\n  steps:\n    - name: Deploy to server via SSH\n      uses: appleboy\u002Fssh-action@v0.1.10\n      with:\n        host: ${{ secrets.PROD_SERVER_HOST }}\n        username: ${{ secrets.PROD_SERVER_USER }}\n        key: ${{ secrets.PROD_SERVER_SSH_KEY }}\n        script: |\n          cd \u002Fapp\n          docker-compose pull\n          docker-compose up -d\n          docker system prune -af\n\n    - name: Wait for health check\n      run: |\n        sleep 10\n        curl -f http:\u002F\u002F${{ secrets.PROD_SERVER_HOST }}\u002Fhealth || exit 1\n\n    - name: Notify Slack on success\n      if: success()\n      uses: slackapi\u002Fslack-github-action@v1.24.0\n      with:\n        webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }}\n        payload: |\n          {\n            \"text\": \"✅ Deployment to production succeeded!\",\n            \"attachments\": [{\n              \"color\": \"good\",\n              \"fields\": [\n                { \"title\": \"Commit\", \"value\": \"${{ github.sha }}\", \"short\": true },\n                { \"title\": \"Author\", \"value\": \"${{ github.actor }}\", \"short\": true }\n              ]\n            }]\n          }\n\n    - name: Notify Slack on failure\n      if: failure()\n      uses: slackapi\u002Fslack-github-action@v1.24.0\n      with:\n        webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }}\n        payload: |\n          {\n            \"text\": \"❌ Deployment to production failed!\",\n            \"attachments\": [{\n              \"color\": \"danger\",\n              \"fields\": [\n                { \"title\": \"Commit\", \"value\": \"${{ github.sha }}\", \"short\": true },\n                { \"title\": \"Author\", \"value\": \"${{ github.actor }}\", \"short\": true },\n                { \"title\": \"Logs\", \"value\": \"${{ github.server_url }}\u002F${{ github.repository }}\u002Factions\u002Fruns\u002F${{ github.run_id }}\", \"short\": false }\n              ]\n            }]\n          }\n",[5173,9089,9090,9097,9107,9116,9125,9137,9141,9148,9159,9169,9176,9186,9196,9206,9215,9220,9225,9230,9235,9239,9250,9259,9264,9269,9273,9284,9294,9303,9309,9319,9328,9333,9338,9343,9348,9353,9358,9363,9368,9373,9378,9382,9393,9402,9410,9416,9424,9432,9436,9441,9445,9450,9454,9458,9463,9468,9472,9476],{"__ignoreMap":5245},[5249,9091,9092,9095],{"class":5251,"line":5252},[5249,9093,9094],{"class":6150},"deploy",[5249,9096,6185],{"class":6154},[5249,9098,9099,9102,9104],{"class":5251,"line":5259},[5249,9100,9101],{"class":6150},"  name",[5249,9103,6155],{"class":6154},[5249,9105,9106],{"class":6158},"Deploy to Production\n",[5249,9108,9109,9112,9114],{"class":5251,"line":5273},[5249,9110,9111],{"class":6150},"  runs-on",[5249,9113,6155],{"class":6154},[5249,9115,6792],{"class":6158},[5249,9117,9118,9120,9122],{"class":5251,"line":5288},[5249,9119,7763],{"class":6150},[5249,9121,6155],{"class":6154},[5249,9123,9124],{"class":6158},"build\n",[5249,9126,9127,9129,9131,9134],{"class":5251,"line":5302},[5249,9128,7997],{"class":6150},[5249,9130,6155],{"class":6154},[5249,9132,9133],{"class":6158},"github.ref == 'refs\u002Fheads\u002Fmain'",[5249,9135,9136],{"class":5255},"  # Лише для main гілки\n",[5249,9138,9139],{"class":5251,"line":5309},[5249,9140,5306],{"emptyLinePlaceholder":5305},[5249,9142,9143,9146],{"class":5251,"line":5315},[5249,9144,9145],{"class":6150},"  steps",[5249,9147,6185],{"class":6154},[5249,9149,9150,9152,9154,9156],{"class":5251,"line":5324},[5249,9151,6529],{"class":6154},[5249,9153,6151],{"class":6150},[5249,9155,6155],{"class":6154},[5249,9157,9158],{"class":6158},"Deploy to server via SSH\n",[5249,9160,9161,9164,9166],{"class":5251,"line":5329},[5249,9162,9163],{"class":6150},"      uses",[5249,9165,6155],{"class":6154},[5249,9167,9168],{"class":6158},"appleboy\u002Fssh-action@v0.1.10\n",[5249,9170,9171,9174],{"class":5251,"line":5335},[5249,9172,9173],{"class":6150},"      with",[5249,9175,6185],{"class":6154},[5249,9177,9178,9181,9183],{"class":5251,"line":5344},[5249,9179,9180],{"class":6150},"        host",[5249,9182,6155],{"class":6154},[5249,9184,9185],{"class":6158},"${{ secrets.PROD_SERVER_HOST }}\n",[5249,9187,9188,9191,9193],{"class":5251,"line":5356},[5249,9189,9190],{"class":6150},"        username",[5249,9192,6155],{"class":6154},[5249,9194,9195],{"class":6158},"${{ secrets.PROD_SERVER_USER }}\n",[5249,9197,9198,9201,9203],{"class":5251,"line":5361},[5249,9199,9200],{"class":6150},"        key",[5249,9202,6155],{"class":6154},[5249,9204,9205],{"class":6158},"${{ secrets.PROD_SERVER_SSH_KEY }}\n",[5249,9207,9208,9211,9213],{"class":5251,"line":5367},[5249,9209,9210],{"class":6150},"        script",[5249,9212,6155],{"class":6154},[5249,9214,8478],{"class":7333},[5249,9216,9217],{"class":5251,"line":5376},[5249,9218,9219],{"class":6158},"          cd \u002Fapp\n",[5249,9221,9222],{"class":5251,"line":5381},[5249,9223,9224],{"class":6158},"          docker-compose pull\n",[5249,9226,9227],{"class":5251,"line":5387},[5249,9228,9229],{"class":6158},"          docker-compose up -d\n",[5249,9231,9232],{"class":5251,"line":5398},[5249,9233,9234],{"class":6158},"          docker system prune -af\n",[5249,9236,9237],{"class":5251,"line":5403},[5249,9238,5306],{"emptyLinePlaceholder":5305},[5249,9240,9241,9243,9245,9247],{"class":5251,"line":5409},[5249,9242,6529],{"class":6154},[5249,9244,6151],{"class":6150},[5249,9246,6155],{"class":6154},[5249,9248,9249],{"class":6158},"Wait for health check\n",[5249,9251,9252,9255,9257],{"class":5251,"line":5419},[5249,9253,9254],{"class":6150},"      run",[5249,9256,6155],{"class":6154},[5249,9258,8478],{"class":7333},[5249,9260,9261],{"class":5251,"line":5424},[5249,9262,9263],{"class":6158},"        sleep 10\n",[5249,9265,9266],{"class":5251,"line":5430},[5249,9267,9268],{"class":6158},"        curl -f http:\u002F\u002F${{ secrets.PROD_SERVER_HOST }}\u002Fhealth || exit 1\n",[5249,9270,9271],{"class":5251,"line":5442},[5249,9272,5306],{"emptyLinePlaceholder":5305},[5249,9274,9275,9277,9279,9281],{"class":5251,"line":5447},[5249,9276,6529],{"class":6154},[5249,9278,6151],{"class":6150},[5249,9280,6155],{"class":6154},[5249,9282,9283],{"class":6158},"Notify Slack on success\n",[5249,9285,9286,9289,9291],{"class":5251,"line":5453},[5249,9287,9288],{"class":6150},"      if",[5249,9290,6155],{"class":6154},[5249,9292,9293],{"class":6158},"success()\n",[5249,9295,9296,9298,9300],{"class":5251,"line":5698},[5249,9297,9163],{"class":6150},[5249,9299,6155],{"class":6154},[5249,9301,9302],{"class":6158},"slackapi\u002Fslack-github-action@v1.24.0\n",[5249,9304,9305,9307],{"class":5251,"line":5704},[5249,9306,9173],{"class":6150},[5249,9308,6185],{"class":6154},[5249,9310,9311,9314,9316],{"class":5251,"line":5710},[5249,9312,9313],{"class":6150},"        webhook-url",[5249,9315,6155],{"class":6154},[5249,9317,9318],{"class":6158},"${{ secrets.SLACK_WEBHOOK_URL }}\n",[5249,9320,9321,9324,9326],{"class":5251,"line":5716},[5249,9322,9323],{"class":6150},"        payload",[5249,9325,6155],{"class":6154},[5249,9327,8478],{"class":7333},[5249,9329,9330],{"class":5251,"line":5722},[5249,9331,9332],{"class":6158},"          {\n",[5249,9334,9335],{"class":5251,"line":5728},[5249,9336,9337],{"class":6158},"            \"text\": \"✅ Deployment to production succeeded!\",\n",[5249,9339,9340],{"class":5251,"line":5734},[5249,9341,9342],{"class":6158},"            \"attachments\": [{\n",[5249,9344,9345],{"class":5251,"line":5740},[5249,9346,9347],{"class":6158},"              \"color\": \"good\",\n",[5249,9349,9350],{"class":5251,"line":5746},[5249,9351,9352],{"class":6158},"              \"fields\": [\n",[5249,9354,9355],{"class":5251,"line":5752},[5249,9356,9357],{"class":6158},"                { \"title\": \"Commit\", \"value\": \"${{ github.sha }}\", \"short\": true },\n",[5249,9359,9360],{"class":5251,"line":5758},[5249,9361,9362],{"class":6158},"                { \"title\": \"Author\", \"value\": \"${{ github.actor }}\", \"short\": true }\n",[5249,9364,9365],{"class":5251,"line":5763},[5249,9366,9367],{"class":6158},"              ]\n",[5249,9369,9370],{"class":5251,"line":5769},[5249,9371,9372],{"class":6158},"            }]\n",[5249,9374,9375],{"class":5251,"line":5775},[5249,9376,9377],{"class":6158},"          }\n",[5249,9379,9380],{"class":5251,"line":5781},[5249,9381,5306],{"emptyLinePlaceholder":5305},[5249,9383,9384,9386,9388,9390],{"class":5251,"line":5787},[5249,9385,6529],{"class":6154},[5249,9387,6151],{"class":6150},[5249,9389,6155],{"class":6154},[5249,9391,9392],{"class":6158},"Notify Slack on failure\n",[5249,9394,9395,9397,9399],{"class":5251,"line":5793},[5249,9396,9288],{"class":6150},[5249,9398,6155],{"class":6154},[5249,9400,9401],{"class":6158},"failure()\n",[5249,9403,9404,9406,9408],{"class":5251,"line":5799},[5249,9405,9163],{"class":6150},[5249,9407,6155],{"class":6154},[5249,9409,9302],{"class":6158},[5249,9411,9412,9414],{"class":5251,"line":5805},[5249,9413,9173],{"class":6150},[5249,9415,6185],{"class":6154},[5249,9417,9418,9420,9422],{"class":5251,"line":5811},[5249,9419,9313],{"class":6150},[5249,9421,6155],{"class":6154},[5249,9423,9318],{"class":6158},[5249,9425,9426,9428,9430],{"class":5251,"line":5817},[5249,9427,9323],{"class":6150},[5249,9429,6155],{"class":6154},[5249,9431,8478],{"class":7333},[5249,9433,9434],{"class":5251,"line":5822},[5249,9435,9332],{"class":6158},[5249,9437,9438],{"class":5251,"line":5828},[5249,9439,9440],{"class":6158},"            \"text\": \"❌ Deployment to production failed!\",\n",[5249,9442,9443],{"class":5251,"line":5834},[5249,9444,9342],{"class":6158},[5249,9446,9447],{"class":5251,"line":5839},[5249,9448,9449],{"class":6158},"              \"color\": \"danger\",\n",[5249,9451,9452],{"class":5251,"line":7092},[5249,9453,9352],{"class":6158},[5249,9455,9456],{"class":5251,"line":7097},[5249,9457,9357],{"class":6158},[5249,9459,9460],{"class":5251,"line":7108},[5249,9461,9462],{"class":6158},"                { \"title\": \"Author\", \"value\": \"${{ github.actor }}\", \"short\": true },\n",[5249,9464,9465],{"class":5251,"line":7117},[5249,9466,9467],{"class":6158},"                { \"title\": \"Logs\", \"value\": \"${{ github.server_url }}\u002F${{ github.repository }}\u002Factions\u002Fruns\u002F${{ github.run_id }}\", \"short\": false }\n",[5249,9469,9470],{"class":5251,"line":7122},[5249,9471,9367],{"class":6158},[5249,9473,9474],{"class":5251,"line":7134},[5249,9475,9372],{"class":6158},[5249,9477,9478],{"class":5251,"line":7144},[5249,9479,9377],{"class":6158},[5849,9481,9483],{"id":9482},"налаштування-ssh-ключа","Налаштування SSH ключа",[5155,9485,9486],{},[5118,9487,9488],{},"Генерація SSH ключа:",[5240,9490,9492],{"className":5242,"code":9491,"language":5244,"meta":5245,"style":5245},"ssh-keygen -t ed25519 -C \"github-actions\" -f github-actions-key\n",[5173,9493,9494],{"__ignoreMap":5245},[5249,9495,9496,9499,9502,9505,9508,9511,9514],{"class":5251,"line":5252},[5249,9497,9498],{"class":5262},"ssh-keygen",[5249,9500,9501],{"class":5281}," -t",[5249,9503,9504],{"class":5266}," ed25519",[5249,9506,9507],{"class":5281}," -C",[5249,9509,9510],{"class":5266}," \"github-actions\"",[5249,9512,9513],{"class":5281}," -f",[5249,9515,9516],{"class":5266}," github-actions-key\n",[5155,9518,9519],{},[5118,9520,9521],{},"Додавання public key на сервер:",[5240,9523,9525],{"className":5242,"code":9524,"language":5244,"meta":5245,"style":5245},"# На production сервері\ncat github-actions-key.pub >> ~\u002F.ssh\u002Fauthorized_keys\n",[5173,9526,9527,9532],{"__ignoreMap":5245},[5249,9528,9529],{"class":5251,"line":5252},[5249,9530,9531],{"class":5255},"# На production сервері\n",[5249,9533,9534,9537,9540,9543],{"class":5251,"line":5259},[5249,9535,9536],{"class":5262},"cat",[5249,9538,9539],{"class":5266}," github-actions-key.pub",[5249,9541,9542],{"class":6154}," >> ",[5249,9544,9545],{"class":5266},"~\u002F.ssh\u002Fauthorized_keys\n",[5155,9547,9548],{},[5118,9549,9550],{},"Додавання private key до GitHub Secrets:",[5467,9552,9553,9560,9563,9569],{},[5095,9554,9555,9556,9559],{},"Скопіюйте вміст ",[5173,9557,9558],{},"github-actions-key"," (private key)",[5095,9561,9562],{},"GitHub repo → Settings → Secrets → New secret",[5095,9564,9565,9566],{},"Name: ",[5173,9567,9568],{},"PROD_SERVER_SSH_KEY",[5095,9570,9571],{},"Value: вставте private key",[5155,9573,9574],{},[5118,9575,9576],{},"Додайте інші secrets:",[5092,9578,9579,9585],{},[5095,9580,9581,9584],{},[5173,9582,9583],{},"PROD_SERVER_HOST"," — IP або domain production сервера",[5095,9586,9587,9590,9591,6488,9594,9596],{},[5173,9588,9589],{},"PROD_SERVER_USER"," — SSH username (наприклад, ",[5173,9592,9593],{},"ubuntu",[5173,9595,9094],{},")",[9598,9599,9600,9603],"warning",{},[5118,9601,9602],{},"Security:"," Ніколи не комітьте SSH ключі у Git! Використовуйте лише GitHub Secrets для зберігання credentials.",[5849,9605,9607],{"id":9606},"deployment-на-aws-ecs","Deployment на AWS ECS",[5240,9609,9611],{"className":6141,"code":9610,"language":6143,"meta":5245,"style":5245},"deploy-ecs:\n  name: Deploy to AWS ECS\n  runs-on: ubuntu-latest\n  needs: build\n\n  steps:\n    - name: Configure AWS credentials\n      uses: aws-actions\u002Fconfigure-aws-credentials@v2\n      with:\n        aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}\n        aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}\n        aws-region: us-east-1\n\n    - name: Download task definition\n      run: |\n        aws ecs describe-task-definition \\\n          --task-definition my-app-task \\\n          --query taskDefinition > task-definition.json\n\n    - name: Fill in new image ID in task definition\n      id: task-def\n      uses: aws-actions\u002Famazon-ecs-render-task-definition@v1\n      with:\n        task-definition: task-definition.json\n        container-name: my-app\n        image: ${{ secrets.ECR_REGISTRY }}\u002Fmy-app:${{ github.sha }}\n\n    - name: Deploy to ECS service\n      uses: aws-actions\u002Famazon-ecs-deploy-task-definition@v1\n      with:\n        task-definition: ${{ steps.task-def.outputs.task-definition }}\n        service: my-app-service\n        cluster: production-cluster\n        wait-for-service-stability: true\n",[5173,9612,9613,9620,9629,9637,9645,9649,9655,9665,9673,9679,9688,9697,9706,9710,9721,9729,9734,9739,9744,9748,9759,9769,9778,9784,9794,9804,9813,9817,9828,9837,9843,9852,9862,9872],{"__ignoreMap":5245},[5249,9614,9615,9618],{"class":5251,"line":5252},[5249,9616,9617],{"class":6150},"deploy-ecs",[5249,9619,6185],{"class":6154},[5249,9621,9622,9624,9626],{"class":5251,"line":5259},[5249,9623,9101],{"class":6150},[5249,9625,6155],{"class":6154},[5249,9627,9628],{"class":6158},"Deploy to AWS ECS\n",[5249,9630,9631,9633,9635],{"class":5251,"line":5273},[5249,9632,9111],{"class":6150},[5249,9634,6155],{"class":6154},[5249,9636,6792],{"class":6158},[5249,9638,9639,9641,9643],{"class":5251,"line":5288},[5249,9640,7763],{"class":6150},[5249,9642,6155],{"class":6154},[5249,9644,9124],{"class":6158},[5249,9646,9647],{"class":5251,"line":5302},[5249,9648,5306],{"emptyLinePlaceholder":5305},[5249,9650,9651,9653],{"class":5251,"line":5309},[5249,9652,9145],{"class":6150},[5249,9654,6185],{"class":6154},[5249,9656,9657,9659,9661,9663],{"class":5251,"line":5315},[5249,9658,6529],{"class":6154},[5249,9660,6151],{"class":6150},[5249,9662,6155],{"class":6154},[5249,9664,8758],{"class":6158},[5249,9666,9667,9669,9671],{"class":5251,"line":5324},[5249,9668,9163],{"class":6150},[5249,9670,6155],{"class":6154},[5249,9672,8767],{"class":6158},[5249,9674,9675,9677],{"class":5251,"line":5329},[5249,9676,9173],{"class":6150},[5249,9678,6185],{"class":6154},[5249,9680,9681,9684,9686],{"class":5251,"line":5335},[5249,9682,9683],{"class":6150},"        aws-access-key-id",[5249,9685,6155],{"class":6154},[5249,9687,8783],{"class":6158},[5249,9689,9690,9693,9695],{"class":5251,"line":5344},[5249,9691,9692],{"class":6150},"        aws-secret-access-key",[5249,9694,6155],{"class":6154},[5249,9696,8793],{"class":6158},[5249,9698,9699,9702,9704],{"class":5251,"line":5356},[5249,9700,9701],{"class":6150},"        aws-region",[5249,9703,6155],{"class":6154},[5249,9705,8803],{"class":6158},[5249,9707,9708],{"class":5251,"line":5361},[5249,9709,5306],{"emptyLinePlaceholder":5305},[5249,9711,9712,9714,9716,9718],{"class":5251,"line":5367},[5249,9713,6529],{"class":6154},[5249,9715,6151],{"class":6150},[5249,9717,6155],{"class":6154},[5249,9719,9720],{"class":6158},"Download task definition\n",[5249,9722,9723,9725,9727],{"class":5251,"line":5376},[5249,9724,9254],{"class":6150},[5249,9726,6155],{"class":6154},[5249,9728,8478],{"class":7333},[5249,9730,9731],{"class":5251,"line":5381},[5249,9732,9733],{"class":6158},"        aws ecs describe-task-definition \\\n",[5249,9735,9736],{"class":5251,"line":5387},[5249,9737,9738],{"class":6158},"          --task-definition my-app-task \\\n",[5249,9740,9741],{"class":5251,"line":5398},[5249,9742,9743],{"class":6158},"          --query taskDefinition > task-definition.json\n",[5249,9745,9746],{"class":5251,"line":5403},[5249,9747,5306],{"emptyLinePlaceholder":5305},[5249,9749,9750,9752,9754,9756],{"class":5251,"line":5409},[5249,9751,6529],{"class":6154},[5249,9753,6151],{"class":6150},[5249,9755,6155],{"class":6154},[5249,9757,9758],{"class":6158},"Fill in new image ID in task definition\n",[5249,9760,9761,9764,9766],{"class":5251,"line":5419},[5249,9762,9763],{"class":6150},"      id",[5249,9765,6155],{"class":6154},[5249,9767,9768],{"class":6158},"task-def\n",[5249,9770,9771,9773,9775],{"class":5251,"line":5424},[5249,9772,9163],{"class":6150},[5249,9774,6155],{"class":6154},[5249,9776,9777],{"class":6158},"aws-actions\u002Famazon-ecs-render-task-definition@v1\n",[5249,9779,9780,9782],{"class":5251,"line":5430},[5249,9781,9173],{"class":6150},[5249,9783,6185],{"class":6154},[5249,9785,9786,9789,9791],{"class":5251,"line":5442},[5249,9787,9788],{"class":6150},"        task-definition",[5249,9790,6155],{"class":6154},[5249,9792,9793],{"class":6158},"task-definition.json\n",[5249,9795,9796,9799,9801],{"class":5251,"line":5447},[5249,9797,9798],{"class":6150},"        container-name",[5249,9800,6155],{"class":6154},[5249,9802,9803],{"class":6158},"my-app\n",[5249,9805,9806,9808,9810],{"class":5251,"line":5453},[5249,9807,7277],{"class":6150},[5249,9809,6155],{"class":6154},[5249,9811,9812],{"class":6158},"${{ secrets.ECR_REGISTRY }}\u002Fmy-app:${{ github.sha }}\n",[5249,9814,9815],{"class":5251,"line":5698},[5249,9816,5306],{"emptyLinePlaceholder":5305},[5249,9818,9819,9821,9823,9825],{"class":5251,"line":5704},[5249,9820,6529],{"class":6154},[5249,9822,6151],{"class":6150},[5249,9824,6155],{"class":6154},[5249,9826,9827],{"class":6158},"Deploy to ECS service\n",[5249,9829,9830,9832,9834],{"class":5251,"line":5710},[5249,9831,9163],{"class":6150},[5249,9833,6155],{"class":6154},[5249,9835,9836],{"class":6158},"aws-actions\u002Famazon-ecs-deploy-task-definition@v1\n",[5249,9838,9839,9841],{"class":5251,"line":5716},[5249,9840,9173],{"class":6150},[5249,9842,6185],{"class":6154},[5249,9844,9845,9847,9849],{"class":5251,"line":5722},[5249,9846,9788],{"class":6150},[5249,9848,6155],{"class":6154},[5249,9850,9851],{"class":6158},"${{ steps.task-def.outputs.task-definition }}\n",[5249,9853,9854,9857,9859],{"class":5251,"line":5728},[5249,9855,9856],{"class":6150},"        service",[5249,9858,6155],{"class":6154},[5249,9860,9861],{"class":6158},"my-app-service\n",[5249,9863,9864,9867,9869],{"class":5251,"line":5734},[5249,9865,9866],{"class":6150},"        cluster",[5249,9868,6155],{"class":6154},[5249,9870,9871],{"class":6158},"production-cluster\n",[5249,9873,9874,9877,9879],{"class":5251,"line":5740},[5249,9875,9876],{"class":6150},"        wait-for-service-stability",[5249,9878,6155],{"class":6154},[5249,9880,6600],{"class":5281},[5147,9882],{},[5150,9884,9886],{"id":9885},"deployment-strategies","Deployment Strategies",[5155,9888,9889],{},"Розглянемо різні стратегії deployment для мінімізації downtime та ризику.",[5849,9891,9893],{"id":9892},"_1-blue-green-deployment","1. Blue-Green Deployment",[5155,9895,9896,9899],{},[5118,9897,9898],{},"Концепція:"," Два identичні production environments (Blue — поточний, Green — новий). Traffic перемикається з Blue на Green після успішного deploy.",[5240,9901,9903],{"className":6141,"code":9902,"language":6143,"meta":5245,"style":5245},"# .github\u002Fworkflows\u002Fblue-green-deploy.yml\nname: Blue-Green Deployment\n\non:\n  push:\n    branches: [main]\n\njobs:\n  deploy:\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Deploy to Green environment\n        uses: appleboy\u002Fssh-action@v0.1.10\n        with:\n          host: ${{ secrets.GREEN_SERVER_HOST }}\n          username: ${{ secrets.SERVER_USER }}\n          key: ${{ secrets.SSH_KEY }}\n          script: |\n            cd \u002Fapp\n            docker-compose pull\n            docker-compose up -d\n\n      - name: Health check Green environment\n        run: |\n          for i in {1..30}; do\n            if curl -f http:\u002F\u002F${{ secrets.GREEN_SERVER_HOST }}\u002Fhealth; then\n              echo \"Green environment healthy\"\n              exit 0\n            fi\n            echo \"Waiting for Green environment... ($i\u002F30)\"\n            sleep 10\n          done\n          echo \"Green environment health check failed\"\n          exit 1\n\n      - name: Switch traffic to Green (update Load Balancer)\n        run: |\n          # AWS ALB приклад\n          aws elbv2 modify-target-group \\\n            --target-group-arn ${{ secrets.TARGET_GROUP_ARN }} \\\n            --health-check-path \u002Fhealth\n          \n          # Перемикання traffic з Blue на Green\n          aws elbv2 register-targets \\\n            --target-group-arn ${{ secrets.TARGET_GROUP_ARN }} \\\n            --targets Id=${{ secrets.GREEN_INSTANCE_ID }}\n          \n          aws elbv2 deregister-targets \\\n            --target-group-arn ${{ secrets.TARGET_GROUP_ARN }} \\\n            --targets Id=${{ secrets.BLUE_INSTANCE_ID }}\n\n      - name: Monitor Green environment\n        run: |\n          sleep 60\n          # Перевірка error rate, response time\n          if [ $(check_error_rate) -gt 1 ]; then\n            echo \"High error rate detected, rolling back\"\n            exit 1\n          fi\n\n      - name: Rollback to Blue on failure\n        if: failure()\n        run: |\n          aws elbv2 register-targets \\\n            --target-group-arn ${{ secrets.TARGET_GROUP_ARN }} \\\n            --targets Id=${{ secrets.BLUE_INSTANCE_ID }}\n          \n          aws elbv2 deregister-targets \\\n            --target-group-arn ${{ secrets.TARGET_GROUP_ARN }} \\\n            --targets Id=${{ secrets.GREEN_INSTANCE_ID }}\n",[5173,9904,9905,9910,9919,9923,9929,9935,9945,9949,9955,9962,9970,9974,9980,9991,9999,10005,10015,10024,10034,10043,10048,10053,10058,10062,10073,10081,10086,10091,10096,10101,10106,10111,10116,10121,10126,10131,10135,10146,10154,10159,10164,10169,10174,10179,10184,10189,10193,10198,10202,10207,10211,10216,10220,10231,10239,10244,10249,10254,10259,10264,10269,10273,10284,10292,10300,10304,10308,10312,10316,10320,10324],{"__ignoreMap":5245},[5249,9906,9907],{"class":5251,"line":5252},[5249,9908,9909],{"class":5255},"# .github\u002Fworkflows\u002Fblue-green-deploy.yml\n",[5249,9911,9912,9914,9916],{"class":5251,"line":5259},[5249,9913,6151],{"class":6150},[5249,9915,6155],{"class":6154},[5249,9917,9918],{"class":6158},"Blue-Green Deployment\n",[5249,9920,9921],{"class":5251,"line":5273},[5249,9922,5306],{"emptyLinePlaceholder":5305},[5249,9924,9925,9927],{"class":5251,"line":5288},[5249,9926,6171],{"class":5281},[5249,9928,6185],{"class":6154},[5249,9930,9931,9933],{"class":5251,"line":5302},[5249,9932,6182],{"class":6150},[5249,9934,6185],{"class":6154},[5249,9936,9937,9939,9941,9943],{"class":5251,"line":5309},[5249,9938,6190],{"class":6150},[5249,9940,6193],{"class":6154},[5249,9942,6196],{"class":6158},[5249,9944,6199],{"class":6154},[5249,9946,9947],{"class":5251,"line":5315},[5249,9948,5306],{"emptyLinePlaceholder":5305},[5249,9950,9951,9953],{"class":5251,"line":5324},[5249,9952,6225],{"class":6150},[5249,9954,6185],{"class":6154},[5249,9956,9957,9960],{"class":5251,"line":5329},[5249,9958,9959],{"class":6150},"  deploy",[5249,9961,6185],{"class":6154},[5249,9963,9964,9966,9968],{"class":5251,"line":5335},[5249,9965,6242],{"class":6150},[5249,9967,6155],{"class":6154},[5249,9969,6792],{"class":6158},[5249,9971,9972],{"class":5251,"line":5344},[5249,9973,5306],{"emptyLinePlaceholder":5305},[5249,9975,9976,9978],{"class":5251,"line":5356},[5249,9977,6255],{"class":6150},[5249,9979,6185],{"class":6154},[5249,9981,9982,9984,9986,9988],{"class":5251,"line":5361},[5249,9983,6265],{"class":6154},[5249,9985,6151],{"class":6150},[5249,9987,6155],{"class":6154},[5249,9989,9990],{"class":6158},"Deploy to Green environment\n",[5249,9992,9993,9995,9997],{"class":5251,"line":5367},[5249,9994,6277],{"class":6150},[5249,9996,6155],{"class":6154},[5249,9998,9168],{"class":6158},[5249,10000,10001,10003],{"class":5251,"line":5376},[5249,10002,6312],{"class":6150},[5249,10004,6185],{"class":6154},[5249,10006,10007,10010,10012],{"class":5251,"line":5381},[5249,10008,10009],{"class":6150},"          host",[5249,10011,6155],{"class":6154},[5249,10013,10014],{"class":6158},"${{ secrets.GREEN_SERVER_HOST }}\n",[5249,10016,10017,10019,10021],{"class":5251,"line":5387},[5249,10018,8403],{"class":6150},[5249,10020,6155],{"class":6154},[5249,10022,10023],{"class":6158},"${{ secrets.SERVER_USER }}\n",[5249,10025,10026,10029,10031],{"class":5251,"line":5398},[5249,10027,10028],{"class":6150},"          key",[5249,10030,6155],{"class":6154},[5249,10032,10033],{"class":6158},"${{ secrets.SSH_KEY }}\n",[5249,10035,10036,10039,10041],{"class":5251,"line":5403},[5249,10037,10038],{"class":6150},"          script",[5249,10040,6155],{"class":6154},[5249,10042,8478],{"class":7333},[5249,10044,10045],{"class":5251,"line":5409},[5249,10046,10047],{"class":6158},"            cd \u002Fapp\n",[5249,10049,10050],{"class":5251,"line":5419},[5249,10051,10052],{"class":6158},"            docker-compose pull\n",[5249,10054,10055],{"class":5251,"line":5424},[5249,10056,10057],{"class":6158},"            docker-compose up -d\n",[5249,10059,10060],{"class":5251,"line":5430},[5249,10061,5306],{"emptyLinePlaceholder":5305},[5249,10063,10064,10066,10068,10070],{"class":5251,"line":5442},[5249,10065,6265],{"class":6154},[5249,10067,6151],{"class":6150},[5249,10069,6155],{"class":6154},[5249,10071,10072],{"class":6158},"Health check Green environment\n",[5249,10074,10075,10077,10079],{"class":5251,"line":5447},[5249,10076,6345],{"class":6150},[5249,10078,6155],{"class":6154},[5249,10080,8478],{"class":7333},[5249,10082,10083],{"class":5251,"line":5453},[5249,10084,10085],{"class":6158},"          for i in {1..30}; do\n",[5249,10087,10088],{"class":5251,"line":5698},[5249,10089,10090],{"class":6158},"            if curl -f http:\u002F\u002F${{ secrets.GREEN_SERVER_HOST }}\u002Fhealth; then\n",[5249,10092,10093],{"class":5251,"line":5704},[5249,10094,10095],{"class":6158},"              echo \"Green environment healthy\"\n",[5249,10097,10098],{"class":5251,"line":5710},[5249,10099,10100],{"class":6158},"              exit 0\n",[5249,10102,10103],{"class":5251,"line":5716},[5249,10104,10105],{"class":6158},"            fi\n",[5249,10107,10108],{"class":5251,"line":5722},[5249,10109,10110],{"class":6158},"            echo \"Waiting for Green environment... ($i\u002F30)\"\n",[5249,10112,10113],{"class":5251,"line":5728},[5249,10114,10115],{"class":6158},"            sleep 10\n",[5249,10117,10118],{"class":5251,"line":5734},[5249,10119,10120],{"class":6158},"          done\n",[5249,10122,10123],{"class":5251,"line":5740},[5249,10124,10125],{"class":6158},"          echo \"Green environment health check failed\"\n",[5249,10127,10128],{"class":5251,"line":5746},[5249,10129,10130],{"class":6158},"          exit 1\n",[5249,10132,10133],{"class":5251,"line":5752},[5249,10134,5306],{"emptyLinePlaceholder":5305},[5249,10136,10137,10139,10141,10143],{"class":5251,"line":5758},[5249,10138,6265],{"class":6154},[5249,10140,6151],{"class":6150},[5249,10142,6155],{"class":6154},[5249,10144,10145],{"class":6158},"Switch traffic to Green (update Load Balancer)\n",[5249,10147,10148,10150,10152],{"class":5251,"line":5763},[5249,10149,6345],{"class":6150},[5249,10151,6155],{"class":6154},[5249,10153,8478],{"class":7333},[5249,10155,10156],{"class":5251,"line":5769},[5249,10157,10158],{"class":6158},"          # AWS ALB приклад\n",[5249,10160,10161],{"class":5251,"line":5775},[5249,10162,10163],{"class":6158},"          aws elbv2 modify-target-group \\\n",[5249,10165,10166],{"class":5251,"line":5781},[5249,10167,10168],{"class":6158},"            --target-group-arn ${{ secrets.TARGET_GROUP_ARN }} \\\n",[5249,10170,10171],{"class":5251,"line":5787},[5249,10172,10173],{"class":6158},"            --health-check-path \u002Fhealth\n",[5249,10175,10176],{"class":5251,"line":5793},[5249,10177,10178],{"class":6158},"          \n",[5249,10180,10181],{"class":5251,"line":5799},[5249,10182,10183],{"class":6158},"          # Перемикання traffic з Blue на Green\n",[5249,10185,10186],{"class":5251,"line":5805},[5249,10187,10188],{"class":6158},"          aws elbv2 register-targets \\\n",[5249,10190,10191],{"class":5251,"line":5811},[5249,10192,10168],{"class":6158},[5249,10194,10195],{"class":5251,"line":5817},[5249,10196,10197],{"class":6158},"            --targets Id=${{ secrets.GREEN_INSTANCE_ID }}\n",[5249,10199,10200],{"class":5251,"line":5822},[5249,10201,10178],{"class":6158},[5249,10203,10204],{"class":5251,"line":5828},[5249,10205,10206],{"class":6158},"          aws elbv2 deregister-targets \\\n",[5249,10208,10209],{"class":5251,"line":5834},[5249,10210,10168],{"class":6158},[5249,10212,10213],{"class":5251,"line":5839},[5249,10214,10215],{"class":6158},"            --targets Id=${{ secrets.BLUE_INSTANCE_ID }}\n",[5249,10217,10218],{"class":5251,"line":7092},[5249,10219,5306],{"emptyLinePlaceholder":5305},[5249,10221,10222,10224,10226,10228],{"class":5251,"line":7097},[5249,10223,6265],{"class":6154},[5249,10225,6151],{"class":6150},[5249,10227,6155],{"class":6154},[5249,10229,10230],{"class":6158},"Monitor Green environment\n",[5249,10232,10233,10235,10237],{"class":5251,"line":7108},[5249,10234,6345],{"class":6150},[5249,10236,6155],{"class":6154},[5249,10238,8478],{"class":7333},[5249,10240,10241],{"class":5251,"line":7117},[5249,10242,10243],{"class":6158},"          sleep 60\n",[5249,10245,10246],{"class":5251,"line":7122},[5249,10247,10248],{"class":6158},"          # Перевірка error rate, response time\n",[5249,10250,10251],{"class":5251,"line":7134},[5249,10252,10253],{"class":6158},"          if [ $(check_error_rate) -gt 1 ]; then\n",[5249,10255,10256],{"class":5251,"line":7144},[5249,10257,10258],{"class":6158},"            echo \"High error rate detected, rolling back\"\n",[5249,10260,10261],{"class":5251,"line":7149},[5249,10262,10263],{"class":6158},"            exit 1\n",[5249,10265,10266],{"class":5251,"line":7161},[5249,10267,10268],{"class":6158},"          fi\n",[5249,10270,10271],{"class":5251,"line":7171},[5249,10272,5306],{"emptyLinePlaceholder":5305},[5249,10274,10275,10277,10279,10281],{"class":5251,"line":7178},[5249,10276,6265],{"class":6154},[5249,10278,6151],{"class":6150},[5249,10280,6155],{"class":6154},[5249,10282,10283],{"class":6158},"Rollback to Blue on failure\n",[5249,10285,10286,10288,10290],{"class":5251,"line":7189},[5249,10287,7685],{"class":6150},[5249,10289,6155],{"class":6154},[5249,10291,9401],{"class":6158},[5249,10293,10294,10296,10298],{"class":5251,"line":7200},[5249,10295,6345],{"class":6150},[5249,10297,6155],{"class":6154},[5249,10299,8478],{"class":7333},[5249,10301,10302],{"class":5251,"line":7211},[5249,10303,10188],{"class":6158},[5249,10305,10306],{"class":5251,"line":7216},[5249,10307,10168],{"class":6158},[5249,10309,10310],{"class":5251,"line":7224},[5249,10311,10215],{"class":6158},[5249,10313,10314],{"class":5251,"line":7234},[5249,10315,10178],{"class":6158},[5249,10317,10318],{"class":5251,"line":7243},[5249,10319,10206],{"class":6158},[5249,10321,10322],{"class":5251,"line":7253},[5249,10323,10168],{"class":6158},[5249,10325,10326],{"class":5251,"line":7258},[5249,10327,10197],{"class":6158},[5155,10329,10330],{},[5118,10331,10332],{},"Переваги:",[5092,10334,10335,10341,10347],{},[5095,10336,10337,10340],{},[5118,10338,10339],{},"Zero downtime"," — миттєве перемикання traffic.",[5095,10342,10343,10346],{},[5118,10344,10345],{},"Easy rollback"," — перемикання назад на Blue за секунди.",[5095,10348,10349,10352],{},[5118,10350,10351],{},"Testing in production-like environment"," — Green environment ідентичний до production.",[5155,10354,10355],{},[5118,10356,10357],{},"Недоліки:",[5092,10359,10360,10366],{},[5095,10361,10362,10365],{},[5118,10363,10364],{},"Resource cost"," — потрібно підтримувати 2 повноцінних environments.",[5095,10367,10368,10371],{},[5118,10369,10370],{},"Database migrations"," — складність при backward-incompatible змінах схеми БД.",[5849,10373,10375],{"id":10374},"_2-rolling-update","2. Rolling Update",[5155,10377,10378,10380],{},[5118,10379,9898],{}," Поступова заміна instances застосунку одна за одною.",[5240,10382,10384],{"className":6141,"code":10383,"language":6143,"meta":5245,"style":5245},"deploy-rolling:\n  name: Rolling Update\n  runs-on: ubuntu-latest\n\n  steps:\n    - name: Get list of instances\n      id: instances\n      run: |\n        INSTANCES=$(aws ec2 describe-instances \\\n          --filters \"Name=tag:Environment,Values=production\" \\\n          --query \"Reservations[*].Instances[*].InstanceId\" \\\n          --output text)\n        echo \"instances=$INSTANCES\" >> $GITHUB_OUTPUT\n\n    - name: Rolling update\n      run: |\n        for instance in ${{ steps.instances.outputs.instances }}; do\n          echo \"Updating instance: $instance\"\n          \n          # 1. Deregister from load balancer\n          aws elbv2 deregister-targets \\\n            --target-group-arn ${{ secrets.TARGET_GROUP_ARN }} \\\n            --targets Id=$instance\n          \n          # 2. Wait for connection draining (30s)\n          sleep 30\n          \n          # 3. Update instance\n          ssh user@$instance \"cd \u002Fapp && docker-compose pull && docker-compose up -d\"\n          \n          # 4. Health check\n          sleep 10\n          if ! ssh user@$instance \"curl -f http:\u002F\u002Flocalhost:3000\u002Fhealth\"; then\n            echo \"Health check failed, aborting\"\n            exit 1\n          fi\n          \n          # 5. Register back to load balancer\n          aws elbv2 register-targets \\\n            --target-group-arn ${{ secrets.TARGET_GROUP_ARN }} \\\n            --targets Id=$instance\n          \n          # 6. Wait for health check in load balancer\n          sleep 20\n          \n          echo \"Instance $instance updated successfully\"\n        done\n",[5173,10385,10386,10393,10402,10410,10414,10420,10431,10440,10448,10453,10458,10463,10468,10473,10477,10488,10496,10501,10506,10510,10515,10519,10523,10528,10532,10537,10542,10546,10551,10556,10560,10565,10570,10575,10580,10584,10588,10592,10597,10601,10605,10609,10613,10618,10623,10627,10632],{"__ignoreMap":5245},[5249,10387,10388,10391],{"class":5251,"line":5252},[5249,10389,10390],{"class":6150},"deploy-rolling",[5249,10392,6185],{"class":6154},[5249,10394,10395,10397,10399],{"class":5251,"line":5259},[5249,10396,9101],{"class":6150},[5249,10398,6155],{"class":6154},[5249,10400,10401],{"class":6158},"Rolling Update\n",[5249,10403,10404,10406,10408],{"class":5251,"line":5273},[5249,10405,9111],{"class":6150},[5249,10407,6155],{"class":6154},[5249,10409,6792],{"class":6158},[5249,10411,10412],{"class":5251,"line":5288},[5249,10413,5306],{"emptyLinePlaceholder":5305},[5249,10415,10416,10418],{"class":5251,"line":5302},[5249,10417,9145],{"class":6150},[5249,10419,6185],{"class":6154},[5249,10421,10422,10424,10426,10428],{"class":5251,"line":5309},[5249,10423,6529],{"class":6154},[5249,10425,6151],{"class":6150},[5249,10427,6155],{"class":6154},[5249,10429,10430],{"class":6158},"Get list of instances\n",[5249,10432,10433,10435,10437],{"class":5251,"line":5315},[5249,10434,9763],{"class":6150},[5249,10436,6155],{"class":6154},[5249,10438,10439],{"class":6158},"instances\n",[5249,10441,10442,10444,10446],{"class":5251,"line":5324},[5249,10443,9254],{"class":6150},[5249,10445,6155],{"class":6154},[5249,10447,8478],{"class":7333},[5249,10449,10450],{"class":5251,"line":5329},[5249,10451,10452],{"class":6158},"        INSTANCES=$(aws ec2 describe-instances \\\n",[5249,10454,10455],{"class":5251,"line":5335},[5249,10456,10457],{"class":6158},"          --filters \"Name=tag:Environment,Values=production\" \\\n",[5249,10459,10460],{"class":5251,"line":5344},[5249,10461,10462],{"class":6158},"          --query \"Reservations[*].Instances[*].InstanceId\" \\\n",[5249,10464,10465],{"class":5251,"line":5356},[5249,10466,10467],{"class":6158},"          --output text)\n",[5249,10469,10470],{"class":5251,"line":5361},[5249,10471,10472],{"class":6158},"        echo \"instances=$INSTANCES\" >> $GITHUB_OUTPUT\n",[5249,10474,10475],{"class":5251,"line":5367},[5249,10476,5306],{"emptyLinePlaceholder":5305},[5249,10478,10479,10481,10483,10485],{"class":5251,"line":5376},[5249,10480,6529],{"class":6154},[5249,10482,6151],{"class":6150},[5249,10484,6155],{"class":6154},[5249,10486,10487],{"class":6158},"Rolling update\n",[5249,10489,10490,10492,10494],{"class":5251,"line":5381},[5249,10491,9254],{"class":6150},[5249,10493,6155],{"class":6154},[5249,10495,8478],{"class":7333},[5249,10497,10498],{"class":5251,"line":5387},[5249,10499,10500],{"class":6158},"        for instance in ${{ steps.instances.outputs.instances }}; do\n",[5249,10502,10503],{"class":5251,"line":5398},[5249,10504,10505],{"class":6158},"          echo \"Updating instance: $instance\"\n",[5249,10507,10508],{"class":5251,"line":5403},[5249,10509,10178],{"class":6158},[5249,10511,10512],{"class":5251,"line":5409},[5249,10513,10514],{"class":6158},"          # 1. Deregister from load balancer\n",[5249,10516,10517],{"class":5251,"line":5419},[5249,10518,10206],{"class":6158},[5249,10520,10521],{"class":5251,"line":5424},[5249,10522,10168],{"class":6158},[5249,10524,10525],{"class":5251,"line":5430},[5249,10526,10527],{"class":6158},"            --targets Id=$instance\n",[5249,10529,10530],{"class":5251,"line":5442},[5249,10531,10178],{"class":6158},[5249,10533,10534],{"class":5251,"line":5447},[5249,10535,10536],{"class":6158},"          # 2. Wait for connection draining (30s)\n",[5249,10538,10539],{"class":5251,"line":5453},[5249,10540,10541],{"class":6158},"          sleep 30\n",[5249,10543,10544],{"class":5251,"line":5698},[5249,10545,10178],{"class":6158},[5249,10547,10548],{"class":5251,"line":5704},[5249,10549,10550],{"class":6158},"          # 3. Update instance\n",[5249,10552,10553],{"class":5251,"line":5710},[5249,10554,10555],{"class":6158},"          ssh user@$instance \"cd \u002Fapp && docker-compose pull && docker-compose up -d\"\n",[5249,10557,10558],{"class":5251,"line":5716},[5249,10559,10178],{"class":6158},[5249,10561,10562],{"class":5251,"line":5722},[5249,10563,10564],{"class":6158},"          # 4. Health check\n",[5249,10566,10567],{"class":5251,"line":5728},[5249,10568,10569],{"class":6158},"          sleep 10\n",[5249,10571,10572],{"class":5251,"line":5734},[5249,10573,10574],{"class":6158},"          if ! ssh user@$instance \"curl -f http:\u002F\u002Flocalhost:3000\u002Fhealth\"; then\n",[5249,10576,10577],{"class":5251,"line":5740},[5249,10578,10579],{"class":6158},"            echo \"Health check failed, aborting\"\n",[5249,10581,10582],{"class":5251,"line":5746},[5249,10583,10263],{"class":6158},[5249,10585,10586],{"class":5251,"line":5752},[5249,10587,10268],{"class":6158},[5249,10589,10590],{"class":5251,"line":5758},[5249,10591,10178],{"class":6158},[5249,10593,10594],{"class":5251,"line":5763},[5249,10595,10596],{"class":6158},"          # 5. Register back to load balancer\n",[5249,10598,10599],{"class":5251,"line":5769},[5249,10600,10188],{"class":6158},[5249,10602,10603],{"class":5251,"line":5775},[5249,10604,10168],{"class":6158},[5249,10606,10607],{"class":5251,"line":5781},[5249,10608,10527],{"class":6158},[5249,10610,10611],{"class":5251,"line":5787},[5249,10612,10178],{"class":6158},[5249,10614,10615],{"class":5251,"line":5793},[5249,10616,10617],{"class":6158},"          # 6. Wait for health check in load balancer\n",[5249,10619,10620],{"class":5251,"line":5799},[5249,10621,10622],{"class":6158},"          sleep 20\n",[5249,10624,10625],{"class":5251,"line":5805},[5249,10626,10178],{"class":6158},[5249,10628,10629],{"class":5251,"line":5811},[5249,10630,10631],{"class":6158},"          echo \"Instance $instance updated successfully\"\n",[5249,10633,10634],{"class":5251,"line":5817},[5249,10635,10636],{"class":6158},"        done\n",[5155,10638,10639],{},[5118,10640,10332],{},[5092,10642,10643,10648,10654],{},[5095,10644,10645,10647],{},[5118,10646,10339],{}," — завжди є працюючі instances.",[5095,10649,10650,10653],{},[5118,10651,10652],{},"No extra infrastructure"," — використовуємо існуючі instances.",[5095,10655,10656,10659],{},[5118,10657,10658],{},"Gradual rollout"," — проблеми виявляються на першій instance, інші не оновлюються.",[5155,10661,10662],{},[5118,10663,10357],{},[5092,10665,10666,10672],{},[5095,10667,10668,10671],{},[5118,10669,10670],{},"Slower deployment"," — update по одній instance займає час.",[5095,10673,10674,10677],{},[5118,10675,10676],{},"Mixed versions"," — під час deploy одночасно працюють стара та нова версії.",[5849,10679,10681],{"id":10680},"_3-canary-release","3. Canary Release",[5155,10683,10684,10686,10687,10690],{},[5118,10685,9898],{}," Нова версія розгортається лише для ",[5118,10688,10689],{},"малої частини користувачів"," (5-10%), решта працюють зі старою версією. Якщо метрики canary group нормальні — rollout на всіх користувачів.",[5240,10692,10694],{"className":6141,"code":10693,"language":6143,"meta":5245,"style":5245},"deploy-canary:\n  name: Canary Release\n  runs-on: ubuntu-latest\n\n  steps:\n    - name: Deploy to canary instance\n      uses: appleboy\u002Fssh-action@v0.1.10\n      with:\n        host: ${{ secrets.CANARY_SERVER_HOST }}\n        username: ${{ secrets.SERVER_USER }}\n        key: ${{ secrets.SSH_KEY }}\n        script: |\n          cd \u002Fapp\n          docker-compose pull\n          docker-compose up -d\n\n    - name: Configure load balancer (10% traffic to canary)\n      run: |\n        # AWS ALB weighted routing\n        aws elbv2 modify-rule \\\n          --rule-arn ${{ secrets.CANARY_RULE_ARN }} \\\n          --actions Type=forward,ForwardConfig='{\n            \"TargetGroups\":[\n              {\"TargetGroupArn\":\"${{ secrets.STABLE_TARGET_GROUP }}\",\"Weight\":90},\n              {\"TargetGroupArn\":\"${{ secrets.CANARY_TARGET_GROUP }}\",\"Weight\":10}\n            ]\n          }'\n\n    - name: Monitor canary metrics (30 min)\n      run: |\n        for i in {1..30}; do\n          ERROR_RATE=$(check_error_rate_canary)\n          LATENCY=$(check_latency_canary)\n          \n          if [ $ERROR_RATE -gt 5 ]; then\n            echo \"Canary error rate too high: $ERROR_RATE%\"\n            exit 1\n          fi\n          \n          if [ $LATENCY -gt 500 ]; then\n            echo \"Canary latency too high: ${LATENCY}ms\"\n            exit 1\n          fi\n          \n          echo \"Canary metrics OK ($i\u002F30)\"\n          sleep 60\n        done\n\n    - name: Promote canary to stable (100% traffic)\n      if: success()\n      run: |\n        # Deploy to all stable instances\n        for instance in $STABLE_INSTANCES; do\n          ssh user@$instance \"cd \u002Fapp && docker-compose pull && docker-compose up -d\"\n        done\n        \n        # Switch 100% traffic to stable\n        aws elbv2 modify-rule \\\n          --rule-arn ${{ secrets.CANARY_RULE_ARN }} \\\n          --actions Type=forward,TargetGroupArn=${{ secrets.STABLE_TARGET_GROUP }}\n\n    - name: Rollback canary on failure\n      if: failure()\n      run: |\n        # Switch 100% traffic to stable (remove canary)\n        aws elbv2 modify-rule \\\n          --rule-arn ${{ secrets.CANARY_RULE_ARN }} \\\n          --actions Type=forward,TargetGroupArn=${{ secrets.STABLE_TARGET_GROUP }}\n        \n        # Rollback canary instance\n        ssh user@${{ secrets.CANARY_SERVER_HOST }} \\\n          \"cd \u002Fapp && docker-compose down && docker-compose up -d\"\n",[5173,10695,10696,10703,10712,10720,10724,10730,10741,10749,10755,10764,10772,10780,10788,10792,10796,10800,10804,10815,10823,10828,10833,10838,10843,10848,10853,10858,10863,10868,10872,10883,10891,10896,10901,10906,10910,10915,10920,10924,10928,10932,10937,10942,10946,10950,10954,10959,10963,10967,10971,10982,10990,10998,11003,11008,11012,11016,11021,11026,11030,11034,11039,11043,11054,11062,11070,11075,11079,11083,11087,11091,11096,11101],{"__ignoreMap":5245},[5249,10697,10698,10701],{"class":5251,"line":5252},[5249,10699,10700],{"class":6150},"deploy-canary",[5249,10702,6185],{"class":6154},[5249,10704,10705,10707,10709],{"class":5251,"line":5259},[5249,10706,9101],{"class":6150},[5249,10708,6155],{"class":6154},[5249,10710,10711],{"class":6158},"Canary Release\n",[5249,10713,10714,10716,10718],{"class":5251,"line":5273},[5249,10715,9111],{"class":6150},[5249,10717,6155],{"class":6154},[5249,10719,6792],{"class":6158},[5249,10721,10722],{"class":5251,"line":5288},[5249,10723,5306],{"emptyLinePlaceholder":5305},[5249,10725,10726,10728],{"class":5251,"line":5302},[5249,10727,9145],{"class":6150},[5249,10729,6185],{"class":6154},[5249,10731,10732,10734,10736,10738],{"class":5251,"line":5309},[5249,10733,6529],{"class":6154},[5249,10735,6151],{"class":6150},[5249,10737,6155],{"class":6154},[5249,10739,10740],{"class":6158},"Deploy to canary instance\n",[5249,10742,10743,10745,10747],{"class":5251,"line":5315},[5249,10744,9163],{"class":6150},[5249,10746,6155],{"class":6154},[5249,10748,9168],{"class":6158},[5249,10750,10751,10753],{"class":5251,"line":5324},[5249,10752,9173],{"class":6150},[5249,10754,6185],{"class":6154},[5249,10756,10757,10759,10761],{"class":5251,"line":5329},[5249,10758,9180],{"class":6150},[5249,10760,6155],{"class":6154},[5249,10762,10763],{"class":6158},"${{ secrets.CANARY_SERVER_HOST }}\n",[5249,10765,10766,10768,10770],{"class":5251,"line":5335},[5249,10767,9190],{"class":6150},[5249,10769,6155],{"class":6154},[5249,10771,10023],{"class":6158},[5249,10773,10774,10776,10778],{"class":5251,"line":5344},[5249,10775,9200],{"class":6150},[5249,10777,6155],{"class":6154},[5249,10779,10033],{"class":6158},[5249,10781,10782,10784,10786],{"class":5251,"line":5356},[5249,10783,9210],{"class":6150},[5249,10785,6155],{"class":6154},[5249,10787,8478],{"class":7333},[5249,10789,10790],{"class":5251,"line":5361},[5249,10791,9219],{"class":6158},[5249,10793,10794],{"class":5251,"line":5367},[5249,10795,9224],{"class":6158},[5249,10797,10798],{"class":5251,"line":5376},[5249,10799,9229],{"class":6158},[5249,10801,10802],{"class":5251,"line":5381},[5249,10803,5306],{"emptyLinePlaceholder":5305},[5249,10805,10806,10808,10810,10812],{"class":5251,"line":5387},[5249,10807,6529],{"class":6154},[5249,10809,6151],{"class":6150},[5249,10811,6155],{"class":6154},[5249,10813,10814],{"class":6158},"Configure load balancer (10% traffic to canary)\n",[5249,10816,10817,10819,10821],{"class":5251,"line":5398},[5249,10818,9254],{"class":6150},[5249,10820,6155],{"class":6154},[5249,10822,8478],{"class":7333},[5249,10824,10825],{"class":5251,"line":5403},[5249,10826,10827],{"class":6158},"        # AWS ALB weighted routing\n",[5249,10829,10830],{"class":5251,"line":5409},[5249,10831,10832],{"class":6158},"        aws elbv2 modify-rule \\\n",[5249,10834,10835],{"class":5251,"line":5419},[5249,10836,10837],{"class":6158},"          --rule-arn ${{ secrets.CANARY_RULE_ARN }} \\\n",[5249,10839,10840],{"class":5251,"line":5424},[5249,10841,10842],{"class":6158},"          --actions Type=forward,ForwardConfig='{\n",[5249,10844,10845],{"class":5251,"line":5430},[5249,10846,10847],{"class":6158},"            \"TargetGroups\":[\n",[5249,10849,10850],{"class":5251,"line":5442},[5249,10851,10852],{"class":6158},"              {\"TargetGroupArn\":\"${{ secrets.STABLE_TARGET_GROUP }}\",\"Weight\":90},\n",[5249,10854,10855],{"class":5251,"line":5447},[5249,10856,10857],{"class":6158},"              {\"TargetGroupArn\":\"${{ secrets.CANARY_TARGET_GROUP }}\",\"Weight\":10}\n",[5249,10859,10860],{"class":5251,"line":5453},[5249,10861,10862],{"class":6158},"            ]\n",[5249,10864,10865],{"class":5251,"line":5698},[5249,10866,10867],{"class":6158},"          }'\n",[5249,10869,10870],{"class":5251,"line":5704},[5249,10871,5306],{"emptyLinePlaceholder":5305},[5249,10873,10874,10876,10878,10880],{"class":5251,"line":5710},[5249,10875,6529],{"class":6154},[5249,10877,6151],{"class":6150},[5249,10879,6155],{"class":6154},[5249,10881,10882],{"class":6158},"Monitor canary metrics (30 min)\n",[5249,10884,10885,10887,10889],{"class":5251,"line":5716},[5249,10886,9254],{"class":6150},[5249,10888,6155],{"class":6154},[5249,10890,8478],{"class":7333},[5249,10892,10893],{"class":5251,"line":5722},[5249,10894,10895],{"class":6158},"        for i in {1..30}; do\n",[5249,10897,10898],{"class":5251,"line":5728},[5249,10899,10900],{"class":6158},"          ERROR_RATE=$(check_error_rate_canary)\n",[5249,10902,10903],{"class":5251,"line":5734},[5249,10904,10905],{"class":6158},"          LATENCY=$(check_latency_canary)\n",[5249,10907,10908],{"class":5251,"line":5740},[5249,10909,10178],{"class":6158},[5249,10911,10912],{"class":5251,"line":5746},[5249,10913,10914],{"class":6158},"          if [ $ERROR_RATE -gt 5 ]; then\n",[5249,10916,10917],{"class":5251,"line":5752},[5249,10918,10919],{"class":6158},"            echo \"Canary error rate too high: $ERROR_RATE%\"\n",[5249,10921,10922],{"class":5251,"line":5758},[5249,10923,10263],{"class":6158},[5249,10925,10926],{"class":5251,"line":5763},[5249,10927,10268],{"class":6158},[5249,10929,10930],{"class":5251,"line":5769},[5249,10931,10178],{"class":6158},[5249,10933,10934],{"class":5251,"line":5775},[5249,10935,10936],{"class":6158},"          if [ $LATENCY -gt 500 ]; then\n",[5249,10938,10939],{"class":5251,"line":5781},[5249,10940,10941],{"class":6158},"            echo \"Canary latency too high: ${LATENCY}ms\"\n",[5249,10943,10944],{"class":5251,"line":5787},[5249,10945,10263],{"class":6158},[5249,10947,10948],{"class":5251,"line":5793},[5249,10949,10268],{"class":6158},[5249,10951,10952],{"class":5251,"line":5799},[5249,10953,10178],{"class":6158},[5249,10955,10956],{"class":5251,"line":5805},[5249,10957,10958],{"class":6158},"          echo \"Canary metrics OK ($i\u002F30)\"\n",[5249,10960,10961],{"class":5251,"line":5811},[5249,10962,10243],{"class":6158},[5249,10964,10965],{"class":5251,"line":5817},[5249,10966,10636],{"class":6158},[5249,10968,10969],{"class":5251,"line":5822},[5249,10970,5306],{"emptyLinePlaceholder":5305},[5249,10972,10973,10975,10977,10979],{"class":5251,"line":5828},[5249,10974,6529],{"class":6154},[5249,10976,6151],{"class":6150},[5249,10978,6155],{"class":6154},[5249,10980,10981],{"class":6158},"Promote canary to stable (100% traffic)\n",[5249,10983,10984,10986,10988],{"class":5251,"line":5834},[5249,10985,9288],{"class":6150},[5249,10987,6155],{"class":6154},[5249,10989,9293],{"class":6158},[5249,10991,10992,10994,10996],{"class":5251,"line":5839},[5249,10993,9254],{"class":6150},[5249,10995,6155],{"class":6154},[5249,10997,8478],{"class":7333},[5249,10999,11000],{"class":5251,"line":7092},[5249,11001,11002],{"class":6158},"        # Deploy to all stable instances\n",[5249,11004,11005],{"class":5251,"line":7097},[5249,11006,11007],{"class":6158},"        for instance in $STABLE_INSTANCES; do\n",[5249,11009,11010],{"class":5251,"line":7108},[5249,11011,10555],{"class":6158},[5249,11013,11014],{"class":5251,"line":7117},[5249,11015,10636],{"class":6158},[5249,11017,11018],{"class":5251,"line":7122},[5249,11019,11020],{"class":6158},"        \n",[5249,11022,11023],{"class":5251,"line":7134},[5249,11024,11025],{"class":6158},"        # Switch 100% traffic to stable\n",[5249,11027,11028],{"class":5251,"line":7144},[5249,11029,10832],{"class":6158},[5249,11031,11032],{"class":5251,"line":7149},[5249,11033,10837],{"class":6158},[5249,11035,11036],{"class":5251,"line":7161},[5249,11037,11038],{"class":6158},"          --actions Type=forward,TargetGroupArn=${{ secrets.STABLE_TARGET_GROUP }}\n",[5249,11040,11041],{"class":5251,"line":7171},[5249,11042,5306],{"emptyLinePlaceholder":5305},[5249,11044,11045,11047,11049,11051],{"class":5251,"line":7178},[5249,11046,6529],{"class":6154},[5249,11048,6151],{"class":6150},[5249,11050,6155],{"class":6154},[5249,11052,11053],{"class":6158},"Rollback canary on failure\n",[5249,11055,11056,11058,11060],{"class":5251,"line":7189},[5249,11057,9288],{"class":6150},[5249,11059,6155],{"class":6154},[5249,11061,9401],{"class":6158},[5249,11063,11064,11066,11068],{"class":5251,"line":7200},[5249,11065,9254],{"class":6150},[5249,11067,6155],{"class":6154},[5249,11069,8478],{"class":7333},[5249,11071,11072],{"class":5251,"line":7211},[5249,11073,11074],{"class":6158},"        # Switch 100% traffic to stable (remove canary)\n",[5249,11076,11077],{"class":5251,"line":7216},[5249,11078,10832],{"class":6158},[5249,11080,11081],{"class":5251,"line":7224},[5249,11082,10837],{"class":6158},[5249,11084,11085],{"class":5251,"line":7234},[5249,11086,11038],{"class":6158},[5249,11088,11089],{"class":5251,"line":7243},[5249,11090,11020],{"class":6158},[5249,11092,11093],{"class":5251,"line":7253},[5249,11094,11095],{"class":6158},"        # Rollback canary instance\n",[5249,11097,11098],{"class":5251,"line":7258},[5249,11099,11100],{"class":6158},"        ssh user@${{ secrets.CANARY_SERVER_HOST }} \\\n",[5249,11102,11103],{"class":5251,"line":7266},[5249,11104,11105],{"class":6158},"          \"cd \u002Fapp && docker-compose down && docker-compose up -d\"\n",[5155,11107,11108],{},[5118,11109,10332],{},[5092,11111,11112,11118,11124],{},[5095,11113,11114,11117],{},[5118,11115,11116],{},"Minimized risk"," — проблеми впливають лише на 10% користувачів.",[5095,11119,11120,11123],{},[5118,11121,11122],{},"Real production testing"," — перевірка на реальному трафіку перед повним rollout.",[5095,11125,11126,11129],{},[5118,11127,11128],{},"Automated rollback"," — при виявленні проблем canary автоматично відключається.",[5155,11131,11132],{},[5118,11133,10357],{},[5092,11135,11136,11142],{},[5095,11137,11138,11141],{},[5118,11139,11140],{},"Complex setup"," — потрібна складна інфраструктура (weighted routing, metrics monitoring).",[5095,11143,11144,11147],{},[5118,11145,11146],{},"Longer deployment"," — від commit до 100% rollout може пройти години.",[5147,11149],{},[5150,11151,11153],{"id":11152},"environment-specific-workflows","Environment-specific Workflows",[5155,11155,11156],{},"Створення окремих workflows для різних environments:",[5849,11158,11160],{"id":11159},"staging-deployment","Staging Deployment",[5240,11162,11164],{"className":6141,"code":11163,"language":6143,"meta":5245,"style":5245},"# .github\u002Fworkflows\u002Fdeploy-staging.yml\nname: Deploy to Staging\n\non:\n  push:\n    branches: [develop]\n\njobs:\n  deploy-staging:\n    runs-on: ubuntu-latest\n    environment:\n      name: staging\n      url: https:\u002F\u002Fstaging.example.com\n\n    steps:\n      - name: Checkout code\n        uses: actions\u002Fcheckout@v3\n\n      - name: Build and push Docker image\n        uses: docker\u002Fbuild-push-action@v4\n        with:\n          push: true\n          tags: myusername\u002Fmy-app:staging-${{ github.sha }}\n\n      - name: Deploy to staging\n        uses: appleboy\u002Fssh-action@v0.1.10\n        with:\n          host: ${{ secrets.STAGING_SERVER_HOST }}\n          username: ${{ secrets.STAGING_SERVER_USER }}\n          key: ${{ secrets.STAGING_SSH_KEY }}\n          script: |\n            cd \u002Fapp\n            export IMAGE_TAG=staging-${{ github.sha }}\n            docker-compose pull\n            docker-compose up -d\n\n      - name: Run smoke tests\n        run: |\n          sleep 10\n          curl -f https:\u002F\u002Fstaging.example.com\u002Fhealth\n          curl -f https:\u002F\u002Fstaging.example.com\u002Fapi\u002Fdocs  # Swagger доступний на staging\n",[5173,11165,11166,11171,11180,11184,11190,11196,11206,11210,11216,11223,11231,11238,11248,11258,11262,11268,11278,11286,11290,11301,11309,11315,11323,11332,11336,11347,11355,11361,11370,11379,11388,11396,11400,11405,11409,11413,11417,11428,11436,11440,11445],{"__ignoreMap":5245},[5249,11167,11168],{"class":5251,"line":5252},[5249,11169,11170],{"class":5255},"# .github\u002Fworkflows\u002Fdeploy-staging.yml\n",[5249,11172,11173,11175,11177],{"class":5251,"line":5259},[5249,11174,6151],{"class":6150},[5249,11176,6155],{"class":6154},[5249,11178,11179],{"class":6158},"Deploy to Staging\n",[5249,11181,11182],{"class":5251,"line":5273},[5249,11183,5306],{"emptyLinePlaceholder":5305},[5249,11185,11186,11188],{"class":5251,"line":5288},[5249,11187,6171],{"class":5281},[5249,11189,6185],{"class":6154},[5249,11191,11192,11194],{"class":5251,"line":5302},[5249,11193,6182],{"class":6150},[5249,11195,6185],{"class":6154},[5249,11197,11198,11200,11202,11204],{"class":5251,"line":5309},[5249,11199,6190],{"class":6150},[5249,11201,6193],{"class":6154},[5249,11203,6738],{"class":6158},[5249,11205,6199],{"class":6154},[5249,11207,11208],{"class":5251,"line":5315},[5249,11209,5306],{"emptyLinePlaceholder":5305},[5249,11211,11212,11214],{"class":5251,"line":5324},[5249,11213,6225],{"class":6150},[5249,11215,6185],{"class":6154},[5249,11217,11218,11221],{"class":5251,"line":5329},[5249,11219,11220],{"class":6150},"  deploy-staging",[5249,11222,6185],{"class":6154},[5249,11224,11225,11227,11229],{"class":5251,"line":5335},[5249,11226,6242],{"class":6150},[5249,11228,6155],{"class":6154},[5249,11230,6792],{"class":6158},[5249,11232,11233,11236],{"class":5251,"line":5344},[5249,11234,11235],{"class":6150},"    environment",[5249,11237,6185],{"class":6154},[5249,11239,11240,11243,11245],{"class":5251,"line":5356},[5249,11241,11242],{"class":6150},"      name",[5249,11244,6155],{"class":6154},[5249,11246,11247],{"class":6158},"staging\n",[5249,11249,11250,11253,11255],{"class":5251,"line":5361},[5249,11251,11252],{"class":6150},"      url",[5249,11254,6155],{"class":6154},[5249,11256,11257],{"class":6158},"https:\u002F\u002Fstaging.example.com\n",[5249,11259,11260],{"class":5251,"line":5367},[5249,11261,5306],{"emptyLinePlaceholder":5305},[5249,11263,11264,11266],{"class":5251,"line":5376},[5249,11265,6255],{"class":6150},[5249,11267,6185],{"class":6154},[5249,11269,11270,11272,11274,11276],{"class":5251,"line":5381},[5249,11271,6265],{"class":6154},[5249,11273,6151],{"class":6150},[5249,11275,6155],{"class":6154},[5249,11277,6272],{"class":6158},[5249,11279,11280,11282,11284],{"class":5251,"line":5387},[5249,11281,6277],{"class":6150},[5249,11283,6155],{"class":6154},[5249,11285,6282],{"class":6158},[5249,11287,11288],{"class":5251,"line":5398},[5249,11289,5306],{"emptyLinePlaceholder":5305},[5249,11291,11292,11294,11296,11298],{"class":5251,"line":5403},[5249,11293,6265],{"class":6154},[5249,11295,6151],{"class":6150},[5249,11297,6155],{"class":6154},[5249,11299,11300],{"class":6158},"Build and push Docker image\n",[5249,11302,11303,11305,11307],{"class":5251,"line":5409},[5249,11304,6277],{"class":6150},[5249,11306,6155],{"class":6154},[5249,11308,8517],{"class":6158},[5249,11310,11311,11313],{"class":5251,"line":5419},[5249,11312,6312],{"class":6150},[5249,11314,6185],{"class":6154},[5249,11316,11317,11319,11321],{"class":5251,"line":5424},[5249,11318,8548],{"class":6150},[5249,11320,6155],{"class":6154},[5249,11322,6600],{"class":5281},[5249,11324,11325,11327,11329],{"class":5251,"line":5430},[5249,11326,8473],{"class":6150},[5249,11328,6155],{"class":6154},[5249,11330,11331],{"class":6158},"myusername\u002Fmy-app:staging-${{ github.sha }}\n",[5249,11333,11334],{"class":5251,"line":5442},[5249,11335,5306],{"emptyLinePlaceholder":5305},[5249,11337,11338,11340,11342,11344],{"class":5251,"line":5447},[5249,11339,6265],{"class":6154},[5249,11341,6151],{"class":6150},[5249,11343,6155],{"class":6154},[5249,11345,11346],{"class":6158},"Deploy to staging\n",[5249,11348,11349,11351,11353],{"class":5251,"line":5453},[5249,11350,6277],{"class":6150},[5249,11352,6155],{"class":6154},[5249,11354,9168],{"class":6158},[5249,11356,11357,11359],{"class":5251,"line":5698},[5249,11358,6312],{"class":6150},[5249,11360,6185],{"class":6154},[5249,11362,11363,11365,11367],{"class":5251,"line":5704},[5249,11364,10009],{"class":6150},[5249,11366,6155],{"class":6154},[5249,11368,11369],{"class":6158},"${{ secrets.STAGING_SERVER_HOST }}\n",[5249,11371,11372,11374,11376],{"class":5251,"line":5710},[5249,11373,8403],{"class":6150},[5249,11375,6155],{"class":6154},[5249,11377,11378],{"class":6158},"${{ secrets.STAGING_SERVER_USER }}\n",[5249,11380,11381,11383,11385],{"class":5251,"line":5716},[5249,11382,10028],{"class":6150},[5249,11384,6155],{"class":6154},[5249,11386,11387],{"class":6158},"${{ secrets.STAGING_SSH_KEY }}\n",[5249,11389,11390,11392,11394],{"class":5251,"line":5722},[5249,11391,10038],{"class":6150},[5249,11393,6155],{"class":6154},[5249,11395,8478],{"class":7333},[5249,11397,11398],{"class":5251,"line":5728},[5249,11399,10047],{"class":6158},[5249,11401,11402],{"class":5251,"line":5734},[5249,11403,11404],{"class":6158},"            export IMAGE_TAG=staging-${{ github.sha }}\n",[5249,11406,11407],{"class":5251,"line":5740},[5249,11408,10052],{"class":6158},[5249,11410,11411],{"class":5251,"line":5746},[5249,11412,10057],{"class":6158},[5249,11414,11415],{"class":5251,"line":5752},[5249,11416,5306],{"emptyLinePlaceholder":5305},[5249,11418,11419,11421,11423,11425],{"class":5251,"line":5758},[5249,11420,6265],{"class":6154},[5249,11422,6151],{"class":6150},[5249,11424,6155],{"class":6154},[5249,11426,11427],{"class":6158},"Run smoke tests\n",[5249,11429,11430,11432,11434],{"class":5251,"line":5763},[5249,11431,6345],{"class":6150},[5249,11433,6155],{"class":6154},[5249,11435,8478],{"class":7333},[5249,11437,11438],{"class":5251,"line":5769},[5249,11439,10569],{"class":6158},[5249,11441,11442],{"class":5251,"line":5775},[5249,11443,11444],{"class":6158},"          curl -f https:\u002F\u002Fstaging.example.com\u002Fhealth\n",[5249,11446,11447],{"class":5251,"line":5781},[5249,11448,11449],{"class":6158},"          curl -f https:\u002F\u002Fstaging.example.com\u002Fapi\u002Fdocs  # Swagger доступний на staging\n",[5849,11451,11453],{"id":11452},"production-deployment-з-manual-approval","Production Deployment з Manual Approval",[5240,11455,11457],{"className":6141,"code":11456,"language":6143,"meta":5245,"style":5245},"# .github\u002Fworkflows\u002Fdeploy-production.yml\nname: Deploy to Production\n\non:\n  workflow_dispatch:  # Ручний запуск\n    inputs:\n      version:\n        description: 'Version to deploy'\n        required: true\n        default: 'latest'\n\njobs:\n  deploy-production:\n    runs-on: ubuntu-latest\n    environment:\n      name: production\n      url: https:\u002F\u002Fexample.com\n\n    steps:\n      - name: Checkout code\n        uses: actions\u002Fcheckout@v3\n\n      - name: Deploy to production\n        uses: appleboy\u002Fssh-action@v0.1.10\n        with:\n          host: ${{ secrets.PROD_SERVER_HOST }}\n          username: ${{ secrets.PROD_SERVER_USER }}\n          key: ${{ secrets.PROD_SSH_KEY }}\n          script: |\n            cd \u002Fapp\n            export IMAGE_TAG=${{ github.event.inputs.version }}\n            docker-compose pull\n            docker-compose up -d\n\n      - name: Health check\n        run: |\n          for i in {1..10}; do\n            if curl -f https:\u002F\u002Fexample.com\u002Fhealth; then\n              echo \"Production healthy\"\n              exit 0\n            fi\n            sleep 5\n          done\n          exit 1\n\n      - name: Create deployment tag\n        run: |\n          git tag -a \"prod-${{ github.event.inputs.version }}\" \\\n            -m \"Production deployment: ${{ github.event.inputs.version }}\"\n          git push origin \"prod-${{ github.event.inputs.version }}\"\n",[5173,11458,11459,11464,11472,11476,11482,11491,11497,11504,11513,11521,11530,11534,11540,11547,11555,11561,11570,11579,11583,11589,11599,11607,11611,11622,11630,11636,11644,11652,11661,11669,11673,11678,11682,11686,11690,11701,11709,11714,11719,11724,11728,11732,11737,11741,11745,11749,11760,11768,11773,11778],{"__ignoreMap":5245},[5249,11460,11461],{"class":5251,"line":5252},[5249,11462,11463],{"class":5255},"# .github\u002Fworkflows\u002Fdeploy-production.yml\n",[5249,11465,11466,11468,11470],{"class":5251,"line":5259},[5249,11467,6151],{"class":6150},[5249,11469,6155],{"class":6154},[5249,11471,9106],{"class":6158},[5249,11473,11474],{"class":5251,"line":5273},[5249,11475,5306],{"emptyLinePlaceholder":5305},[5249,11477,11478,11480],{"class":5251,"line":5288},[5249,11479,6171],{"class":5281},[5249,11481,6185],{"class":6154},[5249,11483,11484,11486,11488],{"class":5251,"line":5302},[5249,11485,6564],{"class":6150},[5249,11487,6174],{"class":6154},[5249,11489,11490],{"class":5255},"# Ручний запуск\n",[5249,11492,11493,11495],{"class":5251,"line":5309},[5249,11494,6571],{"class":6150},[5249,11496,6185],{"class":6154},[5249,11498,11499,11502],{"class":5251,"line":5315},[5249,11500,11501],{"class":6150},"      version",[5249,11503,6185],{"class":6154},[5249,11505,11506,11508,11510],{"class":5251,"line":5324},[5249,11507,6585],{"class":6150},[5249,11509,6155],{"class":6154},[5249,11511,11512],{"class":6158},"'Version to deploy'\n",[5249,11514,11515,11517,11519],{"class":5251,"line":5329},[5249,11516,6595],{"class":6150},[5249,11518,6155],{"class":6154},[5249,11520,6600],{"class":5281},[5249,11522,11523,11525,11527],{"class":5251,"line":5335},[5249,11524,6605],{"class":6150},[5249,11526,6155],{"class":6154},[5249,11528,11529],{"class":6158},"'latest'\n",[5249,11531,11532],{"class":5251,"line":5344},[5249,11533,5306],{"emptyLinePlaceholder":5305},[5249,11535,11536,11538],{"class":5251,"line":5356},[5249,11537,6225],{"class":6150},[5249,11539,6185],{"class":6154},[5249,11541,11542,11545],{"class":5251,"line":5361},[5249,11543,11544],{"class":6150},"  deploy-production",[5249,11546,6185],{"class":6154},[5249,11548,11549,11551,11553],{"class":5251,"line":5367},[5249,11550,6242],{"class":6150},[5249,11552,6155],{"class":6154},[5249,11554,6792],{"class":6158},[5249,11556,11557,11559],{"class":5251,"line":5376},[5249,11558,11235],{"class":6150},[5249,11560,6185],{"class":6154},[5249,11562,11563,11565,11567],{"class":5251,"line":5381},[5249,11564,11242],{"class":6150},[5249,11566,6155],{"class":6154},[5249,11568,11569],{"class":6158},"production\n",[5249,11571,11572,11574,11576],{"class":5251,"line":5387},[5249,11573,11252],{"class":6150},[5249,11575,6155],{"class":6154},[5249,11577,11578],{"class":6158},"https:\u002F\u002Fexample.com\n",[5249,11580,11581],{"class":5251,"line":5398},[5249,11582,5306],{"emptyLinePlaceholder":5305},[5249,11584,11585,11587],{"class":5251,"line":5403},[5249,11586,6255],{"class":6150},[5249,11588,6185],{"class":6154},[5249,11590,11591,11593,11595,11597],{"class":5251,"line":5409},[5249,11592,6265],{"class":6154},[5249,11594,6151],{"class":6150},[5249,11596,6155],{"class":6154},[5249,11598,6272],{"class":6158},[5249,11600,11601,11603,11605],{"class":5251,"line":5419},[5249,11602,6277],{"class":6150},[5249,11604,6155],{"class":6154},[5249,11606,6282],{"class":6158},[5249,11608,11609],{"class":5251,"line":5424},[5249,11610,5306],{"emptyLinePlaceholder":5305},[5249,11612,11613,11615,11617,11619],{"class":5251,"line":5430},[5249,11614,6265],{"class":6154},[5249,11616,6151],{"class":6150},[5249,11618,6155],{"class":6154},[5249,11620,11621],{"class":6158},"Deploy to production\n",[5249,11623,11624,11626,11628],{"class":5251,"line":5442},[5249,11625,6277],{"class":6150},[5249,11627,6155],{"class":6154},[5249,11629,9168],{"class":6158},[5249,11631,11632,11634],{"class":5251,"line":5447},[5249,11633,6312],{"class":6150},[5249,11635,6185],{"class":6154},[5249,11637,11638,11640,11642],{"class":5251,"line":5453},[5249,11639,10009],{"class":6150},[5249,11641,6155],{"class":6154},[5249,11643,9185],{"class":6158},[5249,11645,11646,11648,11650],{"class":5251,"line":5698},[5249,11647,8403],{"class":6150},[5249,11649,6155],{"class":6154},[5249,11651,9195],{"class":6158},[5249,11653,11654,11656,11658],{"class":5251,"line":5704},[5249,11655,10028],{"class":6150},[5249,11657,6155],{"class":6154},[5249,11659,11660],{"class":6158},"${{ secrets.PROD_SSH_KEY }}\n",[5249,11662,11663,11665,11667],{"class":5251,"line":5710},[5249,11664,10038],{"class":6150},[5249,11666,6155],{"class":6154},[5249,11668,8478],{"class":7333},[5249,11670,11671],{"class":5251,"line":5716},[5249,11672,10047],{"class":6158},[5249,11674,11675],{"class":5251,"line":5722},[5249,11676,11677],{"class":6158},"            export IMAGE_TAG=${{ github.event.inputs.version }}\n",[5249,11679,11680],{"class":5251,"line":5728},[5249,11681,10052],{"class":6158},[5249,11683,11684],{"class":5251,"line":5734},[5249,11685,10057],{"class":6158},[5249,11687,11688],{"class":5251,"line":5740},[5249,11689,5306],{"emptyLinePlaceholder":5305},[5249,11691,11692,11694,11696,11698],{"class":5251,"line":5746},[5249,11693,6265],{"class":6154},[5249,11695,6151],{"class":6150},[5249,11697,6155],{"class":6154},[5249,11699,11700],{"class":6158},"Health check\n",[5249,11702,11703,11705,11707],{"class":5251,"line":5752},[5249,11704,6345],{"class":6150},[5249,11706,6155],{"class":6154},[5249,11708,8478],{"class":7333},[5249,11710,11711],{"class":5251,"line":5758},[5249,11712,11713],{"class":6158},"          for i in {1..10}; do\n",[5249,11715,11716],{"class":5251,"line":5763},[5249,11717,11718],{"class":6158},"            if curl -f https:\u002F\u002Fexample.com\u002Fhealth; then\n",[5249,11720,11721],{"class":5251,"line":5769},[5249,11722,11723],{"class":6158},"              echo \"Production healthy\"\n",[5249,11725,11726],{"class":5251,"line":5775},[5249,11727,10100],{"class":6158},[5249,11729,11730],{"class":5251,"line":5781},[5249,11731,10105],{"class":6158},[5249,11733,11734],{"class":5251,"line":5787},[5249,11735,11736],{"class":6158},"            sleep 5\n",[5249,11738,11739],{"class":5251,"line":5793},[5249,11740,10120],{"class":6158},[5249,11742,11743],{"class":5251,"line":5799},[5249,11744,10130],{"class":6158},[5249,11746,11747],{"class":5251,"line":5805},[5249,11748,5306],{"emptyLinePlaceholder":5305},[5249,11750,11751,11753,11755,11757],{"class":5251,"line":5811},[5249,11752,6265],{"class":6154},[5249,11754,6151],{"class":6150},[5249,11756,6155],{"class":6154},[5249,11758,11759],{"class":6158},"Create deployment tag\n",[5249,11761,11762,11764,11766],{"class":5251,"line":5817},[5249,11763,6345],{"class":6150},[5249,11765,6155],{"class":6154},[5249,11767,8478],{"class":7333},[5249,11769,11770],{"class":5251,"line":5822},[5249,11771,11772],{"class":6158},"          git tag -a \"prod-${{ github.event.inputs.version }}\" \\\n",[5249,11774,11775],{"class":5251,"line":5828},[5249,11776,11777],{"class":6158},"            -m \"Production deployment: ${{ github.event.inputs.version }}\"\n",[5249,11779,11780],{"class":5251,"line":5834},[5249,11781,11782],{"class":6158},"          git push origin \"prod-${{ github.event.inputs.version }}\"\n",[5849,11784,11786],{"id":11785},"github-environments-з-protection-rules","GitHub Environments з Protection Rules",[5155,11788,8627,11789],{},[5118,11790,11791],{},"Settings → Environments → New environment",[5155,11793,11794,11795,11798],{},"Створіть ",[5173,11796,11797],{},"production"," environment з правилами:",[5467,11800,11801,11807,11813],{},[5095,11802,11803,11806],{},[5118,11804,11805],{},"Required reviewers"," — deployment потребує approval від senior developers.",[5095,11808,11809,11812],{},[5118,11810,11811],{},"Wait timer"," — 5-хвилинна затримка перед deployment для можливості cancel.",[5095,11814,11815,11818,11819,11821],{},[5118,11816,11817],{},"Deployment branches"," — лише ",[5173,11820,6196],{}," гілка може деплоїтися у production.",[5155,11823,11824],{},"Тепер при запуску production deployment workflow:",[5467,11826,11827,11830,11833],{},[5095,11828,11829],{},"Workflow чекає на approval від reviewers.",[5095,11831,11832],{},"Після approval чекає 5 хвилин (wait timer).",[5095,11834,11835],{},"Виконує deployment.",[11837,11838,11839,11842],"tip",{},[5118,11840,11841],{},"Manual approval"," корисний для production deployments, щоб senior developer міг перевірити зміни перед deploy. Для staging використовуйте automatic deployment без approval.",[5147,11844],{},[5150,11846,6095],{"id":11847},"secrets-management",[5849,11849,11851],{"id":11850},"github-secrets","GitHub Secrets",[5155,11853,11854],{},"GitHub Secrets дозволяють безпечно зберігати sensitive data (паролі, API keys) без комітів у Git.",[5155,11856,11857],{},[5118,11858,11859],{},"Типи secrets:",[5467,11861,11862,11868,11873],{},[5095,11863,11864,11867],{},[5118,11865,11866],{},"Repository secrets"," — доступні лише у цьому репозиторії.",[5095,11869,11870,11872],{},[5118,11871,5211],{}," — специфічні для environment (staging, production).",[5095,11874,11875,11878],{},[5118,11876,11877],{},"Organization secrets"," — доступні у всіх repos організації.",[5155,11880,11881],{},[5118,11882,11883],{},"Додавання secrets:",[5155,11885,11886,11887],{},"GitHub repo → ",[5118,11888,8630],{},[5155,11890,11891],{},[5118,11892,11893],{},"Типові secrets для NestJS проєкту:",[5240,11895,11900],{"className":11896,"code":11898,"language":11899},[11897],"language-text","# Docker Registry\nDOCKER_USERNAME\nDOCKER_PASSWORD\n\n# Deployment\nPROD_SERVER_HOST\nPROD_SERVER_USER\nPROD_SSH_KEY\n\n# Database (для E2E тестів у CI)\nTEST_DATABASE_URL\n\n# External Services\nSLACK_WEBHOOK_URL\nAWS_ACCESS_KEY_ID\nAWS_SECRET_ACCESS_KEY\n\n# Application\nJWT_SECRET\nSMTP_PASSWORD\nSTRIPE_SECRET_KEY\n","text",[5173,11901,11898],{"__ignoreMap":5245},[5849,11903,11905],{"id":11904},"використання-secrets-у-workflow","Використання secrets у workflow",[5240,11907,11909],{"className":6141,"code":11908,"language":6143,"meta":5245,"style":5245},"steps:\n  - name: Login to Docker Hub\n    uses: docker\u002Flogin-action@v2\n    with:\n      username: ${{ secrets.DOCKER_USERNAME }}\n      password: ${{ secrets.DOCKER_PASSWORD }}\n\n  - name: Run E2E tests\n    env:\n      DATABASE_URL: ${{ secrets.TEST_DATABASE_URL }}\n      JWT_SECRET: ${{ secrets.JWT_SECRET }}\n    run: npm run test:e2e\n\n  - name: Deploy\n    uses: appleboy\u002Fssh-action@v0.1.10\n    with:\n      host: ${{ secrets.PROD_SERVER_HOST }}\n      key: ${{ secrets.PROD_SSH_KEY }}\n",[5173,11910,11911,11918,11930,11939,11946,11955,11964,11968,11978,11984,11994,12004,12013,12017,12028,12036,12042,12051],{"__ignoreMap":5245},[5249,11912,11913,11916],{"class":5251,"line":5252},[5249,11914,11915],{"class":6150},"steps",[5249,11917,6185],{"class":6154},[5249,11919,11920,11923,11925,11927],{"class":5251,"line":5259},[5249,11921,11922],{"class":6154},"  - ",[5249,11924,6151],{"class":6150},[5249,11926,6155],{"class":6154},[5249,11928,11929],{"class":6158},"Login to Docker Hub\n",[5249,11931,11932,11935,11937],{"class":5251,"line":5273},[5249,11933,11934],{"class":6150},"    uses",[5249,11936,6155],{"class":6154},[5249,11938,8392],{"class":6158},[5249,11940,11941,11944],{"class":5251,"line":5288},[5249,11942,11943],{"class":6150},"    with",[5249,11945,6185],{"class":6154},[5249,11947,11948,11951,11953],{"class":5251,"line":5302},[5249,11949,11950],{"class":6150},"      username",[5249,11952,6155],{"class":6154},[5249,11954,8408],{"class":6158},[5249,11956,11957,11960,11962],{"class":5251,"line":5309},[5249,11958,11959],{"class":6150},"      password",[5249,11961,6155],{"class":6154},[5249,11963,8418],{"class":6158},[5249,11965,11966],{"class":5251,"line":5315},[5249,11967,5306],{"emptyLinePlaceholder":5305},[5249,11969,11970,11972,11974,11976],{"class":5251,"line":5324},[5249,11971,11922],{"class":6154},[5249,11973,6151],{"class":6150},[5249,11975,6155],{"class":6154},[5249,11977,7572],{"class":6158},[5249,11979,11980,11982],{"class":5251,"line":5329},[5249,11981,7878],{"class":6150},[5249,11983,6185],{"class":6154},[5249,11985,11986,11989,11991],{"class":5251,"line":5335},[5249,11987,11988],{"class":6150},"      DATABASE_URL",[5249,11990,6155],{"class":6154},[5249,11992,11993],{"class":6158},"${{ secrets.TEST_DATABASE_URL }}\n",[5249,11995,11996,11999,12001],{"class":5251,"line":5344},[5249,11997,11998],{"class":6150},"      JWT_SECRET",[5249,12000,6155],{"class":6154},[5249,12002,12003],{"class":6158},"${{ secrets.JWT_SECRET }}\n",[5249,12005,12006,12009,12011],{"class":5251,"line":5356},[5249,12007,12008],{"class":6150},"    run",[5249,12010,6155],{"class":6154},[5249,12012,7582],{"class":6158},[5249,12014,12015],{"class":5251,"line":5361},[5249,12016,5306],{"emptyLinePlaceholder":5305},[5249,12018,12019,12021,12023,12025],{"class":5251,"line":5367},[5249,12020,11922],{"class":6154},[5249,12022,6151],{"class":6150},[5249,12024,6155],{"class":6154},[5249,12026,12027],{"class":6158},"Deploy\n",[5249,12029,12030,12032,12034],{"class":5251,"line":5376},[5249,12031,11934],{"class":6150},[5249,12033,6155],{"class":6154},[5249,12035,9168],{"class":6158},[5249,12037,12038,12040],{"class":5251,"line":5381},[5249,12039,11943],{"class":6150},[5249,12041,6185],{"class":6154},[5249,12043,12044,12047,12049],{"class":5251,"line":5387},[5249,12045,12046],{"class":6150},"      host",[5249,12048,6155],{"class":6154},[5249,12050,9185],{"class":6158},[5249,12052,12053,12056,12058],{"class":5251,"line":5398},[5249,12054,12055],{"class":6150},"      key",[5249,12057,6155],{"class":6154},[5249,12059,11660],{"class":6158},[5849,12061,12063],{"id":12062},"environment-specific-secrets","Environment-specific Secrets",[5155,12065,12066],{},"Створіть окремі environments у GitHub та додайте специфічні secrets:",[5155,12068,12069],{},[5118,12070,12071],{},"Staging environment:",[5092,12073,12074,12083],{},[5095,12075,12076,12079,12080],{},[5173,12077,12078],{},"DATABASE_URL"," → ",[5173,12081,12082],{},"postgres:\u002F\u002Fstaging-db.example.com\u002Fblog_staging",[5095,12084,12085,12079,12088],{},[5173,12086,12087],{},"API_URL",[5173,12089,12090],{},"https:\u002F\u002Fstaging-api.example.com",[5155,12092,12093],{},[5118,12094,12095],{},"Production environment:",[5092,12097,12098,12105],{},[5095,12099,12100,12079,12102],{},[5173,12101,12078],{},[5173,12103,12104],{},"postgres:\u002F\u002Fprod-db.example.com\u002Fblog_prod",[5095,12106,12107,12079,12109],{},[5173,12108,12087],{},[5173,12110,12111],{},"https:\u002F\u002Fapi.example.com",[5155,12113,12114],{},"У workflow вказуйте environment:",[5240,12116,12118],{"className":6141,"code":12117,"language":6143,"meta":5245,"style":5245},"jobs:\n  deploy:\n    runs-on: ubuntu-latest\n    environment: production  # Використовує production secrets\n\n    steps:\n      - name: Deploy\n        env:\n          DATABASE_URL: ${{ secrets.DATABASE_URL }}  # production DATABASE_URL\n",[5173,12119,12120,12126,12132,12140,12151,12155,12161,12171,12177],{"__ignoreMap":5245},[5249,12121,12122,12124],{"class":5251,"line":5252},[5249,12123,6225],{"class":6150},[5249,12125,6185],{"class":6154},[5249,12127,12128,12130],{"class":5251,"line":5259},[5249,12129,9959],{"class":6150},[5249,12131,6185],{"class":6154},[5249,12133,12134,12136,12138],{"class":5251,"line":5273},[5249,12135,6242],{"class":6150},[5249,12137,6155],{"class":6154},[5249,12139,6792],{"class":6158},[5249,12141,12142,12144,12146,12148],{"class":5251,"line":5288},[5249,12143,11235],{"class":6150},[5249,12145,6155],{"class":6154},[5249,12147,11797],{"class":6158},[5249,12149,12150],{"class":5255},"  # Використовує production secrets\n",[5249,12152,12153],{"class":5251,"line":5302},[5249,12154,5306],{"emptyLinePlaceholder":5305},[5249,12156,12157,12159],{"class":5251,"line":5309},[5249,12158,6255],{"class":6150},[5249,12160,6185],{"class":6154},[5249,12162,12163,12165,12167,12169],{"class":5251,"line":5315},[5249,12164,6265],{"class":6154},[5249,12166,6151],{"class":6150},[5249,12168,6155],{"class":6154},[5249,12170,12027],{"class":6158},[5249,12172,12173,12175],{"class":5251,"line":5324},[5249,12174,7288],{"class":6150},[5249,12176,6185],{"class":6154},[5249,12178,12179,12182,12184,12186],{"class":5251,"line":5329},[5249,12180,12181],{"class":6150},"          DATABASE_URL",[5249,12183,6155],{"class":6154},[5249,12185,5215],{"class":6158},[5249,12187,12188],{"class":5255},"  # production DATABASE_URL\n",[9598,12190,12191,12201],{},[5155,12192,12193,12196,12197,12200],{},[5118,12194,12195],{},"Ніколи не виводьте secrets у логи!"," GitHub автоматично маскує secrets у logs, але уникайте ",[5173,12198,12199],{},"echo ${{ secrets.SECRET }}"," або передачі secrets як arguments команд. Використовуйте environment variables:",[5240,12202,12204],{"className":6141,"code":12203,"language":6143,"meta":5245,"style":5245},"# ❌ ПОГАНА ПРАКТИКА\nrun: curl -H \"Authorization: Bearer ${{ secrets.API_KEY }}\" ...\n\n# ✅ ХОРОША ПРАКТИКА\nenv:\n  API_KEY: ${{ secrets.API_KEY }}\nrun: curl -H \"Authorization: Bearer $API_KEY\" ...\n",[5173,12205,12206,12211,12226,12230,12235,12242,12252],{"__ignoreMap":5245},[5249,12207,12208],{"class":5251,"line":5252},[5249,12209,12210],{"class":5255},"# ❌ ПОГАНА ПРАКТИКА\n",[5249,12212,12213,12216,12218,12221,12223],{"class":5251,"line":5259},[5249,12214,12215],{"class":6150},"run",[5249,12217,6155],{"class":6154},[5249,12219,12220],{"class":6150},"curl -H \"Authorization",[5249,12222,6155],{"class":6154},[5249,12224,12225],{"class":6158},"Bearer ${{ secrets.API_KEY }}\" ...\n",[5249,12227,12228],{"class":5251,"line":5273},[5249,12229,5306],{"emptyLinePlaceholder":5305},[5249,12231,12232],{"class":5251,"line":5288},[5249,12233,12234],{"class":5255},"# ✅ ХОРОША ПРАКТИКА\n",[5249,12236,12237,12240],{"class":5251,"line":5302},[5249,12238,12239],{"class":6150},"env",[5249,12241,6185],{"class":6154},[5249,12243,12244,12247,12249],{"class":5251,"line":5309},[5249,12245,12246],{"class":6150},"  API_KEY",[5249,12248,6155],{"class":6154},[5249,12250,12251],{"class":6158},"${{ secrets.API_KEY }}\n",[5249,12253,12254,12256,12258,12260,12262],{"class":5251,"line":5315},[5249,12255,12215],{"class":6150},[5249,12257,6155],{"class":6154},[5249,12259,12220],{"class":6150},[5249,12261,6155],{"class":6154},[5249,12263,12264],{"class":6158},"Bearer $API_KEY\" ...\n",[5147,12266],{},[5150,12268,12270],{"id":12269},"notifications-та-monitoring","Notifications та Monitoring",[5155,12272,12273],{},"Налаштування notifications для інформування команди про статус deployments.",[5849,12275,12277],{"id":12276},"slack-notifications","Slack Notifications",[5240,12279,12281],{"className":6141,"code":12280,"language":6143,"meta":5245,"style":5245},"# .github\u002Fworkflows\u002Fci-cd.yml\njobs:\n  deploy:\n    runs-on: ubuntu-latest\n    # ... deployment steps\n\n  notify:\n    name: Notify Team\n    runs-on: ubuntu-latest\n    needs: deploy\n    if: always()  # Виконується навіть якщо deploy failed\n\n    steps:\n      - name: Notify Slack on Success\n        if: needs.deploy.result == 'success'\n        uses: slackapi\u002Fslack-github-action@v1.24.0\n        with:\n          webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }}\n          payload: |\n            {\n              \"text\": \"✅ Deployment Successful\",\n              \"blocks\": [\n                {\n                  \"type\": \"header\",\n                  \"text\": {\n                    \"type\": \"plain_text\",\n                    \"text\": \"✅ Production Deployment Successful\"\n                  }\n                },\n                {\n                  \"type\": \"section\",\n                  \"fields\": [\n                    {\n                      \"type\": \"mrkdwn\",\n                      \"text\": \"*Repository:*\\n${{ github.repository }}\"\n                    },\n                    {\n                      \"type\": \"mrkdwn\",\n                      \"text\": \"*Branch:*\\n${{ github.ref_name }}\"\n                    },\n                    {\n                      \"type\": \"mrkdwn\",\n                      \"text\": \"*Commit:*\\n\u003C${{ github.event.head_commit.url }}|${{ github.sha }}>\"\n                    },\n                    {\n                      \"type\": \"mrkdwn\",\n                      \"text\": \"*Author:*\\n${{ github.actor }}\"\n                    }\n                  ]\n                },\n                {\n                  \"type\": \"section\",\n                  \"text\": {\n                    \"type\": \"mrkdwn\",\n                    \"text\": \"*Commit Message:*\\n${{ github.event.head_commit.message }}\"\n                  }\n                },\n                {\n                  \"type\": \"actions\",\n                  \"elements\": [\n                    {\n                      \"type\": \"button\",\n                      \"text\": {\n                        \"type\": \"plain_text\",\n                        \"text\": \"View Deployment\"\n                      },\n                      \"url\": \"https:\u002F\u002Fexample.com\"\n                    },\n                    {\n                      \"type\": \"button\",\n                      \"text\": {\n                        \"type\": \"plain_text\",\n                        \"text\": \"View Workflow\"\n                      },\n                      \"url\": \"${{ github.server_url }}\u002F${{ github.repository }}\u002Factions\u002Fruns\u002F${{ github.run_id }}\"\n                    }\n                  ]\n                }\n              ]\n            }\n\n      - name: Notify Slack on Failure\n        if: needs.deploy.result == 'failure'\n        uses: slackapi\u002Fslack-github-action@v1.24.0\n        with:\n          webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }}\n          payload: |\n            {\n              \"text\": \"❌ Deployment Failed\",\n              \"blocks\": [\n                {\n                  \"type\": \"header\",\n                  \"text\": {\n                    \"type\": \"plain_text\",\n                    \"text\": \"❌ Production Deployment Failed\"\n                  }\n                },\n                {\n                  \"type\": \"section\",\n                  \"fields\": [\n                    {\n                      \"type\": \"mrkdwn\",\n                      \"text\": \"*Repository:*\\n${{ github.repository }}\"\n                    },\n                    {\n                      \"type\": \"mrkdwn\",\n                      \"text\": \"*Branch:*\\n${{ github.ref_name }}\"\n                    },\n                    {\n                      \"type\": \"mrkdwn\",\n                      \"text\": \"*Commit:*\\n\u003C${{ github.event.head_commit.url }}|${{ github.sha }}>\"\n                    },\n                    {\n                      \"type\": \"mrkdwn\",\n                      \"text\": \"*Author:*\\n${{ github.actor }}\"\n                    }\n                  ]\n                },\n                {\n                  \"type\": \"section\",\n                  \"text\": {\n                    \"type\": \"mrkdwn\",\n                    \"text\": \"⚠️ *Action Required:* Check logs and fix the issue or rollback to previous version.\"\n                  }\n                },\n                {\n                  \"type\": \"actions\",\n                  \"elements\": [\n                    {\n                      \"type\": \"button\",\n                      \"text\": {\n                        \"type\": \"plain_text\",\n                        \"text\": \"View Logs\"\n                      },\n                      \"url\": \"${{ github.server_url }}\u002F${{ github.repository }}\u002Factions\u002Fruns\u002F${{ github.run_id }}\",\n                      \"style\": \"danger\"\n                    }\n                  ]\n                }\n              ]\n            }\n",[5173,12282,12283,12287,12293,12299,12307,12312,12316,12323,12332,12340,12349,12361,12365,12371,12382,12391,12399,12405,12414,12423,12428,12433,12438,12443,12448,12453,12458,12463,12468,12473,12477,12482,12487,12492,12497,12502,12507,12511,12515,12520,12524,12528,12532,12537,12541,12545,12549,12554,12559,12564,12568,12572,12576,12580,12585,12590,12594,12598,12602,12607,12612,12616,12621,12626,12631,12636,12641,12646,12650,12654,12658,12662,12666,12671,12675,12680,12684,12688,12693,12697,12702,12706,12717,12726,12734,12740,12748,12756,12760,12765,12769,12773,12777,12781,12785,12790,12794,12798,12802,12806,12810,12814,12818,12822,12826,12830,12834,12838,12842,12846,12850,12854,12858,12862,12866,12870,12874,12878,12882,12886,12890,12894,12898,12903,12907,12911,12916,12921,12926,12931,12936,12941,12946,12952,12957,12963,12969,12974,12979,12984,12989],{"__ignoreMap":5245},[5249,12284,12285],{"class":5251,"line":5252},[5249,12286,8220],{"class":5255},[5249,12288,12289,12291],{"class":5251,"line":5259},[5249,12290,6225],{"class":6150},[5249,12292,6185],{"class":6154},[5249,12294,12295,12297],{"class":5251,"line":5273},[5249,12296,9959],{"class":6150},[5249,12298,6185],{"class":6154},[5249,12300,12301,12303,12305],{"class":5251,"line":5288},[5249,12302,6242],{"class":6150},[5249,12304,6155],{"class":6154},[5249,12306,6792],{"class":6158},[5249,12308,12309],{"class":5251,"line":5302},[5249,12310,12311],{"class":5255},"    # ... deployment steps\n",[5249,12313,12314],{"class":5251,"line":5309},[5249,12315,5306],{"emptyLinePlaceholder":5305},[5249,12317,12318,12321],{"class":5251,"line":5315},[5249,12319,12320],{"class":6150},"  notify",[5249,12322,6185],{"class":6154},[5249,12324,12325,12327,12329],{"class":5251,"line":5324},[5249,12326,6778],{"class":6150},[5249,12328,6155],{"class":6154},[5249,12330,12331],{"class":6158},"Notify Team\n",[5249,12333,12334,12336,12338],{"class":5251,"line":5329},[5249,12335,6242],{"class":6150},[5249,12337,6155],{"class":6154},[5249,12339,6792],{"class":6158},[5249,12341,12342,12344,12346],{"class":5251,"line":5335},[5249,12343,6968],{"class":6150},[5249,12345,6155],{"class":6154},[5249,12347,12348],{"class":6158},"deploy\n",[5249,12350,12351,12354,12356,12358],{"class":5251,"line":5344},[5249,12352,12353],{"class":6150},"    if",[5249,12355,6155],{"class":6154},[5249,12357,8002],{"class":6158},[5249,12359,12360],{"class":5255},"  # Виконується навіть якщо deploy failed\n",[5249,12362,12363],{"class":5251,"line":5356},[5249,12364,5306],{"emptyLinePlaceholder":5305},[5249,12366,12367,12369],{"class":5251,"line":5361},[5249,12368,6255],{"class":6150},[5249,12370,6185],{"class":6154},[5249,12372,12373,12375,12377,12379],{"class":5251,"line":5367},[5249,12374,6265],{"class":6154},[5249,12376,6151],{"class":6150},[5249,12378,6155],{"class":6154},[5249,12380,12381],{"class":6158},"Notify Slack on Success\n",[5249,12383,12384,12386,12388],{"class":5251,"line":5376},[5249,12385,7685],{"class":6150},[5249,12387,6155],{"class":6154},[5249,12389,12390],{"class":6158},"needs.deploy.result == 'success'\n",[5249,12392,12393,12395,12397],{"class":5251,"line":5381},[5249,12394,6277],{"class":6150},[5249,12396,6155],{"class":6154},[5249,12398,9302],{"class":6158},[5249,12400,12401,12403],{"class":5251,"line":5387},[5249,12402,6312],{"class":6150},[5249,12404,6185],{"class":6154},[5249,12406,12407,12410,12412],{"class":5251,"line":5398},[5249,12408,12409],{"class":6150},"          webhook-url",[5249,12411,6155],{"class":6154},[5249,12413,9318],{"class":6158},[5249,12415,12416,12419,12421],{"class":5251,"line":5403},[5249,12417,12418],{"class":6150},"          payload",[5249,12420,6155],{"class":6154},[5249,12422,8478],{"class":7333},[5249,12424,12425],{"class":5251,"line":5409},[5249,12426,12427],{"class":6158},"            {\n",[5249,12429,12430],{"class":5251,"line":5419},[5249,12431,12432],{"class":6158},"              \"text\": \"✅ Deployment Successful\",\n",[5249,12434,12435],{"class":5251,"line":5424},[5249,12436,12437],{"class":6158},"              \"blocks\": [\n",[5249,12439,12440],{"class":5251,"line":5430},[5249,12441,12442],{"class":6158},"                {\n",[5249,12444,12445],{"class":5251,"line":5442},[5249,12446,12447],{"class":6158},"                  \"type\": \"header\",\n",[5249,12449,12450],{"class":5251,"line":5447},[5249,12451,12452],{"class":6158},"                  \"text\": {\n",[5249,12454,12455],{"class":5251,"line":5453},[5249,12456,12457],{"class":6158},"                    \"type\": \"plain_text\",\n",[5249,12459,12460],{"class":5251,"line":5698},[5249,12461,12462],{"class":6158},"                    \"text\": \"✅ Production Deployment Successful\"\n",[5249,12464,12465],{"class":5251,"line":5704},[5249,12466,12467],{"class":6158},"                  }\n",[5249,12469,12470],{"class":5251,"line":5710},[5249,12471,12472],{"class":6158},"                },\n",[5249,12474,12475],{"class":5251,"line":5716},[5249,12476,12442],{"class":6158},[5249,12478,12479],{"class":5251,"line":5722},[5249,12480,12481],{"class":6158},"                  \"type\": \"section\",\n",[5249,12483,12484],{"class":5251,"line":5728},[5249,12485,12486],{"class":6158},"                  \"fields\": [\n",[5249,12488,12489],{"class":5251,"line":5734},[5249,12490,12491],{"class":6158},"                    {\n",[5249,12493,12494],{"class":5251,"line":5740},[5249,12495,12496],{"class":6158},"                      \"type\": \"mrkdwn\",\n",[5249,12498,12499],{"class":5251,"line":5746},[5249,12500,12501],{"class":6158},"                      \"text\": \"*Repository:*\\n${{ github.repository }}\"\n",[5249,12503,12504],{"class":5251,"line":5752},[5249,12505,12506],{"class":6158},"                    },\n",[5249,12508,12509],{"class":5251,"line":5758},[5249,12510,12491],{"class":6158},[5249,12512,12513],{"class":5251,"line":5763},[5249,12514,12496],{"class":6158},[5249,12516,12517],{"class":5251,"line":5769},[5249,12518,12519],{"class":6158},"                      \"text\": \"*Branch:*\\n${{ github.ref_name }}\"\n",[5249,12521,12522],{"class":5251,"line":5775},[5249,12523,12506],{"class":6158},[5249,12525,12526],{"class":5251,"line":5781},[5249,12527,12491],{"class":6158},[5249,12529,12530],{"class":5251,"line":5787},[5249,12531,12496],{"class":6158},[5249,12533,12534],{"class":5251,"line":5793},[5249,12535,12536],{"class":6158},"                      \"text\": \"*Commit:*\\n\u003C${{ github.event.head_commit.url }}|${{ github.sha }}>\"\n",[5249,12538,12539],{"class":5251,"line":5799},[5249,12540,12506],{"class":6158},[5249,12542,12543],{"class":5251,"line":5805},[5249,12544,12491],{"class":6158},[5249,12546,12547],{"class":5251,"line":5811},[5249,12548,12496],{"class":6158},[5249,12550,12551],{"class":5251,"line":5817},[5249,12552,12553],{"class":6158},"                      \"text\": \"*Author:*\\n${{ github.actor }}\"\n",[5249,12555,12556],{"class":5251,"line":5822},[5249,12557,12558],{"class":6158},"                    }\n",[5249,12560,12561],{"class":5251,"line":5828},[5249,12562,12563],{"class":6158},"                  ]\n",[5249,12565,12566],{"class":5251,"line":5834},[5249,12567,12472],{"class":6158},[5249,12569,12570],{"class":5251,"line":5839},[5249,12571,12442],{"class":6158},[5249,12573,12574],{"class":5251,"line":7092},[5249,12575,12481],{"class":6158},[5249,12577,12578],{"class":5251,"line":7097},[5249,12579,12452],{"class":6158},[5249,12581,12582],{"class":5251,"line":7108},[5249,12583,12584],{"class":6158},"                    \"type\": \"mrkdwn\",\n",[5249,12586,12587],{"class":5251,"line":7117},[5249,12588,12589],{"class":6158},"                    \"text\": \"*Commit Message:*\\n${{ github.event.head_commit.message }}\"\n",[5249,12591,12592],{"class":5251,"line":7122},[5249,12593,12467],{"class":6158},[5249,12595,12596],{"class":5251,"line":7134},[5249,12597,12472],{"class":6158},[5249,12599,12600],{"class":5251,"line":7144},[5249,12601,12442],{"class":6158},[5249,12603,12604],{"class":5251,"line":7149},[5249,12605,12606],{"class":6158},"                  \"type\": \"actions\",\n",[5249,12608,12609],{"class":5251,"line":7161},[5249,12610,12611],{"class":6158},"                  \"elements\": [\n",[5249,12613,12614],{"class":5251,"line":7171},[5249,12615,12491],{"class":6158},[5249,12617,12618],{"class":5251,"line":7178},[5249,12619,12620],{"class":6158},"                      \"type\": \"button\",\n",[5249,12622,12623],{"class":5251,"line":7189},[5249,12624,12625],{"class":6158},"                      \"text\": {\n",[5249,12627,12628],{"class":5251,"line":7200},[5249,12629,12630],{"class":6158},"                        \"type\": \"plain_text\",\n",[5249,12632,12633],{"class":5251,"line":7211},[5249,12634,12635],{"class":6158},"                        \"text\": \"View Deployment\"\n",[5249,12637,12638],{"class":5251,"line":7216},[5249,12639,12640],{"class":6158},"                      },\n",[5249,12642,12643],{"class":5251,"line":7224},[5249,12644,12645],{"class":6158},"                      \"url\": \"https:\u002F\u002Fexample.com\"\n",[5249,12647,12648],{"class":5251,"line":7234},[5249,12649,12506],{"class":6158},[5249,12651,12652],{"class":5251,"line":7243},[5249,12653,12491],{"class":6158},[5249,12655,12656],{"class":5251,"line":7253},[5249,12657,12620],{"class":6158},[5249,12659,12660],{"class":5251,"line":7258},[5249,12661,12625],{"class":6158},[5249,12663,12664],{"class":5251,"line":7266},[5249,12665,12630],{"class":6158},[5249,12667,12668],{"class":5251,"line":7274},[5249,12669,12670],{"class":6158},"                        \"text\": \"View Workflow\"\n",[5249,12672,12673],{"class":5251,"line":7285},[5249,12674,12640],{"class":6158},[5249,12676,12677],{"class":5251,"line":7293},[5249,12678,12679],{"class":6158},"                      \"url\": \"${{ github.server_url }}\u002F${{ github.repository }}\u002Factions\u002Fruns\u002F${{ github.run_id }}\"\n",[5249,12681,12682],{"class":5251,"line":7304},[5249,12683,12558],{"class":6158},[5249,12685,12686],{"class":5251,"line":7314},[5249,12687,12563],{"class":6158},[5249,12689,12690],{"class":5251,"line":7325},[5249,12691,12692],{"class":6158},"                }\n",[5249,12694,12695],{"class":5251,"line":7340},[5249,12696,9367],{"class":6158},[5249,12698,12699],{"class":5251,"line":7346},[5249,12700,12701],{"class":6158},"            }\n",[5249,12703,12704],{"class":5251,"line":7352},[5249,12705,5306],{"emptyLinePlaceholder":5305},[5249,12707,12708,12710,12712,12714],{"class":5251,"line":7358},[5249,12709,6265],{"class":6154},[5249,12711,6151],{"class":6150},[5249,12713,6155],{"class":6154},[5249,12715,12716],{"class":6158},"Notify Slack on Failure\n",[5249,12718,12719,12721,12723],{"class":5251,"line":7364},[5249,12720,7685],{"class":6150},[5249,12722,6155],{"class":6154},[5249,12724,12725],{"class":6158},"needs.deploy.result == 'failure'\n",[5249,12727,12728,12730,12732],{"class":5251,"line":7372},[5249,12729,6277],{"class":6150},[5249,12731,6155],{"class":6154},[5249,12733,9302],{"class":6158},[5249,12735,12736,12738],{"class":5251,"line":7381},[5249,12737,6312],{"class":6150},[5249,12739,6185],{"class":6154},[5249,12741,12742,12744,12746],{"class":5251,"line":7386},[5249,12743,12409],{"class":6150},[5249,12745,6155],{"class":6154},[5249,12747,9318],{"class":6158},[5249,12749,12750,12752,12754],{"class":5251,"line":7394},[5249,12751,12418],{"class":6150},[5249,12753,6155],{"class":6154},[5249,12755,8478],{"class":7333},[5249,12757,12758],{"class":5251,"line":7404},[5249,12759,12427],{"class":6158},[5249,12761,12762],{"class":5251,"line":7415},[5249,12763,12764],{"class":6158},"              \"text\": \"❌ Deployment Failed\",\n",[5249,12766,12767],{"class":5251,"line":7421},[5249,12768,12437],{"class":6158},[5249,12770,12771],{"class":5251,"line":7426},[5249,12772,12442],{"class":6158},[5249,12774,12775],{"class":5251,"line":7431},[5249,12776,12447],{"class":6158},[5249,12778,12779],{"class":5251,"line":7436},[5249,12780,12452],{"class":6158},[5249,12782,12783],{"class":5251,"line":7443},[5249,12784,12457],{"class":6158},[5249,12786,12787],{"class":5251,"line":7451},[5249,12788,12789],{"class":6158},"                    \"text\": \"❌ Production Deployment Failed\"\n",[5249,12791,12792],{"class":5251,"line":7456},[5249,12793,12467],{"class":6158},[5249,12795,12796],{"class":5251,"line":7463},[5249,12797,12472],{"class":6158},[5249,12799,12800],{"class":5251,"line":7474},[5249,12801,12442],{"class":6158},[5249,12803,12804],{"class":5251,"line":7483},[5249,12805,12481],{"class":6158},[5249,12807,12808],{"class":5251,"line":7488},[5249,12809,12486],{"class":6158},[5249,12811,12812],{"class":5251,"line":7499},[5249,12813,12491],{"class":6158},[5249,12815,12816],{"class":5251,"line":7508},[5249,12817,12496],{"class":6158},[5249,12819,12820],{"class":5251,"line":7515},[5249,12821,12501],{"class":6158},[5249,12823,12824],{"class":5251,"line":7524},[5249,12825,12506],{"class":6158},[5249,12827,12828],{"class":5251,"line":7533},[5249,12829,12491],{"class":6158},[5249,12831,12832],{"class":5251,"line":7538},[5249,12833,12496],{"class":6158},[5249,12835,12836],{"class":5251,"line":7549},[5249,12837,12519],{"class":6158},[5249,12839,12840],{"class":5251,"line":7558},[5249,12841,12506],{"class":6158},[5249,12843,12844],{"class":5251,"line":7563},[5249,12845,12491],{"class":6158},[5249,12847,12848],{"class":5251,"line":7575},[5249,12849,12496],{"class":6158},[5249,12851,12852],{"class":5251,"line":7585},[5249,12853,12536],{"class":6158},[5249,12855,12856],{"class":5251,"line":7592},[5249,12857,12506],{"class":6158},[5249,12859,12860],{"class":5251,"line":7603},[5249,12861,12491],{"class":6158},[5249,12863,12864],{"class":5251,"line":7614},[5249,12865,12496],{"class":6158},[5249,12867,12868],{"class":5251,"line":7624},[5249,12869,12553],{"class":6158},[5249,12871,12872],{"class":5251,"line":7634},[5249,12873,12558],{"class":6158},[5249,12875,12876],{"class":5251,"line":7644},[5249,12877,12563],{"class":6158},[5249,12879,12880],{"class":5251,"line":7654},[5249,12881,12472],{"class":6158},[5249,12883,12884],{"class":5251,"line":7665},[5249,12885,12442],{"class":6158},[5249,12887,12888],{"class":5251,"line":7670},[5249,12889,12481],{"class":6158},[5249,12891,12892],{"class":5251,"line":7682},[5249,12893,12452],{"class":6158},[5249,12895,12896],{"class":5251,"line":7693},[5249,12897,12584],{"class":6158},[5249,12899,12900],{"class":5251,"line":7703},[5249,12901,12902],{"class":6158},"                    \"text\": \"⚠️ *Action Required:* Check logs and fix the issue or rollback to previous version.\"\n",[5249,12904,12905],{"class":5251,"line":7710},[5249,12906,12467],{"class":6158},[5249,12908,12909],{"class":5251,"line":7720},[5249,12910,12472],{"class":6158},[5249,12912,12914],{"class":5251,"line":12913},126,[5249,12915,12442],{"class":6158},[5249,12917,12919],{"class":5251,"line":12918},127,[5249,12920,12606],{"class":6158},[5249,12922,12924],{"class":5251,"line":12923},128,[5249,12925,12611],{"class":6158},[5249,12927,12929],{"class":5251,"line":12928},129,[5249,12930,12491],{"class":6158},[5249,12932,12934],{"class":5251,"line":12933},130,[5249,12935,12620],{"class":6158},[5249,12937,12939],{"class":5251,"line":12938},131,[5249,12940,12625],{"class":6158},[5249,12942,12944],{"class":5251,"line":12943},132,[5249,12945,12630],{"class":6158},[5249,12947,12949],{"class":5251,"line":12948},133,[5249,12950,12951],{"class":6158},"                        \"text\": \"View Logs\"\n",[5249,12953,12955],{"class":5251,"line":12954},134,[5249,12956,12640],{"class":6158},[5249,12958,12960],{"class":5251,"line":12959},135,[5249,12961,12962],{"class":6158},"                      \"url\": \"${{ github.server_url }}\u002F${{ github.repository }}\u002Factions\u002Fruns\u002F${{ github.run_id }}\",\n",[5249,12964,12966],{"class":5251,"line":12965},136,[5249,12967,12968],{"class":6158},"                      \"style\": \"danger\"\n",[5249,12970,12972],{"class":5251,"line":12971},137,[5249,12973,12558],{"class":6158},[5249,12975,12977],{"class":5251,"line":12976},138,[5249,12978,12563],{"class":6158},[5249,12980,12982],{"class":5251,"line":12981},139,[5249,12983,12692],{"class":6158},[5249,12985,12987],{"class":5251,"line":12986},140,[5249,12988,9367],{"class":6158},[5249,12990,12992],{"class":5251,"line":12991},141,[5249,12993,12701],{"class":6158},[5155,12995,12996],{},[5118,12997,12998],{},"Налаштування Slack Webhook:",[5467,13000,13001,13009,13012,13015,13018],{},[5095,13002,13003,13004,13008],{},"Створіть Slack App: ",[8657,13005,13006],{"href":13006,"rel":13007},"https:\u002F\u002Fapi.slack.com\u002Fapps",[8661]," → Create New App",[5095,13010,13011],{},"Enable Incoming Webhooks",[5095,13013,13014],{},"Add New Webhook to Workspace → вибрати канал (наприклад, #deployments)",[5095,13016,13017],{},"Скопіюйте Webhook URL",[5095,13019,13020,13021],{},"Додайте до GitHub Secrets як ",[5173,13022,13023],{},"SLACK_WEBHOOK_URL",[5849,13025,13027],{"id":13026},"discord-notifications","Discord Notifications",[5240,13029,13031],{"className":6141,"code":13030,"language":6143,"meta":5245,"style":5245},"- name: Notify Discord\n  uses: sarisia\u002Factions-status-discord@v1\n  if: always()\n  with:\n    webhook: ${{ secrets.DISCORD_WEBHOOK }}\n    status: ${{ job.status }}\n    title: \"Deployment to Production\"\n    description: |\n      Commit: ${{ github.sha }}\n      Author: ${{ github.actor }}\n      Message: ${{ github.event.head_commit.message }}\n    color: 0x00ff00  # Green for success\n    url: ${{ github.server_url }}\u002F${{ github.repository }}\u002Factions\u002Fruns\u002F${{ github.run_id }}\n",[5173,13032,13033,13044,13053,13061,13067,13077,13087,13097,13106,13111,13116,13121,13134],{"__ignoreMap":5245},[5249,13034,13035,13037,13039,13041],{"class":5251,"line":5252},[5249,13036,7941],{"class":6154},[5249,13038,6151],{"class":6150},[5249,13040,6155],{"class":6154},[5249,13042,13043],{"class":6158},"Notify Discord\n",[5249,13045,13046,13048,13050],{"class":5251,"line":5259},[5249,13047,8694],{"class":6150},[5249,13049,6155],{"class":6154},[5249,13051,13052],{"class":6158},"sarisia\u002Factions-status-discord@v1\n",[5249,13054,13055,13057,13059],{"class":5251,"line":5273},[5249,13056,7997],{"class":6150},[5249,13058,6155],{"class":6154},[5249,13060,7690],{"class":6158},[5249,13062,13063,13065],{"class":5251,"line":5288},[5249,13064,7953],{"class":6150},[5249,13066,6185],{"class":6154},[5249,13068,13069,13072,13074],{"class":5251,"line":5302},[5249,13070,13071],{"class":6150},"    webhook",[5249,13073,6155],{"class":6154},[5249,13075,13076],{"class":6158},"${{ secrets.DISCORD_WEBHOOK }}\n",[5249,13078,13079,13082,13084],{"class":5251,"line":5309},[5249,13080,13081],{"class":6150},"    status",[5249,13083,6155],{"class":6154},[5249,13085,13086],{"class":6158},"${{ job.status }}\n",[5249,13088,13089,13092,13094],{"class":5251,"line":5315},[5249,13090,13091],{"class":6150},"    title",[5249,13093,6155],{"class":6154},[5249,13095,13096],{"class":5266},"\"Deployment to Production\"\n",[5249,13098,13099,13102,13104],{"class":5251,"line":5324},[5249,13100,13101],{"class":6150},"    description",[5249,13103,6155],{"class":6154},[5249,13105,8478],{"class":7333},[5249,13107,13108],{"class":5251,"line":5329},[5249,13109,13110],{"class":6158},"      Commit: ${{ github.sha }}\n",[5249,13112,13113],{"class":5251,"line":5335},[5249,13114,13115],{"class":6158},"      Author: ${{ github.actor }}\n",[5249,13117,13118],{"class":5251,"line":5344},[5249,13119,13120],{"class":6158},"      Message: ${{ github.event.head_commit.message }}\n",[5249,13122,13123,13126,13128,13131],{"class":5251,"line":5356},[5249,13124,13125],{"class":6150},"    color",[5249,13127,6155],{"class":6154},[5249,13129,13130],{"class":6324},"0x00ff00",[5249,13132,13133],{"class":5255},"  # Green for success\n",[5249,13135,13136,13139,13141],{"class":5251,"line":5361},[5249,13137,13138],{"class":6150},"    url",[5249,13140,6155],{"class":6154},[5249,13142,13143],{"class":6158},"${{ github.server_url }}\u002F${{ github.repository }}\u002Factions\u002Fruns\u002F${{ github.run_id }}\n",[5849,13145,13147],{"id":13146},"email-notifications","Email Notifications",[5155,13149,13150],{},"GitHub автоматично надсилає email notifications для failed workflows, але можна налаштувати custom emails:",[5240,13152,13154],{"className":6141,"code":13153,"language":6143,"meta":5245,"style":5245},"- name: Send email on failure\n  if: failure()\n  uses: dawidd6\u002Faction-send-mail@v3\n  with:\n    server_address: smtp.gmail.com\n    server_port: 587\n    username: ${{ secrets.SMTP_USER }}\n    password: ${{ secrets.SMTP_PASSWORD }}\n    subject: \"❌ Deployment Failed: ${{ github.repository }}\"\n    to: devops@example.com\n    from: GitHub Actions \u003Cactions@example.com>\n    body: |\n      Deployment to production failed!\n      \n      Repository: ${{ github.repository }}\n      Branch: ${{ github.ref_name }}\n      Commit: ${{ github.sha }}\n      Author: ${{ github.actor }}\n      \n      Logs: ${{ github.server_url }}\u002F${{ github.repository }}\u002Factions\u002Fruns\u002F${{ github.run_id }}\n",[5173,13155,13156,13167,13175,13184,13190,13200,13210,13219,13228,13238,13248,13258,13267,13272,13276,13281,13286,13290,13294,13298],{"__ignoreMap":5245},[5249,13157,13158,13160,13162,13164],{"class":5251,"line":5252},[5249,13159,7941],{"class":6154},[5249,13161,6151],{"class":6150},[5249,13163,6155],{"class":6154},[5249,13165,13166],{"class":6158},"Send email on failure\n",[5249,13168,13169,13171,13173],{"class":5251,"line":5259},[5249,13170,7997],{"class":6150},[5249,13172,6155],{"class":6154},[5249,13174,9401],{"class":6158},[5249,13176,13177,13179,13181],{"class":5251,"line":5273},[5249,13178,8694],{"class":6150},[5249,13180,6155],{"class":6154},[5249,13182,13183],{"class":6158},"dawidd6\u002Faction-send-mail@v3\n",[5249,13185,13186,13188],{"class":5251,"line":5288},[5249,13187,7953],{"class":6150},[5249,13189,6185],{"class":6154},[5249,13191,13192,13195,13197],{"class":5251,"line":5302},[5249,13193,13194],{"class":6150},"    server_address",[5249,13196,6155],{"class":6154},[5249,13198,13199],{"class":6158},"smtp.gmail.com\n",[5249,13201,13202,13205,13207],{"class":5251,"line":5309},[5249,13203,13204],{"class":6150},"    server_port",[5249,13206,6155],{"class":6154},[5249,13208,13209],{"class":6324},"587\n",[5249,13211,13212,13214,13216],{"class":5251,"line":5315},[5249,13213,8719],{"class":6150},[5249,13215,6155],{"class":6154},[5249,13217,13218],{"class":6158},"${{ secrets.SMTP_USER }}\n",[5249,13220,13221,13223,13225],{"class":5251,"line":5324},[5249,13222,8729],{"class":6150},[5249,13224,6155],{"class":6154},[5249,13226,13227],{"class":6158},"${{ secrets.SMTP_PASSWORD }}\n",[5249,13229,13230,13233,13235],{"class":5251,"line":5329},[5249,13231,13232],{"class":6150},"    subject",[5249,13234,6155],{"class":6154},[5249,13236,13237],{"class":5266},"\"❌ Deployment Failed: ${{ github.repository }}\"\n",[5249,13239,13240,13243,13245],{"class":5251,"line":5335},[5249,13241,13242],{"class":6150},"    to",[5249,13244,6155],{"class":6154},[5249,13246,13247],{"class":6158},"devops@example.com\n",[5249,13249,13250,13253,13255],{"class":5251,"line":5344},[5249,13251,13252],{"class":6150},"    from",[5249,13254,6155],{"class":6154},[5249,13256,13257],{"class":6158},"GitHub Actions \u003Cactions@example.com>\n",[5249,13259,13260,13263,13265],{"class":5251,"line":5356},[5249,13261,13262],{"class":6150},"    body",[5249,13264,6155],{"class":6154},[5249,13266,8478],{"class":7333},[5249,13268,13269],{"class":5251,"line":5361},[5249,13270,13271],{"class":6158},"      Deployment to production failed!\n",[5249,13273,13274],{"class":5251,"line":5367},[5249,13275,6287],{"class":6158},[5249,13277,13278],{"class":5251,"line":5376},[5249,13279,13280],{"class":6158},"      Repository: ${{ github.repository }}\n",[5249,13282,13283],{"class":5251,"line":5381},[5249,13284,13285],{"class":6158},"      Branch: ${{ github.ref_name }}\n",[5249,13287,13288],{"class":5251,"line":5387},[5249,13289,13110],{"class":6158},[5249,13291,13292],{"class":5251,"line":5398},[5249,13293,13115],{"class":6158},[5249,13295,13296],{"class":5251,"line":5403},[5249,13297,6287],{"class":6158},[5249,13299,13300],{"class":5251,"line":5409},[5249,13301,13302],{"class":6158},"      Logs: ${{ github.server_url }}\u002F${{ github.repository }}\u002Factions\u002Fruns\u002F${{ github.run_id }}\n",[5849,13304,13306],{"id":13305},"integration-з-issue-tracking","Integration з Issue Tracking",[5155,13308,13309],{},"Автоматичне створення issue при failed deployment:",[5240,13311,13313],{"className":6141,"code":13312,"language":6143,"meta":5245,"style":5245},"- name: Create issue on deployment failure\n  if: failure()\n  uses: actions\u002Fgithub-script@v6\n  with:\n    script: |\n      const issue = await github.rest.issues.create({\n        owner: context.repo.owner,\n        repo: context.repo.repo,\n        title: `🔥 Production Deployment Failed - ${context.sha.substring(0, 7)}`,\n        body: `\n          ## Deployment Failure Details\n          \n          **Commit:** ${context.sha}\n          **Author:** ${context.actor}\n          **Branch:** ${context.ref}\n          **Workflow:** [View Logs](${context.serverUrl}\u002F${context.repo.owner}\u002F${context.repo.repo}\u002Factions\u002Fruns\u002F${context.runId})\n          \n          **Commit Message:**\n          \\`\\`\\`\n          ${context.payload.head_commit.message}\n          \\`\\`\\`\n          \n          ### Action Required\n          - [ ] Investigate failure cause\n          - [ ] Fix the issue\n          - [ ] Verify fix in staging\n          - [ ] Re-deploy or rollback\n          \n          cc @devops-team\n        `,\n        labels: ['bug', 'deployment', 'production', 'urgent'],\n        assignees: ['tech-lead']\n      });\n      \n      console.log(`Created issue #${issue.data.number}`);\n",[5173,13314,13315,13326,13334,13343,13349,13358,13363,13368,13373,13378,13383,13388,13392,13397,13402,13407,13412,13416,13421,13426,13431,13435,13439,13444,13449,13454,13459,13464,13468,13473,13478,13483,13488,13493,13497],{"__ignoreMap":5245},[5249,13316,13317,13319,13321,13323],{"class":5251,"line":5252},[5249,13318,7941],{"class":6154},[5249,13320,6151],{"class":6150},[5249,13322,6155],{"class":6154},[5249,13324,13325],{"class":6158},"Create issue on deployment failure\n",[5249,13327,13328,13330,13332],{"class":5251,"line":5259},[5249,13329,7997],{"class":6150},[5249,13331,6155],{"class":6154},[5249,13333,9401],{"class":6158},[5249,13335,13336,13338,13340],{"class":5251,"line":5273},[5249,13337,8694],{"class":6150},[5249,13339,6155],{"class":6154},[5249,13341,13342],{"class":6158},"actions\u002Fgithub-script@v6\n",[5249,13344,13345,13347],{"class":5251,"line":5288},[5249,13346,7953],{"class":6150},[5249,13348,6185],{"class":6154},[5249,13350,13351,13354,13356],{"class":5251,"line":5302},[5249,13352,13353],{"class":6150},"    script",[5249,13355,6155],{"class":6154},[5249,13357,8478],{"class":7333},[5249,13359,13360],{"class":5251,"line":5309},[5249,13361,13362],{"class":6158},"      const issue = await github.rest.issues.create({\n",[5249,13364,13365],{"class":5251,"line":5315},[5249,13366,13367],{"class":6158},"        owner: context.repo.owner,\n",[5249,13369,13370],{"class":5251,"line":5324},[5249,13371,13372],{"class":6158},"        repo: context.repo.repo,\n",[5249,13374,13375],{"class":5251,"line":5329},[5249,13376,13377],{"class":6158},"        title: `🔥 Production Deployment Failed - ${context.sha.substring(0, 7)}`,\n",[5249,13379,13380],{"class":5251,"line":5335},[5249,13381,13382],{"class":6158},"        body: `\n",[5249,13384,13385],{"class":5251,"line":5344},[5249,13386,13387],{"class":6158},"          ## Deployment Failure Details\n",[5249,13389,13390],{"class":5251,"line":5356},[5249,13391,10178],{"class":6158},[5249,13393,13394],{"class":5251,"line":5361},[5249,13395,13396],{"class":6158},"          **Commit:** ${context.sha}\n",[5249,13398,13399],{"class":5251,"line":5367},[5249,13400,13401],{"class":6158},"          **Author:** ${context.actor}\n",[5249,13403,13404],{"class":5251,"line":5376},[5249,13405,13406],{"class":6158},"          **Branch:** ${context.ref}\n",[5249,13408,13409],{"class":5251,"line":5381},[5249,13410,13411],{"class":6158},"          **Workflow:** [View Logs](${context.serverUrl}\u002F${context.repo.owner}\u002F${context.repo.repo}\u002Factions\u002Fruns\u002F${context.runId})\n",[5249,13413,13414],{"class":5251,"line":5387},[5249,13415,10178],{"class":6158},[5249,13417,13418],{"class":5251,"line":5398},[5249,13419,13420],{"class":6158},"          **Commit Message:**\n",[5249,13422,13423],{"class":5251,"line":5403},[5249,13424,13425],{"class":6158},"          \\`\\`\\`\n",[5249,13427,13428],{"class":5251,"line":5409},[5249,13429,13430],{"class":6158},"          ${context.payload.head_commit.message}\n",[5249,13432,13433],{"class":5251,"line":5419},[5249,13434,13425],{"class":6158},[5249,13436,13437],{"class":5251,"line":5424},[5249,13438,10178],{"class":6158},[5249,13440,13441],{"class":5251,"line":5430},[5249,13442,13443],{"class":6158},"          ### Action Required\n",[5249,13445,13446],{"class":5251,"line":5442},[5249,13447,13448],{"class":6158},"          - [ ] Investigate failure cause\n",[5249,13450,13451],{"class":5251,"line":5447},[5249,13452,13453],{"class":6158},"          - [ ] Fix the issue\n",[5249,13455,13456],{"class":5251,"line":5453},[5249,13457,13458],{"class":6158},"          - [ ] Verify fix in staging\n",[5249,13460,13461],{"class":5251,"line":5698},[5249,13462,13463],{"class":6158},"          - [ ] Re-deploy or rollback\n",[5249,13465,13466],{"class":5251,"line":5704},[5249,13467,10178],{"class":6158},[5249,13469,13470],{"class":5251,"line":5710},[5249,13471,13472],{"class":6158},"          cc @devops-team\n",[5249,13474,13475],{"class":5251,"line":5716},[5249,13476,13477],{"class":6158},"        `,\n",[5249,13479,13480],{"class":5251,"line":5722},[5249,13481,13482],{"class":6158},"        labels: ['bug', 'deployment', 'production', 'urgent'],\n",[5249,13484,13485],{"class":5251,"line":5728},[5249,13486,13487],{"class":6158},"        assignees: ['tech-lead']\n",[5249,13489,13490],{"class":5251,"line":5734},[5249,13491,13492],{"class":6158},"      });\n",[5249,13494,13495],{"class":5251,"line":5740},[5249,13496,6287],{"class":6158},[5249,13498,13499],{"class":5251,"line":5746},[5249,13500,13501],{"class":6158},"      console.log(`Created issue #${issue.data.number}`);\n",[5147,13503],{},[5150,13505,13507],{"id":13506},"rollback-strategies","Rollback Strategies",[5155,13509,13510],{},"Автоматичне або ручне повернення до попередньої версії при проблемах.",[5849,13512,13514],{"id":13513},"automatic-rollback-на-failed-health-check","Automatic Rollback на Failed Health Check",[5240,13516,13518],{"className":6141,"code":13517,"language":6143,"meta":5245,"style":5245},"deploy:\n  name: Deploy with Auto-Rollback\n  runs-on: ubuntu-latest\n\n  steps:\n    - name: Save current version for rollback\n      id: current-version\n      run: |\n        CURRENT_TAG=$(ssh user@${{ secrets.PROD_SERVER_HOST }} \\\n          \"cd \u002Fapp && docker-compose config | grep 'image:' | awk '{print \\$2}' | cut -d':' -f2\")\n        echo \"tag=$CURRENT_TAG\" >> $GITHUB_OUTPUT\n\n    - name: Deploy new version\n      run: |\n        ssh user@${{ secrets.PROD_SERVER_HOST }} \\\n          \"cd \u002Fapp && export IMAGE_TAG=${{ github.sha }} && docker-compose pull && docker-compose up -d\"\n\n    - name: Health check with retry\n      id: health-check\n      run: |\n        for i in {1..10}; do\n          if curl -f https:\u002F\u002Fexample.com\u002Fhealth; then\n            echo \"Health check passed\"\n            exit 0\n          fi\n          echo \"Health check failed, attempt $i\u002F10\"\n          sleep 10\n        done\n        echo \"Health check failed after 10 attempts\"\n        exit 1\n\n    - name: Rollback on failure\n      if: failure() && steps.health-check.outcome == 'failure'\n      run: |\n        echo \"Rolling back to version: ${{ steps.current-version.outputs.tag }}\"\n        ssh user@${{ secrets.PROD_SERVER_HOST }} \\\n          \"cd \u002Fapp && export IMAGE_TAG=${{ steps.current-version.outputs.tag }} && docker-compose up -d\"\n        \n        # Verify rollback\n        sleep 10\n        curl -f https:\u002F\u002Fexample.com\u002Fhealth || exit 1\n        echo \"Rollback completed successfully\"\n\n    - name: Notify on rollback\n      if: failure() && steps.health-check.outcome == 'failure'\n      uses: slackapi\u002Fslack-github-action@v1.24.0\n      with:\n        webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }}\n        payload: |\n          {\n            \"text\": \"⚠️ Automatic rollback executed due to failed health check\",\n            \"blocks\": [\n              {\n                \"type\": \"section\",\n                \"text\": {\n                  \"type\": \"mrkdwn\",\n                  \"text\": \"*Rolled back to:* ${{ steps.current-version.outputs.tag }}\"\n                }\n              }\n            ]\n          }\n",[5173,13519,13520,13526,13535,13543,13547,13553,13564,13573,13581,13586,13591,13596,13600,13611,13619,13624,13629,13633,13644,13653,13661,13666,13671,13676,13681,13685,13690,13694,13698,13703,13708,13712,13723,13732,13740,13745,13749,13754,13758,13763,13767,13772,13777,13781,13792,13800,13808,13814,13822,13830,13834,13839,13844,13849,13854,13859,13864,13869,13873,13878,13882],{"__ignoreMap":5245},[5249,13521,13522,13524],{"class":5251,"line":5252},[5249,13523,9094],{"class":6150},[5249,13525,6185],{"class":6154},[5249,13527,13528,13530,13532],{"class":5251,"line":5259},[5249,13529,9101],{"class":6150},[5249,13531,6155],{"class":6154},[5249,13533,13534],{"class":6158},"Deploy with Auto-Rollback\n",[5249,13536,13537,13539,13541],{"class":5251,"line":5273},[5249,13538,9111],{"class":6150},[5249,13540,6155],{"class":6154},[5249,13542,6792],{"class":6158},[5249,13544,13545],{"class":5251,"line":5288},[5249,13546,5306],{"emptyLinePlaceholder":5305},[5249,13548,13549,13551],{"class":5251,"line":5302},[5249,13550,9145],{"class":6150},[5249,13552,6185],{"class":6154},[5249,13554,13555,13557,13559,13561],{"class":5251,"line":5309},[5249,13556,6529],{"class":6154},[5249,13558,6151],{"class":6150},[5249,13560,6155],{"class":6154},[5249,13562,13563],{"class":6158},"Save current version for rollback\n",[5249,13565,13566,13568,13570],{"class":5251,"line":5315},[5249,13567,9763],{"class":6150},[5249,13569,6155],{"class":6154},[5249,13571,13572],{"class":6158},"current-version\n",[5249,13574,13575,13577,13579],{"class":5251,"line":5324},[5249,13576,9254],{"class":6150},[5249,13578,6155],{"class":6154},[5249,13580,8478],{"class":7333},[5249,13582,13583],{"class":5251,"line":5329},[5249,13584,13585],{"class":6158},"        CURRENT_TAG=$(ssh user@${{ secrets.PROD_SERVER_HOST }} \\\n",[5249,13587,13588],{"class":5251,"line":5335},[5249,13589,13590],{"class":6158},"          \"cd \u002Fapp && docker-compose config | grep 'image:' | awk '{print \\$2}' | cut -d':' -f2\")\n",[5249,13592,13593],{"class":5251,"line":5344},[5249,13594,13595],{"class":6158},"        echo \"tag=$CURRENT_TAG\" >> $GITHUB_OUTPUT\n",[5249,13597,13598],{"class":5251,"line":5356},[5249,13599,5306],{"emptyLinePlaceholder":5305},[5249,13601,13602,13604,13606,13608],{"class":5251,"line":5361},[5249,13603,6529],{"class":6154},[5249,13605,6151],{"class":6150},[5249,13607,6155],{"class":6154},[5249,13609,13610],{"class":6158},"Deploy new version\n",[5249,13612,13613,13615,13617],{"class":5251,"line":5367},[5249,13614,9254],{"class":6150},[5249,13616,6155],{"class":6154},[5249,13618,8478],{"class":7333},[5249,13620,13621],{"class":5251,"line":5376},[5249,13622,13623],{"class":6158},"        ssh user@${{ secrets.PROD_SERVER_HOST }} \\\n",[5249,13625,13626],{"class":5251,"line":5381},[5249,13627,13628],{"class":6158},"          \"cd \u002Fapp && export IMAGE_TAG=${{ github.sha }} && docker-compose pull && docker-compose up -d\"\n",[5249,13630,13631],{"class":5251,"line":5387},[5249,13632,5306],{"emptyLinePlaceholder":5305},[5249,13634,13635,13637,13639,13641],{"class":5251,"line":5398},[5249,13636,6529],{"class":6154},[5249,13638,6151],{"class":6150},[5249,13640,6155],{"class":6154},[5249,13642,13643],{"class":6158},"Health check with retry\n",[5249,13645,13646,13648,13650],{"class":5251,"line":5403},[5249,13647,9763],{"class":6150},[5249,13649,6155],{"class":6154},[5249,13651,13652],{"class":6158},"health-check\n",[5249,13654,13655,13657,13659],{"class":5251,"line":5409},[5249,13656,9254],{"class":6150},[5249,13658,6155],{"class":6154},[5249,13660,8478],{"class":7333},[5249,13662,13663],{"class":5251,"line":5419},[5249,13664,13665],{"class":6158},"        for i in {1..10}; do\n",[5249,13667,13668],{"class":5251,"line":5424},[5249,13669,13670],{"class":6158},"          if curl -f https:\u002F\u002Fexample.com\u002Fhealth; then\n",[5249,13672,13673],{"class":5251,"line":5430},[5249,13674,13675],{"class":6158},"            echo \"Health check passed\"\n",[5249,13677,13678],{"class":5251,"line":5442},[5249,13679,13680],{"class":6158},"            exit 0\n",[5249,13682,13683],{"class":5251,"line":5447},[5249,13684,10268],{"class":6158},[5249,13686,13687],{"class":5251,"line":5453},[5249,13688,13689],{"class":6158},"          echo \"Health check failed, attempt $i\u002F10\"\n",[5249,13691,13692],{"class":5251,"line":5698},[5249,13693,10569],{"class":6158},[5249,13695,13696],{"class":5251,"line":5704},[5249,13697,10636],{"class":6158},[5249,13699,13700],{"class":5251,"line":5710},[5249,13701,13702],{"class":6158},"        echo \"Health check failed after 10 attempts\"\n",[5249,13704,13705],{"class":5251,"line":5716},[5249,13706,13707],{"class":6158},"        exit 1\n",[5249,13709,13710],{"class":5251,"line":5722},[5249,13711,5306],{"emptyLinePlaceholder":5305},[5249,13713,13714,13716,13718,13720],{"class":5251,"line":5728},[5249,13715,6529],{"class":6154},[5249,13717,6151],{"class":6150},[5249,13719,6155],{"class":6154},[5249,13721,13722],{"class":6158},"Rollback on failure\n",[5249,13724,13725,13727,13729],{"class":5251,"line":5734},[5249,13726,9288],{"class":6150},[5249,13728,6155],{"class":6154},[5249,13730,13731],{"class":6158},"failure() && steps.health-check.outcome == 'failure'\n",[5249,13733,13734,13736,13738],{"class":5251,"line":5740},[5249,13735,9254],{"class":6150},[5249,13737,6155],{"class":6154},[5249,13739,8478],{"class":7333},[5249,13741,13742],{"class":5251,"line":5746},[5249,13743,13744],{"class":6158},"        echo \"Rolling back to version: ${{ steps.current-version.outputs.tag }}\"\n",[5249,13746,13747],{"class":5251,"line":5752},[5249,13748,13623],{"class":6158},[5249,13750,13751],{"class":5251,"line":5758},[5249,13752,13753],{"class":6158},"          \"cd \u002Fapp && export IMAGE_TAG=${{ steps.current-version.outputs.tag }} && docker-compose up -d\"\n",[5249,13755,13756],{"class":5251,"line":5763},[5249,13757,11020],{"class":6158},[5249,13759,13760],{"class":5251,"line":5769},[5249,13761,13762],{"class":6158},"        # Verify rollback\n",[5249,13764,13765],{"class":5251,"line":5775},[5249,13766,9263],{"class":6158},[5249,13768,13769],{"class":5251,"line":5781},[5249,13770,13771],{"class":6158},"        curl -f https:\u002F\u002Fexample.com\u002Fhealth || exit 1\n",[5249,13773,13774],{"class":5251,"line":5787},[5249,13775,13776],{"class":6158},"        echo \"Rollback completed successfully\"\n",[5249,13778,13779],{"class":5251,"line":5793},[5249,13780,5306],{"emptyLinePlaceholder":5305},[5249,13782,13783,13785,13787,13789],{"class":5251,"line":5799},[5249,13784,6529],{"class":6154},[5249,13786,6151],{"class":6150},[5249,13788,6155],{"class":6154},[5249,13790,13791],{"class":6158},"Notify on rollback\n",[5249,13793,13794,13796,13798],{"class":5251,"line":5805},[5249,13795,9288],{"class":6150},[5249,13797,6155],{"class":6154},[5249,13799,13731],{"class":6158},[5249,13801,13802,13804,13806],{"class":5251,"line":5811},[5249,13803,9163],{"class":6150},[5249,13805,6155],{"class":6154},[5249,13807,9302],{"class":6158},[5249,13809,13810,13812],{"class":5251,"line":5817},[5249,13811,9173],{"class":6150},[5249,13813,6185],{"class":6154},[5249,13815,13816,13818,13820],{"class":5251,"line":5822},[5249,13817,9313],{"class":6150},[5249,13819,6155],{"class":6154},[5249,13821,9318],{"class":6158},[5249,13823,13824,13826,13828],{"class":5251,"line":5828},[5249,13825,9323],{"class":6150},[5249,13827,6155],{"class":6154},[5249,13829,8478],{"class":7333},[5249,13831,13832],{"class":5251,"line":5834},[5249,13833,9332],{"class":6158},[5249,13835,13836],{"class":5251,"line":5839},[5249,13837,13838],{"class":6158},"            \"text\": \"⚠️ Automatic rollback executed due to failed health check\",\n",[5249,13840,13841],{"class":5251,"line":7092},[5249,13842,13843],{"class":6158},"            \"blocks\": [\n",[5249,13845,13846],{"class":5251,"line":7097},[5249,13847,13848],{"class":6158},"              {\n",[5249,13850,13851],{"class":5251,"line":7108},[5249,13852,13853],{"class":6158},"                \"type\": \"section\",\n",[5249,13855,13856],{"class":5251,"line":7117},[5249,13857,13858],{"class":6158},"                \"text\": {\n",[5249,13860,13861],{"class":5251,"line":7122},[5249,13862,13863],{"class":6158},"                  \"type\": \"mrkdwn\",\n",[5249,13865,13866],{"class":5251,"line":7134},[5249,13867,13868],{"class":6158},"                  \"text\": \"*Rolled back to:* ${{ steps.current-version.outputs.tag }}\"\n",[5249,13870,13871],{"class":5251,"line":7144},[5249,13872,12692],{"class":6158},[5249,13874,13875],{"class":5251,"line":7149},[5249,13876,13877],{"class":6158},"              }\n",[5249,13879,13880],{"class":5251,"line":7161},[5249,13881,10862],{"class":6158},[5249,13883,13884],{"class":5251,"line":7171},[5249,13885,9377],{"class":6158},[5849,13887,13889],{"id":13888},"manual-rollback-workflow","Manual Rollback Workflow",[5240,13891,13893],{"className":6141,"code":13892,"language":6143,"meta":5245,"style":5245},"# .github\u002Fworkflows\u002Frollback.yml\nname: Manual Rollback\n\non:\n  workflow_dispatch:\n    inputs:\n      version:\n        description: 'Version to rollback to (commit SHA or tag)'\n        required: true\n      environment:\n        description: 'Environment'\n        required: true\n        type: choice\n        options:\n          - staging\n          - production\n\njobs:\n  rollback:\n    name: Rollback to ${{ github.event.inputs.version }}\n    runs-on: ubuntu-latest\n    environment: ${{ github.event.inputs.environment }}\n\n    steps:\n      - name: Confirm rollback\n        run: |\n          echo \"Rolling back ${{ github.event.inputs.environment }} to version ${{ github.event.inputs.version }}\"\n          echo \"This action was initiated by: ${{ github.actor }}\"\n\n      - name: Deploy previous version\n        uses: appleboy\u002Fssh-action@v0.1.10\n        with:\n          host: ${{ secrets.SERVER_HOST }}\n          username: ${{ secrets.SERVER_USER }}\n          key: ${{ secrets.SSH_KEY }}\n          script: |\n            cd \u002Fapp\n            export IMAGE_TAG=${{ github.event.inputs.version }}\n            docker-compose pull\n            docker-compose up -d\n\n      - name: Verify rollback\n        run: |\n          sleep 10\n          for i in {1..5}; do\n            if curl -f ${{ secrets.APP_URL }}\u002Fhealth; then\n              echo \"Rollback verified successfully\"\n              exit 0\n            fi\n            sleep 5\n          done\n          echo \"Rollback verification failed\"\n          exit 1\n\n      - name: Create rollback tag\n        run: |\n          git tag -a \"rollback-${{ github.event.inputs.environment }}-$(date +%Y%m%d-%H%M%S)\" \\\n            -m \"Rollback ${{ github.event.inputs.environment }} to ${{ github.event.inputs.version }}\"\n          git push origin \"rollback-${{ github.event.inputs.environment }}-$(date +%Y%m%d-%H%M%S)\"\n\n      - name: Notify team\n        uses: slackapi\u002Fslack-github-action@v1.24.0\n        with:\n          webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }}\n          payload: |\n            {\n              \"text\": \"🔄 Manual rollback executed\",\n              \"blocks\": [\n                {\n                  \"type\": \"header\",\n                  \"text\": {\n                    \"type\": \"plain_text\",\n                    \"text\": \"🔄 Manual Rollback Executed\"\n                  }\n                },\n                {\n                  \"type\": \"section\",\n                  \"fields\": [\n                    {\n                      \"type\": \"mrkdwn\",\n                      \"text\": \"*Environment:*\\n${{ github.event.inputs.environment }}\"\n                    },\n                    {\n                      \"type\": \"mrkdwn\",\n                      \"text\": \"*Version:*\\n${{ github.event.inputs.version }}\"\n                    },\n                    {\n                      \"type\": \"mrkdwn\",\n                      \"text\": \"*Initiated by:*\\n${{ github.actor }}\"\n                    }\n                  ]\n                }\n              ]\n            }\n",[5173,13894,13895,13900,13909,13913,13919,13925,13931,13937,13946,13954,13960,13969,13977,13987,13993,13999,14005,14009,14015,14022,14031,14039,14048,14052,14058,14069,14077,14082,14087,14091,14102,14110,14116,14125,14133,14141,14149,14153,14157,14161,14165,14169,14180,14188,14192,14197,14202,14207,14211,14215,14219,14223,14228,14232,14236,14247,14255,14260,14265,14270,14274,14285,14293,14299,14307,14315,14319,14324,14328,14332,14336,14340,14344,14349,14353,14357,14361,14365,14369,14373,14377,14382,14386,14390,14394,14399,14403,14407,14411,14416,14420,14424,14428,14432],{"__ignoreMap":5245},[5249,13896,13897],{"class":5251,"line":5252},[5249,13898,13899],{"class":5255},"# .github\u002Fworkflows\u002Frollback.yml\n",[5249,13901,13902,13904,13906],{"class":5251,"line":5259},[5249,13903,6151],{"class":6150},[5249,13905,6155],{"class":6154},[5249,13907,13908],{"class":6158},"Manual Rollback\n",[5249,13910,13911],{"class":5251,"line":5273},[5249,13912,5306],{"emptyLinePlaceholder":5305},[5249,13914,13915,13917],{"class":5251,"line":5288},[5249,13916,6171],{"class":5281},[5249,13918,6185],{"class":6154},[5249,13920,13921,13923],{"class":5251,"line":5302},[5249,13922,6564],{"class":6150},[5249,13924,6185],{"class":6154},[5249,13926,13927,13929],{"class":5251,"line":5309},[5249,13928,6571],{"class":6150},[5249,13930,6185],{"class":6154},[5249,13932,13933,13935],{"class":5251,"line":5315},[5249,13934,11501],{"class":6150},[5249,13936,6185],{"class":6154},[5249,13938,13939,13941,13943],{"class":5251,"line":5324},[5249,13940,6585],{"class":6150},[5249,13942,6155],{"class":6154},[5249,13944,13945],{"class":6158},"'Version to rollback to (commit SHA or tag)'\n",[5249,13947,13948,13950,13952],{"class":5251,"line":5329},[5249,13949,6595],{"class":6150},[5249,13951,6155],{"class":6154},[5249,13953,6600],{"class":5281},[5249,13955,13956,13958],{"class":5251,"line":5335},[5249,13957,6578],{"class":6150},[5249,13959,6185],{"class":6154},[5249,13961,13962,13964,13966],{"class":5251,"line":5344},[5249,13963,6585],{"class":6150},[5249,13965,6155],{"class":6154},[5249,13967,13968],{"class":6158},"'Environment'\n",[5249,13970,13971,13973,13975],{"class":5251,"line":5356},[5249,13972,6595],{"class":6150},[5249,13974,6155],{"class":6154},[5249,13976,6600],{"class":5281},[5249,13978,13979,13982,13984],{"class":5251,"line":5361},[5249,13980,13981],{"class":6150},"        type",[5249,13983,6155],{"class":6154},[5249,13985,13986],{"class":6158},"choice\n",[5249,13988,13989,13991],{"class":5251,"line":5367},[5249,13990,7328],{"class":6150},[5249,13992,6185],{"class":6154},[5249,13994,13995,13997],{"class":5251,"line":5376},[5249,13996,7375],{"class":6154},[5249,13998,11247],{"class":6158},[5249,14000,14001,14003],{"class":5251,"line":5381},[5249,14002,7375],{"class":6154},[5249,14004,11569],{"class":6158},[5249,14006,14007],{"class":5251,"line":5387},[5249,14008,5306],{"emptyLinePlaceholder":5305},[5249,14010,14011,14013],{"class":5251,"line":5398},[5249,14012,6225],{"class":6150},[5249,14014,6185],{"class":6154},[5249,14016,14017,14020],{"class":5251,"line":5403},[5249,14018,14019],{"class":6150},"  rollback",[5249,14021,6185],{"class":6154},[5249,14023,14024,14026,14028],{"class":5251,"line":5409},[5249,14025,6778],{"class":6150},[5249,14027,6155],{"class":6154},[5249,14029,14030],{"class":6158},"Rollback to ${{ github.event.inputs.version }}\n",[5249,14032,14033,14035,14037],{"class":5251,"line":5419},[5249,14034,6242],{"class":6150},[5249,14036,6155],{"class":6154},[5249,14038,6792],{"class":6158},[5249,14040,14041,14043,14045],{"class":5251,"line":5424},[5249,14042,11235],{"class":6150},[5249,14044,6155],{"class":6154},[5249,14046,14047],{"class":6158},"${{ github.event.inputs.environment }}\n",[5249,14049,14050],{"class":5251,"line":5430},[5249,14051,5306],{"emptyLinePlaceholder":5305},[5249,14053,14054,14056],{"class":5251,"line":5442},[5249,14055,6255],{"class":6150},[5249,14057,6185],{"class":6154},[5249,14059,14060,14062,14064,14066],{"class":5251,"line":5447},[5249,14061,6265],{"class":6154},[5249,14063,6151],{"class":6150},[5249,14065,6155],{"class":6154},[5249,14067,14068],{"class":6158},"Confirm rollback\n",[5249,14070,14071,14073,14075],{"class":5251,"line":5453},[5249,14072,6345],{"class":6150},[5249,14074,6155],{"class":6154},[5249,14076,8478],{"class":7333},[5249,14078,14079],{"class":5251,"line":5698},[5249,14080,14081],{"class":6158},"          echo \"Rolling back ${{ github.event.inputs.environment }} to version ${{ github.event.inputs.version }}\"\n",[5249,14083,14084],{"class":5251,"line":5704},[5249,14085,14086],{"class":6158},"          echo \"This action was initiated by: ${{ github.actor }}\"\n",[5249,14088,14089],{"class":5251,"line":5710},[5249,14090,5306],{"emptyLinePlaceholder":5305},[5249,14092,14093,14095,14097,14099],{"class":5251,"line":5716},[5249,14094,6265],{"class":6154},[5249,14096,6151],{"class":6150},[5249,14098,6155],{"class":6154},[5249,14100,14101],{"class":6158},"Deploy previous version\n",[5249,14103,14104,14106,14108],{"class":5251,"line":5722},[5249,14105,6277],{"class":6150},[5249,14107,6155],{"class":6154},[5249,14109,9168],{"class":6158},[5249,14111,14112,14114],{"class":5251,"line":5728},[5249,14113,6312],{"class":6150},[5249,14115,6185],{"class":6154},[5249,14117,14118,14120,14122],{"class":5251,"line":5734},[5249,14119,10009],{"class":6150},[5249,14121,6155],{"class":6154},[5249,14123,14124],{"class":6158},"${{ secrets.SERVER_HOST }}\n",[5249,14126,14127,14129,14131],{"class":5251,"line":5740},[5249,14128,8403],{"class":6150},[5249,14130,6155],{"class":6154},[5249,14132,10023],{"class":6158},[5249,14134,14135,14137,14139],{"class":5251,"line":5746},[5249,14136,10028],{"class":6150},[5249,14138,6155],{"class":6154},[5249,14140,10033],{"class":6158},[5249,14142,14143,14145,14147],{"class":5251,"line":5752},[5249,14144,10038],{"class":6150},[5249,14146,6155],{"class":6154},[5249,14148,8478],{"class":7333},[5249,14150,14151],{"class":5251,"line":5758},[5249,14152,10047],{"class":6158},[5249,14154,14155],{"class":5251,"line":5763},[5249,14156,11677],{"class":6158},[5249,14158,14159],{"class":5251,"line":5769},[5249,14160,10052],{"class":6158},[5249,14162,14163],{"class":5251,"line":5775},[5249,14164,10057],{"class":6158},[5249,14166,14167],{"class":5251,"line":5781},[5249,14168,5306],{"emptyLinePlaceholder":5305},[5249,14170,14171,14173,14175,14177],{"class":5251,"line":5787},[5249,14172,6265],{"class":6154},[5249,14174,6151],{"class":6150},[5249,14176,6155],{"class":6154},[5249,14178,14179],{"class":6158},"Verify rollback\n",[5249,14181,14182,14184,14186],{"class":5251,"line":5793},[5249,14183,6345],{"class":6150},[5249,14185,6155],{"class":6154},[5249,14187,8478],{"class":7333},[5249,14189,14190],{"class":5251,"line":5799},[5249,14191,10569],{"class":6158},[5249,14193,14194],{"class":5251,"line":5805},[5249,14195,14196],{"class":6158},"          for i in {1..5}; do\n",[5249,14198,14199],{"class":5251,"line":5811},[5249,14200,14201],{"class":6158},"            if curl -f ${{ secrets.APP_URL }}\u002Fhealth; then\n",[5249,14203,14204],{"class":5251,"line":5817},[5249,14205,14206],{"class":6158},"              echo \"Rollback verified successfully\"\n",[5249,14208,14209],{"class":5251,"line":5822},[5249,14210,10100],{"class":6158},[5249,14212,14213],{"class":5251,"line":5828},[5249,14214,10105],{"class":6158},[5249,14216,14217],{"class":5251,"line":5834},[5249,14218,11736],{"class":6158},[5249,14220,14221],{"class":5251,"line":5839},[5249,14222,10120],{"class":6158},[5249,14224,14225],{"class":5251,"line":7092},[5249,14226,14227],{"class":6158},"          echo \"Rollback verification failed\"\n",[5249,14229,14230],{"class":5251,"line":7097},[5249,14231,10130],{"class":6158},[5249,14233,14234],{"class":5251,"line":7108},[5249,14235,5306],{"emptyLinePlaceholder":5305},[5249,14237,14238,14240,14242,14244],{"class":5251,"line":7117},[5249,14239,6265],{"class":6154},[5249,14241,6151],{"class":6150},[5249,14243,6155],{"class":6154},[5249,14245,14246],{"class":6158},"Create rollback tag\n",[5249,14248,14249,14251,14253],{"class":5251,"line":7122},[5249,14250,6345],{"class":6150},[5249,14252,6155],{"class":6154},[5249,14254,8478],{"class":7333},[5249,14256,14257],{"class":5251,"line":7134},[5249,14258,14259],{"class":6158},"          git tag -a \"rollback-${{ github.event.inputs.environment }}-$(date +%Y%m%d-%H%M%S)\" \\\n",[5249,14261,14262],{"class":5251,"line":7144},[5249,14263,14264],{"class":6158},"            -m \"Rollback ${{ github.event.inputs.environment }} to ${{ github.event.inputs.version }}\"\n",[5249,14266,14267],{"class":5251,"line":7149},[5249,14268,14269],{"class":6158},"          git push origin \"rollback-${{ github.event.inputs.environment }}-$(date +%Y%m%d-%H%M%S)\"\n",[5249,14271,14272],{"class":5251,"line":7161},[5249,14273,5306],{"emptyLinePlaceholder":5305},[5249,14275,14276,14278,14280,14282],{"class":5251,"line":7171},[5249,14277,6265],{"class":6154},[5249,14279,6151],{"class":6150},[5249,14281,6155],{"class":6154},[5249,14283,14284],{"class":6158},"Notify team\n",[5249,14286,14287,14289,14291],{"class":5251,"line":7178},[5249,14288,6277],{"class":6150},[5249,14290,6155],{"class":6154},[5249,14292,9302],{"class":6158},[5249,14294,14295,14297],{"class":5251,"line":7189},[5249,14296,6312],{"class":6150},[5249,14298,6185],{"class":6154},[5249,14300,14301,14303,14305],{"class":5251,"line":7200},[5249,14302,12409],{"class":6150},[5249,14304,6155],{"class":6154},[5249,14306,9318],{"class":6158},[5249,14308,14309,14311,14313],{"class":5251,"line":7211},[5249,14310,12418],{"class":6150},[5249,14312,6155],{"class":6154},[5249,14314,8478],{"class":7333},[5249,14316,14317],{"class":5251,"line":7216},[5249,14318,12427],{"class":6158},[5249,14320,14321],{"class":5251,"line":7224},[5249,14322,14323],{"class":6158},"              \"text\": \"🔄 Manual rollback executed\",\n",[5249,14325,14326],{"class":5251,"line":7234},[5249,14327,12437],{"class":6158},[5249,14329,14330],{"class":5251,"line":7243},[5249,14331,12442],{"class":6158},[5249,14333,14334],{"class":5251,"line":7253},[5249,14335,12447],{"class":6158},[5249,14337,14338],{"class":5251,"line":7258},[5249,14339,12452],{"class":6158},[5249,14341,14342],{"class":5251,"line":7266},[5249,14343,12457],{"class":6158},[5249,14345,14346],{"class":5251,"line":7274},[5249,14347,14348],{"class":6158},"                    \"text\": \"🔄 Manual Rollback Executed\"\n",[5249,14350,14351],{"class":5251,"line":7285},[5249,14352,12467],{"class":6158},[5249,14354,14355],{"class":5251,"line":7293},[5249,14356,12472],{"class":6158},[5249,14358,14359],{"class":5251,"line":7304},[5249,14360,12442],{"class":6158},[5249,14362,14363],{"class":5251,"line":7314},[5249,14364,12481],{"class":6158},[5249,14366,14367],{"class":5251,"line":7325},[5249,14368,12486],{"class":6158},[5249,14370,14371],{"class":5251,"line":7340},[5249,14372,12491],{"class":6158},[5249,14374,14375],{"class":5251,"line":7346},[5249,14376,12496],{"class":6158},[5249,14378,14379],{"class":5251,"line":7352},[5249,14380,14381],{"class":6158},"                      \"text\": \"*Environment:*\\n${{ github.event.inputs.environment }}\"\n",[5249,14383,14384],{"class":5251,"line":7358},[5249,14385,12506],{"class":6158},[5249,14387,14388],{"class":5251,"line":7364},[5249,14389,12491],{"class":6158},[5249,14391,14392],{"class":5251,"line":7372},[5249,14393,12496],{"class":6158},[5249,14395,14396],{"class":5251,"line":7381},[5249,14397,14398],{"class":6158},"                      \"text\": \"*Version:*\\n${{ github.event.inputs.version }}\"\n",[5249,14400,14401],{"class":5251,"line":7386},[5249,14402,12506],{"class":6158},[5249,14404,14405],{"class":5251,"line":7394},[5249,14406,12491],{"class":6158},[5249,14408,14409],{"class":5251,"line":7404},[5249,14410,12496],{"class":6158},[5249,14412,14413],{"class":5251,"line":7415},[5249,14414,14415],{"class":6158},"                      \"text\": \"*Initiated by:*\\n${{ github.actor }}\"\n",[5249,14417,14418],{"class":5251,"line":7421},[5249,14419,12558],{"class":6158},[5249,14421,14422],{"class":5251,"line":7426},[5249,14423,12563],{"class":6158},[5249,14425,14426],{"class":5251,"line":7431},[5249,14427,12692],{"class":6158},[5249,14429,14430],{"class":5251,"line":7436},[5249,14431,9367],{"class":6158},[5249,14433,14434],{"class":5251,"line":7443},[5249,14435,12701],{"class":6158},[5155,14437,14438],{},"Запуск ручного rollback:",[5467,14440,14441,14446,14455,14458],{},[5095,14442,11886,14443],{},[5118,14444,14445],{},"Actions",[5095,14447,14448,14451,14452],{},[5118,14449,14450],{},"Manual Rollback"," workflow → ",[5118,14453,14454],{},"Run workflow",[5095,14456,14457],{},"Вказати version (commit SHA або tag) та environment",[5095,14459,14460,14461],{},"Клік ",[5118,14462,14454],{},[5147,14464],{},[5150,14466,14468],{"id":14467},"performance-optimization","Performance Optimization",[5155,14470,14471],{},"Оптимізація CI\u002FCD pipelines для швидших builds.",[5849,14473,14475],{"id":14474},"caching-dependencies","Caching Dependencies",[5240,14477,14479],{"className":6141,"code":14478,"language":6143,"meta":5245,"style":5245},"jobs:\n  build:\n    runs-on: ubuntu-latest\n\n    steps:\n      - uses: actions\u002Fcheckout@v3\n\n      - name: Setup Node.js with cache\n        uses: actions\u002Fsetup-node@v3\n        with:\n          node-version: 18\n          cache: 'npm'  # Автоматичний cache node_modules\n\n      - name: Install dependencies\n        run: npm ci  # Швидше ніж npm install\n\n      # Альтернатива — manual caching\n      - name: Cache node_modules\n        uses: actions\u002Fcache@v3\n        with:\n          path: node_modules\n          key: ${{ runner.os }}-node-${{ hashFiles('**\u002Fpackage-lock.json') }}\n          restore-keys: |\n            ${{ runner.os }}-node-\n",[5173,14480,14481,14487,14493,14501,14505,14511,14521,14525,14536,14544,14550,14558,14569,14573,14583,14595,14599,14604,14615,14624,14630,14639,14648,14657],{"__ignoreMap":5245},[5249,14482,14483,14485],{"class":5251,"line":5252},[5249,14484,6225],{"class":6150},[5249,14486,6185],{"class":6154},[5249,14488,14489,14491],{"class":5251,"line":5259},[5249,14490,8279],{"class":6150},[5249,14492,6185],{"class":6154},[5249,14494,14495,14497,14499],{"class":5251,"line":5273},[5249,14496,6242],{"class":6150},[5249,14498,6155],{"class":6154},[5249,14500,6792],{"class":6158},[5249,14502,14503],{"class":5251,"line":5288},[5249,14504,5306],{"emptyLinePlaceholder":5305},[5249,14506,14507,14509],{"class":5251,"line":5302},[5249,14508,6255],{"class":6150},[5249,14510,6185],{"class":6154},[5249,14512,14513,14515,14517,14519],{"class":5251,"line":5309},[5249,14514,6265],{"class":6154},[5249,14516,7944],{"class":6150},[5249,14518,6155],{"class":6154},[5249,14520,6282],{"class":6158},[5249,14522,14523],{"class":5251,"line":5315},[5249,14524,5306],{"emptyLinePlaceholder":5305},[5249,14526,14527,14529,14531,14533],{"class":5251,"line":5324},[5249,14528,6265],{"class":6154},[5249,14530,6151],{"class":6150},[5249,14532,6155],{"class":6154},[5249,14534,14535],{"class":6158},"Setup Node.js with cache\n",[5249,14537,14538,14540,14542],{"class":5251,"line":5329},[5249,14539,6277],{"class":6150},[5249,14541,6155],{"class":6154},[5249,14543,6307],{"class":6158},[5249,14545,14546,14548],{"class":5251,"line":5335},[5249,14547,6312],{"class":6150},[5249,14549,6185],{"class":6154},[5249,14551,14552,14554,14556],{"class":5251,"line":5344},[5249,14553,6319],{"class":6150},[5249,14555,6155],{"class":6154},[5249,14557,6325],{"class":6324},[5249,14559,14560,14562,14564,14566],{"class":5251,"line":5356},[5249,14561,6861],{"class":6150},[5249,14563,6155],{"class":6154},[5249,14565,7965],{"class":6158},[5249,14567,14568],{"class":5255},"  # Автоматичний cache node_modules\n",[5249,14570,14571],{"class":5251,"line":5361},[5249,14572,5306],{"emptyLinePlaceholder":5305},[5249,14574,14575,14577,14579,14581],{"class":5251,"line":5367},[5249,14576,6265],{"class":6154},[5249,14578,6151],{"class":6150},[5249,14580,6155],{"class":6154},[5249,14582,6340],{"class":6158},[5249,14584,14585,14587,14589,14592],{"class":5251,"line":5376},[5249,14586,6345],{"class":6150},[5249,14588,6155],{"class":6154},[5249,14590,14591],{"class":6158},"npm ci",[5249,14593,14594],{"class":5255},"  # Швидше ніж npm install\n",[5249,14596,14597],{"class":5251,"line":5381},[5249,14598,5306],{"emptyLinePlaceholder":5305},[5249,14600,14601],{"class":5251,"line":5387},[5249,14602,14603],{"class":5255},"      # Альтернатива — manual caching\n",[5249,14605,14606,14608,14610,14612],{"class":5251,"line":5398},[5249,14607,6265],{"class":6154},[5249,14609,6151],{"class":6150},[5249,14611,6155],{"class":6154},[5249,14613,14614],{"class":6158},"Cache node_modules\n",[5249,14616,14617,14619,14621],{"class":5251,"line":5403},[5249,14618,6277],{"class":6150},[5249,14620,6155],{"class":6154},[5249,14622,14623],{"class":6158},"actions\u002Fcache@v3\n",[5249,14625,14626,14628],{"class":5251,"line":5409},[5249,14627,6312],{"class":6150},[5249,14629,6185],{"class":6154},[5249,14631,14632,14634,14636],{"class":5251,"line":5419},[5249,14633,7723],{"class":6150},[5249,14635,6155],{"class":6154},[5249,14637,14638],{"class":6158},"node_modules\n",[5249,14640,14641,14643,14645],{"class":5251,"line":5424},[5249,14642,10028],{"class":6150},[5249,14644,6155],{"class":6154},[5249,14646,14647],{"class":6158},"${{ runner.os }}-node-${{ hashFiles('**\u002Fpackage-lock.json') }}\n",[5249,14649,14650,14653,14655],{"class":5251,"line":5430},[5249,14651,14652],{"class":6150},"          restore-keys",[5249,14654,6155],{"class":6154},[5249,14656,8478],{"class":7333},[5249,14658,14659],{"class":5251,"line":5442},[5249,14660,14661],{"class":6158},"            ${{ runner.os }}-node-\n",[5849,14663,14665],{"id":14664},"docker-layer-caching","Docker Layer Caching",[5240,14667,14669],{"className":6141,"code":14668,"language":6143,"meta":5245,"style":5245},"- name: Set up Docker Buildx\n  uses: docker\u002Fsetup-buildx-action@v2\n\n- name: Build and push with cache\n  uses: docker\u002Fbuild-push-action@v4\n  with:\n    context: .\n    push: true\n    tags: myusername\u002Fmy-app:latest\n    cache-from: type=registry,ref=myusername\u002Fmy-app:buildcache\n    cache-to: type=registry,ref=myusername\u002Fmy-app:buildcache,mode=max\n",[5173,14670,14671,14681,14689,14693,14704,14712,14718,14727,14736,14746,14756],{"__ignoreMap":5245},[5249,14672,14673,14675,14677,14679],{"class":5251,"line":5252},[5249,14674,7941],{"class":6154},[5249,14676,6151],{"class":6150},[5249,14678,6155],{"class":6154},[5249,14680,8359],{"class":6158},[5249,14682,14683,14685,14687],{"class":5251,"line":5259},[5249,14684,8694],{"class":6150},[5249,14686,6155],{"class":6154},[5249,14688,8368],{"class":6158},[5249,14690,14691],{"class":5251,"line":5273},[5249,14692,5306],{"emptyLinePlaceholder":5305},[5249,14694,14695,14697,14699,14701],{"class":5251,"line":5288},[5249,14696,7941],{"class":6154},[5249,14698,6151],{"class":6150},[5249,14700,6155],{"class":6154},[5249,14702,14703],{"class":6158},"Build and push with cache\n",[5249,14705,14706,14708,14710],{"class":5251,"line":5302},[5249,14707,8694],{"class":6150},[5249,14709,6155],{"class":6154},[5249,14711,8517],{"class":6158},[5249,14713,14714,14716],{"class":5251,"line":5309},[5249,14715,7953],{"class":6150},[5249,14717,6185],{"class":6154},[5249,14719,14720,14723,14725],{"class":5251,"line":5315},[5249,14721,14722],{"class":6150},"    context",[5249,14724,6155],{"class":6154},[5249,14726,8533],{"class":6324},[5249,14728,14729,14732,14734],{"class":5251,"line":5324},[5249,14730,14731],{"class":6150},"    push",[5249,14733,6155],{"class":6154},[5249,14735,6600],{"class":5281},[5249,14737,14738,14741,14743],{"class":5251,"line":5329},[5249,14739,14740],{"class":6150},"    tags",[5249,14742,6155],{"class":6154},[5249,14744,14745],{"class":6158},"myusername\u002Fmy-app:latest\n",[5249,14747,14748,14751,14753],{"class":5251,"line":5335},[5249,14749,14750],{"class":6150},"    cache-from",[5249,14752,6155],{"class":6154},[5249,14754,14755],{"class":6158},"type=registry,ref=myusername\u002Fmy-app:buildcache\n",[5249,14757,14758,14761,14763],{"class":5251,"line":5344},[5249,14759,14760],{"class":6150},"    cache-to",[5249,14762,6155],{"class":6154},[5249,14764,14765],{"class":6158},"type=registry,ref=myusername\u002Fmy-app:buildcache,mode=max\n",[5849,14767,14769],{"id":14768},"parallel-jobs","Parallel Jobs",[5240,14771,14773],{"className":6141,"code":14772,"language":6143,"meta":5245,"style":5245},"jobs:\n  lint:\n    runs-on: ubuntu-latest\n    steps:\n      - run: npm run lint\n\n  unit-test:\n    runs-on: ubuntu-latest\n    steps:\n      - run: npm run test\n\n  e2e-test:\n    runs-on: ubuntu-latest\n    steps:\n      - run: npm run test:e2e\n\n  # lint, unit-test, e2e-test виконуються паралельно\n\n  build:\n    needs: [lint, unit-test, e2e-test]  # Чекає завершення всіх трьох\n    runs-on: ubuntu-latest\n    steps:\n      - run: docker build -t my-app .\n",[5173,14774,14775,14781,14787,14795,14801,14811,14815,14822,14830,14836,14847,14851,14858,14866,14872,14882,14886,14891,14895,14901,14924,14932,14938],{"__ignoreMap":5245},[5249,14776,14777,14779],{"class":5251,"line":5252},[5249,14778,6225],{"class":6150},[5249,14780,6185],{"class":6154},[5249,14782,14783,14785],{"class":5251,"line":5259},[5249,14784,6771],{"class":6150},[5249,14786,6185],{"class":6154},[5249,14788,14789,14791,14793],{"class":5251,"line":5273},[5249,14790,6242],{"class":6150},[5249,14792,6155],{"class":6154},[5249,14794,6792],{"class":6158},[5249,14796,14797,14799],{"class":5251,"line":5288},[5249,14798,6255],{"class":6150},[5249,14800,6185],{"class":6154},[5249,14802,14803,14805,14807,14809],{"class":5251,"line":5302},[5249,14804,6265],{"class":6154},[5249,14806,12215],{"class":6150},[5249,14808,6155],{"class":6154},[5249,14810,6912],{"class":6158},[5249,14812,14813],{"class":5251,"line":5309},[5249,14814,5306],{"emptyLinePlaceholder":5305},[5249,14816,14817,14820],{"class":5251,"line":5315},[5249,14818,14819],{"class":6150},"  unit-test",[5249,14821,6185],{"class":6154},[5249,14823,14824,14826,14828],{"class":5251,"line":5324},[5249,14825,6242],{"class":6150},[5249,14827,6155],{"class":6154},[5249,14829,6792],{"class":6158},[5249,14831,14832,14834],{"class":5251,"line":5329},[5249,14833,6255],{"class":6150},[5249,14835,6185],{"class":6154},[5249,14837,14838,14840,14842,14844],{"class":5251,"line":5335},[5249,14839,6265],{"class":6154},[5249,14841,12215],{"class":6150},[5249,14843,6155],{"class":6154},[5249,14845,14846],{"class":6158},"npm run test\n",[5249,14848,14849],{"class":5251,"line":5344},[5249,14850,5306],{"emptyLinePlaceholder":5305},[5249,14852,14853,14856],{"class":5251,"line":5356},[5249,14854,14855],{"class":6150},"  e2e-test",[5249,14857,6185],{"class":6154},[5249,14859,14860,14862,14864],{"class":5251,"line":5361},[5249,14861,6242],{"class":6150},[5249,14863,6155],{"class":6154},[5249,14865,6792],{"class":6158},[5249,14867,14868,14870],{"class":5251,"line":5367},[5249,14869,6255],{"class":6150},[5249,14871,6185],{"class":6154},[5249,14873,14874,14876,14878,14880],{"class":5251,"line":5376},[5249,14875,6265],{"class":6154},[5249,14877,12215],{"class":6150},[5249,14879,6155],{"class":6154},[5249,14881,7582],{"class":6158},[5249,14883,14884],{"class":5251,"line":5381},[5249,14885,5306],{"emptyLinePlaceholder":5305},[5249,14887,14888],{"class":5251,"line":5387},[5249,14889,14890],{"class":5255},"  # lint, unit-test, e2e-test виконуються паралельно\n",[5249,14892,14893],{"class":5251,"line":5398},[5249,14894,5306],{"emptyLinePlaceholder":5305},[5249,14896,14897,14899],{"class":5251,"line":5403},[5249,14898,8279],{"class":6150},[5249,14900,6185],{"class":6154},[5249,14902,14903,14905,14907,14909,14911,14914,14916,14919,14921],{"class":5251,"line":5409},[5249,14904,6968],{"class":6150},[5249,14906,6193],{"class":6154},[5249,14908,6973],{"class":6158},[5249,14910,6488],{"class":6154},[5249,14912,14913],{"class":6158},"unit-test",[5249,14915,6488],{"class":6154},[5249,14917,14918],{"class":6158},"e2e-test",[5249,14920,7012],{"class":6154},[5249,14922,14923],{"class":5255},"# Чекає завершення всіх трьох\n",[5249,14925,14926,14928,14930],{"class":5251,"line":5419},[5249,14927,6242],{"class":6150},[5249,14929,6155],{"class":6154},[5249,14931,6792],{"class":6158},[5249,14933,14934,14936],{"class":5251,"line":5424},[5249,14935,6255],{"class":6150},[5249,14937,6185],{"class":6154},[5249,14939,14940,14942,14944,14946],{"class":5251,"line":5430},[5249,14941,6265],{"class":6154},[5249,14943,12215],{"class":6150},[5249,14945,6155],{"class":6154},[5249,14947,14948],{"class":6158},"docker build -t my-app .\n",[5849,14950,14952],{"id":14951},"conditional-execution","Conditional Execution",[5240,14954,14956],{"className":6141,"code":14955,"language":6143,"meta":5245,"style":5245},"jobs:\n  deploy:\n    if: github.ref == 'refs\u002Fheads\u002Fmain' && github.event_name == 'push'\n    # Деплоїться лише при push у main\n\n  test:\n    if: github.event_name == 'pull_request'\n    # Тести запускаються лише для PR\n",[5173,14957,14958,14964,14970,14979,14984,14988,14994,15003],{"__ignoreMap":5245},[5249,14959,14960,14962],{"class":5251,"line":5252},[5249,14961,6225],{"class":6150},[5249,14963,6185],{"class":6154},[5249,14965,14966,14968],{"class":5251,"line":5259},[5249,14967,9959],{"class":6150},[5249,14969,6185],{"class":6154},[5249,14971,14972,14974,14976],{"class":5251,"line":5273},[5249,14973,12353],{"class":6150},[5249,14975,6155],{"class":6154},[5249,14977,14978],{"class":6158},"github.ref == 'refs\u002Fheads\u002Fmain' && github.event_name == 'push'\n",[5249,14980,14981],{"class":5251,"line":5288},[5249,14982,14983],{"class":5255},"    # Деплоїться лише при push у main\n",[5249,14985,14986],{"class":5251,"line":5302},[5249,14987,5306],{"emptyLinePlaceholder":5305},[5249,14989,14990,14992],{"class":5251,"line":5309},[5249,14991,6235],{"class":6150},[5249,14993,6185],{"class":6154},[5249,14995,14996,14998,15000],{"class":5251,"line":5315},[5249,14997,12353],{"class":6150},[5249,14999,6155],{"class":6154},[5249,15001,15002],{"class":6158},"github.event_name == 'pull_request'\n",[5249,15004,15005],{"class":5251,"line":5324},[5249,15006,15007],{"class":5255},"    # Тести запускаються лише для PR\n",[5849,15009,15011],{"id":15010},"skip-ci-для-певних-commits","Skip CI для певних commits",[5240,15013,15015],{"className":5242,"code":15014,"language":5244,"meta":5245,"style":5245},"# Коміт без тригера CI\ngit commit -m \"docs: update README [skip ci]\"\n\n# Або\ngit commit -m \"docs: update README [ci skip]\"\n",[5173,15016,15017,15022,15033,15037,15042],{"__ignoreMap":5245},[5249,15018,15019],{"class":5251,"line":5252},[5249,15020,15021],{"class":5255},"# Коміт без тригера CI\n",[5249,15023,15024,15026,15028,15030],{"class":5251,"line":5259},[5249,15025,5263],{"class":5262},[5249,15027,5278],{"class":5266},[5249,15029,5282],{"class":5281},[5249,15031,15032],{"class":5266}," \"docs: update README [skip ci]\"\n",[5249,15034,15035],{"class":5251,"line":5273},[5249,15036,5306],{"emptyLinePlaceholder":5305},[5249,15038,15039],{"class":5251,"line":5288},[5249,15040,15041],{"class":5255},"# Або\n",[5249,15043,15044,15046,15048,15050],{"class":5251,"line":5302},[5249,15045,5263],{"class":5262},[5249,15047,5278],{"class":5266},[5249,15049,5282],{"class":5281},[5249,15051,15052],{"class":5266}," \"docs: update README [ci skip]\"\n",[5147,15054],{},[5150,15056,15058],{"id":15057},"best-practices","Best Practices",[5849,15060,15062],{"id":15061},"_1-fail-fast","1. Fail Fast",[5155,15064,15065],{},"Організуйте jobs так, щоб швидкі перевірки (lint, type check) виконувалися першими:",[5240,15067,15069],{"className":6141,"code":15068,"language":6143,"meta":5245,"style":5245},"jobs:\n  lint:          # 1-2 хвилини\n    runs-on: ubuntu-latest\n    steps:\n      - run: npm run lint\n\n  unit-test:     # 2-3 хвилини\n    needs: lint\n    steps:\n      - run: npm test\n\n  e2e-test:      # 5-10 хвилин\n    needs: unit-test\n    steps:\n      - run: npm run test:e2e\n\n  build:         # 3-5 хвилин\n    needs: [unit-test, e2e-test]\n    steps:\n      - run: docker build\n",[5173,15070,15071,15077,15087,15095,15101,15111,15115,15125,15133,15139,15149,15153,15163,15172,15178,15188,15192,15202,15216,15222],{"__ignoreMap":5245},[5249,15072,15073,15075],{"class":5251,"line":5252},[5249,15074,6225],{"class":6150},[5249,15076,6185],{"class":6154},[5249,15078,15079,15081,15084],{"class":5251,"line":5259},[5249,15080,6771],{"class":6150},[5249,15082,15083],{"class":6154},":          ",[5249,15085,15086],{"class":5255},"# 1-2 хвилини\n",[5249,15088,15089,15091,15093],{"class":5251,"line":5273},[5249,15090,6242],{"class":6150},[5249,15092,6155],{"class":6154},[5249,15094,6792],{"class":6158},[5249,15096,15097,15099],{"class":5251,"line":5288},[5249,15098,6255],{"class":6150},[5249,15100,6185],{"class":6154},[5249,15102,15103,15105,15107,15109],{"class":5251,"line":5302},[5249,15104,6265],{"class":6154},[5249,15106,12215],{"class":6150},[5249,15108,6155],{"class":6154},[5249,15110,6912],{"class":6158},[5249,15112,15113],{"class":5251,"line":5309},[5249,15114,5306],{"emptyLinePlaceholder":5305},[5249,15116,15117,15119,15122],{"class":5251,"line":5315},[5249,15118,14819],{"class":6150},[5249,15120,15121],{"class":6154},":     ",[5249,15123,15124],{"class":5255},"# 2-3 хвилини\n",[5249,15126,15127,15129,15131],{"class":5251,"line":5324},[5249,15128,6968],{"class":6150},[5249,15130,6155],{"class":6154},[5249,15132,7250],{"class":6158},[5249,15134,15135,15137],{"class":5251,"line":5329},[5249,15136,6255],{"class":6150},[5249,15138,6185],{"class":6154},[5249,15140,15141,15143,15145,15147],{"class":5251,"line":5335},[5249,15142,6265],{"class":6154},[5249,15144,12215],{"class":6150},[5249,15146,6155],{"class":6154},[5249,15148,6374],{"class":6158},[5249,15150,15151],{"class":5251,"line":5344},[5249,15152,5306],{"emptyLinePlaceholder":5305},[5249,15154,15155,15157,15160],{"class":5251,"line":5356},[5249,15156,14855],{"class":6150},[5249,15158,15159],{"class":6154},":      ",[5249,15161,15162],{"class":5255},"# 5-10 хвилин\n",[5249,15164,15165,15167,15169],{"class":5251,"line":5361},[5249,15166,6968],{"class":6150},[5249,15168,6155],{"class":6154},[5249,15170,15171],{"class":6158},"unit-test\n",[5249,15173,15174,15176],{"class":5251,"line":5367},[5249,15175,6255],{"class":6150},[5249,15177,6185],{"class":6154},[5249,15179,15180,15182,15184,15186],{"class":5251,"line":5376},[5249,15181,6265],{"class":6154},[5249,15183,12215],{"class":6150},[5249,15185,6155],{"class":6154},[5249,15187,7582],{"class":6158},[5249,15189,15190],{"class":5251,"line":5381},[5249,15191,5306],{"emptyLinePlaceholder":5305},[5249,15193,15194,15196,15199],{"class":5251,"line":5387},[5249,15195,8279],{"class":6150},[5249,15197,15198],{"class":6154},":         ",[5249,15200,15201],{"class":5255},"# 3-5 хвилин\n",[5249,15203,15204,15206,15208,15210,15212,15214],{"class":5251,"line":5398},[5249,15205,6968],{"class":6150},[5249,15207,6193],{"class":6154},[5249,15209,14913],{"class":6158},[5249,15211,6488],{"class":6154},[5249,15213,14918],{"class":6158},[5249,15215,6199],{"class":6154},[5249,15217,15218,15220],{"class":5251,"line":5403},[5249,15219,6255],{"class":6150},[5249,15221,6185],{"class":6154},[5249,15223,15224,15226,15228,15230],{"class":5251,"line":5409},[5249,15225,6265],{"class":6154},[5249,15227,12215],{"class":6150},[5249,15229,6155],{"class":6154},[5249,15231,15232],{"class":6158},"docker build\n",[5155,15234,15235],{},"Якщо lint падає (1-2 хв), unit\u002Fe2e тести не запускаються → економія часу.",[5849,15237,15239],{"id":15238},"_2-keep-secrets-secure","2. Keep Secrets Secure",[5240,15241,15243],{"className":6141,"code":15242,"language":6143,"meta":5245,"style":5245},"# ✅ ХОРОША ПРАКТИКА\nenv:\n  DATABASE_URL: ${{ secrets.DATABASE_URL }}\nrun: npm run migration:run\n\n# ❌ ПОГАНА ПРАКТИКА\nrun: npm run migration:run --url=${{ secrets.DATABASE_URL }}\n",[5173,15244,15245,15249,15255,15265,15274,15278,15282],{"__ignoreMap":5245},[5249,15246,15247],{"class":5251,"line":5252},[5249,15248,12234],{"class":5255},[5249,15250,15251,15253],{"class":5251,"line":5259},[5249,15252,12239],{"class":6150},[5249,15254,6185],{"class":6154},[5249,15256,15257,15260,15262],{"class":5251,"line":5273},[5249,15258,15259],{"class":6150},"  DATABASE_URL",[5249,15261,6155],{"class":6154},[5249,15263,15264],{"class":6158},"${{ secrets.DATABASE_URL }}\n",[5249,15266,15267,15269,15271],{"class":5251,"line":5288},[5249,15268,12215],{"class":6150},[5249,15270,6155],{"class":6154},[5249,15272,15273],{"class":6158},"npm run migration:run\n",[5249,15275,15276],{"class":5251,"line":5302},[5249,15277,5306],{"emptyLinePlaceholder":5305},[5249,15279,15280],{"class":5251,"line":5309},[5249,15281,12210],{"class":5255},[5249,15283,15284,15286,15288],{"class":5251,"line":5315},[5249,15285,12215],{"class":6150},[5249,15287,6155],{"class":6154},[5249,15289,15290],{"class":6158},"npm run migration:run --url=${{ secrets.DATABASE_URL }}\n",[5849,15292,15294],{"id":15293},"_3-use-specific-action-versions","3. Use Specific Action Versions",[5240,15296,15298],{"className":6141,"code":15297,"language":6143,"meta":5245,"style":5245},"# ✅ ХОРОША ПРАКТИКА: pinned version\n- uses: actions\u002Fcheckout@v3.5.2\n\n# ❌ ПОГАНА ПРАКТИКА: floating version\n- uses: actions\u002Fcheckout@main\n",[5173,15299,15300,15305,15316,15320,15325],{"__ignoreMap":5245},[5249,15301,15302],{"class":5251,"line":5252},[5249,15303,15304],{"class":5255},"# ✅ ХОРОША ПРАКТИКА: pinned version\n",[5249,15306,15307,15309,15311,15313],{"class":5251,"line":5259},[5249,15308,7941],{"class":6154},[5249,15310,7944],{"class":6150},[5249,15312,6155],{"class":6154},[5249,15314,15315],{"class":6158},"actions\u002Fcheckout@v3.5.2\n",[5249,15317,15318],{"class":5251,"line":5273},[5249,15319,5306],{"emptyLinePlaceholder":5305},[5249,15321,15322],{"class":5251,"line":5288},[5249,15323,15324],{"class":5255},"# ❌ ПОГАНА ПРАКТИКА: floating version\n",[5249,15326,15327,15329,15331,15333],{"class":5251,"line":5302},[5249,15328,7941],{"class":6154},[5249,15330,7944],{"class":6150},[5249,15332,6155],{"class":6154},[5249,15334,15335],{"class":6158},"actions\u002Fcheckout@main\n",[5849,15337,15339],{"id":15338},"_4-limit-concurrent-deployments","4. Limit Concurrent Deployments",[5240,15341,15343],{"className":6141,"code":15342,"language":6143,"meta":5245,"style":5245},"concurrency:\n  group: production-deployment\n  cancel-in-progress: false  # Не cancel поточний deploy\n",[5173,15344,15345,15352,15362],{"__ignoreMap":5245},[5249,15346,15347,15350],{"class":5251,"line":5252},[5249,15348,15349],{"class":6150},"concurrency",[5249,15351,6185],{"class":6154},[5249,15353,15354,15357,15359],{"class":5251,"line":5259},[5249,15355,15356],{"class":6150},"  group",[5249,15358,6155],{"class":6154},[5249,15360,15361],{"class":6158},"production-deployment\n",[5249,15363,15364,15367,15369,15371],{"class":5251,"line":5273},[5249,15365,15366],{"class":6150},"  cancel-in-progress",[5249,15368,6155],{"class":6154},[5249,15370,8057],{"class":5281},[5249,15372,15373],{"class":5255},"  # Не cancel поточний deploy\n",[5155,15375,15376],{},"Це запобігає одночасним deploys, які можуть конфліктувати.",[5849,15378,15380],{"id":15379},"_5-add-timeouts","5. Add Timeouts",[5240,15382,15384],{"className":6141,"code":15383,"language":6143,"meta":5245,"style":5245},"jobs:\n  test:\n    runs-on: ubuntu-latest\n    timeout-minutes: 10  # Автоматичний cancel після 10 хв\n\n    steps:\n      - name: Run tests\n        timeout-minutes: 5  # Timeout для конкретного step\n        run: npm test\n",[5173,15385,15386,15392,15398,15406,15419,15423,15429,15439,15452],{"__ignoreMap":5245},[5249,15387,15388,15390],{"class":5251,"line":5252},[5249,15389,6225],{"class":6150},[5249,15391,6185],{"class":6154},[5249,15393,15394,15396],{"class":5251,"line":5259},[5249,15395,6235],{"class":6150},[5249,15397,6185],{"class":6154},[5249,15399,15400,15402,15404],{"class":5251,"line":5273},[5249,15401,6242],{"class":6150},[5249,15403,6155],{"class":6154},[5249,15405,6792],{"class":6158},[5249,15407,15408,15411,15413,15416],{"class":5251,"line":5288},[5249,15409,15410],{"class":6150},"    timeout-minutes",[5249,15412,6155],{"class":6154},[5249,15414,15415],{"class":6324},"10",[5249,15417,15418],{"class":5255},"  # Автоматичний cancel після 10 хв\n",[5249,15420,15421],{"class":5251,"line":5302},[5249,15422,5306],{"emptyLinePlaceholder":5305},[5249,15424,15425,15427],{"class":5251,"line":5309},[5249,15426,6255],{"class":6150},[5249,15428,6185],{"class":6154},[5249,15430,15431,15433,15435,15437],{"class":5251,"line":5315},[5249,15432,6265],{"class":6154},[5249,15434,6151],{"class":6150},[5249,15436,6155],{"class":6154},[5249,15438,6365],{"class":6158},[5249,15440,15441,15444,15446,15449],{"class":5251,"line":5324},[5249,15442,15443],{"class":6150},"        timeout-minutes",[5249,15445,6155],{"class":6154},[5249,15447,15448],{"class":6324},"5",[5249,15450,15451],{"class":5255},"  # Timeout для конкретного step\n",[5249,15453,15454,15456,15458],{"class":5251,"line":5329},[5249,15455,6345],{"class":6150},[5249,15457,6155],{"class":6154},[5249,15459,6374],{"class":6158},[5849,15461,15463],{"id":15462},"_6-monitor-build-duration","6. Monitor Build Duration",[5155,15465,15466],{},"Регулярно переглядайте metrics:",[5240,15468,15470],{"className":5242,"code":15469,"language":5244,"meta":5245,"style":5245},"# GitHub repo → Insights → Actions\n# Перевіряйте:\n# - Average build time (мета: \u003C 10 хв)\n# - Success rate (мета: > 95%)\n# - Queue time (мета: \u003C 1 хв)\n",[5173,15471,15472,15477,15482,15487,15492],{"__ignoreMap":5245},[5249,15473,15474],{"class":5251,"line":5252},[5249,15475,15476],{"class":5255},"# GitHub repo → Insights → Actions\n",[5249,15478,15479],{"class":5251,"line":5259},[5249,15480,15481],{"class":5255},"# Перевіряйте:\n",[5249,15483,15484],{"class":5251,"line":5273},[5249,15485,15486],{"class":5255},"# - Average build time (мета: \u003C 10 хв)\n",[5249,15488,15489],{"class":5251,"line":5288},[5249,15490,15491],{"class":5255},"# - Success rate (мета: > 95%)\n",[5249,15493,15494],{"class":5251,"line":5302},[5249,15495,15496],{"class":5255},"# - Queue time (мета: \u003C 1 хв)\n",[5849,15498,15500],{"id":15499},"_7-keep-workflows-dry","7. Keep Workflows DRY",[5155,15502,15503],{},"Використовуйте reusable workflows для спільної логіки:",[5240,15505,15507],{"className":6141,"code":15506,"language":6143,"meta":5245,"style":5245},"# .github\u002Fworkflows\u002Freusable-test.yml\nname: Reusable Test Workflow\n\non:\n  workflow_call:\n    inputs:\n      node-version:\n        required: true\n        type: string\n\njobs:\n  test:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions\u002Fcheckout@v3\n      - uses: actions\u002Fsetup-node@v3\n        with:\n          node-version: ${{ inputs.node-version }}\n      - run: npm ci\n      - run: npm test\n",[5173,15508,15509,15514,15523,15527,15533,15540,15546,15553,15561,15570,15574,15580,15586,15594,15600,15610,15620,15626,15635,15645],{"__ignoreMap":5245},[5249,15510,15511],{"class":5251,"line":5252},[5249,15512,15513],{"class":5255},"# .github\u002Fworkflows\u002Freusable-test.yml\n",[5249,15515,15516,15518,15520],{"class":5251,"line":5259},[5249,15517,6151],{"class":6150},[5249,15519,6155],{"class":6154},[5249,15521,15522],{"class":6158},"Reusable Test Workflow\n",[5249,15524,15525],{"class":5251,"line":5273},[5249,15526,5306],{"emptyLinePlaceholder":5305},[5249,15528,15529,15531],{"class":5251,"line":5288},[5249,15530,6171],{"class":5281},[5249,15532,6185],{"class":6154},[5249,15534,15535,15538],{"class":5251,"line":5302},[5249,15536,15537],{"class":6150},"  workflow_call",[5249,15539,6185],{"class":6154},[5249,15541,15542,15544],{"class":5251,"line":5309},[5249,15543,6571],{"class":6150},[5249,15545,6185],{"class":6154},[5249,15547,15548,15551],{"class":5251,"line":5315},[5249,15549,15550],{"class":6150},"      node-version",[5249,15552,6185],{"class":6154},[5249,15554,15555,15557,15559],{"class":5251,"line":5324},[5249,15556,6595],{"class":6150},[5249,15558,6155],{"class":6154},[5249,15560,6600],{"class":5281},[5249,15562,15563,15565,15567],{"class":5251,"line":5329},[5249,15564,13981],{"class":6150},[5249,15566,6155],{"class":6154},[5249,15568,15569],{"class":6158},"string\n",[5249,15571,15572],{"class":5251,"line":5335},[5249,15573,5306],{"emptyLinePlaceholder":5305},[5249,15575,15576,15578],{"class":5251,"line":5344},[5249,15577,6225],{"class":6150},[5249,15579,6185],{"class":6154},[5249,15581,15582,15584],{"class":5251,"line":5356},[5249,15583,6235],{"class":6150},[5249,15585,6185],{"class":6154},[5249,15587,15588,15590,15592],{"class":5251,"line":5361},[5249,15589,6242],{"class":6150},[5249,15591,6155],{"class":6154},[5249,15593,6792],{"class":6158},[5249,15595,15596,15598],{"class":5251,"line":5367},[5249,15597,6255],{"class":6150},[5249,15599,6185],{"class":6154},[5249,15601,15602,15604,15606,15608],{"class":5251,"line":5376},[5249,15603,6265],{"class":6154},[5249,15605,7944],{"class":6150},[5249,15607,6155],{"class":6154},[5249,15609,6282],{"class":6158},[5249,15611,15612,15614,15616,15618],{"class":5251,"line":5381},[5249,15613,6265],{"class":6154},[5249,15615,7944],{"class":6150},[5249,15617,6155],{"class":6154},[5249,15619,6307],{"class":6158},[5249,15621,15622,15624],{"class":5251,"line":5387},[5249,15623,6312],{"class":6150},[5249,15625,6185],{"class":6154},[5249,15627,15628,15630,15632],{"class":5251,"line":5398},[5249,15629,6319],{"class":6150},[5249,15631,6155],{"class":6154},[5249,15633,15634],{"class":6158},"${{ inputs.node-version }}\n",[5249,15636,15637,15639,15641,15643],{"class":5251,"line":5403},[5249,15638,6265],{"class":6154},[5249,15640,12215],{"class":6150},[5249,15642,6155],{"class":6154},[5249,15644,6350],{"class":6158},[5249,15646,15647,15649,15651,15653],{"class":5251,"line":5409},[5249,15648,6265],{"class":6154},[5249,15650,12215],{"class":6150},[5249,15652,6155],{"class":6154},[5249,15654,6374],{"class":6158},[5240,15656,15658],{"className":6141,"code":15657,"language":6143,"meta":5245,"style":5245},"# .github\u002Fworkflows\u002Fci.yml\nname: CI\n\non: [push]\n\njobs:\n  test-node-18:\n    uses: .\u002F.github\u002Fworkflows\u002Freusable-test.yml\n    with:\n      node-version: '18'\n\n  test-node-20:\n    uses: .\u002F.github\u002Fworkflows\u002Freusable-test.yml\n    with:\n      node-version: '20'\n",[5173,15659,15660,15664,15672,15676,15686,15690,15696,15703,15712,15718,15727,15731,15738,15746,15752],{"__ignoreMap":5245},[5249,15661,15662],{"class":5251,"line":5252},[5249,15663,6700],{"class":5255},[5249,15665,15666,15668,15670],{"class":5251,"line":5259},[5249,15667,6151],{"class":6150},[5249,15669,6155],{"class":6154},[5249,15671,6709],{"class":6158},[5249,15673,15674],{"class":5251,"line":5273},[5249,15675,5306],{"emptyLinePlaceholder":5305},[5249,15677,15678,15680,15682,15684],{"class":5251,"line":5288},[5249,15679,6171],{"class":5281},[5249,15681,6193],{"class":6154},[5249,15683,6385],{"class":6158},[5249,15685,6199],{"class":6154},[5249,15687,15688],{"class":5251,"line":5302},[5249,15689,5306],{"emptyLinePlaceholder":5305},[5249,15691,15692,15694],{"class":5251,"line":5309},[5249,15693,6225],{"class":6150},[5249,15695,6185],{"class":6154},[5249,15697,15698,15701],{"class":5251,"line":5315},[5249,15699,15700],{"class":6150},"  test-node-18",[5249,15702,6185],{"class":6154},[5249,15704,15705,15707,15709],{"class":5251,"line":5324},[5249,15706,11934],{"class":6150},[5249,15708,6155],{"class":6154},[5249,15710,15711],{"class":6158},".\u002F.github\u002Fworkflows\u002Freusable-test.yml\n",[5249,15713,15714,15716],{"class":5251,"line":5329},[5249,15715,11943],{"class":6150},[5249,15717,6185],{"class":6154},[5249,15719,15720,15722,15724],{"class":5251,"line":5335},[5249,15721,15550],{"class":6150},[5249,15723,6155],{"class":6154},[5249,15725,15726],{"class":6158},"'18'\n",[5249,15728,15729],{"class":5251,"line":5344},[5249,15730,5306],{"emptyLinePlaceholder":5305},[5249,15732,15733,15736],{"class":5251,"line":5356},[5249,15734,15735],{"class":6150},"  test-node-20",[5249,15737,6185],{"class":6154},[5249,15739,15740,15742,15744],{"class":5251,"line":5361},[5249,15741,11934],{"class":6150},[5249,15743,6155],{"class":6154},[5249,15745,15711],{"class":6158},[5249,15747,15748,15750],{"class":5251,"line":5367},[5249,15749,11943],{"class":6150},[5249,15751,6185],{"class":6154},[5249,15753,15754,15756,15758],{"class":5251,"line":5376},[5249,15755,15550],{"class":6150},[5249,15757,6155],{"class":6154},[5249,15759,15760],{"class":6158},"'20'\n",[5849,15762,15764],{"id":15763},"_8-document-deployment-process","8. Document Deployment Process",[5155,15766,11794,15767,7747],{},[5173,15768,15769],{},"DEPLOYMENT.md",[5240,15771,15775],{"className":15772,"code":15773,"language":15774,"meta":5245,"style":5245},"language-markdown shiki shiki-themes light-plus dark-plus dark-plus","# Deployment Guide\n\n## Automatic Deployment\n\nPush to `main` branch triggers automatic deployment to production.\n\n## Manual Deployment\n\n1. Go to Actions → \"Deploy to Production\"\n2. Click \"Run workflow\"\n3. Select version\u002Ftag to deploy\n4. Click \"Run workflow\"\n5. Wait for approval from tech lead\n6. Deployment executes after 5-minute wait timer\n\n## Rollback\n\n1. Go to Actions → \"Manual Rollback\"\n2. Click \"Run workflow\"\n3. Enter commit SHA or tag of stable version\n4. Select environment (staging\u002Fproduction)\n5. Click \"Run workflow\"\n\n## Troubleshooting\n\nIf deployment fails:\n1. Check workflow logs for errors\n2. Verify health endpoint: `curl https:\u002F\u002Fexample.com\u002Fhealth`\n3. Check application logs: `ssh prod-server \"docker logs blog-app\"`\n4. If needed, execute manual rollback\n","markdown",[5173,15776,15777,15783,15787,15792,15796,15808,15812,15817,15821,15830,15838,15846,15853,15861,15869,15873,15878,15882,15889,15895,15902,15909,15915,15919,15924,15928,15933,15940,15950,15960],{"__ignoreMap":5245},[5249,15778,15779],{"class":5251,"line":5252},[5249,15780,15782],{"class":15781},"s9ZQM","# Deployment Guide\n",[5249,15784,15785],{"class":5251,"line":5259},[5249,15786,5306],{"emptyLinePlaceholder":5305},[5249,15788,15789],{"class":5251,"line":5273},[5249,15790,15791],{"class":15781},"## Automatic Deployment\n",[5249,15793,15794],{"class":5251,"line":5288},[5249,15795,5306],{"emptyLinePlaceholder":5305},[5249,15797,15798,15801,15805],{"class":5251,"line":5302},[5249,15799,15800],{"class":6154},"Push to ",[5249,15802,15804],{"class":15803},"sJtsr","`main`",[5249,15806,15807],{"class":6154}," branch triggers automatic deployment to production.\n",[5249,15809,15810],{"class":5251,"line":5309},[5249,15811,5306],{"emptyLinePlaceholder":5305},[5249,15813,15814],{"class":5251,"line":5315},[5249,15815,15816],{"class":15781},"## Manual Deployment\n",[5249,15818,15819],{"class":5251,"line":5324},[5249,15820,5306],{"emptyLinePlaceholder":5305},[5249,15822,15823,15827],{"class":5251,"line":5329},[5249,15824,15826],{"class":15825},"sxDXj","1.",[5249,15828,15829],{"class":6154}," Go to Actions → \"Deploy to Production\"\n",[5249,15831,15832,15835],{"class":5251,"line":5335},[5249,15833,15834],{"class":15825},"2.",[5249,15836,15837],{"class":6154}," Click \"Run workflow\"\n",[5249,15839,15840,15843],{"class":5251,"line":5344},[5249,15841,15842],{"class":15825},"3.",[5249,15844,15845],{"class":6154}," Select version\u002Ftag to deploy\n",[5249,15847,15848,15851],{"class":5251,"line":5356},[5249,15849,15850],{"class":15825},"4.",[5249,15852,15837],{"class":6154},[5249,15854,15855,15858],{"class":5251,"line":5361},[5249,15856,15857],{"class":15825},"5.",[5249,15859,15860],{"class":6154}," Wait for approval from tech lead\n",[5249,15862,15863,15866],{"class":5251,"line":5367},[5249,15864,15865],{"class":15825},"6.",[5249,15867,15868],{"class":6154}," Deployment executes after 5-minute wait timer\n",[5249,15870,15871],{"class":5251,"line":5376},[5249,15872,5306],{"emptyLinePlaceholder":5305},[5249,15874,15875],{"class":5251,"line":5381},[5249,15876,15877],{"class":15781},"## Rollback\n",[5249,15879,15880],{"class":5251,"line":5387},[5249,15881,5306],{"emptyLinePlaceholder":5305},[5249,15883,15884,15886],{"class":5251,"line":5398},[5249,15885,15826],{"class":15825},[5249,15887,15888],{"class":6154}," Go to Actions → \"Manual Rollback\"\n",[5249,15890,15891,15893],{"class":5251,"line":5403},[5249,15892,15834],{"class":15825},[5249,15894,15837],{"class":6154},[5249,15896,15897,15899],{"class":5251,"line":5409},[5249,15898,15842],{"class":15825},[5249,15900,15901],{"class":6154}," Enter commit SHA or tag of stable version\n",[5249,15903,15904,15906],{"class":5251,"line":5419},[5249,15905,15850],{"class":15825},[5249,15907,15908],{"class":6154}," Select environment (staging\u002Fproduction)\n",[5249,15910,15911,15913],{"class":5251,"line":5424},[5249,15912,15857],{"class":15825},[5249,15914,15837],{"class":6154},[5249,15916,15917],{"class":5251,"line":5430},[5249,15918,5306],{"emptyLinePlaceholder":5305},[5249,15920,15921],{"class":5251,"line":5442},[5249,15922,15923],{"class":15781},"## Troubleshooting\n",[5249,15925,15926],{"class":5251,"line":5447},[5249,15927,5306],{"emptyLinePlaceholder":5305},[5249,15929,15930],{"class":5251,"line":5453},[5249,15931,15932],{"class":6154},"If deployment fails:\n",[5249,15934,15935,15937],{"class":5251,"line":5698},[5249,15936,15826],{"class":15825},[5249,15938,15939],{"class":6154}," Check workflow logs for errors\n",[5249,15941,15942,15944,15947],{"class":5251,"line":5704},[5249,15943,15834],{"class":15825},[5249,15945,15946],{"class":6154}," Verify health endpoint: ",[5249,15948,15949],{"class":15803},"`curl https:\u002F\u002Fexample.com\u002Fhealth`\n",[5249,15951,15952,15954,15957],{"class":5251,"line":5710},[5249,15953,15842],{"class":15825},[5249,15955,15956],{"class":6154}," Check application logs: ",[5249,15958,15959],{"class":15803},"`ssh prod-server \"docker logs blog-app\"`\n",[5249,15961,15962,15964],{"class":5251,"line":5716},[5249,15963,15850],{"class":15825},[5249,15965,15966],{"class":6154}," If needed, execute manual rollback\n",[5147,15968],{},[5150,15970,15972],{"id":15971},"повний-приклад-production-ready-cicd-pipeline","Повний приклад: Production-ready CI\u002FCD Pipeline",[5155,15974,15975],{},"Комплексний workflow для NestJS застосунку з усіма best practices:",[5240,15977,15979],{"className":6141,"code":15978,"language":6143,"meta":5245,"style":5245},"# .github\u002Fworkflows\u002Fproduction-pipeline.yml\nname: Production CI\u002FCD Pipeline\n\non:\n  push:\n    branches: [main]\n  pull_request:\n    branches: [main]\n\nenv:\n  NODE_VERSION: 18\n  DOCKER_IMAGE: myusername\u002Fmy-nestjs-app\n\njobs:\n  # ============================================\n  # Stage 1: Code Quality Checks\n  # ============================================\n  lint:\n    name: Lint and Type Check\n    runs-on: ubuntu-latest\n    timeout-minutes: 5\n\n    steps:\n      - name: Checkout code\n        uses: actions\u002Fcheckout@v3\n\n      - name: Setup Node.js\n        uses: actions\u002Fsetup-node@v3\n        with:\n          node-version: ${{ env.NODE_VERSION }}\n          cache: 'npm'\n\n      - name: Install dependencies\n        run: npm ci\n\n      - name: Run ESLint\n        run: npm run lint\n\n      - name: Run Prettier check\n        run: npm run format:check\n\n      - name: TypeScript type check\n        run: npx tsc --noEmit\n\n  # ============================================\n  # Stage 2: Unit Tests\n  # ============================================\n  unit-test:\n    name: Unit Tests\n    runs-on: ubuntu-latest\n    needs: lint\n    timeout-minutes: 10\n\n    strategy:\n      matrix:\n        node-version: [18, 20]\n\n    steps:\n      - uses: actions\u002Fcheckout@v3\n\n      - name: Setup Node.js ${{ matrix.node-version }}\n        uses: actions\u002Fsetup-node@v3\n        with:\n          node-version: ${{ matrix.node-version }}\n          cache: 'npm'\n\n      - name: Install dependencies\n        run: npm ci\n\n      - name: Run unit tests with coverage\n        run: npm run test:cov\n\n      - name: Upload coverage to Codecov\n        if: matrix.node-version == 18\n        uses: codecov\u002Fcodecov-action@v3\n        with:\n          files: .\u002Fcoverage\u002Flcov.info\n          flags: unittests\n\n      - name: Archive test results\n        if: always()\n        uses: actions\u002Fupload-artifact@v3\n        with:\n          name: unit-test-results-node-${{ matrix.node-version }}\n          path: |\n            coverage\u002F\n            test-results\u002F\n\n  # ============================================\n  # Stage 3: E2E Tests\n  # ============================================\n  e2e-test:\n    name: E2E Tests\n    runs-on: ubuntu-latest\n    needs: lint\n    timeout-minutes: 15\n\n    services:\n      postgres:\n        image: postgres:15-alpine\n        env:\n          POSTGRES_USER: postgres\n          POSTGRES_PASSWORD: postgres\n          POSTGRES_DB: test_db\n        options: >-\n          --health-cmd pg_isready\n          --health-interval 10s\n          --health-timeout 5s\n          --health-retries 5\n        ports:\n          - 5432:5432\n\n      redis:\n        image: redis:7-alpine\n        options: >-\n          --health-cmd \"redis-cli ping\"\n          --health-interval 10s\n          --health-timeout 5s\n          --health-retries 5\n        ports:\n          - 6379:6379\n\n    steps:\n      - uses: actions\u002Fcheckout@v3\n\n      - name: Setup Node.js\n        uses: actions\u002Fsetup-node@v3\n        with:\n          node-version: ${{ env.NODE_VERSION }}\n          cache: 'npm'\n\n      - name: Install dependencies\n        run: npm ci\n\n      - name: Run E2E tests\n        env:\n          NODE_ENV: test\n          DATABASE_HOST: localhost\n          DATABASE_PORT: 5432\n          DATABASE_USER: postgres\n          DATABASE_PASSWORD: postgres\n          DATABASE_NAME: test_db\n          REDIS_HOST: localhost\n          REDIS_PORT: 6379\n          JWT_SECRET: test-jwt-secret-for-e2e-tests-only\n        run: npm run test:e2e\n\n      - name: Upload E2E test results\n        if: always()\n        uses: actions\u002Fupload-artifact@v3\n        with:\n          name: e2e-test-results\n          path: test-results\u002F\n\n  # ============================================\n  # Stage 4: Security Scan\n  # ============================================\n  security:\n    name: Security Scan\n    runs-on: ubuntu-latest\n    needs: lint\n    timeout-minutes: 5\n\n    steps:\n      - uses: actions\u002Fcheckout@v3\n\n      - name: Run npm audit\n        run: npm audit --audit-level=high\n        continue-on-error: true\n\n      - name: Run Snyk security scan\n        uses: snyk\u002Factions\u002Fnode@master\n        continue-on-error: true\n        env:\n          SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}\n        with:\n          args: --severity-threshold=high\n\n  # ============================================\n  # Stage 5: Build Docker Image\n  # ============================================\n  build:\n    name: Build Docker Image\n    runs-on: ubuntu-latest\n    needs: [unit-test, e2e-test]\n    if: github.event_name == 'push' && github.ref == 'refs\u002Fheads\u002Fmain'\n    timeout-minutes: 15\n\n    steps:\n      - uses: actions\u002Fcheckout@v3\n\n      - name: Set up Docker Buildx\n        uses: docker\u002Fsetup-buildx-action@v2\n\n      - name: Log in to Docker Hub\n        uses: docker\u002Flogin-action@v2\n        with:\n          username: ${{ secrets.DOCKER_USERNAME }}\n          password: ${{ secrets.DOCKER_PASSWORD }}\n\n      - name: Extract metadata\n        id: meta\n        uses: docker\u002Fmetadata-action@v4\n        with:\n          images: ${{ env.DOCKER_IMAGE }}\n          tags: |\n            type=sha,prefix=main-\n            type=raw,value=latest\n\n      - name: Build and push\n        uses: docker\u002Fbuild-push-action@v4\n        with:\n          context: .\n          file: .\u002FDockerfile\n          push: true\n          tags: ${{ steps.meta.outputs.tags }}\n          labels: ${{ steps.meta.outputs.labels }}\n          cache-from: type=registry,ref=${{ env.DOCKER_IMAGE }}:buildcache\n          cache-to: type=registry,ref=${{ env.DOCKER_IMAGE }}:buildcache,mode=max\n\n      - name: Image digest\n        run: echo \"Image pushed with digest ${{ steps.build.outputs.digest }}\"\n\n  # ============================================\n  # Stage 6: Deploy to Production\n  # ============================================\n  deploy:\n    name: Deploy to Production\n    runs-on: ubuntu-latest\n    needs: build\n    environment:\n      name: production\n      url: https:\u002F\u002Fexample.com\n    concurrency:\n      group: production-deployment\n      cancel-in-progress: false\n    timeout-minutes: 10\n\n    steps:\n      - name: Save current version for rollback\n        id: current-version\n        run: |\n          CURRENT_TAG=$(ssh -o StrictHostKeyChecking=no \\\n            ${{ secrets.PROD_SERVER_USER }}@${{ secrets.PROD_SERVER_HOST }} \\\n            \"cd \u002Fapp && docker-compose config | grep 'image:' | awk '{print \\$2}' | cut -d':' -f2\" || echo \"none\")\n          echo \"tag=$CURRENT_TAG\" >> $GITHUB_OUTPUT\n\n      - name: Deploy via SSH\n        uses: appleboy\u002Fssh-action@v0.1.10\n        with:\n          host: ${{ secrets.PROD_SERVER_HOST }}\n          username: ${{ secrets.PROD_SERVER_USER }}\n          key: ${{ secrets.PROD_SSH_KEY }}\n          script: |\n            cd \u002Fapp\n            export IMAGE_TAG=main-${{ github.sha }}\n            docker-compose pull\n            docker-compose up -d\n            docker system prune -af\n\n      - name: Health check with retry\n        id: health-check\n        run: |\n          echo \"Waiting for application to start...\"\n          sleep 15\n          \n          for i in {1..10}; do\n            if curl -f https:\u002F\u002Fexample.com\u002Fhealth; then\n              echo \"✅ Health check passed\"\n              exit 0\n            fi\n            echo \"⏳ Health check attempt $i\u002F10 failed, retrying...\"\n            sleep 10\n          done\n          \n          echo \"❌ Health check failed after 10 attempts\"\n          exit 1\n\n      - name: Rollback on failure\n        if: failure() && steps.health-check.outcome == 'failure'\n        uses: appleboy\u002Fssh-action@v0.1.10\n        with:\n          host: ${{ secrets.PROD_SERVER_HOST }}\n          username: ${{ secrets.PROD_SERVER_USER }}\n          key: ${{ secrets.PROD_SSH_KEY }}\n          script: |\n            cd \u002Fapp\n            export IMAGE_TAG=${{ steps.current-version.outputs.tag }}\n            docker-compose up -d\n            \n            # Verify rollback\n            sleep 10\n            curl -f http:\u002F\u002Flocalhost:3000\u002Fhealth || exit 1\n\n  # ============================================\n  # Stage 7: Notifications\n  # ============================================\n  notify:\n    name: Send Notifications\n    runs-on: ubuntu-latest\n    needs: [deploy]\n    if: always()\n\n    steps:\n      - name: Notify Slack on Success\n        if: needs.deploy.result == 'success'\n        uses: slackapi\u002Fslack-github-action@v1.24.0\n        with:\n          webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }}\n          payload: |\n            {\n              \"text\": \"✅ Production deployment successful\",\n              \"blocks\": [\n                {\n                  \"type\": \"header\",\n                  \"text\": {\n                    \"type\": \"plain_text\",\n                    \"text\": \"✅ Production Deployment Successful\"\n                  }\n                },\n                {\n                  \"type\": \"section\",\n                  \"fields\": [\n                    {\"type\": \"mrkdwn\", \"text\": \"*Commit:*\\n${{ github.sha }}\"},\n                    {\"type\": \"mrkdwn\", \"text\": \"*Author:*\\n${{ github.actor }}\"}\n                  ]\n                },\n                {\n                  \"type\": \"section\",\n                  \"text\": {\n                    \"type\": \"mrkdwn\",\n                    \"text\": \"*Message:* ${{ github.event.head_commit.message }}\"\n                  }\n                }\n              ]\n            }\n\n      - name: Notify Slack on Failure\n        if: needs.deploy.result == 'failure'\n        uses: slackapi\u002Fslack-github-action@v1.24.0\n        with:\n          webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }}\n          payload: |\n            {\n              \"text\": \"❌ Production deployment failed\",\n              \"blocks\": [\n                {\n                  \"type\": \"header\",\n                  \"text\": {\n                    \"type\": \"plain_text\",\n                    \"text\": \"❌ Production Deployment Failed\"\n                  }\n                },\n                {\n                  \"type\": \"section\",\n                  \"text\": {\n                    \"type\": \"mrkdwn\",\n                    \"text\": \"⚠️ *Action Required:* \u003C${{ github.server_url }}\u002F${{ github.repository }}\u002Factions\u002Fruns\u002F${{ github.run_id }}|View Logs>\"\n                  }\n                }\n              ]\n            }\n\n      - name: Create issue on failure\n        if: needs.deploy.result == 'failure'\n        uses: actions\u002Fgithub-script@v6\n        with:\n          script: |\n            await github.rest.issues.create({\n              owner: context.repo.owner,\n              repo: context.repo.repo,\n              title: `🔥 Production Deployment Failed - ${context.sha.substring(0, 7)}`,\n              body: `Deployment failed. [View logs](${context.serverUrl}\u002F${context.repo.owner}\u002F${context.repo.repo}\u002Factions\u002Fruns\u002F${context.runId})`,\n              labels: ['bug', 'deployment', 'production']\n            });\n",[5173,15980,15981,15986,15995,15999,16005,16011,16021,16027,16037,16041,16047,16056,16065,16069,16075,16080,16085,16089,16095,16104,16112,16121,16125,16131,16141,16149,16153,16163,16171,16177,16186,16194,16198,16208,16216,16220,16230,16238,16242,16253,16262,16266,16277,16286,16290,16294,16299,16303,16309,16318,16326,16334,16343,16347,16353,16359,16373,16377,16383,16393,16397,16407,16415,16421,16429,16437,16441,16451,16459,16463,16474,16482,16486,16496,16505,16513,16519,16527,16535,16539,16550,16558,16566,16572,16581,16589,16594,16599,16603,16607,16612,16616,16622,16630,16638,16646,16655,16659,16665,16671,16680,16686,16694,16702,16710,16720,16724,16728,16732,16736,16742,16748,16752,16758,16767,16777,16781,16785,16789,16793,16799,16805,16809,16815,16825,16829,16839,16847,16853,16861,16869,16873,16883,16891,16895,16905,16911,16921,16929,16937,16945,16953,16962,16971,16980,16991,17000,17005,17016,17025,17034,17041,17050,17059,17064,17069,17075,17080,17088,17098,17107,17116,17125,17130,17137,17148,17153,17165,17175,17185,17190,17202,17212,17221,17228,17239,17246,17257,17262,17267,17273,17278,17285,17295,17304,17319,17329,17338,17343,17350,17361,17366,17377,17386,17391,17402,17411,17418,17427,17436,17441,17452,17461,17470,17477,17487,17496,17502,17508,17513,17524,17533,17540,17549,17558,17567,17576,17585,17595,17605,17610,17621,17631,17636,17641,17647,17652,17659,17668,17677,17686,17693,17702,17711,17719,17729,17740,17749,17754,17761,17772,17781,17790,17796,17802,17808,17814,17819,17831,17840,17847,17856,17865,17874,17883,17888,17894,17899,17904,17910,17915,17926,17935,17944,17950,17956,17961,17966,17971,17977,17982,17987,17993,17998,18003,18008,18014,18019,18024,18035,18044,18053,18060,18069,18078,18087,18096,18101,18107,18112,18118,18124,18129,18135,18140,18145,18151,18156,18163,18173,18182,18193,18202,18207,18214,18225,18234,18243,18250,18259,18268,18273,18279,18284,18289,18294,18299,18304,18309,18314,18319,18324,18329,18334,18340,18346,18351,18356,18361,18366,18371,18376,18382,18387,18392,18397,18402,18407,18418,18427,18436,18443,18452,18461,18466,18472,18477,18482,18487,18492,18497,18502,18507,18512,18517,18522,18527,18532,18538,18543,18548,18553,18558,18563,18575,18584,18593,18600,18609,18615,18621,18627,18633,18639,18645],{"__ignoreMap":5245},[5249,15982,15983],{"class":5251,"line":5252},[5249,15984,15985],{"class":5255},"# .github\u002Fworkflows\u002Fproduction-pipeline.yml\n",[5249,15987,15988,15990,15992],{"class":5251,"line":5259},[5249,15989,6151],{"class":6150},[5249,15991,6155],{"class":6154},[5249,15993,15994],{"class":6158},"Production CI\u002FCD Pipeline\n",[5249,15996,15997],{"class":5251,"line":5273},[5249,15998,5306],{"emptyLinePlaceholder":5305},[5249,16000,16001,16003],{"class":5251,"line":5288},[5249,16002,6171],{"class":5281},[5249,16004,6185],{"class":6154},[5249,16006,16007,16009],{"class":5251,"line":5302},[5249,16008,6182],{"class":6150},[5249,16010,6185],{"class":6154},[5249,16012,16013,16015,16017,16019],{"class":5251,"line":5309},[5249,16014,6190],{"class":6150},[5249,16016,6193],{"class":6154},[5249,16018,6196],{"class":6158},[5249,16020,6199],{"class":6154},[5249,16022,16023,16025],{"class":5251,"line":5315},[5249,16024,6204],{"class":6150},[5249,16026,6185],{"class":6154},[5249,16028,16029,16031,16033,16035],{"class":5251,"line":5324},[5249,16030,6190],{"class":6150},[5249,16032,6193],{"class":6154},[5249,16034,6196],{"class":6158},[5249,16036,6199],{"class":6154},[5249,16038,16039],{"class":5251,"line":5329},[5249,16040,5306],{"emptyLinePlaceholder":5305},[5249,16042,16043,16045],{"class":5251,"line":5335},[5249,16044,12239],{"class":6150},[5249,16046,6185],{"class":6154},[5249,16048,16049,16052,16054],{"class":5251,"line":5344},[5249,16050,16051],{"class":6150},"  NODE_VERSION",[5249,16053,6155],{"class":6154},[5249,16055,6325],{"class":6324},[5249,16057,16058,16061,16063],{"class":5251,"line":5356},[5249,16059,16060],{"class":6150},"  DOCKER_IMAGE",[5249,16062,6155],{"class":6154},[5249,16064,8468],{"class":6158},[5249,16066,16067],{"class":5251,"line":5361},[5249,16068,5306],{"emptyLinePlaceholder":5305},[5249,16070,16071,16073],{"class":5251,"line":5367},[5249,16072,6225],{"class":6150},[5249,16074,6185],{"class":6154},[5249,16076,16077],{"class":5251,"line":5376},[5249,16078,16079],{"class":5255},"  # ============================================\n",[5249,16081,16082],{"class":5251,"line":5381},[5249,16083,16084],{"class":5255},"  # Stage 1: Code Quality Checks\n",[5249,16086,16087],{"class":5251,"line":5387},[5249,16088,16079],{"class":5255},[5249,16090,16091,16093],{"class":5251,"line":5398},[5249,16092,6771],{"class":6150},[5249,16094,6185],{"class":6154},[5249,16096,16097,16099,16101],{"class":5251,"line":5403},[5249,16098,6778],{"class":6150},[5249,16100,6155],{"class":6154},[5249,16102,16103],{"class":6158},"Lint and Type Check\n",[5249,16105,16106,16108,16110],{"class":5251,"line":5409},[5249,16107,6242],{"class":6150},[5249,16109,6155],{"class":6154},[5249,16111,6792],{"class":6158},[5249,16113,16114,16116,16118],{"class":5251,"line":5419},[5249,16115,15410],{"class":6150},[5249,16117,6155],{"class":6154},[5249,16119,16120],{"class":6324},"5\n",[5249,16122,16123],{"class":5251,"line":5424},[5249,16124,5306],{"emptyLinePlaceholder":5305},[5249,16126,16127,16129],{"class":5251,"line":5430},[5249,16128,6255],{"class":6150},[5249,16130,6185],{"class":6154},[5249,16132,16133,16135,16137,16139],{"class":5251,"line":5442},[5249,16134,6265],{"class":6154},[5249,16136,6151],{"class":6150},[5249,16138,6155],{"class":6154},[5249,16140,6272],{"class":6158},[5249,16142,16143,16145,16147],{"class":5251,"line":5447},[5249,16144,6277],{"class":6150},[5249,16146,6155],{"class":6154},[5249,16148,6282],{"class":6158},[5249,16150,16151],{"class":5251,"line":5453},[5249,16152,5306],{"emptyLinePlaceholder":5305},[5249,16154,16155,16157,16159,16161],{"class":5251,"line":5698},[5249,16156,6265],{"class":6154},[5249,16158,6151],{"class":6150},[5249,16160,6155],{"class":6154},[5249,16162,6298],{"class":6158},[5249,16164,16165,16167,16169],{"class":5251,"line":5704},[5249,16166,6277],{"class":6150},[5249,16168,6155],{"class":6154},[5249,16170,6307],{"class":6158},[5249,16172,16173,16175],{"class":5251,"line":5710},[5249,16174,6312],{"class":6150},[5249,16176,6185],{"class":6154},[5249,16178,16179,16181,16183],{"class":5251,"line":5716},[5249,16180,6319],{"class":6150},[5249,16182,6155],{"class":6154},[5249,16184,16185],{"class":6158},"${{ env.NODE_VERSION }}\n",[5249,16187,16188,16190,16192],{"class":5251,"line":5722},[5249,16189,6861],{"class":6150},[5249,16191,6155],{"class":6154},[5249,16193,6866],{"class":6158},[5249,16195,16196],{"class":5251,"line":5728},[5249,16197,5306],{"emptyLinePlaceholder":5305},[5249,16199,16200,16202,16204,16206],{"class":5251,"line":5734},[5249,16201,6265],{"class":6154},[5249,16203,6151],{"class":6150},[5249,16205,6155],{"class":6154},[5249,16207,6340],{"class":6158},[5249,16209,16210,16212,16214],{"class":5251,"line":5740},[5249,16211,6345],{"class":6150},[5249,16213,6155],{"class":6154},[5249,16215,6350],{"class":6158},[5249,16217,16218],{"class":5251,"line":5746},[5249,16219,5306],{"emptyLinePlaceholder":5305},[5249,16221,16222,16224,16226,16228],{"class":5251,"line":5752},[5249,16223,6265],{"class":6154},[5249,16225,6151],{"class":6150},[5249,16227,6155],{"class":6154},[5249,16229,6903],{"class":6158},[5249,16231,16232,16234,16236],{"class":5251,"line":5758},[5249,16233,6345],{"class":6150},[5249,16235,6155],{"class":6154},[5249,16237,6912],{"class":6158},[5249,16239,16240],{"class":5251,"line":5763},[5249,16241,5306],{"emptyLinePlaceholder":5305},[5249,16243,16244,16246,16248,16250],{"class":5251,"line":5769},[5249,16245,6265],{"class":6154},[5249,16247,6151],{"class":6150},[5249,16249,6155],{"class":6154},[5249,16251,16252],{"class":6158},"Run Prettier check\n",[5249,16254,16255,16257,16259],{"class":5251,"line":5775},[5249,16256,6345],{"class":6150},[5249,16258,6155],{"class":6154},[5249,16260,16261],{"class":6158},"npm run format:check\n",[5249,16263,16264],{"class":5251,"line":5781},[5249,16265,5306],{"emptyLinePlaceholder":5305},[5249,16267,16268,16270,16272,16274],{"class":5251,"line":5787},[5249,16269,6265],{"class":6154},[5249,16271,6151],{"class":6150},[5249,16273,6155],{"class":6154},[5249,16275,16276],{"class":6158},"TypeScript type check\n",[5249,16278,16279,16281,16283],{"class":5251,"line":5793},[5249,16280,6345],{"class":6150},[5249,16282,6155],{"class":6154},[5249,16284,16285],{"class":6158},"npx tsc --noEmit\n",[5249,16287,16288],{"class":5251,"line":5799},[5249,16289,5306],{"emptyLinePlaceholder":5305},[5249,16291,16292],{"class":5251,"line":5805},[5249,16293,16079],{"class":5255},[5249,16295,16296],{"class":5251,"line":5811},[5249,16297,16298],{"class":5255},"  # Stage 2: Unit Tests\n",[5249,16300,16301],{"class":5251,"line":5817},[5249,16302,16079],{"class":5255},[5249,16304,16305,16307],{"class":5251,"line":5822},[5249,16306,14819],{"class":6150},[5249,16308,6185],{"class":6154},[5249,16310,16311,16313,16315],{"class":5251,"line":5828},[5249,16312,6778],{"class":6150},[5249,16314,6155],{"class":6154},[5249,16316,16317],{"class":6158},"Unit Tests\n",[5249,16319,16320,16322,16324],{"class":5251,"line":5834},[5249,16321,6242],{"class":6150},[5249,16323,6155],{"class":6154},[5249,16325,6792],{"class":6158},[5249,16327,16328,16330,16332],{"class":5251,"line":5839},[5249,16329,6968],{"class":6150},[5249,16331,6155],{"class":6154},[5249,16333,7250],{"class":6158},[5249,16335,16336,16338,16340],{"class":5251,"line":7092},[5249,16337,15410],{"class":6150},[5249,16339,6155],{"class":6154},[5249,16341,16342],{"class":6324},"10\n",[5249,16344,16345],{"class":5251,"line":7097},[5249,16346,5306],{"emptyLinePlaceholder":5305},[5249,16348,16349,16351],{"class":5251,"line":7108},[5249,16350,6985],{"class":6150},[5249,16352,6185],{"class":6154},[5249,16354,16355,16357],{"class":5251,"line":7117},[5249,16356,6992],{"class":6150},[5249,16358,6185],{"class":6154},[5249,16360,16361,16363,16365,16367,16369,16371],{"class":5251,"line":7122},[5249,16362,6999],{"class":6150},[5249,16364,6193],{"class":6154},[5249,16366,7004],{"class":6324},[5249,16368,6488],{"class":6154},[5249,16370,7009],{"class":6324},[5249,16372,6199],{"class":6154},[5249,16374,16375],{"class":5251,"line":7134},[5249,16376,5306],{"emptyLinePlaceholder":5305},[5249,16378,16379,16381],{"class":5251,"line":7144},[5249,16380,6255],{"class":6150},[5249,16382,6185],{"class":6154},[5249,16384,16385,16387,16389,16391],{"class":5251,"line":7149},[5249,16386,6265],{"class":6154},[5249,16388,7944],{"class":6150},[5249,16390,6155],{"class":6154},[5249,16392,6282],{"class":6158},[5249,16394,16395],{"class":5251,"line":7161},[5249,16396,5306],{"emptyLinePlaceholder":5305},[5249,16398,16399,16401,16403,16405],{"class":5251,"line":7171},[5249,16400,6265],{"class":6154},[5249,16402,6151],{"class":6150},[5249,16404,6155],{"class":6154},[5249,16406,7058],{"class":6158},[5249,16408,16409,16411,16413],{"class":5251,"line":7178},[5249,16410,6277],{"class":6150},[5249,16412,6155],{"class":6154},[5249,16414,6307],{"class":6158},[5249,16416,16417,16419],{"class":5251,"line":7189},[5249,16418,6312],{"class":6150},[5249,16420,6185],{"class":6154},[5249,16422,16423,16425,16427],{"class":5251,"line":7200},[5249,16424,6319],{"class":6150},[5249,16426,6155],{"class":6154},[5249,16428,7081],{"class":6158},[5249,16430,16431,16433,16435],{"class":5251,"line":7211},[5249,16432,6861],{"class":6150},[5249,16434,6155],{"class":6154},[5249,16436,6866],{"class":6158},[5249,16438,16439],{"class":5251,"line":7216},[5249,16440,5306],{"emptyLinePlaceholder":5305},[5249,16442,16443,16445,16447,16449],{"class":5251,"line":7224},[5249,16444,6265],{"class":6154},[5249,16446,6151],{"class":6150},[5249,16448,6155],{"class":6154},[5249,16450,6340],{"class":6158},[5249,16452,16453,16455,16457],{"class":5251,"line":7234},[5249,16454,6345],{"class":6150},[5249,16456,6155],{"class":6154},[5249,16458,6350],{"class":6158},[5249,16460,16461],{"class":5251,"line":7243},[5249,16462,5306],{"emptyLinePlaceholder":5305},[5249,16464,16465,16467,16469,16471],{"class":5251,"line":7253},[5249,16466,6265],{"class":6154},[5249,16468,6151],{"class":6150},[5249,16470,6155],{"class":6154},[5249,16472,16473],{"class":6158},"Run unit tests with coverage\n",[5249,16475,16476,16478,16480],{"class":5251,"line":7258},[5249,16477,6345],{"class":6150},[5249,16479,6155],{"class":6154},[5249,16481,7141],{"class":6158},[5249,16483,16484],{"class":5251,"line":7266},[5249,16485,5306],{"emptyLinePlaceholder":5305},[5249,16487,16488,16490,16492,16494],{"class":5251,"line":7274},[5249,16489,6265],{"class":6154},[5249,16491,6151],{"class":6150},[5249,16493,6155],{"class":6154},[5249,16495,7158],{"class":6158},[5249,16497,16498,16500,16502],{"class":5251,"line":7285},[5249,16499,7685],{"class":6150},[5249,16501,6155],{"class":6154},[5249,16503,16504],{"class":6158},"matrix.node-version == 18\n",[5249,16506,16507,16509,16511],{"class":5251,"line":7293},[5249,16508,6277],{"class":6150},[5249,16510,6155],{"class":6154},[5249,16512,7168],{"class":6158},[5249,16514,16515,16517],{"class":5251,"line":7304},[5249,16516,6312],{"class":6150},[5249,16518,6185],{"class":6154},[5249,16520,16521,16523,16525],{"class":5251,"line":7314},[5249,16522,7181],{"class":6150},[5249,16524,6155],{"class":6154},[5249,16526,7186],{"class":6158},[5249,16528,16529,16531,16533],{"class":5251,"line":7325},[5249,16530,7192],{"class":6150},[5249,16532,6155],{"class":6154},[5249,16534,7197],{"class":6158},[5249,16536,16537],{"class":5251,"line":7340},[5249,16538,5306],{"emptyLinePlaceholder":5305},[5249,16540,16541,16543,16545,16547],{"class":5251,"line":7346},[5249,16542,6265],{"class":6154},[5249,16544,6151],{"class":6150},[5249,16546,6155],{"class":6154},[5249,16548,16549],{"class":6158},"Archive test results\n",[5249,16551,16552,16554,16556],{"class":5251,"line":7352},[5249,16553,7685],{"class":6150},[5249,16555,6155],{"class":6154},[5249,16557,7690],{"class":6158},[5249,16559,16560,16562,16564],{"class":5251,"line":7358},[5249,16561,6277],{"class":6150},[5249,16563,6155],{"class":6154},[5249,16565,7700],{"class":6158},[5249,16567,16568,16570],{"class":5251,"line":7364},[5249,16569,6312],{"class":6150},[5249,16571,6185],{"class":6154},[5249,16573,16574,16576,16578],{"class":5251,"line":7372},[5249,16575,7203],{"class":6150},[5249,16577,6155],{"class":6154},[5249,16579,16580],{"class":6158},"unit-test-results-node-${{ matrix.node-version }}\n",[5249,16582,16583,16585,16587],{"class":5251,"line":7381},[5249,16584,7723],{"class":6150},[5249,16586,6155],{"class":6154},[5249,16588,8478],{"class":7333},[5249,16590,16591],{"class":5251,"line":7386},[5249,16592,16593],{"class":6158},"            coverage\u002F\n",[5249,16595,16596],{"class":5251,"line":7394},[5249,16597,16598],{"class":6158},"            test-results\u002F\n",[5249,16600,16601],{"class":5251,"line":7404},[5249,16602,5306],{"emptyLinePlaceholder":5305},[5249,16604,16605],{"class":5251,"line":7415},[5249,16606,16079],{"class":5255},[5249,16608,16609],{"class":5251,"line":7421},[5249,16610,16611],{"class":5255},"  # Stage 3: E2E Tests\n",[5249,16613,16614],{"class":5251,"line":7426},[5249,16615,16079],{"class":5255},[5249,16617,16618,16620],{"class":5251,"line":7431},[5249,16619,14855],{"class":6150},[5249,16621,6185],{"class":6154},[5249,16623,16624,16626,16628],{"class":5251,"line":7436},[5249,16625,6778],{"class":6150},[5249,16627,6155],{"class":6154},[5249,16629,7231],{"class":6158},[5249,16631,16632,16634,16636],{"class":5251,"line":7443},[5249,16633,6242],{"class":6150},[5249,16635,6155],{"class":6154},[5249,16637,6792],{"class":6158},[5249,16639,16640,16642,16644],{"class":5251,"line":7451},[5249,16641,6968],{"class":6150},[5249,16643,6155],{"class":6154},[5249,16645,7250],{"class":6158},[5249,16647,16648,16650,16652],{"class":5251,"line":7456},[5249,16649,15410],{"class":6150},[5249,16651,6155],{"class":6154},[5249,16653,16654],{"class":6324},"15\n",[5249,16656,16657],{"class":5251,"line":7463},[5249,16658,5306],{"emptyLinePlaceholder":5305},[5249,16660,16661,16663],{"class":5251,"line":7474},[5249,16662,7261],{"class":6150},[5249,16664,6185],{"class":6154},[5249,16666,16667,16669],{"class":5251,"line":7483},[5249,16668,7269],{"class":6150},[5249,16670,6185],{"class":6154},[5249,16672,16673,16675,16677],{"class":5251,"line":7488},[5249,16674,7277],{"class":6150},[5249,16676,6155],{"class":6154},[5249,16678,16679],{"class":6158},"postgres:15-alpine\n",[5249,16681,16682,16684],{"class":5251,"line":7499},[5249,16683,7288],{"class":6150},[5249,16685,6185],{"class":6154},[5249,16687,16688,16690,16692],{"class":5251,"line":7508},[5249,16689,7296],{"class":6150},[5249,16691,6155],{"class":6154},[5249,16693,7301],{"class":6158},[5249,16695,16696,16698,16700],{"class":5251,"line":7515},[5249,16697,7307],{"class":6150},[5249,16699,6155],{"class":6154},[5249,16701,7301],{"class":6158},[5249,16703,16704,16706,16708],{"class":5251,"line":7524},[5249,16705,7317],{"class":6150},[5249,16707,6155],{"class":6154},[5249,16709,7322],{"class":6158},[5249,16711,16712,16714,16716,16718],{"class":5251,"line":7533},[5249,16713,7328],{"class":6150},[5249,16715,6155],{"class":6154},[5249,16717,7334],{"class":7333},[5249,16719,7337],{"class":5281},[5249,16721,16722],{"class":5251,"line":7538},[5249,16723,7343],{"class":6158},[5249,16725,16726],{"class":5251,"line":7549},[5249,16727,7349],{"class":6158},[5249,16729,16730],{"class":5251,"line":7558},[5249,16731,7355],{"class":6158},[5249,16733,16734],{"class":5251,"line":7563},[5249,16735,7361],{"class":6158},[5249,16737,16738,16740],{"class":5251,"line":7575},[5249,16739,7367],{"class":6150},[5249,16741,6185],{"class":6154},[5249,16743,16744,16746],{"class":5251,"line":7585},[5249,16745,7375],{"class":6154},[5249,16747,7378],{"class":6158},[5249,16749,16750],{"class":5251,"line":7592},[5249,16751,5306],{"emptyLinePlaceholder":5305},[5249,16753,16754,16756],{"class":5251,"line":7603},[5249,16755,7389],{"class":6150},[5249,16757,6185],{"class":6154},[5249,16759,16760,16762,16764],{"class":5251,"line":7614},[5249,16761,7277],{"class":6150},[5249,16763,6155],{"class":6154},[5249,16765,16766],{"class":6158},"redis:7-alpine\n",[5249,16768,16769,16771,16773,16775],{"class":5251,"line":7624},[5249,16770,7328],{"class":6150},[5249,16772,6155],{"class":6154},[5249,16774,7334],{"class":7333},[5249,16776,7337],{"class":5281},[5249,16778,16779],{"class":5251,"line":7634},[5249,16780,7418],{"class":6158},[5249,16782,16783],{"class":5251,"line":7644},[5249,16784,7349],{"class":6158},[5249,16786,16787],{"class":5251,"line":7654},[5249,16788,7355],{"class":6158},[5249,16790,16791],{"class":5251,"line":7665},[5249,16792,7361],{"class":6158},[5249,16794,16795,16797],{"class":5251,"line":7670},[5249,16796,7367],{"class":6150},[5249,16798,6185],{"class":6154},[5249,16800,16801,16803],{"class":5251,"line":7682},[5249,16802,7375],{"class":6154},[5249,16804,7448],{"class":6158},[5249,16806,16807],{"class":5251,"line":7693},[5249,16808,5306],{"emptyLinePlaceholder":5305},[5249,16810,16811,16813],{"class":5251,"line":7703},[5249,16812,6255],{"class":6150},[5249,16814,6185],{"class":6154},[5249,16816,16817,16819,16821,16823],{"class":5251,"line":7710},[5249,16818,6265],{"class":6154},[5249,16820,7944],{"class":6150},[5249,16822,6155],{"class":6154},[5249,16824,6282],{"class":6158},[5249,16826,16827],{"class":5251,"line":7720},[5249,16828,5306],{"emptyLinePlaceholder":5305},[5249,16830,16831,16833,16835,16837],{"class":5251,"line":12913},[5249,16832,6265],{"class":6154},[5249,16834,6151],{"class":6150},[5249,16836,6155],{"class":6154},[5249,16838,6298],{"class":6158},[5249,16840,16841,16843,16845],{"class":5251,"line":12918},[5249,16842,6277],{"class":6150},[5249,16844,6155],{"class":6154},[5249,16846,6307],{"class":6158},[5249,16848,16849,16851],{"class":5251,"line":12923},[5249,16850,6312],{"class":6150},[5249,16852,6185],{"class":6154},[5249,16854,16855,16857,16859],{"class":5251,"line":12928},[5249,16856,6319],{"class":6150},[5249,16858,6155],{"class":6154},[5249,16860,16185],{"class":6158},[5249,16862,16863,16865,16867],{"class":5251,"line":12933},[5249,16864,6861],{"class":6150},[5249,16866,6155],{"class":6154},[5249,16868,6866],{"class":6158},[5249,16870,16871],{"class":5251,"line":12938},[5249,16872,5306],{"emptyLinePlaceholder":5305},[5249,16874,16875,16877,16879,16881],{"class":5251,"line":12943},[5249,16876,6265],{"class":6154},[5249,16878,6151],{"class":6150},[5249,16880,6155],{"class":6154},[5249,16882,6340],{"class":6158},[5249,16884,16885,16887,16889],{"class":5251,"line":12948},[5249,16886,6345],{"class":6150},[5249,16888,6155],{"class":6154},[5249,16890,6350],{"class":6158},[5249,16892,16893],{"class":5251,"line":12954},[5249,16894,5306],{"emptyLinePlaceholder":5305},[5249,16896,16897,16899,16901,16903],{"class":5251,"line":12959},[5249,16898,6265],{"class":6154},[5249,16900,6151],{"class":6150},[5249,16902,6155],{"class":6154},[5249,16904,7572],{"class":6158},[5249,16906,16907,16909],{"class":5251,"line":12965},[5249,16908,7288],{"class":6150},[5249,16910,6185],{"class":6154},[5249,16912,16913,16916,16918],{"class":5251,"line":12971},[5249,16914,16915],{"class":6150},"          NODE_ENV",[5249,16917,6155],{"class":6154},[5249,16919,16920],{"class":6158},"test\n",[5249,16922,16923,16925,16927],{"class":5251,"line":12976},[5249,16924,7595],{"class":6150},[5249,16926,6155],{"class":6154},[5249,16928,7600],{"class":6158},[5249,16930,16931,16933,16935],{"class":5251,"line":12981},[5249,16932,7606],{"class":6150},[5249,16934,6155],{"class":6154},[5249,16936,7611],{"class":6324},[5249,16938,16939,16941,16943],{"class":5251,"line":12986},[5249,16940,7617],{"class":6150},[5249,16942,6155],{"class":6154},[5249,16944,7301],{"class":6158},[5249,16946,16947,16949,16951],{"class":5251,"line":12991},[5249,16948,7627],{"class":6150},[5249,16950,6155],{"class":6154},[5249,16952,7301],{"class":6158},[5249,16954,16956,16958,16960],{"class":5251,"line":16955},142,[5249,16957,7637],{"class":6150},[5249,16959,6155],{"class":6154},[5249,16961,7322],{"class":6158},[5249,16963,16965,16967,16969],{"class":5251,"line":16964},143,[5249,16966,7647],{"class":6150},[5249,16968,6155],{"class":6154},[5249,16970,7600],{"class":6158},[5249,16972,16974,16976,16978],{"class":5251,"line":16973},144,[5249,16975,7657],{"class":6150},[5249,16977,6155],{"class":6154},[5249,16979,7662],{"class":6324},[5249,16981,16983,16986,16988],{"class":5251,"line":16982},145,[5249,16984,16985],{"class":6150},"          JWT_SECRET",[5249,16987,6155],{"class":6154},[5249,16989,16990],{"class":6158},"test-jwt-secret-for-e2e-tests-only\n",[5249,16992,16994,16996,16998],{"class":5251,"line":16993},146,[5249,16995,6345],{"class":6150},[5249,16997,6155],{"class":6154},[5249,16999,7582],{"class":6158},[5249,17001,17003],{"class":5251,"line":17002},147,[5249,17004,5306],{"emptyLinePlaceholder":5305},[5249,17006,17008,17010,17012,17014],{"class":5251,"line":17007},148,[5249,17009,6265],{"class":6154},[5249,17011,6151],{"class":6150},[5249,17013,6155],{"class":6154},[5249,17015,7679],{"class":6158},[5249,17017,17019,17021,17023],{"class":5251,"line":17018},149,[5249,17020,7685],{"class":6150},[5249,17022,6155],{"class":6154},[5249,17024,7690],{"class":6158},[5249,17026,17028,17030,17032],{"class":5251,"line":17027},150,[5249,17029,6277],{"class":6150},[5249,17031,6155],{"class":6154},[5249,17033,7700],{"class":6158},[5249,17035,17037,17039],{"class":5251,"line":17036},151,[5249,17038,6312],{"class":6150},[5249,17040,6185],{"class":6154},[5249,17042,17044,17046,17048],{"class":5251,"line":17043},152,[5249,17045,7203],{"class":6150},[5249,17047,6155],{"class":6154},[5249,17049,7717],{"class":6158},[5249,17051,17053,17055,17057],{"class":5251,"line":17052},153,[5249,17054,7723],{"class":6150},[5249,17056,6155],{"class":6154},[5249,17058,7728],{"class":6158},[5249,17060,17062],{"class":5251,"line":17061},154,[5249,17063,5306],{"emptyLinePlaceholder":5305},[5249,17065,17067],{"class":5251,"line":17066},155,[5249,17068,16079],{"class":5255},[5249,17070,17072],{"class":5251,"line":17071},156,[5249,17073,17074],{"class":5255},"  # Stage 4: Security Scan\n",[5249,17076,17078],{"class":5251,"line":17077},157,[5249,17079,16079],{"class":5255},[5249,17081,17083,17086],{"class":5251,"line":17082},158,[5249,17084,17085],{"class":6150},"  security",[5249,17087,6185],{"class":6154},[5249,17089,17091,17093,17095],{"class":5251,"line":17090},159,[5249,17092,6778],{"class":6150},[5249,17094,6155],{"class":6154},[5249,17096,17097],{"class":6158},"Security Scan\n",[5249,17099,17101,17103,17105],{"class":5251,"line":17100},160,[5249,17102,6242],{"class":6150},[5249,17104,6155],{"class":6154},[5249,17106,6792],{"class":6158},[5249,17108,17110,17112,17114],{"class":5251,"line":17109},161,[5249,17111,6968],{"class":6150},[5249,17113,6155],{"class":6154},[5249,17115,7250],{"class":6158},[5249,17117,17119,17121,17123],{"class":5251,"line":17118},162,[5249,17120,15410],{"class":6150},[5249,17122,6155],{"class":6154},[5249,17124,16120],{"class":6324},[5249,17126,17128],{"class":5251,"line":17127},163,[5249,17129,5306],{"emptyLinePlaceholder":5305},[5249,17131,17133,17135],{"class":5251,"line":17132},164,[5249,17134,6255],{"class":6150},[5249,17136,6185],{"class":6154},[5249,17138,17140,17142,17144,17146],{"class":5251,"line":17139},165,[5249,17141,6265],{"class":6154},[5249,17143,7944],{"class":6150},[5249,17145,6155],{"class":6154},[5249,17147,6282],{"class":6158},[5249,17149,17151],{"class":5251,"line":17150},166,[5249,17152,5306],{"emptyLinePlaceholder":5305},[5249,17154,17156,17158,17160,17162],{"class":5251,"line":17155},167,[5249,17157,6265],{"class":6154},[5249,17159,6151],{"class":6150},[5249,17161,6155],{"class":6154},[5249,17163,17164],{"class":6158},"Run npm audit\n",[5249,17166,17168,17170,17172],{"class":5251,"line":17167},168,[5249,17169,6345],{"class":6150},[5249,17171,6155],{"class":6154},[5249,17173,17174],{"class":6158},"npm audit --audit-level=high\n",[5249,17176,17178,17181,17183],{"class":5251,"line":17177},169,[5249,17179,17180],{"class":6150},"        continue-on-error",[5249,17182,6155],{"class":6154},[5249,17184,6600],{"class":5281},[5249,17186,17188],{"class":5251,"line":17187},170,[5249,17189,5306],{"emptyLinePlaceholder":5305},[5249,17191,17193,17195,17197,17199],{"class":5251,"line":17192},171,[5249,17194,6265],{"class":6154},[5249,17196,6151],{"class":6150},[5249,17198,6155],{"class":6154},[5249,17200,17201],{"class":6158},"Run Snyk security scan\n",[5249,17203,17205,17207,17209],{"class":5251,"line":17204},172,[5249,17206,6277],{"class":6150},[5249,17208,6155],{"class":6154},[5249,17210,17211],{"class":6158},"snyk\u002Factions\u002Fnode@master\n",[5249,17213,17215,17217,17219],{"class":5251,"line":17214},173,[5249,17216,17180],{"class":6150},[5249,17218,6155],{"class":6154},[5249,17220,6600],{"class":5281},[5249,17222,17224,17226],{"class":5251,"line":17223},174,[5249,17225,7288],{"class":6150},[5249,17227,6185],{"class":6154},[5249,17229,17231,17234,17236],{"class":5251,"line":17230},175,[5249,17232,17233],{"class":6150},"          SNYK_TOKEN",[5249,17235,6155],{"class":6154},[5249,17237,17238],{"class":6158},"${{ secrets.SNYK_TOKEN }}\n",[5249,17240,17242,17244],{"class":5251,"line":17241},176,[5249,17243,6312],{"class":6150},[5249,17245,6185],{"class":6154},[5249,17247,17249,17252,17254],{"class":5251,"line":17248},177,[5249,17250,17251],{"class":6150},"          args",[5249,17253,6155],{"class":6154},[5249,17255,17256],{"class":6158},"--severity-threshold=high\n",[5249,17258,17260],{"class":5251,"line":17259},178,[5249,17261,5306],{"emptyLinePlaceholder":5305},[5249,17263,17265],{"class":5251,"line":17264},179,[5249,17266,16079],{"class":5255},[5249,17268,17270],{"class":5251,"line":17269},180,[5249,17271,17272],{"class":5255},"  # Stage 5: Build Docker Image\n",[5249,17274,17276],{"class":5251,"line":17275},181,[5249,17277,16079],{"class":5255},[5249,17279,17281,17283],{"class":5251,"line":17280},182,[5249,17282,8279],{"class":6150},[5249,17284,6185],{"class":6154},[5249,17286,17288,17290,17292],{"class":5251,"line":17287},183,[5249,17289,6778],{"class":6150},[5249,17291,6155],{"class":6154},[5249,17293,17294],{"class":6158},"Build Docker Image\n",[5249,17296,17298,17300,17302],{"class":5251,"line":17297},184,[5249,17299,6242],{"class":6150},[5249,17301,6155],{"class":6154},[5249,17303,6792],{"class":6158},[5249,17305,17307,17309,17311,17313,17315,17317],{"class":5251,"line":17306},185,[5249,17308,6968],{"class":6150},[5249,17310,6193],{"class":6154},[5249,17312,14913],{"class":6158},[5249,17314,6488],{"class":6154},[5249,17316,14918],{"class":6158},[5249,17318,6199],{"class":6154},[5249,17320,17322,17324,17326],{"class":5251,"line":17321},186,[5249,17323,12353],{"class":6150},[5249,17325,6155],{"class":6154},[5249,17327,17328],{"class":6158},"github.event_name == 'push' && github.ref == 'refs\u002Fheads\u002Fmain'\n",[5249,17330,17332,17334,17336],{"class":5251,"line":17331},187,[5249,17333,15410],{"class":6150},[5249,17335,6155],{"class":6154},[5249,17337,16654],{"class":6324},[5249,17339,17341],{"class":5251,"line":17340},188,[5249,17342,5306],{"emptyLinePlaceholder":5305},[5249,17344,17346,17348],{"class":5251,"line":17345},189,[5249,17347,6255],{"class":6150},[5249,17349,6185],{"class":6154},[5249,17351,17353,17355,17357,17359],{"class":5251,"line":17352},190,[5249,17354,6265],{"class":6154},[5249,17356,7944],{"class":6150},[5249,17358,6155],{"class":6154},[5249,17360,6282],{"class":6158},[5249,17362,17364],{"class":5251,"line":17363},191,[5249,17365,5306],{"emptyLinePlaceholder":5305},[5249,17367,17369,17371,17373,17375],{"class":5251,"line":17368},192,[5249,17370,6265],{"class":6154},[5249,17372,6151],{"class":6150},[5249,17374,6155],{"class":6154},[5249,17376,8359],{"class":6158},[5249,17378,17380,17382,17384],{"class":5251,"line":17379},193,[5249,17381,6277],{"class":6150},[5249,17383,6155],{"class":6154},[5249,17385,8368],{"class":6158},[5249,17387,17389],{"class":5251,"line":17388},194,[5249,17390,5306],{"emptyLinePlaceholder":5305},[5249,17392,17394,17396,17398,17400],{"class":5251,"line":17393},195,[5249,17395,6265],{"class":6154},[5249,17397,6151],{"class":6150},[5249,17399,6155],{"class":6154},[5249,17401,8383],{"class":6158},[5249,17403,17405,17407,17409],{"class":5251,"line":17404},196,[5249,17406,6277],{"class":6150},[5249,17408,6155],{"class":6154},[5249,17410,8392],{"class":6158},[5249,17412,17414,17416],{"class":5251,"line":17413},197,[5249,17415,6312],{"class":6150},[5249,17417,6185],{"class":6154},[5249,17419,17421,17423,17425],{"class":5251,"line":17420},198,[5249,17422,8403],{"class":6150},[5249,17424,6155],{"class":6154},[5249,17426,8408],{"class":6158},[5249,17428,17430,17432,17434],{"class":5251,"line":17429},199,[5249,17431,8413],{"class":6150},[5249,17433,6155],{"class":6154},[5249,17435,8418],{"class":6158},[5249,17437,17439],{"class":5251,"line":17438},200,[5249,17440,5306],{"emptyLinePlaceholder":5305},[5249,17442,17444,17446,17448,17450],{"class":5251,"line":17443},201,[5249,17445,6265],{"class":6154},[5249,17447,6151],{"class":6150},[5249,17449,6155],{"class":6154},[5249,17451,8433],{"class":6158},[5249,17453,17455,17457,17459],{"class":5251,"line":17454},202,[5249,17456,8438],{"class":6150},[5249,17458,6155],{"class":6154},[5249,17460,8443],{"class":6158},[5249,17462,17464,17466,17468],{"class":5251,"line":17463},203,[5249,17465,6277],{"class":6150},[5249,17467,6155],{"class":6154},[5249,17469,8452],{"class":6158},[5249,17471,17473,17475],{"class":5251,"line":17472},204,[5249,17474,6312],{"class":6150},[5249,17476,6185],{"class":6154},[5249,17478,17480,17482,17484],{"class":5251,"line":17479},205,[5249,17481,8463],{"class":6150},[5249,17483,6155],{"class":6154},[5249,17485,17486],{"class":6158},"${{ env.DOCKER_IMAGE }}\n",[5249,17488,17490,17492,17494],{"class":5251,"line":17489},206,[5249,17491,8473],{"class":6150},[5249,17493,6155],{"class":6154},[5249,17495,8478],{"class":7333},[5249,17497,17499],{"class":5251,"line":17498},207,[5249,17500,17501],{"class":6158},"            type=sha,prefix=main-\n",[5249,17503,17505],{"class":5251,"line":17504},208,[5249,17506,17507],{"class":6158},"            type=raw,value=latest\n",[5249,17509,17511],{"class":5251,"line":17510},209,[5249,17512,5306],{"emptyLinePlaceholder":5305},[5249,17514,17516,17518,17520,17522],{"class":5251,"line":17515},210,[5249,17517,6265],{"class":6154},[5249,17519,6151],{"class":6150},[5249,17521,6155],{"class":6154},[5249,17523,8508],{"class":6158},[5249,17525,17527,17529,17531],{"class":5251,"line":17526},211,[5249,17528,6277],{"class":6150},[5249,17530,6155],{"class":6154},[5249,17532,8517],{"class":6158},[5249,17534,17536,17538],{"class":5251,"line":17535},212,[5249,17537,6312],{"class":6150},[5249,17539,6185],{"class":6154},[5249,17541,17543,17545,17547],{"class":5251,"line":17542},213,[5249,17544,8528],{"class":6150},[5249,17546,6155],{"class":6154},[5249,17548,8533],{"class":6324},[5249,17550,17552,17554,17556],{"class":5251,"line":17551},214,[5249,17553,8538],{"class":6150},[5249,17555,6155],{"class":6154},[5249,17557,8543],{"class":6158},[5249,17559,17561,17563,17565],{"class":5251,"line":17560},215,[5249,17562,8548],{"class":6150},[5249,17564,6155],{"class":6154},[5249,17566,6600],{"class":5281},[5249,17568,17570,17572,17574],{"class":5251,"line":17569},216,[5249,17571,8473],{"class":6150},[5249,17573,6155],{"class":6154},[5249,17575,8561],{"class":6158},[5249,17577,17579,17581,17583],{"class":5251,"line":17578},217,[5249,17580,8566],{"class":6150},[5249,17582,6155],{"class":6154},[5249,17584,8571],{"class":6158},[5249,17586,17588,17590,17592],{"class":5251,"line":17587},218,[5249,17589,8576],{"class":6150},[5249,17591,6155],{"class":6154},[5249,17593,17594],{"class":6158},"type=registry,ref=${{ env.DOCKER_IMAGE }}:buildcache\n",[5249,17596,17598,17600,17602],{"class":5251,"line":17597},219,[5249,17599,8586],{"class":6150},[5249,17601,6155],{"class":6154},[5249,17603,17604],{"class":6158},"type=registry,ref=${{ env.DOCKER_IMAGE }}:buildcache,mode=max\n",[5249,17606,17608],{"class":5251,"line":17607},220,[5249,17609,5306],{"emptyLinePlaceholder":5305},[5249,17611,17613,17615,17617,17619],{"class":5251,"line":17612},221,[5249,17614,6265],{"class":6154},[5249,17616,6151],{"class":6150},[5249,17618,6155],{"class":6154},[5249,17620,8606],{"class":6158},[5249,17622,17624,17626,17628],{"class":5251,"line":17623},222,[5249,17625,6345],{"class":6150},[5249,17627,6155],{"class":6154},[5249,17629,17630],{"class":6158},"echo \"Image pushed with digest ${{ steps.build.outputs.digest }}\"\n",[5249,17632,17634],{"class":5251,"line":17633},223,[5249,17635,5306],{"emptyLinePlaceholder":5305},[5249,17637,17639],{"class":5251,"line":17638},224,[5249,17640,16079],{"class":5255},[5249,17642,17644],{"class":5251,"line":17643},225,[5249,17645,17646],{"class":5255},"  # Stage 6: Deploy to Production\n",[5249,17648,17650],{"class":5251,"line":17649},226,[5249,17651,16079],{"class":5255},[5249,17653,17655,17657],{"class":5251,"line":17654},227,[5249,17656,9959],{"class":6150},[5249,17658,6185],{"class":6154},[5249,17660,17662,17664,17666],{"class":5251,"line":17661},228,[5249,17663,6778],{"class":6150},[5249,17665,6155],{"class":6154},[5249,17667,9106],{"class":6158},[5249,17669,17671,17673,17675],{"class":5251,"line":17670},229,[5249,17672,6242],{"class":6150},[5249,17674,6155],{"class":6154},[5249,17676,6792],{"class":6158},[5249,17678,17680,17682,17684],{"class":5251,"line":17679},230,[5249,17681,6968],{"class":6150},[5249,17683,6155],{"class":6154},[5249,17685,9124],{"class":6158},[5249,17687,17689,17691],{"class":5251,"line":17688},231,[5249,17690,11235],{"class":6150},[5249,17692,6185],{"class":6154},[5249,17694,17696,17698,17700],{"class":5251,"line":17695},232,[5249,17697,11242],{"class":6150},[5249,17699,6155],{"class":6154},[5249,17701,11569],{"class":6158},[5249,17703,17705,17707,17709],{"class":5251,"line":17704},233,[5249,17706,11252],{"class":6150},[5249,17708,6155],{"class":6154},[5249,17710,11578],{"class":6158},[5249,17712,17714,17717],{"class":5251,"line":17713},234,[5249,17715,17716],{"class":6150},"    concurrency",[5249,17718,6185],{"class":6154},[5249,17720,17722,17725,17727],{"class":5251,"line":17721},235,[5249,17723,17724],{"class":6150},"      group",[5249,17726,6155],{"class":6154},[5249,17728,15361],{"class":6158},[5249,17730,17732,17735,17737],{"class":5251,"line":17731},236,[5249,17733,17734],{"class":6150},"      cancel-in-progress",[5249,17736,6155],{"class":6154},[5249,17738,17739],{"class":5281},"false\n",[5249,17741,17743,17745,17747],{"class":5251,"line":17742},237,[5249,17744,15410],{"class":6150},[5249,17746,6155],{"class":6154},[5249,17748,16342],{"class":6324},[5249,17750,17752],{"class":5251,"line":17751},238,[5249,17753,5306],{"emptyLinePlaceholder":5305},[5249,17755,17757,17759],{"class":5251,"line":17756},239,[5249,17758,6255],{"class":6150},[5249,17760,6185],{"class":6154},[5249,17762,17764,17766,17768,17770],{"class":5251,"line":17763},240,[5249,17765,6265],{"class":6154},[5249,17767,6151],{"class":6150},[5249,17769,6155],{"class":6154},[5249,17771,13563],{"class":6158},[5249,17773,17775,17777,17779],{"class":5251,"line":17774},241,[5249,17776,8438],{"class":6150},[5249,17778,6155],{"class":6154},[5249,17780,13572],{"class":6158},[5249,17782,17784,17786,17788],{"class":5251,"line":17783},242,[5249,17785,6345],{"class":6150},[5249,17787,6155],{"class":6154},[5249,17789,8478],{"class":7333},[5249,17791,17793],{"class":5251,"line":17792},243,[5249,17794,17795],{"class":6158},"          CURRENT_TAG=$(ssh -o StrictHostKeyChecking=no \\\n",[5249,17797,17799],{"class":5251,"line":17798},244,[5249,17800,17801],{"class":6158},"            ${{ secrets.PROD_SERVER_USER }}@${{ secrets.PROD_SERVER_HOST }} \\\n",[5249,17803,17805],{"class":5251,"line":17804},245,[5249,17806,17807],{"class":6158},"            \"cd \u002Fapp && docker-compose config | grep 'image:' | awk '{print \\$2}' | cut -d':' -f2\" || echo \"none\")\n",[5249,17809,17811],{"class":5251,"line":17810},246,[5249,17812,17813],{"class":6158},"          echo \"tag=$CURRENT_TAG\" >> $GITHUB_OUTPUT\n",[5249,17815,17817],{"class":5251,"line":17816},247,[5249,17818,5306],{"emptyLinePlaceholder":5305},[5249,17820,17822,17824,17826,17828],{"class":5251,"line":17821},248,[5249,17823,6265],{"class":6154},[5249,17825,6151],{"class":6150},[5249,17827,6155],{"class":6154},[5249,17829,17830],{"class":6158},"Deploy via SSH\n",[5249,17832,17834,17836,17838],{"class":5251,"line":17833},249,[5249,17835,6277],{"class":6150},[5249,17837,6155],{"class":6154},[5249,17839,9168],{"class":6158},[5249,17841,17843,17845],{"class":5251,"line":17842},250,[5249,17844,6312],{"class":6150},[5249,17846,6185],{"class":6154},[5249,17848,17850,17852,17854],{"class":5251,"line":17849},251,[5249,17851,10009],{"class":6150},[5249,17853,6155],{"class":6154},[5249,17855,9185],{"class":6158},[5249,17857,17859,17861,17863],{"class":5251,"line":17858},252,[5249,17860,8403],{"class":6150},[5249,17862,6155],{"class":6154},[5249,17864,9195],{"class":6158},[5249,17866,17868,17870,17872],{"class":5251,"line":17867},253,[5249,17869,10028],{"class":6150},[5249,17871,6155],{"class":6154},[5249,17873,11660],{"class":6158},[5249,17875,17877,17879,17881],{"class":5251,"line":17876},254,[5249,17878,10038],{"class":6150},[5249,17880,6155],{"class":6154},[5249,17882,8478],{"class":7333},[5249,17884,17886],{"class":5251,"line":17885},255,[5249,17887,10047],{"class":6158},[5249,17889,17891],{"class":5251,"line":17890},256,[5249,17892,17893],{"class":6158},"            export IMAGE_TAG=main-${{ github.sha }}\n",[5249,17895,17897],{"class":5251,"line":17896},257,[5249,17898,10052],{"class":6158},[5249,17900,17902],{"class":5251,"line":17901},258,[5249,17903,10057],{"class":6158},[5249,17905,17907],{"class":5251,"line":17906},259,[5249,17908,17909],{"class":6158},"            docker system prune -af\n",[5249,17911,17913],{"class":5251,"line":17912},260,[5249,17914,5306],{"emptyLinePlaceholder":5305},[5249,17916,17918,17920,17922,17924],{"class":5251,"line":17917},261,[5249,17919,6265],{"class":6154},[5249,17921,6151],{"class":6150},[5249,17923,6155],{"class":6154},[5249,17925,13643],{"class":6158},[5249,17927,17929,17931,17933],{"class":5251,"line":17928},262,[5249,17930,8438],{"class":6150},[5249,17932,6155],{"class":6154},[5249,17934,13652],{"class":6158},[5249,17936,17938,17940,17942],{"class":5251,"line":17937},263,[5249,17939,6345],{"class":6150},[5249,17941,6155],{"class":6154},[5249,17943,8478],{"class":7333},[5249,17945,17947],{"class":5251,"line":17946},264,[5249,17948,17949],{"class":6158},"          echo \"Waiting for application to start...\"\n",[5249,17951,17953],{"class":5251,"line":17952},265,[5249,17954,17955],{"class":6158},"          sleep 15\n",[5249,17957,17959],{"class":5251,"line":17958},266,[5249,17960,10178],{"class":6158},[5249,17962,17964],{"class":5251,"line":17963},267,[5249,17965,11713],{"class":6158},[5249,17967,17969],{"class":5251,"line":17968},268,[5249,17970,11718],{"class":6158},[5249,17972,17974],{"class":5251,"line":17973},269,[5249,17975,17976],{"class":6158},"              echo \"✅ Health check passed\"\n",[5249,17978,17980],{"class":5251,"line":17979},270,[5249,17981,10100],{"class":6158},[5249,17983,17985],{"class":5251,"line":17984},271,[5249,17986,10105],{"class":6158},[5249,17988,17990],{"class":5251,"line":17989},272,[5249,17991,17992],{"class":6158},"            echo \"⏳ Health check attempt $i\u002F10 failed, retrying...\"\n",[5249,17994,17996],{"class":5251,"line":17995},273,[5249,17997,10115],{"class":6158},[5249,17999,18001],{"class":5251,"line":18000},274,[5249,18002,10120],{"class":6158},[5249,18004,18006],{"class":5251,"line":18005},275,[5249,18007,10178],{"class":6158},[5249,18009,18011],{"class":5251,"line":18010},276,[5249,18012,18013],{"class":6158},"          echo \"❌ Health check failed after 10 attempts\"\n",[5249,18015,18017],{"class":5251,"line":18016},277,[5249,18018,10130],{"class":6158},[5249,18020,18022],{"class":5251,"line":18021},278,[5249,18023,5306],{"emptyLinePlaceholder":5305},[5249,18025,18027,18029,18031,18033],{"class":5251,"line":18026},279,[5249,18028,6265],{"class":6154},[5249,18030,6151],{"class":6150},[5249,18032,6155],{"class":6154},[5249,18034,13722],{"class":6158},[5249,18036,18038,18040,18042],{"class":5251,"line":18037},280,[5249,18039,7685],{"class":6150},[5249,18041,6155],{"class":6154},[5249,18043,13731],{"class":6158},[5249,18045,18047,18049,18051],{"class":5251,"line":18046},281,[5249,18048,6277],{"class":6150},[5249,18050,6155],{"class":6154},[5249,18052,9168],{"class":6158},[5249,18054,18056,18058],{"class":5251,"line":18055},282,[5249,18057,6312],{"class":6150},[5249,18059,6185],{"class":6154},[5249,18061,18063,18065,18067],{"class":5251,"line":18062},283,[5249,18064,10009],{"class":6150},[5249,18066,6155],{"class":6154},[5249,18068,9185],{"class":6158},[5249,18070,18072,18074,18076],{"class":5251,"line":18071},284,[5249,18073,8403],{"class":6150},[5249,18075,6155],{"class":6154},[5249,18077,9195],{"class":6158},[5249,18079,18081,18083,18085],{"class":5251,"line":18080},285,[5249,18082,10028],{"class":6150},[5249,18084,6155],{"class":6154},[5249,18086,11660],{"class":6158},[5249,18088,18090,18092,18094],{"class":5251,"line":18089},286,[5249,18091,10038],{"class":6150},[5249,18093,6155],{"class":6154},[5249,18095,8478],{"class":7333},[5249,18097,18099],{"class":5251,"line":18098},287,[5249,18100,10047],{"class":6158},[5249,18102,18104],{"class":5251,"line":18103},288,[5249,18105,18106],{"class":6158},"            export IMAGE_TAG=${{ steps.current-version.outputs.tag }}\n",[5249,18108,18110],{"class":5251,"line":18109},289,[5249,18111,10057],{"class":6158},[5249,18113,18115],{"class":5251,"line":18114},290,[5249,18116,18117],{"class":6158},"            \n",[5249,18119,18121],{"class":5251,"line":18120},291,[5249,18122,18123],{"class":6158},"            # Verify rollback\n",[5249,18125,18127],{"class":5251,"line":18126},292,[5249,18128,10115],{"class":6158},[5249,18130,18132],{"class":5251,"line":18131},293,[5249,18133,18134],{"class":6158},"            curl -f http:\u002F\u002Flocalhost:3000\u002Fhealth || exit 1\n",[5249,18136,18138],{"class":5251,"line":18137},294,[5249,18139,5306],{"emptyLinePlaceholder":5305},[5249,18141,18143],{"class":5251,"line":18142},295,[5249,18144,16079],{"class":5255},[5249,18146,18148],{"class":5251,"line":18147},296,[5249,18149,18150],{"class":5255},"  # Stage 7: Notifications\n",[5249,18152,18154],{"class":5251,"line":18153},297,[5249,18155,16079],{"class":5255},[5249,18157,18159,18161],{"class":5251,"line":18158},298,[5249,18160,12320],{"class":6150},[5249,18162,6185],{"class":6154},[5249,18164,18166,18168,18170],{"class":5251,"line":18165},299,[5249,18167,6778],{"class":6150},[5249,18169,6155],{"class":6154},[5249,18171,18172],{"class":6158},"Send Notifications\n",[5249,18174,18176,18178,18180],{"class":5251,"line":18175},300,[5249,18177,6242],{"class":6150},[5249,18179,6155],{"class":6154},[5249,18181,6792],{"class":6158},[5249,18183,18185,18187,18189,18191],{"class":5251,"line":18184},301,[5249,18186,6968],{"class":6150},[5249,18188,6193],{"class":6154},[5249,18190,9094],{"class":6158},[5249,18192,6199],{"class":6154},[5249,18194,18196,18198,18200],{"class":5251,"line":18195},302,[5249,18197,12353],{"class":6150},[5249,18199,6155],{"class":6154},[5249,18201,7690],{"class":6158},[5249,18203,18205],{"class":5251,"line":18204},303,[5249,18206,5306],{"emptyLinePlaceholder":5305},[5249,18208,18210,18212],{"class":5251,"line":18209},304,[5249,18211,6255],{"class":6150},[5249,18213,6185],{"class":6154},[5249,18215,18217,18219,18221,18223],{"class":5251,"line":18216},305,[5249,18218,6265],{"class":6154},[5249,18220,6151],{"class":6150},[5249,18222,6155],{"class":6154},[5249,18224,12381],{"class":6158},[5249,18226,18228,18230,18232],{"class":5251,"line":18227},306,[5249,18229,7685],{"class":6150},[5249,18231,6155],{"class":6154},[5249,18233,12390],{"class":6158},[5249,18235,18237,18239,18241],{"class":5251,"line":18236},307,[5249,18238,6277],{"class":6150},[5249,18240,6155],{"class":6154},[5249,18242,9302],{"class":6158},[5249,18244,18246,18248],{"class":5251,"line":18245},308,[5249,18247,6312],{"class":6150},[5249,18249,6185],{"class":6154},[5249,18251,18253,18255,18257],{"class":5251,"line":18252},309,[5249,18254,12409],{"class":6150},[5249,18256,6155],{"class":6154},[5249,18258,9318],{"class":6158},[5249,18260,18262,18264,18266],{"class":5251,"line":18261},310,[5249,18263,12418],{"class":6150},[5249,18265,6155],{"class":6154},[5249,18267,8478],{"class":7333},[5249,18269,18271],{"class":5251,"line":18270},311,[5249,18272,12427],{"class":6158},[5249,18274,18276],{"class":5251,"line":18275},312,[5249,18277,18278],{"class":6158},"              \"text\": \"✅ Production deployment successful\",\n",[5249,18280,18282],{"class":5251,"line":18281},313,[5249,18283,12437],{"class":6158},[5249,18285,18287],{"class":5251,"line":18286},314,[5249,18288,12442],{"class":6158},[5249,18290,18292],{"class":5251,"line":18291},315,[5249,18293,12447],{"class":6158},[5249,18295,18297],{"class":5251,"line":18296},316,[5249,18298,12452],{"class":6158},[5249,18300,18302],{"class":5251,"line":18301},317,[5249,18303,12457],{"class":6158},[5249,18305,18307],{"class":5251,"line":18306},318,[5249,18308,12462],{"class":6158},[5249,18310,18312],{"class":5251,"line":18311},319,[5249,18313,12467],{"class":6158},[5249,18315,18317],{"class":5251,"line":18316},320,[5249,18318,12472],{"class":6158},[5249,18320,18322],{"class":5251,"line":18321},321,[5249,18323,12442],{"class":6158},[5249,18325,18327],{"class":5251,"line":18326},322,[5249,18328,12481],{"class":6158},[5249,18330,18332],{"class":5251,"line":18331},323,[5249,18333,12486],{"class":6158},[5249,18335,18337],{"class":5251,"line":18336},324,[5249,18338,18339],{"class":6158},"                    {\"type\": \"mrkdwn\", \"text\": \"*Commit:*\\n${{ github.sha }}\"},\n",[5249,18341,18343],{"class":5251,"line":18342},325,[5249,18344,18345],{"class":6158},"                    {\"type\": \"mrkdwn\", \"text\": \"*Author:*\\n${{ github.actor }}\"}\n",[5249,18347,18349],{"class":5251,"line":18348},326,[5249,18350,12563],{"class":6158},[5249,18352,18354],{"class":5251,"line":18353},327,[5249,18355,12472],{"class":6158},[5249,18357,18359],{"class":5251,"line":18358},328,[5249,18360,12442],{"class":6158},[5249,18362,18364],{"class":5251,"line":18363},329,[5249,18365,12481],{"class":6158},[5249,18367,18369],{"class":5251,"line":18368},330,[5249,18370,12452],{"class":6158},[5249,18372,18374],{"class":5251,"line":18373},331,[5249,18375,12584],{"class":6158},[5249,18377,18379],{"class":5251,"line":18378},332,[5249,18380,18381],{"class":6158},"                    \"text\": \"*Message:* ${{ github.event.head_commit.message }}\"\n",[5249,18383,18385],{"class":5251,"line":18384},333,[5249,18386,12467],{"class":6158},[5249,18388,18390],{"class":5251,"line":18389},334,[5249,18391,12692],{"class":6158},[5249,18393,18395],{"class":5251,"line":18394},335,[5249,18396,9367],{"class":6158},[5249,18398,18400],{"class":5251,"line":18399},336,[5249,18401,12701],{"class":6158},[5249,18403,18405],{"class":5251,"line":18404},337,[5249,18406,5306],{"emptyLinePlaceholder":5305},[5249,18408,18410,18412,18414,18416],{"class":5251,"line":18409},338,[5249,18411,6265],{"class":6154},[5249,18413,6151],{"class":6150},[5249,18415,6155],{"class":6154},[5249,18417,12716],{"class":6158},[5249,18419,18421,18423,18425],{"class":5251,"line":18420},339,[5249,18422,7685],{"class":6150},[5249,18424,6155],{"class":6154},[5249,18426,12725],{"class":6158},[5249,18428,18430,18432,18434],{"class":5251,"line":18429},340,[5249,18431,6277],{"class":6150},[5249,18433,6155],{"class":6154},[5249,18435,9302],{"class":6158},[5249,18437,18439,18441],{"class":5251,"line":18438},341,[5249,18440,6312],{"class":6150},[5249,18442,6185],{"class":6154},[5249,18444,18446,18448,18450],{"class":5251,"line":18445},342,[5249,18447,12409],{"class":6150},[5249,18449,6155],{"class":6154},[5249,18451,9318],{"class":6158},[5249,18453,18455,18457,18459],{"class":5251,"line":18454},343,[5249,18456,12418],{"class":6150},[5249,18458,6155],{"class":6154},[5249,18460,8478],{"class":7333},[5249,18462,18464],{"class":5251,"line":18463},344,[5249,18465,12427],{"class":6158},[5249,18467,18469],{"class":5251,"line":18468},345,[5249,18470,18471],{"class":6158},"              \"text\": \"❌ Production deployment failed\",\n",[5249,18473,18475],{"class":5251,"line":18474},346,[5249,18476,12437],{"class":6158},[5249,18478,18480],{"class":5251,"line":18479},347,[5249,18481,12442],{"class":6158},[5249,18483,18485],{"class":5251,"line":18484},348,[5249,18486,12447],{"class":6158},[5249,18488,18490],{"class":5251,"line":18489},349,[5249,18491,12452],{"class":6158},[5249,18493,18495],{"class":5251,"line":18494},350,[5249,18496,12457],{"class":6158},[5249,18498,18500],{"class":5251,"line":18499},351,[5249,18501,12789],{"class":6158},[5249,18503,18505],{"class":5251,"line":18504},352,[5249,18506,12467],{"class":6158},[5249,18508,18510],{"class":5251,"line":18509},353,[5249,18511,12472],{"class":6158},[5249,18513,18515],{"class":5251,"line":18514},354,[5249,18516,12442],{"class":6158},[5249,18518,18520],{"class":5251,"line":18519},355,[5249,18521,12481],{"class":6158},[5249,18523,18525],{"class":5251,"line":18524},356,[5249,18526,12452],{"class":6158},[5249,18528,18530],{"class":5251,"line":18529},357,[5249,18531,12584],{"class":6158},[5249,18533,18535],{"class":5251,"line":18534},358,[5249,18536,18537],{"class":6158},"                    \"text\": \"⚠️ *Action Required:* \u003C${{ github.server_url }}\u002F${{ github.repository }}\u002Factions\u002Fruns\u002F${{ github.run_id }}|View Logs>\"\n",[5249,18539,18541],{"class":5251,"line":18540},359,[5249,18542,12467],{"class":6158},[5249,18544,18546],{"class":5251,"line":18545},360,[5249,18547,12692],{"class":6158},[5249,18549,18551],{"class":5251,"line":18550},361,[5249,18552,9367],{"class":6158},[5249,18554,18556],{"class":5251,"line":18555},362,[5249,18557,12701],{"class":6158},[5249,18559,18561],{"class":5251,"line":18560},363,[5249,18562,5306],{"emptyLinePlaceholder":5305},[5249,18564,18566,18568,18570,18572],{"class":5251,"line":18565},364,[5249,18567,6265],{"class":6154},[5249,18569,6151],{"class":6150},[5249,18571,6155],{"class":6154},[5249,18573,18574],{"class":6158},"Create issue on failure\n",[5249,18576,18578,18580,18582],{"class":5251,"line":18577},365,[5249,18579,7685],{"class":6150},[5249,18581,6155],{"class":6154},[5249,18583,12725],{"class":6158},[5249,18585,18587,18589,18591],{"class":5251,"line":18586},366,[5249,18588,6277],{"class":6150},[5249,18590,6155],{"class":6154},[5249,18592,13342],{"class":6158},[5249,18594,18596,18598],{"class":5251,"line":18595},367,[5249,18597,6312],{"class":6150},[5249,18599,6185],{"class":6154},[5249,18601,18603,18605,18607],{"class":5251,"line":18602},368,[5249,18604,10038],{"class":6150},[5249,18606,6155],{"class":6154},[5249,18608,8478],{"class":7333},[5249,18610,18612],{"class":5251,"line":18611},369,[5249,18613,18614],{"class":6158},"            await github.rest.issues.create({\n",[5249,18616,18618],{"class":5251,"line":18617},370,[5249,18619,18620],{"class":6158},"              owner: context.repo.owner,\n",[5249,18622,18624],{"class":5251,"line":18623},371,[5249,18625,18626],{"class":6158},"              repo: context.repo.repo,\n",[5249,18628,18630],{"class":5251,"line":18629},372,[5249,18631,18632],{"class":6158},"              title: `🔥 Production Deployment Failed - ${context.sha.substring(0, 7)}`,\n",[5249,18634,18636],{"class":5251,"line":18635},373,[5249,18637,18638],{"class":6158},"              body: `Deployment failed. [View logs](${context.serverUrl}\u002F${context.repo.owner}\u002F${context.repo.repo}\u002Factions\u002Fruns\u002F${context.runId})`,\n",[5249,18640,18642],{"class":5251,"line":18641},374,[5249,18643,18644],{"class":6158},"              labels: ['bug', 'deployment', 'production']\n",[5249,18646,18648],{"class":5251,"line":18647},375,[5249,18649,18650],{"class":6158},"            });\n",[8055,18652,18654,18663,18670,18673,18682,18689,18696,18699,18708,18715,18718,18727,18734,18737,18746,18753,18761,18764,18773,18782,18789,18796,18803,18810,18813,18822,18829,18832],{":cursor":8057,"title":18653},"Production Pipeline — Execution",[8060,18655,18657,5977,18660],{"className":18656},[5251],[5249,18658,8080],{"className":18659},[8079],[5118,18661,18662],{},"Stage 1: Code Quality Checks",[8060,18664,8075,18666,18669],{"className":18665},[5251],[5249,18667,8067],{"className":18668},[8066]," Lint and Type Check (1m 23s)",[8060,18671],{"className":18672},[5251],[8060,18674,18676,5977,18679],{"className":18675},[5251],[5249,18677,8080],{"className":18678},[8079],[5118,18680,18681],{},"Stage 2: Unit Tests",[8060,18683,8075,18685,18688],{"className":18684},[5251],[5249,18686,8067],{"className":18687},[8066]," Unit Tests (Node 18) (2m 45s)",[8060,18690,8075,18692,18695],{"className":18691},[5251],[5249,18693,8067],{"className":18694},[8066]," Unit Tests (Node 20) (2m 38s)",[8060,18697],{"className":18698},[5251],[8060,18700,18702,5977,18705],{"className":18701},[5251],[5249,18703,8080],{"className":18704},[8079],[5118,18706,18707],{},"Stage 3: E2E Tests",[8060,18709,8075,18711,18714],{"className":18710},[5251],[5249,18712,8067],{"className":18713},[8066]," E2E Tests (3m 12s)",[8060,18716],{"className":18717},[5251],[8060,18719,18721,5977,18724],{"className":18720},[5251],[5249,18722,8080],{"className":18723},[8079],[5118,18725,18726],{},"Stage 4: Security Scan",[8060,18728,8075,18730,18733],{"className":18729},[5251],[5249,18731,8067],{"className":18732},[8066]," Security Scan (45s)",[8060,18735],{"className":18736},[5251],[8060,18738,18740,5977,18743],{"className":18739},[5251],[5249,18741,8080],{"className":18742},[8079],[5118,18744,18745],{},"Stage 5: Build Docker Image",[8060,18747,8075,18749,18752],{"className":18748},[5251],[5249,18750,8067],{"className":18751},[8066]," Build Docker Image (4m 32s)",[8060,18754,8075,18756,18760],{"className":18755},[5251],[5249,18757,18759],{"className":18758},[8079],"  →"," Image: myusername\u002Fmy-app:main-abc123d",[8060,18762],{"className":18763},[5251],[8060,18765,18767,5977,18770],{"className":18766},[5251],[5249,18768,8080],{"className":18769},[8079],[5118,18771,18772],{},"Stage 6: Deploy to Production",[8060,18774,8075,18776,18781],{"className":18775},[5251],[5249,18777,18780],{"className":18778},[18779],"text-amber-400","⏳"," Waiting for approval...",[8060,18783,8075,18785,18788],{"className":18784},[5251],[5249,18786,8067],{"className":18787},[8066]," Approved by @tech-lead",[8060,18790,8075,18792,18795],{"className":18791},[5251],[5249,18793,18759],{"className":18794},[8079]," Deploying to production...",[8060,18797,8075,18799,18802],{"className":18798},[5251],[5249,18800,8067],{"className":18801},[8066]," Health check passed",[8060,18804,8075,18806,18809],{"className":18805},[5251],[5249,18807,8067],{"className":18808},[8066]," Deploy to Production (2m 15s)",[8060,18811],{"className":18812},[5251],[8060,18814,18816,5977,18819],{"className":18815},[5251],[5249,18817,8080],{"className":18818},[8079],[5118,18820,18821],{},"Stage 7: Notifications",[8060,18823,8075,18825,18828],{"className":18824},[5251],[5249,18826,8067],{"className":18827},[8066]," Slack notification sent",[8060,18830],{"className":18831},[5251],[8060,18833,18835],{"className":18834},[5251],[5249,18836,18838],{"className":18837},[8066,8200],"✓ Pipeline completed successfully in 14m 32s",[5147,18840],{},[5150,18842,18844],{"id":18843},"висновки","Висновки",[5084,18846,18847,18894],{},[5087,18848,18851],{"icon":18849,"title":18850},"i-lucide-check-circle","✅ Переваги CI\u002FCD автоматизації",[5092,18852,18853,18859,18865,18871,18877,18883,18889],{},[5095,18854,18855,18858],{},[5118,18856,18857],{},"Швидший delivery:"," features потрапляють до користувачів через хвилини після merge замість днів\u002Fтижнів.",[5095,18860,18861,18864],{},[5118,18862,18863],{},"Вища якість коду:"," автоматичні тести та lint перевіряють кожен commit, запобігаючи багам у production.",[5095,18866,18867,18870],{},[5118,18868,18869],{},"Менше людських помилок:"," автоматизація виключає ручні помилки при deployment (забув запустити тести, неправильна команда).",[5095,18872,18873,18876],{},[5118,18874,18875],{},"Швидкий rollback:"," при проблемах автоматичне повернення до попередньої версії за хвилини.",[5095,18878,18879,18882],{},[5118,18880,18881],{},"Впевненість у deployments:"," висока test coverage дозволяє деплоїти без страху «зламати production».",[5095,18884,18885,18888],{},[5118,18886,18887],{},"Transparency:"," вся команда бачить статус build\u002Fdeployment у GitHub Actions та Slack notifications.",[5095,18890,18891,6012],{},[5118,18892,18893],{},"Productive developers:",[5087,18895,18898],{"icon":18896,"title":18897},"i-lucide-alert-triangle","⚠️ Поширені помилки",[5092,18899,18900,18906,18912,18918,18924,18930,18936],{},[5095,18901,18902,18905],{},[5118,18903,18904],{},"Недостатня test coverage"," — CD без comprehensive test suite = deployment багів у production.",[5095,18907,18908,18911],{},[5118,18909,18910],{},"Повільні тести"," — E2E тести, що виконуються 30+ хвилин, блокують delivery pipeline.",[5095,18913,18914,18917],{},[5118,18915,18916],{},"Відсутність rollback strategy"," — при проблемах немає швидкого способу повернутися до стабільної версії.",[5095,18919,18920,18923],{},[5118,18921,18922],{},"Secrets у коді"," — комітити API keys або паролі у workflow files замість GitHub Secrets.",[5095,18925,18926,18929],{},[5118,18927,18928],{},"Відсутність notifications"," — команда не знає про failed deployments до скарг користувачів.",[5095,18931,18932,18935],{},[5118,18933,18934],{},"No environment separation"," — deploy у production без попереднього тестування на staging.",[5095,18937,18938,18941],{},[5118,18939,18940],{},"Missing health checks"," — deployment вважається успішним навіть якщо застосунок не працює.",[5155,18943,18944],{},[5118,18945,18946],{},"Що ми розглянули:",[5467,18948,18949,18954,18959,18965,18971,18976,18981,18987,18993,18998,19004,19010,19015],{},[5095,18950,18951,18953],{},[5118,18952,5231],{}," — людські помилки, втрата часу, inconsistency, відсутність rollback.",[5095,18955,18956,18958],{},[5118,18957,5164],{}," — Continuous Integration для раннього виявлення багів, Continuous Deployment для швидкого delivery.",[5095,18960,18961,18964],{},[5118,18962,18963],{},"GitHub Actions основи"," — workflow syntax, тригери (push, PR, schedule), jobs, steps, runners.",[5095,18966,18967,18970],{},[5118,18968,18969],{},"Базовий CI workflow"," — lint → unit tests → E2E tests з PostgreSQL\u002FRedis services, matrix builds.",[5095,18972,18973,18975],{},[5118,18974,5199],{}," — автоматична побудова Docker images, публікація у registry (Docker Hub, GHCR, AWS ECR).",[5095,18977,18978,18980],{},[5118,18979,5205],{}," — Blue-Green (zero downtime switch), Rolling Update (поступова заміна), Canary (gradual rollout).",[5095,18982,18983,18986],{},[5118,18984,18985],{},"Environment-specific workflows"," — окремі pipelines для staging та production, manual approval gates.",[5095,18988,18989,18992],{},[5118,18990,18991],{},"Secrets management"," — безпечне зберігання credentials у GitHub Secrets, environment-specific secrets.",[5095,18994,18995,18997],{},[5118,18996,5221],{}," — Slack\u002FDiscord\u002FEmail alerts при failed builds, automatic issue creation.",[5095,18999,19000,19003],{},[5118,19001,19002],{},"Rollback strategies"," — automatic rollback на failed health check, manual rollback workflow.",[5095,19005,19006,19009],{},[5118,19007,19008],{},"Performance optimization"," — caching dependencies, Docker layer caching, parallel jobs, conditional execution.",[5095,19011,19012,19014],{},[5118,19013,15058],{}," — fail fast, secure secrets, pinned versions, timeouts, reusable workflows, documentation.",[5095,19016,19017,19020],{},[5118,19018,19019],{},"Production-ready pipeline"," — комплексний workflow з lint, tests, security scan, build, deploy, notifications.",[5155,19022,19023],{},[5118,19024,19025],{},"Ключові висновки:",[5092,19027,19028,19034,19040,19046,19052,19058,19064,19070],{},[5095,19029,19030,19033],{},[5118,19031,19032],{},"Автоматизуйте все"," — від lint до deployment, мінімізуючи ручне втручання.",[5095,19035,19036,19039],{},[5118,19037,19038],{},"Висока test coverage обов'язкова"," — 80%+ coverage для впевненого Continuous Deployment.",[5095,19041,19042,19045],{},[5118,19043,19044],{},"Fail fast principle"," — швидкі перевірки (lint) першими, повільні (E2E) останніми.",[5095,19047,19048,19051],{},[5118,19049,19050],{},"Multiple environments"," — завжди тестуйте на staging перед production.",[5095,19053,19054,19057],{},[5118,19055,19056],{},"Automatic rollback"," — здатність швидко повернутися до стабільної версії критично важлива.",[5095,19059,19060,19063],{},[5118,19061,19062],{},"Notifications essential"," — команда має знати про проблеми одразу, не через користувачів.",[5095,19065,19066,19069],{},[5118,19067,19068],{},"Security first"," — використовуйте GitHub Secrets, не комітьте credentials, pinned action versions.",[5095,19071,19072,19075],{},[5118,19073,19074],{},"Monitor and optimize"," — регулярно переглядайте build duration та success rate, оптимізуйте повільні jobs.",[5155,19077,19078,19079,19082],{},"CI\u002FCD автоматизація — це ",[5118,19080,19081],{},"ключ до швидкого та надійного delivery"," у сучасній розробці. Вона дозволяє командам деплоїти по кілька разів на день без страху зламати production, забезпечуючи швидкий time-to-market для нових features та bugfixes.",[5147,19084],{},[5150,19086,19088],{"id":19087},"часті-запитання-faq","Часті запитання (FAQ)",[19090,19091,19092,19210,19669,19919,20438,20848],"accordion",{},[19093,19094,19096,19101,19106,19130,19135,19146,19151,19187,19192,19195],"accordion-item",{"title":19095},"Скільки коштує GitHub Actions?",[5155,19097,19098],{},[5118,19099,19100],{},"GitHub Actions pricing:",[5155,19102,19103],{},[5118,19104,19105],{},"Free tier (для всіх):",[5092,19107,19108,19114],{},[5095,19109,19110,19113],{},[5118,19111,19112],{},"Public repositories:"," необмежені хвилини",[5095,19115,19116,19119],{},[5118,19117,19118],{},"Private repositories:",[5092,19120,19121,19124,19127],{},[5095,19122,19123],{},"2000 хвилин\u002Fмісяць для Free plan",[5095,19125,19126],{},"3000 хвилин\u002Fмісяць для Pro plan",[5095,19128,19129],{},"50000 хвилин\u002Fмісяць для Teams\u002FEnterprise",[5155,19131,19132],{},[5118,19133,19134],{},"Множники за різні ОС:",[5092,19136,19137,19140,19143],{},[5095,19138,19139],{},"Linux: 1x (1 хвилина = 1 хвилина)",[5095,19141,19142],{},"Windows: 2x (1 хвилина = 2 хвилини)",[5095,19144,19145],{},"macOS: 10x (1 хвилина = 10 хвилин)",[5155,19147,19148],{},[5118,19149,19150],{},"Оптимізація витрат:",[5467,19152,19153,19159,19165,19171,19181],{},[5095,19154,19155,19158],{},[5118,19156,19157],{},"Використовуйте Linux runners"," — найдешевші (безкоштовні для public repos).",[5095,19160,19161,19164],{},[5118,19162,19163],{},"Cache dependencies"," — зменшує build time на 50-70%.",[5095,19166,19167,19170],{},[5118,19168,19169],{},"Parallel jobs"," — швидші builds, менше загального часу.",[5095,19172,19173,19176,19177,19180],{},[5118,19174,19175],{},"Conditional execution"," — skip CI для documentation commits (",[5173,19178,19179],{},"[skip ci]",").",[5095,19182,19183,19186],{},[5118,19184,19185],{},"Self-hosted runners"," — для private repos з великим навантаженням (безкоштовно, але потребує власного сервера).",[5155,19188,19189],{},[5118,19190,19191],{},"Приклад витрат:",[5155,19193,19194],{},"Проєкт з 100 commits\u002Fмісяць, кожен build 10 хвилин:",[5092,19196,19197,19204,19207],{},[5095,19198,19199,19200,19203],{},"Public repo: ",[5118,19201,19202],{},"$0"," (необмежено)",[5095,19205,19206],{},"Private repo Free plan: 100 × 10 = 1000 хвилин ✅ (в межах 2000)",[5095,19208,19209],{},"Private repo з 300 commits\u002Fмісяць: 3000 хвилин → потрібен Pro plan або оптимізація",[19093,19211,19213,19216,19225,19385,19390,19636,19641,19666],{"title":19212},"Як налаштувати CI\u002FCD для монорепозиторію?",[5155,19214,19215],{},"Монорепозиторій містить кілька застосунків\u002Fсервісів у одному Git repo. CI\u002FCD має запускати тести\u002Fdeployment лише для змінених сервісів.",[5155,19217,19218],{},[5118,19219,19220,19221,19224],{},"Використовуйте ",[5173,19222,19223],{},"paths"," фільтри:",[5240,19226,19228],{"className":6141,"code":19227,"language":6143,"meta":5245,"style":5245},"# .github\u002Fworkflows\u002Fauth-service.yml\nname: Auth Service CI\u002FCD\n\non:\n  push:\n    branches: [main]\n    paths:\n      - 'services\u002Fauth-service\u002F**'\n      - 'shared\u002F**'\n      - 'package.json'\n\njobs:\n  test:\n    runs-on: ubuntu-latest\n    defaults:\n      run:\n        working-directory: services\u002Fauth-service\n\n    steps:\n      - uses: actions\u002Fcheckout@v3\n      - run: npm ci\n      - run: npm test\n",[5173,19229,19230,19235,19244,19248,19254,19260,19270,19277,19284,19291,19298,19302,19308,19314,19322,19329,19335,19345,19349,19355,19365,19375],{"__ignoreMap":5245},[5249,19231,19232],{"class":5251,"line":5252},[5249,19233,19234],{"class":5255},"# .github\u002Fworkflows\u002Fauth-service.yml\n",[5249,19236,19237,19239,19241],{"class":5251,"line":5259},[5249,19238,6151],{"class":6150},[5249,19240,6155],{"class":6154},[5249,19242,19243],{"class":6158},"Auth Service CI\u002FCD\n",[5249,19245,19246],{"class":5251,"line":5273},[5249,19247,5306],{"emptyLinePlaceholder":5305},[5249,19249,19250,19252],{"class":5251,"line":5288},[5249,19251,6171],{"class":5281},[5249,19253,6185],{"class":6154},[5249,19255,19256,19258],{"class":5251,"line":5302},[5249,19257,6182],{"class":6150},[5249,19259,6185],{"class":6154},[5249,19261,19262,19264,19266,19268],{"class":5251,"line":5309},[5249,19263,6190],{"class":6150},[5249,19265,6193],{"class":6154},[5249,19267,6196],{"class":6158},[5249,19269,6199],{"class":6154},[5249,19271,19272,19275],{"class":5251,"line":5315},[5249,19273,19274],{"class":6150},"    paths",[5249,19276,6185],{"class":6154},[5249,19278,19279,19281],{"class":5251,"line":5324},[5249,19280,6265],{"class":6154},[5249,19282,19283],{"class":6158},"'services\u002Fauth-service\u002F**'\n",[5249,19285,19286,19288],{"class":5251,"line":5329},[5249,19287,6265],{"class":6154},[5249,19289,19290],{"class":6158},"'shared\u002F**'\n",[5249,19292,19293,19295],{"class":5251,"line":5335},[5249,19294,6265],{"class":6154},[5249,19296,19297],{"class":6158},"'package.json'\n",[5249,19299,19300],{"class":5251,"line":5344},[5249,19301,5306],{"emptyLinePlaceholder":5305},[5249,19303,19304,19306],{"class":5251,"line":5356},[5249,19305,6225],{"class":6150},[5249,19307,6185],{"class":6154},[5249,19309,19310,19312],{"class":5251,"line":5361},[5249,19311,6235],{"class":6150},[5249,19313,6185],{"class":6154},[5249,19315,19316,19318,19320],{"class":5251,"line":5367},[5249,19317,6242],{"class":6150},[5249,19319,6155],{"class":6154},[5249,19321,6792],{"class":6158},[5249,19323,19324,19327],{"class":5251,"line":5376},[5249,19325,19326],{"class":6150},"    defaults",[5249,19328,6185],{"class":6154},[5249,19330,19331,19333],{"class":5251,"line":5381},[5249,19332,9254],{"class":6150},[5249,19334,6185],{"class":6154},[5249,19336,19337,19340,19342],{"class":5251,"line":5387},[5249,19338,19339],{"class":6150},"        working-directory",[5249,19341,6155],{"class":6154},[5249,19343,19344],{"class":6158},"services\u002Fauth-service\n",[5249,19346,19347],{"class":5251,"line":5398},[5249,19348,5306],{"emptyLinePlaceholder":5305},[5249,19350,19351,19353],{"class":5251,"line":5403},[5249,19352,6255],{"class":6150},[5249,19354,6185],{"class":6154},[5249,19356,19357,19359,19361,19363],{"class":5251,"line":5409},[5249,19358,6265],{"class":6154},[5249,19360,7944],{"class":6150},[5249,19362,6155],{"class":6154},[5249,19364,6282],{"class":6158},[5249,19366,19367,19369,19371,19373],{"class":5251,"line":5419},[5249,19368,6265],{"class":6154},[5249,19370,12215],{"class":6150},[5249,19372,6155],{"class":6154},[5249,19374,6350],{"class":6158},[5249,19376,19377,19379,19381,19383],{"class":5251,"line":5424},[5249,19378,6265],{"class":6154},[5249,19380,12215],{"class":6150},[5249,19382,6155],{"class":6154},[5249,19384,6374],{"class":6158},[5155,19386,19387],{},[5118,19388,19389],{},"Альтернатива — detect changes у workflow:",[5240,19391,19393],{"className":6141,"code":19392,"language":6143,"meta":5245,"style":5245},"jobs:\n  detect-changes:\n    runs-on: ubuntu-latest\n    outputs:\n      auth-changed: ${{ steps.changes.outputs.auth }}\n      user-changed: ${{ steps.changes.outputs.user }}\n    steps:\n      - uses: actions\u002Fcheckout@v3\n      - uses: dorny\u002Fpaths-filter@v2\n        id: changes\n        with:\n          filters: |\n            auth:\n              - 'services\u002Fauth-service\u002F**'\n            user:\n              - 'services\u002Fuser-service\u002F**'\n\n  test-auth:\n    needs: detect-changes\n    if: needs.detect-changes.outputs.auth-changed == 'true'\n    runs-on: ubuntu-latest\n    steps:\n      - run: npm test\n        working-directory: services\u002Fauth-service\n\n  test-user:\n    needs: detect-changes\n    if: needs.detect-changes.outputs.user-changed == 'true'\n    runs-on: ubuntu-latest\n    steps:\n      - run: npm test\n        working-directory: services\u002Fuser-service\n",[5173,19394,19395,19401,19408,19416,19423,19433,19443,19449,19459,19470,19479,19485,19494,19499,19504,19509,19514,19518,19525,19534,19543,19551,19557,19567,19575,19579,19586,19594,19603,19611,19617,19627],{"__ignoreMap":5245},[5249,19396,19397,19399],{"class":5251,"line":5252},[5249,19398,6225],{"class":6150},[5249,19400,6185],{"class":6154},[5249,19402,19403,19406],{"class":5251,"line":5259},[5249,19404,19405],{"class":6150},"  detect-changes",[5249,19407,6185],{"class":6154},[5249,19409,19410,19412,19414],{"class":5251,"line":5273},[5249,19411,6242],{"class":6150},[5249,19413,6155],{"class":6154},[5249,19415,6792],{"class":6158},[5249,19417,19418,19421],{"class":5251,"line":5288},[5249,19419,19420],{"class":6150},"    outputs",[5249,19422,6185],{"class":6154},[5249,19424,19425,19428,19430],{"class":5251,"line":5302},[5249,19426,19427],{"class":6150},"      auth-changed",[5249,19429,6155],{"class":6154},[5249,19431,19432],{"class":6158},"${{ steps.changes.outputs.auth }}\n",[5249,19434,19435,19438,19440],{"class":5251,"line":5309},[5249,19436,19437],{"class":6150},"      user-changed",[5249,19439,6155],{"class":6154},[5249,19441,19442],{"class":6158},"${{ steps.changes.outputs.user }}\n",[5249,19444,19445,19447],{"class":5251,"line":5315},[5249,19446,6255],{"class":6150},[5249,19448,6185],{"class":6154},[5249,19450,19451,19453,19455,19457],{"class":5251,"line":5324},[5249,19452,6265],{"class":6154},[5249,19454,7944],{"class":6150},[5249,19456,6155],{"class":6154},[5249,19458,6282],{"class":6158},[5249,19460,19461,19463,19465,19467],{"class":5251,"line":5329},[5249,19462,6265],{"class":6154},[5249,19464,7944],{"class":6150},[5249,19466,6155],{"class":6154},[5249,19468,19469],{"class":6158},"dorny\u002Fpaths-filter@v2\n",[5249,19471,19472,19474,19476],{"class":5251,"line":5335},[5249,19473,8438],{"class":6150},[5249,19475,6155],{"class":6154},[5249,19477,19478],{"class":6158},"changes\n",[5249,19480,19481,19483],{"class":5251,"line":5344},[5249,19482,6312],{"class":6150},[5249,19484,6185],{"class":6154},[5249,19486,19487,19490,19492],{"class":5251,"line":5356},[5249,19488,19489],{"class":6150},"          filters",[5249,19491,6155],{"class":6154},[5249,19493,8478],{"class":7333},[5249,19495,19496],{"class":5251,"line":5361},[5249,19497,19498],{"class":6158},"            auth:\n",[5249,19500,19501],{"class":5251,"line":5367},[5249,19502,19503],{"class":6158},"              - 'services\u002Fauth-service\u002F**'\n",[5249,19505,19506],{"class":5251,"line":5376},[5249,19507,19508],{"class":6158},"            user:\n",[5249,19510,19511],{"class":5251,"line":5381},[5249,19512,19513],{"class":6158},"              - 'services\u002Fuser-service\u002F**'\n",[5249,19515,19516],{"class":5251,"line":5387},[5249,19517,5306],{"emptyLinePlaceholder":5305},[5249,19519,19520,19523],{"class":5251,"line":5398},[5249,19521,19522],{"class":6150},"  test-auth",[5249,19524,6185],{"class":6154},[5249,19526,19527,19529,19531],{"class":5251,"line":5403},[5249,19528,6968],{"class":6150},[5249,19530,6155],{"class":6154},[5249,19532,19533],{"class":6158},"detect-changes\n",[5249,19535,19536,19538,19540],{"class":5251,"line":5409},[5249,19537,12353],{"class":6150},[5249,19539,6155],{"class":6154},[5249,19541,19542],{"class":6158},"needs.detect-changes.outputs.auth-changed == 'true'\n",[5249,19544,19545,19547,19549],{"class":5251,"line":5419},[5249,19546,6242],{"class":6150},[5249,19548,6155],{"class":6154},[5249,19550,6792],{"class":6158},[5249,19552,19553,19555],{"class":5251,"line":5424},[5249,19554,6255],{"class":6150},[5249,19556,6185],{"class":6154},[5249,19558,19559,19561,19563,19565],{"class":5251,"line":5430},[5249,19560,6265],{"class":6154},[5249,19562,12215],{"class":6150},[5249,19564,6155],{"class":6154},[5249,19566,6374],{"class":6158},[5249,19568,19569,19571,19573],{"class":5251,"line":5442},[5249,19570,19339],{"class":6150},[5249,19572,6155],{"class":6154},[5249,19574,19344],{"class":6158},[5249,19576,19577],{"class":5251,"line":5447},[5249,19578,5306],{"emptyLinePlaceholder":5305},[5249,19580,19581,19584],{"class":5251,"line":5453},[5249,19582,19583],{"class":6150},"  test-user",[5249,19585,6185],{"class":6154},[5249,19587,19588,19590,19592],{"class":5251,"line":5698},[5249,19589,6968],{"class":6150},[5249,19591,6155],{"class":6154},[5249,19593,19533],{"class":6158},[5249,19595,19596,19598,19600],{"class":5251,"line":5704},[5249,19597,12353],{"class":6150},[5249,19599,6155],{"class":6154},[5249,19601,19602],{"class":6158},"needs.detect-changes.outputs.user-changed == 'true'\n",[5249,19604,19605,19607,19609],{"class":5251,"line":5710},[5249,19606,6242],{"class":6150},[5249,19608,6155],{"class":6154},[5249,19610,6792],{"class":6158},[5249,19612,19613,19615],{"class":5251,"line":5716},[5249,19614,6255],{"class":6150},[5249,19616,6185],{"class":6154},[5249,19618,19619,19621,19623,19625],{"class":5251,"line":5722},[5249,19620,6265],{"class":6154},[5249,19622,12215],{"class":6150},[5249,19624,6155],{"class":6154},[5249,19626,6374],{"class":6158},[5249,19628,19629,19631,19633],{"class":5251,"line":5728},[5249,19630,19339],{"class":6150},[5249,19632,6155],{"class":6154},[5249,19634,19635],{"class":6158},"services\u002Fuser-service\n",[5155,19637,19638],{},[5118,19639,19640],{},"Nx \u002F Turborepo integration:",[5240,19642,19644],{"className":6141,"code":19643,"language":6143,"meta":5245,"style":5245},"- name: Run affected tests\n  run: npx nx affected --target=test --base=origin\u002Fmain\n",[5173,19645,19646,19657],{"__ignoreMap":5245},[5249,19647,19648,19650,19652,19654],{"class":5251,"line":5252},[5249,19649,7941],{"class":6154},[5249,19651,6151],{"class":6150},[5249,19653,6155],{"class":6154},[5249,19655,19656],{"class":6158},"Run affected tests\n",[5249,19658,19659,19661,19663],{"class":5251,"line":5259},[5249,19660,8894],{"class":6150},[5249,19662,6155],{"class":6154},[5249,19664,19665],{"class":6158},"npx nx affected --target=test --base=origin\u002Fmain\n",[5155,19667,19668],{},"Nx автоматично визначає affected projects та запускає тести лише для них.",[19093,19670,19672,19677,19680,19685,19739,19747,19839,19844,19847,19865,19868,19873,19916],{"title":19671},"Як дебагити failed workflow?",[5155,19673,19674],{},[5118,19675,19676],{},"1. Перегляд логів у GitHub UI:",[5155,19678,19679],{},"GitHub repo → Actions → клікнути на failed workflow → розгорнути failed step",[5155,19681,19682],{},[5118,19683,19684],{},"2. Re-run з debug logging:",[5240,19686,19688],{"className":6141,"code":19687,"language":6143,"meta":5245,"style":5245},"# Тимчасово додайте у workflow\n- name: Debug environment\n  run: |\n    echo \"Node version: $(node --version)\"\n    echo \"npm version: $(npm --version)\"\n    echo \"Working directory: $(pwd)\"\n    ls -la\n    env | sort\n",[5173,19689,19690,19695,19706,19714,19719,19724,19729,19734],{"__ignoreMap":5245},[5249,19691,19692],{"class":5251,"line":5252},[5249,19693,19694],{"class":5255},"# Тимчасово додайте у workflow\n",[5249,19696,19697,19699,19701,19703],{"class":5251,"line":5259},[5249,19698,7941],{"class":6154},[5249,19700,6151],{"class":6150},[5249,19702,6155],{"class":6154},[5249,19704,19705],{"class":6158},"Debug environment\n",[5249,19707,19708,19710,19712],{"class":5251,"line":5273},[5249,19709,8894],{"class":6150},[5249,19711,6155],{"class":6154},[5249,19713,8478],{"class":7333},[5249,19715,19716],{"class":5251,"line":5288},[5249,19717,19718],{"class":6158},"    echo \"Node version: $(node --version)\"\n",[5249,19720,19721],{"class":5251,"line":5302},[5249,19722,19723],{"class":6158},"    echo \"npm version: $(npm --version)\"\n",[5249,19725,19726],{"class":5251,"line":5309},[5249,19727,19728],{"class":6158},"    echo \"Working directory: $(pwd)\"\n",[5249,19730,19731],{"class":5251,"line":5315},[5249,19732,19733],{"class":6158},"    ls -la\n",[5249,19735,19736],{"class":5251,"line":5324},[5249,19737,19738],{"class":6158},"    env | sort\n",[5155,19740,19741],{},[5118,19742,19743,19744,7747],{},"3. Локальне тестування workflow через ",[5173,19745,19746],{},"act",[5240,19748,19750],{"className":5242,"code":19749,"language":5244,"meta":5245,"style":5245},"# Встановлення act (local GitHub Actions runner)\nbrew install act  # macOS\n# або\ncurl https:\u002F\u002Fraw.githubusercontent.com\u002Fnektos\u002Fact\u002Fmaster\u002Finstall.sh | sudo bash\n\n# Запуск workflow локально\nact -j test  # Запускає job з назвою \"test\"\n\n# Запуск з secrets\nact -j deploy --secret-file .secrets\n",[5173,19751,19752,19757,19771,19776,19793,19797,19802,19815,19819,19824],{"__ignoreMap":5245},[5249,19753,19754],{"class":5251,"line":5252},[5249,19755,19756],{"class":5255},"# Встановлення act (local GitHub Actions runner)\n",[5249,19758,19759,19762,19765,19768],{"class":5251,"line":5259},[5249,19760,19761],{"class":5262},"brew",[5249,19763,19764],{"class":5266}," install",[5249,19766,19767],{"class":5266}," act",[5249,19769,19770],{"class":5255},"  # macOS\n",[5249,19772,19773],{"class":5251,"line":5273},[5249,19774,19775],{"class":5255},"# або\n",[5249,19777,19778,19781,19784,19787,19790],{"class":5251,"line":5288},[5249,19779,19780],{"class":5262},"curl",[5249,19782,19783],{"class":5266}," https:\u002F\u002Fraw.githubusercontent.com\u002Fnektos\u002Fact\u002Fmaster\u002Finstall.sh",[5249,19785,19786],{"class":6154}," | ",[5249,19788,19789],{"class":5262},"sudo",[5249,19791,19792],{"class":5266}," bash\n",[5249,19794,19795],{"class":5251,"line":5302},[5249,19796,5306],{"emptyLinePlaceholder":5305},[5249,19798,19799],{"class":5251,"line":5309},[5249,19800,19801],{"class":5255},"# Запуск workflow локально\n",[5249,19803,19804,19806,19809,19812],{"class":5251,"line":5315},[5249,19805,19746],{"class":5262},[5249,19807,19808],{"class":5281}," -j",[5249,19810,19811],{"class":5266}," test",[5249,19813,19814],{"class":5255},"  # Запускає job з назвою \"test\"\n",[5249,19816,19817],{"class":5251,"line":5324},[5249,19818,5306],{"emptyLinePlaceholder":5305},[5249,19820,19821],{"class":5251,"line":5329},[5249,19822,19823],{"class":5255},"# Запуск з secrets\n",[5249,19825,19826,19828,19830,19833,19836],{"class":5251,"line":5335},[5249,19827,19746],{"class":5262},[5249,19829,19808],{"class":5281},[5249,19831,19832],{"class":5266}," deploy",[5249,19834,19835],{"class":5281}," --secret-file",[5249,19837,19838],{"class":5266}," .secrets\n",[5155,19840,19841],{},[5118,19842,19843],{},"4. Enable debug logging:",[5155,19845,19846],{},"GitHub repo → Settings → Secrets → додайте:",[5092,19848,19849,19858],{},[5095,19850,19851,19854,19855],{},[5173,19852,19853],{},"ACTIONS_STEP_DEBUG"," = ",[5173,19856,19857],{},"true",[5095,19859,19860,19854,19863],{},[5173,19861,19862],{},"ACTIONS_RUNNER_DEBUG",[5173,19864,19857],{},[5155,19866,19867],{},"Наступний workflow run виводитиме детальні debug logs.",[5155,19869,19870],{},[5118,19871,19872],{},"5. SSH до runner для debugging:",[5240,19874,19876],{"className":6141,"code":19875,"language":6143,"meta":5245,"style":5245},"- name: Setup tmate session (SSH debugging)\n  if: failure()\n  uses: mxschmitt\u002Faction-tmate@v3\n  timeout-minutes: 30\n",[5173,19877,19878,19889,19897,19906],{"__ignoreMap":5245},[5249,19879,19880,19882,19884,19886],{"class":5251,"line":5252},[5249,19881,7941],{"class":6154},[5249,19883,6151],{"class":6150},[5249,19885,6155],{"class":6154},[5249,19887,19888],{"class":6158},"Setup tmate session (SSH debugging)\n",[5249,19890,19891,19893,19895],{"class":5251,"line":5259},[5249,19892,7997],{"class":6150},[5249,19894,6155],{"class":6154},[5249,19896,9401],{"class":6158},[5249,19898,19899,19901,19903],{"class":5251,"line":5273},[5249,19900,8694],{"class":6150},[5249,19902,6155],{"class":6154},[5249,19904,19905],{"class":6158},"mxschmitt\u002Faction-tmate@v3\n",[5249,19907,19908,19911,19913],{"class":5251,"line":5288},[5249,19909,19910],{"class":6150},"  timeout-minutes",[5249,19912,6155],{"class":6154},[5249,19914,19915],{"class":6324},"30\n",[5155,19917,19918],{},"При падінні workflow ви отримаєте SSH команду у логах для підключення до runner.",[19093,19920,19922,19925,20153,20158,20362,20367],{"title":19921},"Як деплоїти на VPS без Docker?",[5155,19923,19924],{},"Deployment на VPS через SSH без Docker (traditional approach):",[5240,19926,19928],{"className":6141,"code":19927,"language":6143,"meta":5245,"style":5245},"deploy:\n  name: Deploy to VPS\n  runs-on: ubuntu-latest\n\n  steps:\n    - name: Deploy via SSH\n      uses: appleboy\u002Fssh-action@v0.1.10\n      with:\n        host: ${{ secrets.VPS_HOST }}\n        username: ${{ secrets.VPS_USER }}\n        key: ${{ secrets.VPS_SSH_KEY }}\n        script: |\n          # Navigate to app directory\n          cd \u002Fvar\u002Fwww\u002Fmy-app\n\n          # Pull latest code\n          git pull origin main\n\n          # Install dependencies\n          npm ci --production\n\n          # Run migrations\n          npm run migration:run\n\n          # Build application\n          npm run build\n\n          # Restart PM2 process\n          pm2 restart my-app\n\n          # Wait and check health\n          sleep 5\n          curl -f http:\u002F\u002Flocalhost:3000\u002Fhealth || exit 1\n\n    - name: Verify deployment\n      run: |\n        sleep 10\n        curl -f https:\u002F\u002Fexample.com\u002Fhealth\n",[5173,19929,19930,19936,19945,19953,19957,19963,19973,19981,19987,19996,20005,20014,20022,20027,20032,20036,20041,20046,20050,20055,20060,20064,20069,20074,20078,20083,20088,20092,20097,20102,20106,20111,20116,20121,20125,20136,20144,20148],{"__ignoreMap":5245},[5249,19931,19932,19934],{"class":5251,"line":5252},[5249,19933,9094],{"class":6150},[5249,19935,6185],{"class":6154},[5249,19937,19938,19940,19942],{"class":5251,"line":5259},[5249,19939,9101],{"class":6150},[5249,19941,6155],{"class":6154},[5249,19943,19944],{"class":6158},"Deploy to VPS\n",[5249,19946,19947,19949,19951],{"class":5251,"line":5273},[5249,19948,9111],{"class":6150},[5249,19950,6155],{"class":6154},[5249,19952,6792],{"class":6158},[5249,19954,19955],{"class":5251,"line":5288},[5249,19956,5306],{"emptyLinePlaceholder":5305},[5249,19958,19959,19961],{"class":5251,"line":5302},[5249,19960,9145],{"class":6150},[5249,19962,6185],{"class":6154},[5249,19964,19965,19967,19969,19971],{"class":5251,"line":5309},[5249,19966,6529],{"class":6154},[5249,19968,6151],{"class":6150},[5249,19970,6155],{"class":6154},[5249,19972,17830],{"class":6158},[5249,19974,19975,19977,19979],{"class":5251,"line":5315},[5249,19976,9163],{"class":6150},[5249,19978,6155],{"class":6154},[5249,19980,9168],{"class":6158},[5249,19982,19983,19985],{"class":5251,"line":5324},[5249,19984,9173],{"class":6150},[5249,19986,6185],{"class":6154},[5249,19988,19989,19991,19993],{"class":5251,"line":5329},[5249,19990,9180],{"class":6150},[5249,19992,6155],{"class":6154},[5249,19994,19995],{"class":6158},"${{ secrets.VPS_HOST }}\n",[5249,19997,19998,20000,20002],{"class":5251,"line":5335},[5249,19999,9190],{"class":6150},[5249,20001,6155],{"class":6154},[5249,20003,20004],{"class":6158},"${{ secrets.VPS_USER }}\n",[5249,20006,20007,20009,20011],{"class":5251,"line":5344},[5249,20008,9200],{"class":6150},[5249,20010,6155],{"class":6154},[5249,20012,20013],{"class":6158},"${{ secrets.VPS_SSH_KEY }}\n",[5249,20015,20016,20018,20020],{"class":5251,"line":5356},[5249,20017,9210],{"class":6150},[5249,20019,6155],{"class":6154},[5249,20021,8478],{"class":7333},[5249,20023,20024],{"class":5251,"line":5361},[5249,20025,20026],{"class":6158},"          # Navigate to app directory\n",[5249,20028,20029],{"class":5251,"line":5367},[5249,20030,20031],{"class":6158},"          cd \u002Fvar\u002Fwww\u002Fmy-app\n",[5249,20033,20034],{"class":5251,"line":5376},[5249,20035,5306],{"emptyLinePlaceholder":5305},[5249,20037,20038],{"class":5251,"line":5381},[5249,20039,20040],{"class":6158},"          # Pull latest code\n",[5249,20042,20043],{"class":5251,"line":5387},[5249,20044,20045],{"class":6158},"          git pull origin main\n",[5249,20047,20048],{"class":5251,"line":5398},[5249,20049,5306],{"emptyLinePlaceholder":5305},[5249,20051,20052],{"class":5251,"line":5403},[5249,20053,20054],{"class":6158},"          # Install dependencies\n",[5249,20056,20057],{"class":5251,"line":5409},[5249,20058,20059],{"class":6158},"          npm ci --production\n",[5249,20061,20062],{"class":5251,"line":5419},[5249,20063,5306],{"emptyLinePlaceholder":5305},[5249,20065,20066],{"class":5251,"line":5424},[5249,20067,20068],{"class":6158},"          # Run migrations\n",[5249,20070,20071],{"class":5251,"line":5430},[5249,20072,20073],{"class":6158},"          npm run migration:run\n",[5249,20075,20076],{"class":5251,"line":5442},[5249,20077,5306],{"emptyLinePlaceholder":5305},[5249,20079,20080],{"class":5251,"line":5447},[5249,20081,20082],{"class":6158},"          # Build application\n",[5249,20084,20085],{"class":5251,"line":5453},[5249,20086,20087],{"class":6158},"          npm run build\n",[5249,20089,20090],{"class":5251,"line":5698},[5249,20091,5306],{"emptyLinePlaceholder":5305},[5249,20093,20094],{"class":5251,"line":5704},[5249,20095,20096],{"class":6158},"          # Restart PM2 process\n",[5249,20098,20099],{"class":5251,"line":5710},[5249,20100,20101],{"class":6158},"          pm2 restart my-app\n",[5249,20103,20104],{"class":5251,"line":5716},[5249,20105,5306],{"emptyLinePlaceholder":5305},[5249,20107,20108],{"class":5251,"line":5722},[5249,20109,20110],{"class":6158},"          # Wait and check health\n",[5249,20112,20113],{"class":5251,"line":5728},[5249,20114,20115],{"class":6158},"          sleep 5\n",[5249,20117,20118],{"class":5251,"line":5734},[5249,20119,20120],{"class":6158},"          curl -f http:\u002F\u002Flocalhost:3000\u002Fhealth || exit 1\n",[5249,20122,20123],{"class":5251,"line":5740},[5249,20124,5306],{"emptyLinePlaceholder":5305},[5249,20126,20127,20129,20131,20133],{"class":5251,"line":5746},[5249,20128,6529],{"class":6154},[5249,20130,6151],{"class":6150},[5249,20132,6155],{"class":6154},[5249,20134,20135],{"class":6158},"Verify deployment\n",[5249,20137,20138,20140,20142],{"class":5251,"line":5752},[5249,20139,9254],{"class":6150},[5249,20141,6155],{"class":6154},[5249,20143,8478],{"class":7333},[5249,20145,20146],{"class":5251,"line":5758},[5249,20147,9263],{"class":6158},[5249,20149,20150],{"class":5251,"line":5763},[5249,20151,20152],{"class":6158},"        curl -f https:\u002F\u002Fexample.com\u002Fhealth\n",[5155,20154,20155],{},[5118,20156,20157],{},"Setup на VPS (one-time):",[5240,20159,20161],{"className":5242,"code":20160,"language":5244,"meta":5245,"style":5245},"# 1. Встановлення Node.js\ncurl -fsSL https:\u002F\u002Fdeb.nodesource.com\u002Fsetup_18.x | sudo -E bash -\nsudo apt-get install -y nodejs\n\n# 2. Встановлення PM2\nsudo npm install -g pm2\n\n# 3. Клонування репозиторію\ncd \u002Fvar\u002Fwww\ngit clone https:\u002F\u002Fgithub.com\u002Fyour-org\u002Fmy-app.git\ncd my-app\n\n# 4. Initial setup\nnpm ci --production\nnpm run build\n\n# 5. Start with PM2\npm2 start dist\u002Fmain.js --name my-app\npm2 save\npm2 startup  # Auto-start on reboot\n\n# 6. Setup nginx reverse proxy\nsudo apt install nginx\nsudo nano \u002Fetc\u002Fnginx\u002Fsites-available\u002Fmy-app\n",[5173,20162,20163,20168,20191,20206,20210,20215,20230,20234,20239,20246,20256,20263,20267,20272,20282,20290,20294,20299,20314,20321,20331,20335,20340,20352],{"__ignoreMap":5245},[5249,20164,20165],{"class":5251,"line":5252},[5249,20166,20167],{"class":5255},"# 1. Встановлення Node.js\n",[5249,20169,20170,20172,20175,20178,20180,20182,20185,20188],{"class":5251,"line":5259},[5249,20171,19780],{"class":5262},[5249,20173,20174],{"class":5281}," -fsSL",[5249,20176,20177],{"class":5266}," https:\u002F\u002Fdeb.nodesource.com\u002Fsetup_18.x",[5249,20179,19786],{"class":6154},[5249,20181,19789],{"class":5262},[5249,20183,20184],{"class":5281}," -E",[5249,20186,20187],{"class":5266}," bash",[5249,20189,20190],{"class":5266}," -\n",[5249,20192,20193,20195,20198,20200,20203],{"class":5251,"line":5273},[5249,20194,19789],{"class":5262},[5249,20196,20197],{"class":5266}," apt-get",[5249,20199,19764],{"class":5266},[5249,20201,20202],{"class":5281}," -y",[5249,20204,20205],{"class":5266}," nodejs\n",[5249,20207,20208],{"class":5251,"line":5288},[5249,20209,5306],{"emptyLinePlaceholder":5305},[5249,20211,20212],{"class":5251,"line":5302},[5249,20213,20214],{"class":5255},"# 2. Встановлення PM2\n",[5249,20216,20217,20219,20222,20224,20227],{"class":5251,"line":5309},[5249,20218,19789],{"class":5262},[5249,20220,20221],{"class":5266}," npm",[5249,20223,19764],{"class":5266},[5249,20225,20226],{"class":5281}," -g",[5249,20228,20229],{"class":5266}," pm2\n",[5249,20231,20232],{"class":5251,"line":5315},[5249,20233,5306],{"emptyLinePlaceholder":5305},[5249,20235,20236],{"class":5251,"line":5324},[5249,20237,20238],{"class":5255},"# 3. Клонування репозиторію\n",[5249,20240,20241,20243],{"class":5251,"line":5329},[5249,20242,5338],{"class":5262},[5249,20244,20245],{"class":5266}," \u002Fvar\u002Fwww\n",[5249,20247,20248,20250,20253],{"class":5251,"line":5335},[5249,20249,5263],{"class":5262},[5249,20251,20252],{"class":5266}," clone",[5249,20254,20255],{"class":5266}," https:\u002F\u002Fgithub.com\u002Fyour-org\u002Fmy-app.git\n",[5249,20257,20258,20260],{"class":5251,"line":5344},[5249,20259,5338],{"class":5262},[5249,20261,20262],{"class":5266}," my-app\n",[5249,20264,20265],{"class":5251,"line":5356},[5249,20266,5306],{"emptyLinePlaceholder":5305},[5249,20268,20269],{"class":5251,"line":5361},[5249,20270,20271],{"class":5255},"# 4. Initial setup\n",[5249,20273,20274,20276,20279],{"class":5251,"line":5367},[5249,20275,5370],{"class":5262},[5249,20277,20278],{"class":5266}," ci",[5249,20280,20281],{"class":5281}," --production\n",[5249,20283,20284,20286,20288],{"class":5251,"line":5376},[5249,20285,5370],{"class":5262},[5249,20287,5392],{"class":5266},[5249,20289,5416],{"class":5266},[5249,20291,20292],{"class":5251,"line":5381},[5249,20293,5306],{"emptyLinePlaceholder":5305},[5249,20295,20296],{"class":5251,"line":5387},[5249,20297,20298],{"class":5255},"# 5. Start with PM2\n",[5249,20300,20301,20303,20306,20309,20312],{"class":5251,"line":5398},[5249,20302,5433],{"class":5262},[5249,20304,20305],{"class":5266}," start",[5249,20307,20308],{"class":5266}," dist\u002Fmain.js",[5249,20310,20311],{"class":5281}," --name",[5249,20313,20262],{"class":5266},[5249,20315,20316,20318],{"class":5251,"line":5403},[5249,20317,5433],{"class":5262},[5249,20319,20320],{"class":5266}," save\n",[5249,20322,20323,20325,20328],{"class":5251,"line":5409},[5249,20324,5433],{"class":5262},[5249,20326,20327],{"class":5266}," startup",[5249,20329,20330],{"class":5255},"  # Auto-start on reboot\n",[5249,20332,20333],{"class":5251,"line":5419},[5249,20334,5306],{"emptyLinePlaceholder":5305},[5249,20336,20337],{"class":5251,"line":5424},[5249,20338,20339],{"class":5255},"# 6. Setup nginx reverse proxy\n",[5249,20341,20342,20344,20347,20349],{"class":5251,"line":5430},[5249,20343,19789],{"class":5262},[5249,20345,20346],{"class":5266}," apt",[5249,20348,19764],{"class":5266},[5249,20350,20351],{"class":5266}," nginx\n",[5249,20353,20354,20356,20359],{"class":5251,"line":5442},[5249,20355,19789],{"class":5262},[5249,20357,20358],{"class":5266}," nano",[5249,20360,20361],{"class":5266}," \u002Fetc\u002Fnginx\u002Fsites-available\u002Fmy-app\n",[5155,20363,20364],{},[5118,20365,20366],{},"Nginx config:",[5240,20368,20372],{"className":20369,"code":20370,"language":20371,"meta":5245,"style":5245},"language-nginx shiki shiki-themes light-plus dark-plus dark-plus","server {\n    listen 80;\n    server_name example.com;\n\n    location \u002F {\n        proxy_pass http:\u002F\u002Flocalhost:3000;\n        proxy_http_version 1.1;\n        proxy_set_header Upgrade $http_upgrade;\n        proxy_set_header Connection 'upgrade';\n        proxy_set_header Host $host;\n        proxy_cache_bypass $http_upgrade;\n    }\n}\n","nginx",[5173,20373,20374,20379,20384,20389,20393,20398,20403,20408,20413,20418,20423,20428,20433],{"__ignoreMap":5245},[5249,20375,20376],{"class":5251,"line":5252},[5249,20377,20378],{},"server {\n",[5249,20380,20381],{"class":5251,"line":5259},[5249,20382,20383],{},"    listen 80;\n",[5249,20385,20386],{"class":5251,"line":5273},[5249,20387,20388],{},"    server_name example.com;\n",[5249,20390,20391],{"class":5251,"line":5288},[5249,20392,5306],{"emptyLinePlaceholder":5305},[5249,20394,20395],{"class":5251,"line":5302},[5249,20396,20397],{},"    location \u002F {\n",[5249,20399,20400],{"class":5251,"line":5309},[5249,20401,20402],{},"        proxy_pass http:\u002F\u002Flocalhost:3000;\n",[5249,20404,20405],{"class":5251,"line":5315},[5249,20406,20407],{},"        proxy_http_version 1.1;\n",[5249,20409,20410],{"class":5251,"line":5324},[5249,20411,20412],{},"        proxy_set_header Upgrade $http_upgrade;\n",[5249,20414,20415],{"class":5251,"line":5329},[5249,20416,20417],{},"        proxy_set_header Connection 'upgrade';\n",[5249,20419,20420],{"class":5251,"line":5335},[5249,20421,20422],{},"        proxy_set_header Host $host;\n",[5249,20424,20425],{"class":5251,"line":5344},[5249,20426,20427],{},"        proxy_cache_bypass $http_upgrade;\n",[5249,20429,20430],{"class":5251,"line":5356},[5249,20431,20432],{},"    }\n",[5249,20434,20435],{"class":5251,"line":5361},[5249,20436,20437],{},"}\n",[19093,20439,20441,20446,20448,20451,20470,20475,20683,20688],{"title":20440},"Як налаштувати multi-environment deployment (dev\u002Fstaging\u002Fprod)?",[5155,20442,20443],{},[5118,20444,20445],{},"1. Створіть окремі environments у GitHub:",[5155,20447,11791],{},[5155,20449,20450],{},"Створіть:",[5092,20452,20453,20459,20465],{},[5095,20454,20455,20458],{},[5173,20456,20457],{},"development"," (no protection rules)",[5095,20460,20461,20464],{},[5173,20462,20463],{},"staging"," (optional: require approval)",[5095,20466,20467,20469],{},[5173,20468,11797],{}," (require approval + wait timer)",[5155,20471,20472],{},[5118,20473,20474],{},"2. Workflow з environment selection:",[5240,20476,20478],{"className":6141,"code":20477,"language":6143,"meta":5245,"style":5245},"name: Multi-Environment Deploy\n\non:\n  workflow_dispatch:\n    inputs:\n      environment:\n        description: 'Environment to deploy'\n        required: true\n        type: choice\n        options:\n          - development\n          - staging\n          - production\n\njobs:\n  deploy:\n    runs-on: ubuntu-latest\n    environment: ${{ github.event.inputs.environment }}\n\n    steps:\n      - name: Deploy to ${{ github.event.inputs.environment }}\n        uses: appleboy\u002Fssh-action@v0.1.10\n        with:\n          host: ${{ secrets.SERVER_HOST }}  # Environment-specific\n          username: ${{ secrets.SERVER_USER }}\n          key: ${{ secrets.SSH_KEY }}\n          script: |\n            cd \u002Fapp\n            export ENVIRONMENT=${{ github.event.inputs.environment }}\n            docker-compose -f docker-compose.$ENVIRONMENT.yml up -d\n",[5173,20479,20480,20489,20493,20499,20505,20511,20517,20525,20533,20541,20547,20554,20560,20566,20570,20576,20582,20590,20598,20602,20608,20619,20627,20633,20645,20653,20661,20669,20673,20678],{"__ignoreMap":5245},[5249,20481,20482,20484,20486],{"class":5251,"line":5252},[5249,20483,6151],{"class":6150},[5249,20485,6155],{"class":6154},[5249,20487,20488],{"class":6158},"Multi-Environment Deploy\n",[5249,20490,20491],{"class":5251,"line":5259},[5249,20492,5306],{"emptyLinePlaceholder":5305},[5249,20494,20495,20497],{"class":5251,"line":5273},[5249,20496,6171],{"class":5281},[5249,20498,6185],{"class":6154},[5249,20500,20501,20503],{"class":5251,"line":5288},[5249,20502,6564],{"class":6150},[5249,20504,6185],{"class":6154},[5249,20506,20507,20509],{"class":5251,"line":5302},[5249,20508,6571],{"class":6150},[5249,20510,6185],{"class":6154},[5249,20512,20513,20515],{"class":5251,"line":5309},[5249,20514,6578],{"class":6150},[5249,20516,6185],{"class":6154},[5249,20518,20519,20521,20523],{"class":5251,"line":5315},[5249,20520,6585],{"class":6150},[5249,20522,6155],{"class":6154},[5249,20524,6590],{"class":6158},[5249,20526,20527,20529,20531],{"class":5251,"line":5324},[5249,20528,6595],{"class":6150},[5249,20530,6155],{"class":6154},[5249,20532,6600],{"class":5281},[5249,20534,20535,20537,20539],{"class":5251,"line":5329},[5249,20536,13981],{"class":6150},[5249,20538,6155],{"class":6154},[5249,20540,13986],{"class":6158},[5249,20542,20543,20545],{"class":5251,"line":5335},[5249,20544,7328],{"class":6150},[5249,20546,6185],{"class":6154},[5249,20548,20549,20551],{"class":5251,"line":5344},[5249,20550,7375],{"class":6154},[5249,20552,20553],{"class":6158},"development\n",[5249,20555,20556,20558],{"class":5251,"line":5356},[5249,20557,7375],{"class":6154},[5249,20559,11247],{"class":6158},[5249,20561,20562,20564],{"class":5251,"line":5361},[5249,20563,7375],{"class":6154},[5249,20565,11569],{"class":6158},[5249,20567,20568],{"class":5251,"line":5367},[5249,20569,5306],{"emptyLinePlaceholder":5305},[5249,20571,20572,20574],{"class":5251,"line":5376},[5249,20573,6225],{"class":6150},[5249,20575,6185],{"class":6154},[5249,20577,20578,20580],{"class":5251,"line":5381},[5249,20579,9959],{"class":6150},[5249,20581,6185],{"class":6154},[5249,20583,20584,20586,20588],{"class":5251,"line":5387},[5249,20585,6242],{"class":6150},[5249,20587,6155],{"class":6154},[5249,20589,6792],{"class":6158},[5249,20591,20592,20594,20596],{"class":5251,"line":5398},[5249,20593,11235],{"class":6150},[5249,20595,6155],{"class":6154},[5249,20597,14047],{"class":6158},[5249,20599,20600],{"class":5251,"line":5403},[5249,20601,5306],{"emptyLinePlaceholder":5305},[5249,20603,20604,20606],{"class":5251,"line":5409},[5249,20605,6255],{"class":6150},[5249,20607,6185],{"class":6154},[5249,20609,20610,20612,20614,20616],{"class":5251,"line":5419},[5249,20611,6265],{"class":6154},[5249,20613,6151],{"class":6150},[5249,20615,6155],{"class":6154},[5249,20617,20618],{"class":6158},"Deploy to ${{ github.event.inputs.environment }}\n",[5249,20620,20621,20623,20625],{"class":5251,"line":5424},[5249,20622,6277],{"class":6150},[5249,20624,6155],{"class":6154},[5249,20626,9168],{"class":6158},[5249,20628,20629,20631],{"class":5251,"line":5430},[5249,20630,6312],{"class":6150},[5249,20632,6185],{"class":6154},[5249,20634,20635,20637,20639,20642],{"class":5251,"line":5442},[5249,20636,10009],{"class":6150},[5249,20638,6155],{"class":6154},[5249,20640,20641],{"class":6158},"${{ secrets.SERVER_HOST }}",[5249,20643,20644],{"class":5255},"  # Environment-specific\n",[5249,20646,20647,20649,20651],{"class":5251,"line":5447},[5249,20648,8403],{"class":6150},[5249,20650,6155],{"class":6154},[5249,20652,10023],{"class":6158},[5249,20654,20655,20657,20659],{"class":5251,"line":5453},[5249,20656,10028],{"class":6150},[5249,20658,6155],{"class":6154},[5249,20660,10033],{"class":6158},[5249,20662,20663,20665,20667],{"class":5251,"line":5698},[5249,20664,10038],{"class":6150},[5249,20666,6155],{"class":6154},[5249,20668,8478],{"class":7333},[5249,20670,20671],{"class":5251,"line":5704},[5249,20672,10047],{"class":6158},[5249,20674,20675],{"class":5251,"line":5710},[5249,20676,20677],{"class":6158},"            export ENVIRONMENT=${{ github.event.inputs.environment }}\n",[5249,20679,20680],{"class":5251,"line":5716},[5249,20681,20682],{"class":6158},"            docker-compose -f docker-compose.$ENVIRONMENT.yml up -d\n",[5155,20684,20685],{},[5118,20686,20687],{},"3. Automatic deployment based on branch:",[5240,20689,20691],{"className":6141,"code":20690,"language":6143,"meta":5245,"style":5245},"on:\n  push:\n    branches:\n      - develop    # → staging\n      - main       # → production\n\njobs:\n  deploy:\n    runs-on: ubuntu-latest\n    environment: ${{ github.ref == 'refs\u002Fheads\u002Fmain' && 'production' || 'staging' }}\n    \n    steps:\n      - name: Determine environment\n        id: env\n        run: |\n          if [ \"${{ github.ref }}\" == \"refs\u002Fheads\u002Fmain\" ]; then\n            echo \"name=production\" >> $GITHUB_OUTPUT\n          else\n            echo \"name=staging\" >> $GITHUB_OUTPUT\n          fi\n\n      - name: Deploy\n        run: echo \"Deploying to ${{ steps.env.outputs.name }}\"\n",[5173,20692,20693,20699,20705,20711,20720,20729,20733,20739,20745,20753,20762,20767,20773,20784,20793,20801,20806,20811,20816,20821,20825,20829,20839],{"__ignoreMap":5245},[5249,20694,20695,20697],{"class":5251,"line":5252},[5249,20696,6171],{"class":5281},[5249,20698,6185],{"class":6154},[5249,20700,20701,20703],{"class":5251,"line":5259},[5249,20702,6182],{"class":6150},[5249,20704,6185],{"class":6154},[5249,20706,20707,20709],{"class":5251,"line":5273},[5249,20708,6190],{"class":6150},[5249,20710,6185],{"class":6154},[5249,20712,20713,20715,20717],{"class":5251,"line":5288},[5249,20714,6265],{"class":6154},[5249,20716,6738],{"class":6158},[5249,20718,20719],{"class":5255},"    # → staging\n",[5249,20721,20722,20724,20726],{"class":5251,"line":5302},[5249,20723,6265],{"class":6154},[5249,20725,6196],{"class":6158},[5249,20727,20728],{"class":5255},"       # → production\n",[5249,20730,20731],{"class":5251,"line":5309},[5249,20732,5306],{"emptyLinePlaceholder":5305},[5249,20734,20735,20737],{"class":5251,"line":5315},[5249,20736,6225],{"class":6150},[5249,20738,6185],{"class":6154},[5249,20740,20741,20743],{"class":5251,"line":5324},[5249,20742,9959],{"class":6150},[5249,20744,6185],{"class":6154},[5249,20746,20747,20749,20751],{"class":5251,"line":5329},[5249,20748,6242],{"class":6150},[5249,20750,6155],{"class":6154},[5249,20752,6792],{"class":6158},[5249,20754,20755,20757,20759],{"class":5251,"line":5335},[5249,20756,11235],{"class":6150},[5249,20758,6155],{"class":6154},[5249,20760,20761],{"class":6158},"${{ github.ref == 'refs\u002Fheads\u002Fmain' && 'production' || 'staging' }}\n",[5249,20763,20764],{"class":5251,"line":5344},[5249,20765,20766],{"class":6154},"    \n",[5249,20768,20769,20771],{"class":5251,"line":5356},[5249,20770,6255],{"class":6150},[5249,20772,6185],{"class":6154},[5249,20774,20775,20777,20779,20781],{"class":5251,"line":5361},[5249,20776,6265],{"class":6154},[5249,20778,6151],{"class":6150},[5249,20780,6155],{"class":6154},[5249,20782,20783],{"class":6158},"Determine environment\n",[5249,20785,20786,20788,20790],{"class":5251,"line":5367},[5249,20787,8438],{"class":6150},[5249,20789,6155],{"class":6154},[5249,20791,20792],{"class":6158},"env\n",[5249,20794,20795,20797,20799],{"class":5251,"line":5376},[5249,20796,6345],{"class":6150},[5249,20798,6155],{"class":6154},[5249,20800,8478],{"class":7333},[5249,20802,20803],{"class":5251,"line":5381},[5249,20804,20805],{"class":6158},"          if [ \"${{ github.ref }}\" == \"refs\u002Fheads\u002Fmain\" ]; then\n",[5249,20807,20808],{"class":5251,"line":5387},[5249,20809,20810],{"class":6158},"            echo \"name=production\" >> $GITHUB_OUTPUT\n",[5249,20812,20813],{"class":5251,"line":5398},[5249,20814,20815],{"class":6158},"          else\n",[5249,20817,20818],{"class":5251,"line":5403},[5249,20819,20820],{"class":6158},"            echo \"name=staging\" >> $GITHUB_OUTPUT\n",[5249,20822,20823],{"class":5251,"line":5409},[5249,20824,10268],{"class":6158},[5249,20826,20827],{"class":5251,"line":5419},[5249,20828,5306],{"emptyLinePlaceholder":5305},[5249,20830,20831,20833,20835,20837],{"class":5251,"line":5424},[5249,20832,6265],{"class":6154},[5249,20834,6151],{"class":6150},[5249,20836,6155],{"class":6154},[5249,20838,12027],{"class":6158},[5249,20840,20841,20843,20845],{"class":5251,"line":5430},[5249,20842,6345],{"class":6150},[5249,20844,6155],{"class":6154},[5249,20846,20847],{"class":6158},"echo \"Deploying to ${{ steps.env.outputs.name }}\"\n",[19093,20849,20851,20856,21160,21165],{"title":20850},"Як інтегрувати CI\u002FCD з іншими платформами (GitLab, Bitbucket)?",[5155,20852,20853],{},[5118,20854,20855],{},"GitLab CI\u002FCD:",[5240,20857,20859],{"className":6141,"code":20858,"language":6143,"meta":5245,"style":5245},"# .gitlab-ci.yml\nstages:\n  - test\n  - build\n  - deploy\n\nvariables:\n  NODE_VERSION: \"18\"\n\ntest:\n  stage: test\n  image: node:$NODE_VERSION\n  script:\n    - npm ci\n    - npm run lint\n    - npm test\n  coverage: '\u002FAll files[^|]*\\|[^|]*\\s+([\\d\\.]+)\u002F'\n\nbuild:\n  stage: build\n  image: docker:latest\n  services:\n    - docker:dind\n  script:\n    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY\n    - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .\n    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA\n  only:\n    - main\n\ndeploy:\n  stage: deploy\n  image: alpine:latest\n  before_script:\n    - apk add --no-cache openssh-client\n    - eval $(ssh-agent -s)\n    - echo \"$SSH_PRIVATE_KEY\" | ssh-add -\n  script:\n    - ssh user@server \"cd \u002Fapp && docker-compose pull && docker-compose up -d\"\n  only:\n    - main\n  environment:\n    name: production\n    url: https:\u002F\u002Fexample.com\n",[5173,20860,20861,20866,20873,20879,20885,20891,20895,20902,20911,20915,20921,20930,20940,20947,20953,20959,20965,20975,20979,20986,20994,21003,21010,21017,21023,21030,21037,21044,21051,21057,21061,21067,21075,21084,21091,21098,21105,21112,21118,21125,21131,21137,21144,21152],{"__ignoreMap":5245},[5249,20862,20863],{"class":5251,"line":5252},[5249,20864,20865],{"class":5255},"# .gitlab-ci.yml\n",[5249,20867,20868,20871],{"class":5251,"line":5259},[5249,20869,20870],{"class":6150},"stages",[5249,20872,6185],{"class":6154},[5249,20874,20875,20877],{"class":5251,"line":5273},[5249,20876,11922],{"class":6154},[5249,20878,16920],{"class":6158},[5249,20880,20881,20883],{"class":5251,"line":5288},[5249,20882,11922],{"class":6154},[5249,20884,9124],{"class":6158},[5249,20886,20887,20889],{"class":5251,"line":5302},[5249,20888,11922],{"class":6154},[5249,20890,12348],{"class":6158},[5249,20892,20893],{"class":5251,"line":5309},[5249,20894,5306],{"emptyLinePlaceholder":5305},[5249,20896,20897,20900],{"class":5251,"line":5315},[5249,20898,20899],{"class":6150},"variables",[5249,20901,6185],{"class":6154},[5249,20903,20904,20906,20908],{"class":5251,"line":5324},[5249,20905,16051],{"class":6150},[5249,20907,6155],{"class":6154},[5249,20909,20910],{"class":5266},"\"18\"\n",[5249,20912,20913],{"class":5251,"line":5329},[5249,20914,5306],{"emptyLinePlaceholder":5305},[5249,20916,20917,20919],{"class":5251,"line":5335},[5249,20918,7743],{"class":6150},[5249,20920,6185],{"class":6154},[5249,20922,20923,20926,20928],{"class":5251,"line":5344},[5249,20924,20925],{"class":6150},"  stage",[5249,20927,6155],{"class":6154},[5249,20929,16920],{"class":6158},[5249,20931,20932,20935,20937],{"class":5251,"line":5356},[5249,20933,20934],{"class":6150},"  image",[5249,20936,6155],{"class":6154},[5249,20938,20939],{"class":6158},"node:$NODE_VERSION\n",[5249,20941,20942,20945],{"class":5251,"line":5361},[5249,20943,20944],{"class":6150},"  script",[5249,20946,6185],{"class":6154},[5249,20948,20949,20951],{"class":5251,"line":5367},[5249,20950,6529],{"class":6154},[5249,20952,6350],{"class":6158},[5249,20954,20955,20957],{"class":5251,"line":5376},[5249,20956,6529],{"class":6154},[5249,20958,6912],{"class":6158},[5249,20960,20961,20963],{"class":5251,"line":5381},[5249,20962,6529],{"class":6154},[5249,20964,6374],{"class":6158},[5249,20966,20967,20970,20972],{"class":5251,"line":5387},[5249,20968,20969],{"class":6150},"  coverage",[5249,20971,6155],{"class":6154},[5249,20973,20974],{"class":6158},"'\u002FAll files[^|]*\\|[^|]*\\s+([\\d\\.]+)\u002F'\n",[5249,20976,20977],{"class":5251,"line":5398},[5249,20978,5306],{"emptyLinePlaceholder":5305},[5249,20980,20981,20984],{"class":5251,"line":5403},[5249,20982,20983],{"class":6150},"build",[5249,20985,6185],{"class":6154},[5249,20987,20988,20990,20992],{"class":5251,"line":5409},[5249,20989,20925],{"class":6150},[5249,20991,6155],{"class":6154},[5249,20993,9124],{"class":6158},[5249,20995,20996,20998,21000],{"class":5251,"line":5419},[5249,20997,20934],{"class":6150},[5249,20999,6155],{"class":6154},[5249,21001,21002],{"class":6158},"docker:latest\n",[5249,21004,21005,21008],{"class":5251,"line":5424},[5249,21006,21007],{"class":6150},"  services",[5249,21009,6185],{"class":6154},[5249,21011,21012,21014],{"class":5251,"line":5430},[5249,21013,6529],{"class":6154},[5249,21015,21016],{"class":6158},"docker:dind\n",[5249,21018,21019,21021],{"class":5251,"line":5442},[5249,21020,20944],{"class":6150},[5249,21022,6185],{"class":6154},[5249,21024,21025,21027],{"class":5251,"line":5447},[5249,21026,6529],{"class":6154},[5249,21028,21029],{"class":6158},"docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY\n",[5249,21031,21032,21034],{"class":5251,"line":5453},[5249,21033,6529],{"class":6154},[5249,21035,21036],{"class":6158},"docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .\n",[5249,21038,21039,21041],{"class":5251,"line":5698},[5249,21040,6529],{"class":6154},[5249,21042,21043],{"class":6158},"docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA\n",[5249,21045,21046,21049],{"class":5251,"line":5704},[5249,21047,21048],{"class":6150},"  only",[5249,21050,6185],{"class":6154},[5249,21052,21053,21055],{"class":5251,"line":5710},[5249,21054,6529],{"class":6154},[5249,21056,6416],{"class":6158},[5249,21058,21059],{"class":5251,"line":5716},[5249,21060,5306],{"emptyLinePlaceholder":5305},[5249,21062,21063,21065],{"class":5251,"line":5722},[5249,21064,9094],{"class":6150},[5249,21066,6185],{"class":6154},[5249,21068,21069,21071,21073],{"class":5251,"line":5728},[5249,21070,20925],{"class":6150},[5249,21072,6155],{"class":6154},[5249,21074,12348],{"class":6158},[5249,21076,21077,21079,21081],{"class":5251,"line":5734},[5249,21078,20934],{"class":6150},[5249,21080,6155],{"class":6154},[5249,21082,21083],{"class":6158},"alpine:latest\n",[5249,21085,21086,21089],{"class":5251,"line":5740},[5249,21087,21088],{"class":6150},"  before_script",[5249,21090,6185],{"class":6154},[5249,21092,21093,21095],{"class":5251,"line":5746},[5249,21094,6529],{"class":6154},[5249,21096,21097],{"class":6158},"apk add --no-cache openssh-client\n",[5249,21099,21100,21102],{"class":5251,"line":5752},[5249,21101,6529],{"class":6154},[5249,21103,21104],{"class":6158},"eval $(ssh-agent -s)\n",[5249,21106,21107,21109],{"class":5251,"line":5758},[5249,21108,6529],{"class":6154},[5249,21110,21111],{"class":6158},"echo \"$SSH_PRIVATE_KEY\" | ssh-add -\n",[5249,21113,21114,21116],{"class":5251,"line":5763},[5249,21115,20944],{"class":6150},[5249,21117,6185],{"class":6154},[5249,21119,21120,21122],{"class":5251,"line":5769},[5249,21121,6529],{"class":6154},[5249,21123,21124],{"class":6158},"ssh user@server \"cd \u002Fapp && docker-compose pull && docker-compose up -d\"\n",[5249,21126,21127,21129],{"class":5251,"line":5775},[5249,21128,21048],{"class":6150},[5249,21130,6185],{"class":6154},[5249,21132,21133,21135],{"class":5251,"line":5781},[5249,21134,6529],{"class":6154},[5249,21136,6416],{"class":6158},[5249,21138,21139,21142],{"class":5251,"line":5787},[5249,21140,21141],{"class":6150},"  environment",[5249,21143,6185],{"class":6154},[5249,21145,21146,21148,21150],{"class":5251,"line":5793},[5249,21147,6778],{"class":6150},[5249,21149,6155],{"class":6154},[5249,21151,11569],{"class":6158},[5249,21153,21154,21156,21158],{"class":5251,"line":5799},[5249,21155,13138],{"class":6150},[5249,21157,6155],{"class":6154},[5249,21159,11578],{"class":6158},[5155,21161,21162],{},[5118,21163,21164],{},"Bitbucket Pipelines:",[5240,21166,21168],{"className":6141,"code":21167,"language":6143,"meta":5245,"style":5245},"# bitbucket-pipelines.yml\nimage: node:18\n\npipelines:\n  default:\n    - step:\n        name: Test\n        caches:\n          - node\n        script:\n          - npm ci\n          - npm run lint\n          - npm test\n\n  branches:\n    main:\n      - step:\n          name: Build Docker Image\n          services:\n            - docker\n          script:\n            - docker build -t myapp:$BITBUCKET_COMMIT .\n            - docker push myapp:$BITBUCKET_COMMIT\n\n      - step:\n          name: Deploy to Production\n          deployment: production\n          script:\n            - pipe: atlassian\u002Fssh-run:0.2.2\n              variables:\n                SSH_USER: 'deploy'\n                SERVER: 'example.com'\n                COMMAND: 'cd \u002Fapp && docker-compose up -d'\n",[5173,21169,21170,21175,21185,21189,21196,21203,21212,21222,21229,21236,21242,21248,21254,21260,21264,21271,21278,21286,21294,21301,21309,21315,21322,21329,21333,21341,21349,21358,21364,21376,21383,21393,21403],{"__ignoreMap":5245},[5249,21171,21172],{"class":5251,"line":5252},[5249,21173,21174],{"class":5255},"# bitbucket-pipelines.yml\n",[5249,21176,21177,21180,21182],{"class":5251,"line":5259},[5249,21178,21179],{"class":6150},"image",[5249,21181,6155],{"class":6154},[5249,21183,21184],{"class":6158},"node:18\n",[5249,21186,21187],{"class":5251,"line":5273},[5249,21188,5306],{"emptyLinePlaceholder":5305},[5249,21190,21191,21194],{"class":5251,"line":5288},[5249,21192,21193],{"class":6150},"pipelines",[5249,21195,6185],{"class":6154},[5249,21197,21198,21201],{"class":5251,"line":5302},[5249,21199,21200],{"class":6150},"  default",[5249,21202,6185],{"class":6154},[5249,21204,21205,21207,21210],{"class":5251,"line":5309},[5249,21206,6529],{"class":6154},[5249,21208,21209],{"class":6150},"step",[5249,21211,6185],{"class":6154},[5249,21213,21214,21217,21219],{"class":5251,"line":5315},[5249,21215,21216],{"class":6150},"        name",[5249,21218,6155],{"class":6154},[5249,21220,21221],{"class":6158},"Test\n",[5249,21223,21224,21227],{"class":5251,"line":5324},[5249,21225,21226],{"class":6150},"        caches",[5249,21228,6185],{"class":6154},[5249,21230,21231,21233],{"class":5251,"line":5329},[5249,21232,7375],{"class":6154},[5249,21234,21235],{"class":6158},"node\n",[5249,21237,21238,21240],{"class":5251,"line":5335},[5249,21239,9210],{"class":6150},[5249,21241,6185],{"class":6154},[5249,21243,21244,21246],{"class":5251,"line":5344},[5249,21245,7375],{"class":6154},[5249,21247,6350],{"class":6158},[5249,21249,21250,21252],{"class":5251,"line":5356},[5249,21251,7375],{"class":6154},[5249,21253,6912],{"class":6158},[5249,21255,21256,21258],{"class":5251,"line":5361},[5249,21257,7375],{"class":6154},[5249,21259,6374],{"class":6158},[5249,21261,21262],{"class":5251,"line":5367},[5249,21263,5306],{"emptyLinePlaceholder":5305},[5249,21265,21266,21269],{"class":5251,"line":5376},[5249,21267,21268],{"class":6150},"  branches",[5249,21270,6185],{"class":6154},[5249,21272,21273,21276],{"class":5251,"line":5381},[5249,21274,21275],{"class":6150},"    main",[5249,21277,6185],{"class":6154},[5249,21279,21280,21282,21284],{"class":5251,"line":5387},[5249,21281,6265],{"class":6154},[5249,21283,21209],{"class":6150},[5249,21285,6185],{"class":6154},[5249,21287,21288,21290,21292],{"class":5251,"line":5398},[5249,21289,7203],{"class":6150},[5249,21291,6155],{"class":6154},[5249,21293,17294],{"class":6158},[5249,21295,21296,21299],{"class":5251,"line":5403},[5249,21297,21298],{"class":6150},"          services",[5249,21300,6185],{"class":6154},[5249,21302,21303,21306],{"class":5251,"line":5409},[5249,21304,21305],{"class":6154},"            - ",[5249,21307,21308],{"class":6158},"docker\n",[5249,21310,21311,21313],{"class":5251,"line":5419},[5249,21312,10038],{"class":6150},[5249,21314,6185],{"class":6154},[5249,21316,21317,21319],{"class":5251,"line":5424},[5249,21318,21305],{"class":6154},[5249,21320,21321],{"class":6158},"docker build -t myapp:$BITBUCKET_COMMIT .\n",[5249,21323,21324,21326],{"class":5251,"line":5430},[5249,21325,21305],{"class":6154},[5249,21327,21328],{"class":6158},"docker push myapp:$BITBUCKET_COMMIT\n",[5249,21330,21331],{"class":5251,"line":5442},[5249,21332,5306],{"emptyLinePlaceholder":5305},[5249,21334,21335,21337,21339],{"class":5251,"line":5447},[5249,21336,6265],{"class":6154},[5249,21338,21209],{"class":6150},[5249,21340,6185],{"class":6154},[5249,21342,21343,21345,21347],{"class":5251,"line":5453},[5249,21344,7203],{"class":6150},[5249,21346,6155],{"class":6154},[5249,21348,9106],{"class":6158},[5249,21350,21351,21354,21356],{"class":5251,"line":5698},[5249,21352,21353],{"class":6150},"          deployment",[5249,21355,6155],{"class":6154},[5249,21357,11569],{"class":6158},[5249,21359,21360,21362],{"class":5251,"line":5704},[5249,21361,10038],{"class":6150},[5249,21363,6185],{"class":6154},[5249,21365,21366,21368,21371,21373],{"class":5251,"line":5710},[5249,21367,21305],{"class":6154},[5249,21369,21370],{"class":6150},"pipe",[5249,21372,6155],{"class":6154},[5249,21374,21375],{"class":6158},"atlassian\u002Fssh-run:0.2.2\n",[5249,21377,21378,21381],{"class":5251,"line":5716},[5249,21379,21380],{"class":6150},"              variables",[5249,21382,6185],{"class":6154},[5249,21384,21385,21388,21390],{"class":5251,"line":5722},[5249,21386,21387],{"class":6150},"                SSH_USER",[5249,21389,6155],{"class":6154},[5249,21391,21392],{"class":6158},"'deploy'\n",[5249,21394,21395,21398,21400],{"class":5251,"line":5728},[5249,21396,21397],{"class":6150},"                SERVER",[5249,21399,6155],{"class":6154},[5249,21401,21402],{"class":6158},"'example.com'\n",[5249,21404,21405,21408,21410],{"class":5251,"line":5734},[5249,21406,21407],{"class":6150},"                COMMAND",[5249,21409,6155],{"class":6154},[5249,21411,21412],{"class":6158},"'cd \u002Fapp && docker-compose up -d'\n",[5155,21414,21415],{},[5118,21416,21417],{},"Додаткові ресурси:",[5092,21419,21420,21427,21434,21441],{},[5095,21421,21422],{},[8657,21423,21426],{"href":21424,"rel":21425},"https:\u002F\u002Fdocs.github.com\u002Fen\u002Factions",[8661],"GitHub Actions Documentation",[5095,21428,21429],{},[8657,21430,21433],{"href":21431,"rel":21432},"https:\u002F\u002Fgithub.com\u002Fmarketplace?type=actions",[8661],"GitHub Actions Marketplace",[5095,21435,21436],{},[8657,21437,21440],{"href":21438,"rel":21439},"https:\u002F\u002Fgithub.com\u002Fsdras\u002Fawesome-actions",[8661],"Awesome GitHub Actions",[5095,21442,21443],{},[8657,21444,21447],{"href":21445,"rel":21446},"https:\u002F\u002Fwww.atlassian.com\u002Fcontinuous-delivery\u002Fprinciples\u002Fcontinuous-integration-vs-delivery-vs-deployment",[8661],"CI\u002FCD Best Practices Guide",[21449,21450,21451],"style",{},"html pre.shiki code .spJ8K, html code.shiki .spJ8K{--shiki-light:#008000;--shiki-default:#6A9955;--shiki-dark:#6A9955}html pre.shiki code .s8Opu, html code.shiki .s8Opu{--shiki-light:#795E26;--shiki-default:#DCDCAA;--shiki-dark:#DCDCAA}html pre.shiki code .sbdoH, html code.shiki .sbdoH{--shiki-light:#A31515;--shiki-default:#CE9178;--shiki-dark:#CE9178}html pre.shiki code .su1O8, html code.shiki .su1O8{--shiki-light:#0000FF;--shiki-default:#569CD6;--shiki-dark:#569CD6}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sKtos, html code.shiki .sKtos{--shiki-light:#800000;--shiki-default:#569CD6;--shiki-dark:#569CD6}html pre.shiki code .sHH4Y, html code.shiki .sHH4Y{--shiki-light:#000000;--shiki-default:#D4D4D4;--shiki-dark:#D4D4D4}html pre.shiki code .su9tN, html code.shiki .su9tN{--shiki-light:#0000FF;--shiki-default:#CE9178;--shiki-dark:#CE9178}html pre.shiki code .sJj4R, html code.shiki .sJj4R{--shiki-light:#098658;--shiki-default:#B5CEA8;--shiki-dark:#B5CEA8}html pre.shiki code .s8xlr, html code.shiki .s8xlr{--shiki-light:#AF00DB;--shiki-default:#C586C0;--shiki-dark:#C586C0}html pre.shiki code .s9ZQM, html code.shiki .s9ZQM{--shiki-light:#800000;--shiki-light-font-weight:bold;--shiki-default:#569CD6;--shiki-default-font-weight:bold;--shiki-dark:#569CD6;--shiki-dark-font-weight:bold}html pre.shiki code .sJtsr, html code.shiki .sJtsr{--shiki-light:#800000;--shiki-default:#CE9178;--shiki-dark:#CE9178}html pre.shiki code .sxDXj, html code.shiki .sxDXj{--shiki-light:#0451A5;--shiki-default:#6796E6;--shiki-dark:#6796E6}",{"title":5245,"searchDepth":5259,"depth":5259,"links":21453},[21454,21455,21456,21460,21465,21468,21472,21477,21482,21487,21492,21498,21502,21509,21519,21520,21521],{"id":5152,"depth":5259,"text":5153},{"id":5230,"depth":5259,"text":5231},{"id":5847,"depth":5259,"text":5164,"children":21457},[21458,21459],{"id":5851,"depth":5273,"text":5852},{"id":5947,"depth":5273,"text":5948},{"id":6048,"depth":5259,"text":6049,"children":21461},[21462,21463,21464],{"id":6057,"depth":5273,"text":6058},{"id":6099,"depth":5273,"text":6100},{"id":6377,"depth":5273,"text":6378},{"id":6686,"depth":5259,"text":6687,"children":21466},[21467],{"id":7731,"depth":5273,"text":7732},{"id":8206,"depth":5259,"text":8207,"children":21469},[21470,21471],{"id":8618,"depth":5273,"text":8619},{"id":8911,"depth":5273,"text":8912},{"id":9076,"depth":5259,"text":9077,"children":21473},[21474,21475,21476],{"id":9083,"depth":5273,"text":9084},{"id":9482,"depth":5273,"text":9483},{"id":9606,"depth":5273,"text":9607},{"id":9885,"depth":5259,"text":9886,"children":21478},[21479,21480,21481],{"id":9892,"depth":5273,"text":9893},{"id":10374,"depth":5273,"text":10375},{"id":10680,"depth":5273,"text":10681},{"id":11152,"depth":5259,"text":11153,"children":21483},[21484,21485,21486],{"id":11159,"depth":5273,"text":11160},{"id":11452,"depth":5273,"text":11453},{"id":11785,"depth":5273,"text":11786},{"id":11847,"depth":5259,"text":6095,"children":21488},[21489,21490,21491],{"id":11850,"depth":5273,"text":11851},{"id":11904,"depth":5273,"text":11905},{"id":12062,"depth":5273,"text":12063},{"id":12269,"depth":5259,"text":12270,"children":21493},[21494,21495,21496,21497],{"id":12276,"depth":5273,"text":12277},{"id":13026,"depth":5273,"text":13027},{"id":13146,"depth":5273,"text":13147},{"id":13305,"depth":5273,"text":13306},{"id":13506,"depth":5259,"text":13507,"children":21499},[21500,21501],{"id":13513,"depth":5273,"text":13514},{"id":13888,"depth":5273,"text":13889},{"id":14467,"depth":5259,"text":14468,"children":21503},[21504,21505,21506,21507,21508],{"id":14474,"depth":5273,"text":14475},{"id":14664,"depth":5273,"text":14665},{"id":14768,"depth":5273,"text":14769},{"id":14951,"depth":5273,"text":14952},{"id":15010,"depth":5273,"text":15011},{"id":15057,"depth":5259,"text":15058,"children":21510},[21511,21512,21513,21514,21515,21516,21517,21518],{"id":15061,"depth":5273,"text":15062},{"id":15238,"depth":5273,"text":15239},{"id":15293,"depth":5273,"text":15294},{"id":15338,"depth":5273,"text":15339},{"id":15379,"depth":5273,"text":15380},{"id":15462,"depth":5273,"text":15463},{"id":15499,"depth":5273,"text":15500},{"id":15763,"depth":5273,"text":15764},{"id":15971,"depth":5259,"text":15972},{"id":18843,"depth":5259,"text":18844},{"id":19087,"depth":5259,"text":19088},"md",null,{},{"title":4738,"description":5245},"ZWyVjQZkB8rj_0dcaQSt4JasC4lgd_rOJN85EYOsJtI",[21528,21529],{"title":4734,"path":4735,"stem":4736,"description":5245,"children":-1},{"title":4742,"path":4743,"stem":4744,"description":5245,"children":-1},1790322252891]