<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ua">
    <title>Куток Ігоря</title>
    <subtitle>Публічне сховище речей, які я захочу згадати в майбутньому і які ви 🧑‍🔧 теж можете знайти корисними.</subtitle>
    <link rel="self" type="application/atom+xml" href="https://shtein.me/ua/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://shtein.me/ua/"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2024-03-28T14:13:00-07:00</updated>
    <id>https://shtein.me/ua/atom.xml</id>
    <entry xml:lang="ua">
        <title>Зворотньо несуміснісні Django міграції</title>
        <published>2024-03-28T14:13:00-07:00</published>
        <updated>2024-03-28T14:13:00-07:00</updated>
        
        <author>
          <name>
            
              Igor Shtein
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://shtein.me/ua/posts/blue-green-deployment-django-orm/"/>
        <id>https://shtein.me/ua/posts/blue-green-deployment-django-orm/</id>
        
        <content type="html" xml:base="https://shtein.me/ua/posts/blue-green-deployment-django-orm/">&lt;h2 id=&quot;zvorotn-o-nesumisni-migratsiyi&quot;&gt;Зворотньо несумісні міграції&lt;&#x2F;h2&gt;
&lt;p&gt;Деякі міграції можуть бути зворотно несумісними та призвести до переривання роботи на деякий період часу між моментами коли застосовується міграція та моментом ротації докер подів.
Це також буде проблемою для синьо-зеленого розгортання, якщо ви вирішите його реалізувати.
Щоб уникнути такого простою, міграцію слід виконувати в 2 кроки.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;uvaga&quot;&gt;Увага&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Використовуйте цей метод лише для перелічених нижче операцій. Цей спосіб небезпечний. Він може диссинхронізувати стан програми з БД. Ніколи не використовуйте самостійно написаний SQL. Завжди копіюйте його з команди &lt;code&gt;sqlmigrate&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Цей тип міграції важко повернути зі зрозумілих причин, тому будьте дуже обережні, виконуючи це.&lt;&#x2F;li&gt;
&lt;li&gt;Виконання цієї операції без використання &lt;code&gt;SeparateDatabaseAndState&lt;&#x2F;code&gt; призведе до простою. Робочі версії відмовляться починати, якщо зміни внесено до моделі, але не застосовано до внутрішнього стану django.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;spisok-operatsii-robliachikh-migratsiyi-zvorotn-o-nesumisnimi&quot;&gt;Список операцій роблячих міграції зворотньо несумісними.&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;RemoveField&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;DeleteModel&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;AddField&lt;&#x2F;code&gt;, коли поле не обнуляється (в декларації поля немає &lt;code&gt;null=True&lt;&#x2F;code&gt;)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;rishennia&quot;&gt;Рішення&lt;&#x2F;h3&gt;
&lt;p&gt;Створіть 2 PR і випустіть їх у 2-ї окремих релізах:&lt;&#x2F;p&gt;
&lt;h4 id=&quot;pr-vidalennia-vikoristannia&quot;&gt;PR видалення використання&lt;&#x2F;h4&gt;
&lt;ol&gt;
&lt;li&gt;Видаліть усі використання поля з коду&lt;&#x2F;li&gt;
&lt;li&gt;Зніміть поле з моделі&lt;&#x2F;li&gt;
&lt;li&gt;Автоматично генеруйте міграцію (&lt;code&gt;.&#x2F;manage.py makemigrations your_app&lt;&#x2F;code&gt;)&lt;&#x2F;li&gt;
&lt;li&gt;Отримайте SQL міграції (він нам знадобиться пізніше) (&lt;code&gt;.&#x2F;manage.py sqlmigrate your_app 0002_auto_20240328_1527&lt;&#x2F;code&gt;)&lt;&#x2F;li&gt;
&lt;li&gt;Перемістіть зворотньо несумісні операції до &lt;code&gt;state_operations&lt;&#x2F;code&gt; в &lt;code&gt;SeparateDatabaseAndState&lt;&#x2F;code&gt; (приклад нижче).&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h4 id=&quot;pr-zmin-v-bd&quot;&gt;PR змін в БД&lt;&#x2F;h4&gt;
&lt;ol&gt;
&lt;li&gt;Створіть порожню міграцію (&lt;code&gt;.&#x2F;manage.py makemigrations your_app --empty&lt;&#x2F;code&gt;)&lt;&#x2F;li&gt;
&lt;li&gt;Знову додайте &lt;code&gt;SeparateDatabaseAndState&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Помістіть SQL, отриманого з кроку 4 попереднього PR-у, в &lt;code&gt;database_operations&lt;&#x2F;code&gt;. Без коментарів та транзацій.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h4 id=&quot;priklad&quot;&gt;Приклад&lt;&#x2F;h4&gt;
&lt;h5 id=&quot;avtomatichno-zgenerovana-migratsiia&quot;&gt;Автоматично згенерована міграція:&lt;&#x2F;h5&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#000000, #D4D4D4); background-color: light-dark(#FFFFFF, #1E1E1E);&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#AF00DB, #C586C0);&quot;&gt;from&lt;&#x2F;span&gt;&lt;span&gt; django&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;db&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#AF00DB, #C586C0);&quot;&gt; import&lt;&#x2F;span&gt;&lt;span&gt; migrations&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#0000FF, #569CD6);&quot;&gt;class&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#267F99, #4EC9B0);&quot;&gt; Migration&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#267F99, #4EC9B0);&quot;&gt;migrations&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#267F99, #4EC9B0);&quot;&gt;Migration&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    dependencies&lt;&#x2F;span&gt;&lt;span&gt; =&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        (&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#A31515, #CE9178);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#A31515, #CE9178);&quot;&gt;team_management&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#A31515, #CE9178);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#A31515, #CE9178);&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#A31515, #CE9178);&quot;&gt;0001_auto_20240319_1146&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#A31515, #CE9178);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    ]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    operations&lt;&#x2F;span&gt;&lt;span&gt; =&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        migrations&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;RemoveField&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#001080, #9CDCFE);&quot;&gt;            model_name&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#A31515, #CE9178);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#A31515, #CE9178);&quot;&gt;organization&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#A31515, #CE9178);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#001080, #9CDCFE);&quot;&gt;            name&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#A31515, #CE9178);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#A31515, #CE9178);&quot;&gt;session_expiration_time&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#A31515, #CE9178);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        )&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    ]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h5 id=&quot;pr-1&quot;&gt;PR 1&lt;&#x2F;h5&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#000000, #D4D4D4); background-color: light-dark(#FFFFFF, #1E1E1E);&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#AF00DB, #C586C0);&quot;&gt;from&lt;&#x2F;span&gt;&lt;span&gt; django&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;db&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#AF00DB, #C586C0);&quot;&gt; import&lt;&#x2F;span&gt;&lt;span&gt; migrations&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#0000FF, #569CD6);&quot;&gt;class&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#267F99, #4EC9B0);&quot;&gt; Migration&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#267F99, #4EC9B0);&quot;&gt;migrations&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#267F99, #4EC9B0);&quot;&gt;Migration&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    dependencies&lt;&#x2F;span&gt;&lt;span&gt; =&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        (&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#A31515, #CE9178);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#A31515, #CE9178);&quot;&gt;team_management&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#A31515, #CE9178);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#A31515, #CE9178);&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#A31515, #CE9178);&quot;&gt;00001_auto_20240319_1146&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#A31515, #CE9178);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    ]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    operations&lt;&#x2F;span&gt;&lt;span&gt; =&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        migrations&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;SeparateDatabaseAndState&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#001080, #9CDCFE);&quot;&gt;            state_operations&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                migrations&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;RemoveField&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#001080, #9CDCFE);&quot;&gt;                    model_name&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#A31515, #CE9178);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#A31515, #CE9178);&quot;&gt;organization&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#A31515, #CE9178);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#001080, #9CDCFE);&quot;&gt;                    name&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#A31515, #CE9178);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#A31515, #CE9178);&quot;&gt;session_expiration_time&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#A31515, #CE9178);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                )&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            ]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        )&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    ]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h5 id=&quot;pr-2&quot;&gt;PR 2&lt;&#x2F;h5&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#000000, #D4D4D4); background-color: light-dark(#FFFFFF, #1E1E1E);&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#AF00DB, #C586C0);&quot;&gt;from&lt;&#x2F;span&gt;&lt;span&gt; django&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;db&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#AF00DB, #C586C0);&quot;&gt; import&lt;&#x2F;span&gt;&lt;span&gt; migrations&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#0000FF, #569CD6);&quot;&gt;class&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#267F99, #4EC9B0);&quot;&gt; Migration&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#267F99, #4EC9B0);&quot;&gt;migrations&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#267F99, #4EC9B0);&quot;&gt;Migration&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    dependencies&lt;&#x2F;span&gt;&lt;span&gt; =&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        (&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#A31515, #CE9178);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#A31515, #CE9178);&quot;&gt;team_management&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#A31515, #CE9178);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#A31515, #CE9178);&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#A31515, #CE9178);&quot;&gt;0002_auto_20240328_1527&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#A31515, #CE9178);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    ]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    operations&lt;&#x2F;span&gt;&lt;span&gt; =&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        migrations&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;SeparateDatabaseAndState&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#001080, #9CDCFE);&quot;&gt;            database_operations&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                migrations&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;RunSQL&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#001080, #9CDCFE);&quot;&gt;                    sql&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#A31515, #CE9178);&quot;&gt;                        &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#A31515, #CE9178);&quot;&gt;ALTER TABLE &amp;quot;team_management_organization&amp;quot; DROP COLUMN &amp;quot;session_expiration_time&amp;quot; CASCADE;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#A31515, #CE9178);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                    )&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                )&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            ]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        )&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    ]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;</content>
        
    </entry>
    <entry xml:lang="ua">
        <title>Додаємо тести Флаттер застосунку в Гітлаб пайплайн</title>
        <published>2024-02-25T13:23:17+10:00</published>
        <updated>2024-02-25T13:23:17+10:00</updated>
        
        <author>
          <name>
            
              Igor Shtein
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://shtein.me/ua/posts/flutter-gitlab-tests/"/>
        <id>https://shtein.me/ua/posts/flutter-gitlab-tests/</id>
        
        <content type="html" xml:base="https://shtein.me/ua/posts/flutter-gitlab-tests/">&lt;p&gt;Сподіваюсь цей сніпет збереже Вам і мені майбутьному 5 хвилин при наступному сетапі флатер додатку.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;zd-nch-thinking&quot;&gt;зд;нч?🤔&lt;&#x2F;h2&gt;
&lt;p&gt;В &lt;code&gt;.gitlab-ci.yml&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#000000, #D4D4D4); background-color: light-dark(#FFFFFF, #1E1E1E);&quot;&gt;&lt;code data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#800000, #569CD6);&quot;&gt;i&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#800000, #569CD6);&quot;&gt;mage&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#0000FF, #CE9178);&quot;&gt; i&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#0000FF, #CE9178);&quot;&gt;nstrumentisto&#x2F;flutter:latest&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#800000, #569CD6);&quot;&gt;s&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#800000, #569CD6);&quot;&gt;tages&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#0000FF, #CE9178);&quot;&gt; t&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#0000FF, #CE9178);&quot;&gt;est&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#800000, #569CD6);&quot;&gt;t&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#800000, #569CD6);&quot;&gt;est&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#800000, #569CD6);&quot;&gt;  s&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#800000, #569CD6);&quot;&gt;tage&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#0000FF, #CE9178);&quot;&gt; t&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#0000FF, #CE9178);&quot;&gt;est&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#800000, #569CD6);&quot;&gt;  b&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#800000, #569CD6);&quot;&gt;efore_script&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#0000FF, #CE9178);&quot;&gt; e&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#0000FF, #CE9178);&quot;&gt;xport PATH=&amp;quot;$PATH&amp;quot;:&amp;quot;$HOME&#x2F;.pub-cache&#x2F;bin&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#0000FF, #CE9178);&quot;&gt; f&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#0000FF, #CE9178);&quot;&gt;lutter pub get&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#0000FF, #CE9178);&quot;&gt; f&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#0000FF, #CE9178);&quot;&gt;lutter clean&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#0000FF, #CE9178);&quot;&gt; f&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#0000FF, #CE9178);&quot;&gt;lutter pub global activate junitreport&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#0000FF, #CE9178);&quot;&gt; f&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#0000FF, #CE9178);&quot;&gt;lutter pub global activate cobertura&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#800000, #569CD6);&quot;&gt;  s&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#800000, #569CD6);&quot;&gt;cript&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#0000FF, #CE9178);&quot;&gt; f&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#0000FF, #CE9178);&quot;&gt;lutter test --coverage --machine | tojunit -o report.xml&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#0000FF, #CE9178);&quot;&gt; g&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#0000FF, #CE9178);&quot;&gt;enhtml coverage&#x2F;lcov.info --output=coverage&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#0000FF, #CE9178);&quot;&gt; c&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#0000FF, #CE9178);&quot;&gt;obertura convert&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#800000, #569CD6);&quot;&gt;  c&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#800000, #569CD6);&quot;&gt;overage&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#0000FF, #CE9178);&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#0000FF, #CE9178);&quot;&gt;&#x2F;^\s*lines\.+: (\d+\.\d+)% .*&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#0000FF, #CE9178);&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#800000, #569CD6);&quot;&gt;  a&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#800000, #569CD6);&quot;&gt;rtifacts&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#800000, #569CD6);&quot;&gt;    w&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#800000, #569CD6);&quot;&gt;hen&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#0000FF, #CE9178);&quot;&gt; a&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#0000FF, #CE9178);&quot;&gt;lways&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#800000, #569CD6);&quot;&gt;    p&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#800000, #569CD6);&quot;&gt;aths&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#0000FF, #CE9178);&quot;&gt; r&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#0000FF, #CE9178);&quot;&gt;eport.xml&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#0000FF, #CE9178);&quot;&gt; c&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#0000FF, #CE9178);&quot;&gt;overage&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#800000, #569CD6);&quot;&gt;    r&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#800000, #569CD6);&quot;&gt;eports&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#800000, #569CD6);&quot;&gt;      j&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#800000, #569CD6);&quot;&gt;unit&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#0000FF, #CE9178);&quot;&gt; r&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#0000FF, #CE9178);&quot;&gt;eport.xml&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#800000, #569CD6);&quot;&gt;      c&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#800000, #569CD6);&quot;&gt;overage_report&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#800000, #569CD6);&quot;&gt;        c&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#800000, #569CD6);&quot;&gt;overage_format&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#0000FF, #CE9178);&quot;&gt; c&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#0000FF, #CE9178);&quot;&gt;oberturaz&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#800000, #569CD6);&quot;&gt;        p&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#800000, #569CD6);&quot;&gt;ath&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#0000FF, #CE9178);&quot;&gt; c&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#0000FF, #CE9178);&quot;&gt;overage&#x2F;cobertura.xml&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#800000, #569CD6);&quot;&gt;    e&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#800000, #569CD6);&quot;&gt;xpire_in&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#0000FF, #CE9178);&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#0000FF, #CE9178);&quot;&gt; week&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;troshki-poiasnen&quot;&gt;Трошки пояснень&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;code&gt;- export PATH=&quot;$PATH&quot;:&quot;$HOME&#x2F;.pub-cache&#x2F;bin&quot;&lt;&#x2F;code&gt; - Потрібно аби дати змогу скрипту викликати додатки які ми встановлюємо глобально нижче.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;- flutter pub get&lt;&#x2F;code&gt; - Скоріш за все тобі потрібні твої пакети аби запустити тести.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;- flutter pub global activate junitreport&lt;&#x2F;code&gt; - &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;pub.dev&#x2F;packages&#x2F;junitreport&quot;&gt;Бібліотека&lt;&#x2F;a&gt; що зконвертує результати дартовських тестів в junit формат зрозумілий Гітлабу.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;- flutter pub global activate cobertura&lt;&#x2F;code&gt; - &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;pub.dev&#x2F;packages&#x2F;cobertura&quot;&gt;Бібліотека&lt;&#x2F;a&gt; що зконвертує звіт покриття формату &lt;code&gt;lcov&lt;&#x2F;code&gt;, який використовується Дартом, в &lt;code&gt;cobertura.xml&lt;&#x2F;code&gt; зрозумілий Гітлабу.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;| tojunit -o report.xml&lt;&#x2F;code&gt; - власне сама конвертація тестів. Звіт, за замовчанням, буде в &lt;code&gt;%project_root%&#x2F;report.xml&lt;&#x2F;code&gt;, ви, звичайно ж, можете змінити це, але не забудьте тоді підправити і &lt;code&gt;artifacts&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;- genhtml coverage&#x2F;lcov.info --output=coverage&lt;&#x2F;code&gt; - Конвертація Дартовського звіту покриття в xml звіт. За замовчанням Дарт генерує звіт в &lt;code&gt;coverage&#x2F;lcov.info&lt;&#x2F;code&gt;. Команда &lt;code&gt;genhtml&lt;&#x2F;code&gt; ж зконвертує &lt;code&gt;lcov.info&lt;&#x2F;code&gt; в xml зрозумілий cobertura`і. Для цього вам буде потрібен застосунок &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;packages.debian.org&#x2F;search?keywords=lcov&quot;&gt;lcov&lt;&#x2F;a&gt;. Я використовую докер імідж &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;hub.docker.com&#x2F;r&#x2F;instrumentisto&#x2F;flutter&quot;&gt;instrumentisto&#x2F;flutter&lt;&#x2F;a&gt; який вже містить цей застосунок.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;cobertura convert&lt;&#x2F;code&gt; - Генерає кінцевий результат. Cobertura згенерує файл під назвою &lt;code&gt;cobertura.xml&lt;&#x2F;code&gt; в теці вказаній в команді, в нашому випадку це &lt;code&gt;%project_root&#x2F;coverage&#x2F;&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;coverage: &#x27;&#x2F;^\s*lines\.+: (\d+\.\d+)% .*&#x2F;&#x27;&lt;&#x2F;code&gt; - Регулярка яка парсить лог и відтягує звідти загальний відсоток покриття проекту. Ця цифра буде вказана в результатах пайплайну і в графіку покриття на сторінці аналітики репозиторію. (Див. зображення нижче, але не саму цифру. Прошу 🫠)
&lt;img src=&quot;https:&#x2F;&#x2F;shtein.me&#x2F;ua&#x2F;posts&#x2F;flutter-gitlab-tests&#x2F;img&#x2F;coverage_in_job.png&quot; alt=&quot;Відсоток покриття в джобі&quot; &#x2F;&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;shtein.me&#x2F;ua&#x2F;posts&#x2F;flutter-gitlab-tests&#x2F;img&#x2F;coverage_in_repo_stats.png&quot; alt=&quot;Відсоток покриття в аналітиці репозиторію&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;В &lt;code&gt;artifacts&lt;&#x2F;code&gt; ми пояснюємо гітлабу де знайти попередньо згенеровані звіти та що кожен з них містить. Зміни імена файлів тут якщо змінював їх в самих командах.&lt;&#x2F;p&gt;
&lt;p&gt;В результаті ви отримуєте результати кожного тесту окремо в пайплайні, з розбивкою по часу який кожен тест виконується. Ну і покриття буде підсвічуватись в РМ діфі і в пайплайні також.&lt;&#x2F;p&gt;
</content>
        
    </entry>
</feed>
