[{"data":1,"prerenderedAt":5295},["ShallowReactive",2],{"blog-current-shopware-plugin-gitlab-pipeline-test-en":3,"blog-previous-shopware-plugin-gitlab-pipeline-test-en":2444,"blog-next-shopware-plugin-gitlab-pipeline-test-en":2455,"blog-alt-de-shopware-plugin-gitlab-pipeline-test-en":2466,"blog-alt-en-shopware-plugin-gitlab-pipeline-test-en":2468,"employee-robert-juzak":2469,"content-query-TPh1lFGN9c":2567,"content-query-GCsGFnjSrZ":4058,"content-query-KtO3wftRle":5210,"related-refs-shopware_devops--en":5245},{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":8,"description":9,"author":10,"image":11,"releaseDate":12,"blogCategories":13,"articleTags":16,"tags":19,"body":22,"_type":2438,"_id":2439,"_source":2440,"_file":2441,"_stem":2442,"_extension":2443},"/en/blog/shopware-plugin-gitlab-pipeline-test","blog",false,"","Test, build and release a Shopware 6 Plugin with GitLab CI - Part 3: test","Part 3 - Test - of using GitLab for testing, building and releasing a Shopware 6 Plugin","robert-juzak","/images/dev-ops-dark.svg","2026-02-26",[14,15],"What moves us","DevOps",[15,17,18],"Open Source","Shopware",[20,21],"shopware","devops",{"type":23,"children":24,"toc":2429},"root",[25,34,41,95,102,116,129,182,195,203,214,221,226,534,539,544,549,554,565,570,595,601,613,1437,1442,1470,1475,1481,1486,2423],{"type":26,"tag":27,"props":28,"children":33},"element","img",{"alt":7,"aspect-ratio":29,"height":30,"object-fit":31,"src":32},"1.78",300,"fill","/blog/shopware-plugin-test.png",[],{"type":26,"tag":35,"props":36,"children":37},"p",{},[38],{"type":39,"value":40},"text","When it comes to testing a Shopware 6 plugin, there are two types of test that can be performed:",{"type":26,"tag":42,"props":43,"children":44},"ol",{},[45,83],{"type":26,"tag":46,"props":47,"children":48},"li",{},[49,51,58,60],{"type":39,"value":50},"Testing the code itself (",{"type":26,"tag":52,"props":53,"children":55},"a",{"href":54},"https://developer.shopware.com/docs/guides/plugins/plugins/testing/",[56],{"type":39,"value":57},"more in the official documentation",{"type":39,"value":59},")\n",{"type":26,"tag":42,"props":61,"children":62},{},[63,68,73,78],{"type":26,"tag":46,"props":64,"children":65},{},[66],{"type":39,"value":67},"PHP unit test",{"type":26,"tag":46,"props":69,"children":70},{},[71],{"type":39,"value":72},"Jest unit tests in Shopware's storefront",{"type":26,"tag":46,"props":74,"children":75},{},[76],{"type":39,"value":77},"Jest unit tests in Shopware's administration",{"type":26,"tag":46,"props":79,"children":80},{},[81],{"type":39,"value":82},"End-to-End (E2E) Testing",{"type":26,"tag":46,"props":84,"children":85},{},[86,88,93],{"type":39,"value":87},"Ensuring high code quality (",{"type":26,"tag":52,"props":89,"children":91},{"href":90},"https://developer.shopware.com/docs/products/cli/validation.html",[92],{"type":39,"value":57},{"type":39,"value":94},")",{"type":26,"tag":96,"props":97,"children":99},"h2",{"id":98},"code-quality",[100],{"type":39,"value":101},"Code quality",{"type":26,"tag":35,"props":103,"children":104},{},[105,107,114],{"type":39,"value":106},"Let's start with code quality because it's easier to run it outside a ",{"type":26,"tag":108,"props":109,"children":111},"code",{"className":110},[],[112],{"type":39,"value":113},"CI/CD",{"type":39,"value":115}," environment.",{"type":26,"tag":35,"props":117,"children":118},{},[119,121,127],{"type":39,"value":120},"We will again use the ",{"type":26,"tag":108,"props":122,"children":124},{"className":123},[],[125],{"type":39,"value":126},"shopware-cli",{"type":39,"value":128},".",{"type":26,"tag":130,"props":131,"children":135},"pre",{"code":132,"language":133,"meta":7,"className":134,"style":7},"shopware-cli extension validate --full --reporter summary .\n","shell","language-shell shiki shiki-themes github-dark github-dark monokai",[136],{"type":26,"tag":108,"props":137,"children":138},{"__ignoreMap":7},[139],{"type":26,"tag":140,"props":141,"children":144},"span",{"class":142,"line":143},"line",1,[145,150,156,161,167,172,177],{"type":26,"tag":140,"props":146,"children":148},{"style":147},"--shiki-default:#B392F0;--shiki-dark:#B392F0;--shiki-sepia:#A6E22E",[149],{"type":39,"value":126},{"type":26,"tag":140,"props":151,"children":153},{"style":152},"--shiki-default:#9ECBFF;--shiki-dark:#9ECBFF;--shiki-sepia:#E6DB74",[154],{"type":39,"value":155}," extension",{"type":26,"tag":140,"props":157,"children":158},{"style":152},[159],{"type":39,"value":160}," validate",{"type":26,"tag":140,"props":162,"children":164},{"style":163},"--shiki-default:#79B8FF;--shiki-dark:#79B8FF;--shiki-sepia:#AE81FF",[165],{"type":39,"value":166}," --full",{"type":26,"tag":140,"props":168,"children":169},{"style":163},[170],{"type":39,"value":171}," --reporter",{"type":26,"tag":140,"props":173,"children":174},{"style":152},[175],{"type":39,"value":176}," summary",{"type":26,"tag":140,"props":178,"children":179},{"style":152},[180],{"type":39,"value":181}," .\n",{"type":26,"tag":35,"props":183,"children":184},{},[185,187,193],{"type":39,"value":186},"This will run all the tests described ",{"type":26,"tag":52,"props":188,"children":190},{"href":189},"https://developer.shopware.com/docs/products/cli/validation.html#running-all-validation-tools",[191],{"type":39,"value":192},"here",{"type":39,"value":194},"\nand hopefully produce an output like this:",{"type":26,"tag":130,"props":196,"children":198},{"code":197},"✖ 0 problems (0 errors, 0 warnings)\n",[199],{"type":26,"tag":108,"props":200,"children":201},{"__ignoreMap":7},[202],{"type":39,"value":197},{"type":26,"tag":35,"props":204,"children":205},{},[206,208,213],{"type":39,"value":207},"If there are any errors, refer to the Shopware documentation how to fix them and rerun only the failed tests like described ",{"type":26,"tag":52,"props":209,"children":211},{"href":210},"https://developer.shopware.com/docs/products/cli/validation.html#running-specific-tools",[212],{"type":39,"value":192},{"type":39,"value":128},{"type":26,"tag":215,"props":216,"children":218},"h3",{"id":217},"code-quality-pipeline",[219],{"type":39,"value":220},"Code quality pipeline",{"type":26,"tag":35,"props":222,"children":223},{},[224],{"type":39,"value":225},"Now we will run it in GitLab.",{"type":26,"tag":130,"props":227,"children":239},{"code":228,"filename":229,"highlights":230,"language":237,"meta":7,"className":238,"style":7},"stages:\n  - test\n\ncode-quality:\n   image:\n      name: ghcr.io/shopware/shopware-cli:latest-php-8.2\n      entrypoint: [\"\"]\n   stage: test\n   script:\n      - shopware-cli extension validate --full . | tee report.json\n   artifacts:\n      reports:\n         codequality: report.json\n   rules:\n      - if: $CI_PIPELINE_SOURCE == \"merge_request_event\"\n      - if: $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH\n        when: never\n      - if: $CI_COMMIT_BRANCH\n","\u003Cplugin-root>/.gitlab-ci.yml",[231,232,233,234,235,236],6,14,15,16,17,18,"yaml","language-yaml shiki shiki-themes github-dark github-dark monokai",[240],{"type":26,"tag":108,"props":241,"children":242},{"__ignoreMap":7},[243,258,272,282,294,307,327,351,368,381,395,408,421,439,452,474,495,513],{"type":26,"tag":140,"props":244,"children":245},{"class":142,"line":143},[246,252],{"type":26,"tag":140,"props":247,"children":249},{"style":248},"--shiki-default:#85E89D;--shiki-dark:#85E89D;--shiki-sepia:#F92672",[250],{"type":39,"value":251},"stages",{"type":26,"tag":140,"props":253,"children":255},{"style":254},"--shiki-default:#E1E4E8;--shiki-dark:#E1E4E8;--shiki-sepia:#F8F8F2",[256],{"type":39,"value":257},":\n",{"type":26,"tag":140,"props":259,"children":261},{"class":142,"line":260},2,[262,267],{"type":26,"tag":140,"props":263,"children":264},{"style":254},[265],{"type":39,"value":266},"  - ",{"type":26,"tag":140,"props":268,"children":269},{"style":152},[270],{"type":39,"value":271},"test\n",{"type":26,"tag":140,"props":273,"children":275},{"class":142,"line":274},3,[276],{"type":26,"tag":140,"props":277,"children":279},{"emptyLinePlaceholder":278},true,[280],{"type":39,"value":281},"\n",{"type":26,"tag":140,"props":283,"children":285},{"class":142,"line":284},4,[286,290],{"type":26,"tag":140,"props":287,"children":288},{"style":248},[289],{"type":39,"value":98},{"type":26,"tag":140,"props":291,"children":292},{"style":254},[293],{"type":39,"value":257},{"type":26,"tag":140,"props":295,"children":297},{"class":142,"line":296},5,[298,303],{"type":26,"tag":140,"props":299,"children":300},{"style":248},[301],{"type":39,"value":302},"   image",{"type":26,"tag":140,"props":304,"children":305},{"style":254},[306],{"type":39,"value":257},{"type":26,"tag":140,"props":308,"children":311},{"class":309,"line":231},[142,310],"highlight",[312,317,322],{"type":26,"tag":140,"props":313,"children":314},{"style":248},[315],{"type":39,"value":316},"      name",{"type":26,"tag":140,"props":318,"children":319},{"style":254},[320],{"type":39,"value":321},": ",{"type":26,"tag":140,"props":323,"children":324},{"style":152},[325],{"type":39,"value":326},"ghcr.io/shopware/shopware-cli:latest-php-8.2\n",{"type":26,"tag":140,"props":328,"children":330},{"class":142,"line":329},7,[331,336,341,346],{"type":26,"tag":140,"props":332,"children":333},{"style":248},[334],{"type":39,"value":335},"      entrypoint",{"type":26,"tag":140,"props":337,"children":338},{"style":254},[339],{"type":39,"value":340},": [",{"type":26,"tag":140,"props":342,"children":343},{"style":152},[344],{"type":39,"value":345},"\"\"",{"type":26,"tag":140,"props":347,"children":348},{"style":254},[349],{"type":39,"value":350},"]\n",{"type":26,"tag":140,"props":352,"children":354},{"class":142,"line":353},8,[355,360,364],{"type":26,"tag":140,"props":356,"children":357},{"style":248},[358],{"type":39,"value":359},"   stage",{"type":26,"tag":140,"props":361,"children":362},{"style":254},[363],{"type":39,"value":321},{"type":26,"tag":140,"props":365,"children":366},{"style":152},[367],{"type":39,"value":271},{"type":26,"tag":140,"props":369,"children":371},{"class":142,"line":370},9,[372,377],{"type":26,"tag":140,"props":373,"children":374},{"style":248},[375],{"type":39,"value":376},"   script",{"type":26,"tag":140,"props":378,"children":379},{"style":254},[380],{"type":39,"value":257},{"type":26,"tag":140,"props":382,"children":384},{"class":142,"line":383},10,[385,390],{"type":26,"tag":140,"props":386,"children":387},{"style":254},[388],{"type":39,"value":389},"      - ",{"type":26,"tag":140,"props":391,"children":392},{"style":152},[393],{"type":39,"value":394},"shopware-cli extension validate --full . | tee report.json\n",{"type":26,"tag":140,"props":396,"children":398},{"class":142,"line":397},11,[399,404],{"type":26,"tag":140,"props":400,"children":401},{"style":248},[402],{"type":39,"value":403},"   artifacts",{"type":26,"tag":140,"props":405,"children":406},{"style":254},[407],{"type":39,"value":257},{"type":26,"tag":140,"props":409,"children":411},{"class":142,"line":410},12,[412,417],{"type":26,"tag":140,"props":413,"children":414},{"style":248},[415],{"type":39,"value":416},"      reports",{"type":26,"tag":140,"props":418,"children":419},{"style":254},[420],{"type":39,"value":257},{"type":26,"tag":140,"props":422,"children":424},{"class":142,"line":423},13,[425,430,434],{"type":26,"tag":140,"props":426,"children":427},{"style":248},[428],{"type":39,"value":429},"         codequality",{"type":26,"tag":140,"props":431,"children":432},{"style":254},[433],{"type":39,"value":321},{"type":26,"tag":140,"props":435,"children":436},{"style":152},[437],{"type":39,"value":438},"report.json\n",{"type":26,"tag":140,"props":440,"children":442},{"class":441,"line":232},[142,310],[443,448],{"type":26,"tag":140,"props":444,"children":445},{"style":248},[446],{"type":39,"value":447},"   rules",{"type":26,"tag":140,"props":449,"children":450},{"style":254},[451],{"type":39,"value":257},{"type":26,"tag":140,"props":453,"children":455},{"class":454,"line":233},[142,310],[456,460,465,469],{"type":26,"tag":140,"props":457,"children":458},{"style":254},[459],{"type":39,"value":389},{"type":26,"tag":140,"props":461,"children":462},{"style":248},[463],{"type":39,"value":464},"if",{"type":26,"tag":140,"props":466,"children":467},{"style":254},[468],{"type":39,"value":321},{"type":26,"tag":140,"props":470,"children":471},{"style":152},[472],{"type":39,"value":473},"$CI_PIPELINE_SOURCE == \"merge_request_event\"\n",{"type":26,"tag":140,"props":475,"children":477},{"class":476,"line":234},[142,310],[478,482,486,490],{"type":26,"tag":140,"props":479,"children":480},{"style":254},[481],{"type":39,"value":389},{"type":26,"tag":140,"props":483,"children":484},{"style":248},[485],{"type":39,"value":464},{"type":26,"tag":140,"props":487,"children":488},{"style":254},[489],{"type":39,"value":321},{"type":26,"tag":140,"props":491,"children":492},{"style":152},[493],{"type":39,"value":494},"$CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH\n",{"type":26,"tag":140,"props":496,"children":498},{"class":497,"line":235},[142,310],[499,504,508],{"type":26,"tag":140,"props":500,"children":501},{"style":248},[502],{"type":39,"value":503},"        when",{"type":26,"tag":140,"props":505,"children":506},{"style":254},[507],{"type":39,"value":321},{"type":26,"tag":140,"props":509,"children":510},{"style":152},[511],{"type":39,"value":512},"never\n",{"type":26,"tag":140,"props":514,"children":516},{"class":515,"line":236},[142,310],[517,521,525,529],{"type":26,"tag":140,"props":518,"children":519},{"style":254},[520],{"type":39,"value":389},{"type":26,"tag":140,"props":522,"children":523},{"style":248},[524],{"type":39,"value":464},{"type":26,"tag":140,"props":526,"children":527},{"style":254},[528],{"type":39,"value":321},{"type":26,"tag":140,"props":530,"children":531},{"style":152},[532],{"type":39,"value":533},"$CI_COMMIT_BRANCH\n",{"type":26,"tag":35,"props":535,"children":536},{},[537],{"type":39,"value":538},"This pipeline will run on the default branch and on merge request pipelines.",{"type":26,"tag":35,"props":540,"children":541},{},[542],{"type":39,"value":543},"Running on the default branch before build and release prevents from accidentally creating a low-quality release.",{"type":26,"tag":35,"props":545,"children":546},{},[547],{"type":39,"value":548},"A nice touch to the MR pipeline is the Code Quality report integration!",{"type":26,"tag":96,"props":550,"children":552},{"id":551},"php-unit-test",[553],{"type":39,"value":67},{"type":26,"tag":35,"props":555,"children":556},{},[557,559],{"type":39,"value":558},"First of all, we need to configure PHPUnit by following the ",{"type":26,"tag":52,"props":560,"children":562},{"href":561},"https://developer.shopware.com/docs/guides/plugins/plugins/testing/php-unit.html",[563],{"type":39,"value":564},"official shopware documentation",{"type":26,"tag":35,"props":566,"children":567},{},[568],{"type":39,"value":569},"We won't focus here on this process. When everything is setup, we should be able to run our test like this in out Shopware project root:",{"type":26,"tag":130,"props":571,"children":573},{"code":572,"language":133,"meta":7,"className":134,"style":7},"./vendor/bin/phpunit --configuration=\"custom/static-plugins/SwagBasicExample\"\n",[574],{"type":26,"tag":108,"props":575,"children":576},{"__ignoreMap":7},[577],{"type":26,"tag":140,"props":578,"children":579},{"class":142,"line":143},[580,585,590],{"type":26,"tag":140,"props":581,"children":582},{"style":147},[583],{"type":39,"value":584},"./vendor/bin/phpunit",{"type":26,"tag":140,"props":586,"children":587},{"style":163},[588],{"type":39,"value":589}," --configuration=",{"type":26,"tag":140,"props":591,"children":592},{"style":152},[593],{"type":39,"value":594},"\"custom/static-plugins/SwagBasicExample\"\n",{"type":26,"tag":215,"props":596,"children":598},{"id":597},"phpunit-pipeline",[599],{"type":39,"value":600},"PHPUnit pipeline",{"type":26,"tag":35,"props":602,"children":603},{},[604,606,611],{"type":39,"value":605},"Running PHPUnit for a plugin requires a full Shopware instance. Fortunately, ",{"type":26,"tag":108,"props":607,"children":609},{"className":608},[],[610],{"type":39,"value":126},{"type":39,"value":612}," can help us to deal with it.",{"type":26,"tag":130,"props":614,"children":632},{"code":615,"filename":229,"highlights":616,"language":237,"meta":7,"className":238,"style":7},"stages:\n   - test\n\nphpunit:\n  stage: test\n  image:\n    name: ghcr.io/shopware/shopware-cli:latest-php-8.2\n    entrypoint: [\"\"]\n  services:\n    - name: mysql:8.3.0\n      alias: test_database\n      variables:\n        MYSQL_SKIP_TEST_DB: 'yes'\n        MYSQL_ALLOW_EMPTY_PASSWORD: yes\n  variables:\n    GIT_STRATEGY: none\n    SHOPWARE_ROOT: ${CI_PROJECT_DIR}/shopware\n    SHOPWARE_VERSION: 6.6.10.13\n    \n    APP_SECRET: def00000bb5acb32b54ff8ee130270586eec0e878f7337dc7a837acc31d3ff00f93a56b595448b4b29664847dd51991b3314ff65aeeeb761a133b0ec0e070433bff08e48\n    MESSENGER_TRANSPORT_DSN: sync://\n    DATABASE_URL: mysql://root@test_database/shopware\n    COMPOSER_CACHE_DIR: ${CI_PROJECT_DIR}/.composer\n\n    XDEBUG_MODE: coverage\n  before_script:\n    - apk add --no-cache php-8.2-xdebug\n    - shopware-cli project create shopware ${SHOPWARE_VERSION}\n    - cd $SHOPWARE_ROOT\n    - composer req --dev shopware/dev-tools phpunit/phpunit\n    - git clone \"https://${GITLAB_USERNAME}:${GITLAB_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git\" \"custom/plugins/${CI_PROJECT_NAME}\"\n    - cd custom/plugins/${CI_PROJECT_NAME}\n    - git checkout ${CI_COMMIT_SHA}\n    - cd ${SHOPWARE_ROOT}\n    - composer require $(composer -d custom/plugins/${CI_PROJECT_NAME} config name)\n    - cd custom/plugins/${CI_PROJECT_NAME}\n  script:\n    - ${SHOPWARE_ROOT}/vendor/bin/phpunit --coverage-text --coverage-cobertura=coverage.cobertura.xml\n\n  cache:\n    - key: $CI_JOB_NAME\n      paths:\n        - $COMPOSER_CACHE_DIR\n  coverage: /^\\s*Lines:\\s*\\d+.\\d+\\%/\n  artifacts:\n    reports:\n      coverage_report:\n        coverage_format: cobertura\n        path: coverage.cobertura.xml\n  rules:\n    - if: $CI_PIPELINE_SOURCE == \"merge_request_event\"\n    - if: $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH\n      when: never\n    - if: $CI_COMMIT_BRANCH\n",[370,383,397,410,423,232,234,617,618,619,620,621,622,623,624,625,626,627,628,629,630,631],20,21,22,23,27,28,29,30,31,32,33,34,35,36,38,[633],{"type":26,"tag":108,"props":634,"children":635},{"__ignoreMap":7},[636,647,659,666,678,694,706,722,742,755,778,796,809,827,845,857,875,892,909,918,936,954,972,990,998,1016,1029,1042,1055,1068,1081,1094,1107,1120,1133,1146,1158,1171,1184,1192,1205,1227,1240,1254,1272,1285,1298,1311,1329,1347,1360,1380,1400,1417],{"type":26,"tag":140,"props":637,"children":638},{"class":142,"line":143},[639,643],{"type":26,"tag":140,"props":640,"children":641},{"style":248},[642],{"type":39,"value":251},{"type":26,"tag":140,"props":644,"children":645},{"style":254},[646],{"type":39,"value":257},{"type":26,"tag":140,"props":648,"children":649},{"class":142,"line":260},[650,655],{"type":26,"tag":140,"props":651,"children":652},{"style":254},[653],{"type":39,"value":654},"   - ",{"type":26,"tag":140,"props":656,"children":657},{"style":152},[658],{"type":39,"value":271},{"type":26,"tag":140,"props":660,"children":661},{"class":142,"line":274},[662],{"type":26,"tag":140,"props":663,"children":664},{"emptyLinePlaceholder":278},[665],{"type":39,"value":281},{"type":26,"tag":140,"props":667,"children":668},{"class":142,"line":284},[669,674],{"type":26,"tag":140,"props":670,"children":671},{"style":248},[672],{"type":39,"value":673},"phpunit",{"type":26,"tag":140,"props":675,"children":676},{"style":254},[677],{"type":39,"value":257},{"type":26,"tag":140,"props":679,"children":680},{"class":142,"line":296},[681,686,690],{"type":26,"tag":140,"props":682,"children":683},{"style":248},[684],{"type":39,"value":685},"  stage",{"type":26,"tag":140,"props":687,"children":688},{"style":254},[689],{"type":39,"value":321},{"type":26,"tag":140,"props":691,"children":692},{"style":152},[693],{"type":39,"value":271},{"type":26,"tag":140,"props":695,"children":696},{"class":142,"line":231},[697,702],{"type":26,"tag":140,"props":698,"children":699},{"style":248},[700],{"type":39,"value":701},"  image",{"type":26,"tag":140,"props":703,"children":704},{"style":254},[705],{"type":39,"value":257},{"type":26,"tag":140,"props":707,"children":708},{"class":142,"line":329},[709,714,718],{"type":26,"tag":140,"props":710,"children":711},{"style":248},[712],{"type":39,"value":713},"    name",{"type":26,"tag":140,"props":715,"children":716},{"style":254},[717],{"type":39,"value":321},{"type":26,"tag":140,"props":719,"children":720},{"style":152},[721],{"type":39,"value":326},{"type":26,"tag":140,"props":723,"children":724},{"class":142,"line":353},[725,730,734,738],{"type":26,"tag":140,"props":726,"children":727},{"style":248},[728],{"type":39,"value":729},"    entrypoint",{"type":26,"tag":140,"props":731,"children":732},{"style":254},[733],{"type":39,"value":340},{"type":26,"tag":140,"props":735,"children":736},{"style":152},[737],{"type":39,"value":345},{"type":26,"tag":140,"props":739,"children":740},{"style":254},[741],{"type":39,"value":350},{"type":26,"tag":140,"props":743,"children":745},{"class":744,"line":370},[142,310],[746,751],{"type":26,"tag":140,"props":747,"children":748},{"style":248},[749],{"type":39,"value":750},"  services",{"type":26,"tag":140,"props":752,"children":753},{"style":254},[754],{"type":39,"value":257},{"type":26,"tag":140,"props":756,"children":758},{"class":757,"line":383},[142,310],[759,764,769,773],{"type":26,"tag":140,"props":760,"children":761},{"style":254},[762],{"type":39,"value":763},"    - ",{"type":26,"tag":140,"props":765,"children":766},{"style":248},[767],{"type":39,"value":768},"name",{"type":26,"tag":140,"props":770,"children":771},{"style":254},[772],{"type":39,"value":321},{"type":26,"tag":140,"props":774,"children":775},{"style":152},[776],{"type":39,"value":777},"mysql:8.3.0\n",{"type":26,"tag":140,"props":779,"children":781},{"class":780,"line":397},[142,310],[782,787,791],{"type":26,"tag":140,"props":783,"children":784},{"style":248},[785],{"type":39,"value":786},"      alias",{"type":26,"tag":140,"props":788,"children":789},{"style":254},[790],{"type":39,"value":321},{"type":26,"tag":140,"props":792,"children":793},{"style":152},[794],{"type":39,"value":795},"test_database\n",{"type":26,"tag":140,"props":797,"children":799},{"class":798,"line":410},[142,310],[800,805],{"type":26,"tag":140,"props":801,"children":802},{"style":248},[803],{"type":39,"value":804},"      variables",{"type":26,"tag":140,"props":806,"children":807},{"style":254},[808],{"type":39,"value":257},{"type":26,"tag":140,"props":810,"children":812},{"class":811,"line":423},[142,310],[813,818,822],{"type":26,"tag":140,"props":814,"children":815},{"style":248},[816],{"type":39,"value":817},"        MYSQL_SKIP_TEST_DB",{"type":26,"tag":140,"props":819,"children":820},{"style":254},[821],{"type":39,"value":321},{"type":26,"tag":140,"props":823,"children":824},{"style":152},[825],{"type":39,"value":826},"'yes'\n",{"type":26,"tag":140,"props":828,"children":830},{"class":829,"line":232},[142,310],[831,836,840],{"type":26,"tag":140,"props":832,"children":833},{"style":248},[834],{"type":39,"value":835},"        MYSQL_ALLOW_EMPTY_PASSWORD",{"type":26,"tag":140,"props":837,"children":838},{"style":254},[839],{"type":39,"value":321},{"type":26,"tag":140,"props":841,"children":842},{"style":163},[843],{"type":39,"value":844},"yes\n",{"type":26,"tag":140,"props":846,"children":847},{"class":142,"line":233},[848,853],{"type":26,"tag":140,"props":849,"children":850},{"style":248},[851],{"type":39,"value":852},"  variables",{"type":26,"tag":140,"props":854,"children":855},{"style":254},[856],{"type":39,"value":257},{"type":26,"tag":140,"props":858,"children":860},{"class":859,"line":234},[142,310],[861,866,870],{"type":26,"tag":140,"props":862,"children":863},{"style":248},[864],{"type":39,"value":865},"    GIT_STRATEGY",{"type":26,"tag":140,"props":867,"children":868},{"style":254},[869],{"type":39,"value":321},{"type":26,"tag":140,"props":871,"children":872},{"style":152},[873],{"type":39,"value":874},"none\n",{"type":26,"tag":140,"props":876,"children":877},{"class":142,"line":235},[878,883,887],{"type":26,"tag":140,"props":879,"children":880},{"style":248},[881],{"type":39,"value":882},"    SHOPWARE_ROOT",{"type":26,"tag":140,"props":884,"children":885},{"style":254},[886],{"type":39,"value":321},{"type":26,"tag":140,"props":888,"children":889},{"style":152},[890],{"type":39,"value":891},"${CI_PROJECT_DIR}/shopware\n",{"type":26,"tag":140,"props":893,"children":894},{"class":142,"line":236},[895,900,904],{"type":26,"tag":140,"props":896,"children":897},{"style":248},[898],{"type":39,"value":899},"    SHOPWARE_VERSION",{"type":26,"tag":140,"props":901,"children":902},{"style":254},[903],{"type":39,"value":321},{"type":26,"tag":140,"props":905,"children":906},{"style":163},[907],{"type":39,"value":908},"6.6.10.13\n",{"type":26,"tag":140,"props":910,"children":912},{"class":142,"line":911},19,[913],{"type":26,"tag":140,"props":914,"children":915},{"style":254},[916],{"type":39,"value":917},"    \n",{"type":26,"tag":140,"props":919,"children":921},{"class":920,"line":617},[142,310],[922,927,931],{"type":26,"tag":140,"props":923,"children":924},{"style":248},[925],{"type":39,"value":926},"    APP_SECRET",{"type":26,"tag":140,"props":928,"children":929},{"style":254},[930],{"type":39,"value":321},{"type":26,"tag":140,"props":932,"children":933},{"style":152},[934],{"type":39,"value":935},"def00000bb5acb32b54ff8ee130270586eec0e878f7337dc7a837acc31d3ff00f93a56b595448b4b29664847dd51991b3314ff65aeeeb761a133b0ec0e070433bff08e48\n",{"type":26,"tag":140,"props":937,"children":939},{"class":938,"line":618},[142,310],[940,945,949],{"type":26,"tag":140,"props":941,"children":942},{"style":248},[943],{"type":39,"value":944},"    MESSENGER_TRANSPORT_DSN",{"type":26,"tag":140,"props":946,"children":947},{"style":254},[948],{"type":39,"value":321},{"type":26,"tag":140,"props":950,"children":951},{"style":152},[952],{"type":39,"value":953},"sync://\n",{"type":26,"tag":140,"props":955,"children":957},{"class":956,"line":619},[142,310],[958,963,967],{"type":26,"tag":140,"props":959,"children":960},{"style":248},[961],{"type":39,"value":962},"    DATABASE_URL",{"type":26,"tag":140,"props":964,"children":965},{"style":254},[966],{"type":39,"value":321},{"type":26,"tag":140,"props":968,"children":969},{"style":152},[970],{"type":39,"value":971},"mysql://root@test_database/shopware\n",{"type":26,"tag":140,"props":973,"children":975},{"class":974,"line":620},[142,310],[976,981,985],{"type":26,"tag":140,"props":977,"children":978},{"style":248},[979],{"type":39,"value":980},"    COMPOSER_CACHE_DIR",{"type":26,"tag":140,"props":982,"children":983},{"style":254},[984],{"type":39,"value":321},{"type":26,"tag":140,"props":986,"children":987},{"style":152},[988],{"type":39,"value":989},"${CI_PROJECT_DIR}/.composer\n",{"type":26,"tag":140,"props":991,"children":993},{"class":142,"line":992},24,[994],{"type":26,"tag":140,"props":995,"children":996},{"emptyLinePlaceholder":278},[997],{"type":39,"value":281},{"type":26,"tag":140,"props":999,"children":1001},{"class":142,"line":1000},25,[1002,1007,1011],{"type":26,"tag":140,"props":1003,"children":1004},{"style":248},[1005],{"type":39,"value":1006},"    XDEBUG_MODE",{"type":26,"tag":140,"props":1008,"children":1009},{"style":254},[1010],{"type":39,"value":321},{"type":26,"tag":140,"props":1012,"children":1013},{"style":152},[1014],{"type":39,"value":1015},"coverage\n",{"type":26,"tag":140,"props":1017,"children":1019},{"class":142,"line":1018},26,[1020,1025],{"type":26,"tag":140,"props":1021,"children":1022},{"style":248},[1023],{"type":39,"value":1024},"  before_script",{"type":26,"tag":140,"props":1026,"children":1027},{"style":254},[1028],{"type":39,"value":257},{"type":26,"tag":140,"props":1030,"children":1032},{"class":1031,"line":621},[142,310],[1033,1037],{"type":26,"tag":140,"props":1034,"children":1035},{"style":254},[1036],{"type":39,"value":763},{"type":26,"tag":140,"props":1038,"children":1039},{"style":152},[1040],{"type":39,"value":1041},"apk add --no-cache php-8.2-xdebug\n",{"type":26,"tag":140,"props":1043,"children":1045},{"class":1044,"line":622},[142,310],[1046,1050],{"type":26,"tag":140,"props":1047,"children":1048},{"style":254},[1049],{"type":39,"value":763},{"type":26,"tag":140,"props":1051,"children":1052},{"style":152},[1053],{"type":39,"value":1054},"shopware-cli project create shopware ${SHOPWARE_VERSION}\n",{"type":26,"tag":140,"props":1056,"children":1058},{"class":1057,"line":623},[142,310],[1059,1063],{"type":26,"tag":140,"props":1060,"children":1061},{"style":254},[1062],{"type":39,"value":763},{"type":26,"tag":140,"props":1064,"children":1065},{"style":152},[1066],{"type":39,"value":1067},"cd $SHOPWARE_ROOT\n",{"type":26,"tag":140,"props":1069,"children":1071},{"class":1070,"line":624},[142,310],[1072,1076],{"type":26,"tag":140,"props":1073,"children":1074},{"style":254},[1075],{"type":39,"value":763},{"type":26,"tag":140,"props":1077,"children":1078},{"style":152},[1079],{"type":39,"value":1080},"composer req --dev shopware/dev-tools phpunit/phpunit\n",{"type":26,"tag":140,"props":1082,"children":1084},{"class":1083,"line":625},[142,310],[1085,1089],{"type":26,"tag":140,"props":1086,"children":1087},{"style":254},[1088],{"type":39,"value":763},{"type":26,"tag":140,"props":1090,"children":1091},{"style":152},[1092],{"type":39,"value":1093},"git clone \"https://${GITLAB_USERNAME}:${GITLAB_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git\" \"custom/plugins/${CI_PROJECT_NAME}\"\n",{"type":26,"tag":140,"props":1095,"children":1097},{"class":1096,"line":626},[142,310],[1098,1102],{"type":26,"tag":140,"props":1099,"children":1100},{"style":254},[1101],{"type":39,"value":763},{"type":26,"tag":140,"props":1103,"children":1104},{"style":152},[1105],{"type":39,"value":1106},"cd custom/plugins/${CI_PROJECT_NAME}\n",{"type":26,"tag":140,"props":1108,"children":1110},{"class":1109,"line":627},[142,310],[1111,1115],{"type":26,"tag":140,"props":1112,"children":1113},{"style":254},[1114],{"type":39,"value":763},{"type":26,"tag":140,"props":1116,"children":1117},{"style":152},[1118],{"type":39,"value":1119},"git checkout ${CI_COMMIT_SHA}\n",{"type":26,"tag":140,"props":1121,"children":1123},{"class":1122,"line":628},[142,310],[1124,1128],{"type":26,"tag":140,"props":1125,"children":1126},{"style":254},[1127],{"type":39,"value":763},{"type":26,"tag":140,"props":1129,"children":1130},{"style":152},[1131],{"type":39,"value":1132},"cd ${SHOPWARE_ROOT}\n",{"type":26,"tag":140,"props":1134,"children":1136},{"class":1135,"line":629},[142,310],[1137,1141],{"type":26,"tag":140,"props":1138,"children":1139},{"style":254},[1140],{"type":39,"value":763},{"type":26,"tag":140,"props":1142,"children":1143},{"style":152},[1144],{"type":39,"value":1145},"composer require $(composer -d custom/plugins/${CI_PROJECT_NAME} config name)\n",{"type":26,"tag":140,"props":1147,"children":1149},{"class":1148,"line":630},[142,310],[1150,1154],{"type":26,"tag":140,"props":1151,"children":1152},{"style":254},[1153],{"type":39,"value":763},{"type":26,"tag":140,"props":1155,"children":1156},{"style":152},[1157],{"type":39,"value":1106},{"type":26,"tag":140,"props":1159,"children":1161},{"class":142,"line":1160},37,[1162,1167],{"type":26,"tag":140,"props":1163,"children":1164},{"style":248},[1165],{"type":39,"value":1166},"  script",{"type":26,"tag":140,"props":1168,"children":1169},{"style":254},[1170],{"type":39,"value":257},{"type":26,"tag":140,"props":1172,"children":1174},{"class":1173,"line":631},[142,310],[1175,1179],{"type":26,"tag":140,"props":1176,"children":1177},{"style":254},[1178],{"type":39,"value":763},{"type":26,"tag":140,"props":1180,"children":1181},{"style":152},[1182],{"type":39,"value":1183},"${SHOPWARE_ROOT}/vendor/bin/phpunit --coverage-text --coverage-cobertura=coverage.cobertura.xml\n",{"type":26,"tag":140,"props":1185,"children":1187},{"class":142,"line":1186},39,[1188],{"type":26,"tag":140,"props":1189,"children":1190},{"emptyLinePlaceholder":278},[1191],{"type":39,"value":281},{"type":26,"tag":140,"props":1193,"children":1195},{"class":142,"line":1194},40,[1196,1201],{"type":26,"tag":140,"props":1197,"children":1198},{"style":248},[1199],{"type":39,"value":1200},"  cache",{"type":26,"tag":140,"props":1202,"children":1203},{"style":254},[1204],{"type":39,"value":257},{"type":26,"tag":140,"props":1206,"children":1208},{"class":142,"line":1207},41,[1209,1213,1218,1222],{"type":26,"tag":140,"props":1210,"children":1211},{"style":254},[1212],{"type":39,"value":763},{"type":26,"tag":140,"props":1214,"children":1215},{"style":248},[1216],{"type":39,"value":1217},"key",{"type":26,"tag":140,"props":1219,"children":1220},{"style":254},[1221],{"type":39,"value":321},{"type":26,"tag":140,"props":1223,"children":1224},{"style":152},[1225],{"type":39,"value":1226},"$CI_JOB_NAME\n",{"type":26,"tag":140,"props":1228,"children":1230},{"class":142,"line":1229},42,[1231,1236],{"type":26,"tag":140,"props":1232,"children":1233},{"style":248},[1234],{"type":39,"value":1235},"      paths",{"type":26,"tag":140,"props":1237,"children":1238},{"style":254},[1239],{"type":39,"value":257},{"type":26,"tag":140,"props":1241,"children":1243},{"class":142,"line":1242},43,[1244,1249],{"type":26,"tag":140,"props":1245,"children":1246},{"style":254},[1247],{"type":39,"value":1248},"        - ",{"type":26,"tag":140,"props":1250,"children":1251},{"style":152},[1252],{"type":39,"value":1253},"$COMPOSER_CACHE_DIR\n",{"type":26,"tag":140,"props":1255,"children":1257},{"class":142,"line":1256},44,[1258,1263,1267],{"type":26,"tag":140,"props":1259,"children":1260},{"style":248},[1261],{"type":39,"value":1262},"  coverage",{"type":26,"tag":140,"props":1264,"children":1265},{"style":254},[1266],{"type":39,"value":321},{"type":26,"tag":140,"props":1268,"children":1269},{"style":152},[1270],{"type":39,"value":1271},"/^\\s*Lines:\\s*\\d+.\\d+\\%/\n",{"type":26,"tag":140,"props":1273,"children":1275},{"class":142,"line":1274},45,[1276,1281],{"type":26,"tag":140,"props":1277,"children":1278},{"style":248},[1279],{"type":39,"value":1280},"  artifacts",{"type":26,"tag":140,"props":1282,"children":1283},{"style":254},[1284],{"type":39,"value":257},{"type":26,"tag":140,"props":1286,"children":1288},{"class":142,"line":1287},46,[1289,1294],{"type":26,"tag":140,"props":1290,"children":1291},{"style":248},[1292],{"type":39,"value":1293},"    reports",{"type":26,"tag":140,"props":1295,"children":1296},{"style":254},[1297],{"type":39,"value":257},{"type":26,"tag":140,"props":1299,"children":1301},{"class":142,"line":1300},47,[1302,1307],{"type":26,"tag":140,"props":1303,"children":1304},{"style":248},[1305],{"type":39,"value":1306},"      coverage_report",{"type":26,"tag":140,"props":1308,"children":1309},{"style":254},[1310],{"type":39,"value":257},{"type":26,"tag":140,"props":1312,"children":1314},{"class":142,"line":1313},48,[1315,1320,1324],{"type":26,"tag":140,"props":1316,"children":1317},{"style":248},[1318],{"type":39,"value":1319},"        coverage_format",{"type":26,"tag":140,"props":1321,"children":1322},{"style":254},[1323],{"type":39,"value":321},{"type":26,"tag":140,"props":1325,"children":1326},{"style":152},[1327],{"type":39,"value":1328},"cobertura\n",{"type":26,"tag":140,"props":1330,"children":1332},{"class":142,"line":1331},49,[1333,1338,1342],{"type":26,"tag":140,"props":1334,"children":1335},{"style":248},[1336],{"type":39,"value":1337},"        path",{"type":26,"tag":140,"props":1339,"children":1340},{"style":254},[1341],{"type":39,"value":321},{"type":26,"tag":140,"props":1343,"children":1344},{"style":152},[1345],{"type":39,"value":1346},"coverage.cobertura.xml\n",{"type":26,"tag":140,"props":1348,"children":1350},{"class":142,"line":1349},50,[1351,1356],{"type":26,"tag":140,"props":1352,"children":1353},{"style":248},[1354],{"type":39,"value":1355},"  rules",{"type":26,"tag":140,"props":1357,"children":1358},{"style":254},[1359],{"type":39,"value":257},{"type":26,"tag":140,"props":1361,"children":1363},{"class":142,"line":1362},51,[1364,1368,1372,1376],{"type":26,"tag":140,"props":1365,"children":1366},{"style":254},[1367],{"type":39,"value":763},{"type":26,"tag":140,"props":1369,"children":1370},{"style":248},[1371],{"type":39,"value":464},{"type":26,"tag":140,"props":1373,"children":1374},{"style":254},[1375],{"type":39,"value":321},{"type":26,"tag":140,"props":1377,"children":1378},{"style":152},[1379],{"type":39,"value":473},{"type":26,"tag":140,"props":1381,"children":1383},{"class":142,"line":1382},52,[1384,1388,1392,1396],{"type":26,"tag":140,"props":1385,"children":1386},{"style":254},[1387],{"type":39,"value":763},{"type":26,"tag":140,"props":1389,"children":1390},{"style":248},[1391],{"type":39,"value":464},{"type":26,"tag":140,"props":1393,"children":1394},{"style":254},[1395],{"type":39,"value":321},{"type":26,"tag":140,"props":1397,"children":1398},{"style":152},[1399],{"type":39,"value":494},{"type":26,"tag":140,"props":1401,"children":1403},{"class":142,"line":1402},53,[1404,1409,1413],{"type":26,"tag":140,"props":1405,"children":1406},{"style":248},[1407],{"type":39,"value":1408},"      when",{"type":26,"tag":140,"props":1410,"children":1411},{"style":254},[1412],{"type":39,"value":321},{"type":26,"tag":140,"props":1414,"children":1415},{"style":152},[1416],{"type":39,"value":512},{"type":26,"tag":140,"props":1418,"children":1420},{"class":142,"line":1419},54,[1421,1425,1429,1433],{"type":26,"tag":140,"props":1422,"children":1423},{"style":254},[1424],{"type":39,"value":763},{"type":26,"tag":140,"props":1426,"children":1427},{"style":248},[1428],{"type":39,"value":464},{"type":26,"tag":140,"props":1430,"children":1431},{"style":254},[1432],{"type":39,"value":321},{"type":26,"tag":140,"props":1434,"children":1435},{"style":152},[1436],{"type":39,"value":533},{"type":26,"tag":35,"props":1438,"children":1439},{},[1440],{"type":39,"value":1441},"Let's explain this a little",{"type":26,"tag":42,"props":1443,"children":1444},{},[1445,1450,1455,1460,1465],{"type":26,"tag":46,"props":1446,"children":1447},{},[1448],{"type":39,"value":1449},"We disable automatic repository cloning (16)",{"type":26,"tag":46,"props":1451,"children":1452},{},[1453],{"type":39,"value":1454},"We include a database service (9-14)",{"type":26,"tag":46,"props":1456,"children":1457},{},[1458],{"type":39,"value":1459},"We set some required Shopware env variables (20-23)",{"type":26,"tag":46,"props":1461,"children":1462},{},[1463],{"type":39,"value":1464},"Create an empty shopware project with phpunit and xdebug (27-36)",{"type":26,"tag":46,"props":1466,"children":1467},{},[1468],{"type":39,"value":1469},"Run PHPUnit with code coverage generation as text and in cobertura format (38)",{"type":26,"tag":35,"props":1471,"children":1472},{},[1473],{"type":39,"value":1474},"Like the Code quality pipeline, this takes full advantage of GitLab coverage reporting",{"type":26,"tag":96,"props":1476,"children":1478},{"id":1477},"putting-it-all-together",[1479],{"type":39,"value":1480},"Putting it all together",{"type":26,"tag":35,"props":1482,"children":1483},{},[1484],{"type":39,"value":1485},"For this part, we don't include the build and release part.",{"type":26,"tag":130,"props":1487,"children":1489},{"code":1488,"filename":229,"language":237,"meta":7,"className":238,"style":7},"stages:\n   - test\n\nphpunit:\n  stage: test\n  image:\n    name: ghcr.io/shopware/shopware-cli:latest-php-8.2\n    entrypoint: [\"\"]\n  services:\n    - name: mysql:8.3.0\n      alias: test_database\n      variables:\n        MYSQL_SKIP_TEST_DB: 'yes'\n        MYSQL_ALLOW_EMPTY_PASSWORD: yes\n  variables:\n    GIT_STRATEGY: none\n    SHOPWARE_ROOT: ${CI_PROJECT_DIR}/shopware\n    SHOPWARE_VERSION: 6.6.10.13\n    \n    APP_SECRET: def00000bb5acb32b54ff8ee130270586eec0e878f7337dc7a837acc31d3ff00f93a56b595448b4b29664847dd51991b3314ff65aeeeb761a133b0ec0e070433bff08e48\n    MESSENGER_TRANSPORT_DSN: sync://\n    DATABASE_URL: mysql://root@test_database/shopware\n    COMPOSER_CACHE_DIR: ${CI_PROJECT_DIR}/.composer\n\n    XDEBUG_MODE: coverage\n  before_script:\n    - apk add --no-cache php-8.2-xdebug\n    - shopware-cli project create shopware ${SHOPWARE_VERSION}\n    - cd $SHOPWARE_ROOT\n    - composer req --dev shopware/dev-tools phpunit/phpunit\n    - git clone \"https://${GITLAB_USERNAME}:${GITLAB_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git\" \"custom/plugins/${CI_PROJECT_NAME}\"\n    - cd custom/plugins/${CI_PROJECT_NAME}\n    - git checkout ${CI_COMMIT_SHA}\n    - cd ${SHOPWARE_ROOT}\n    - composer require $(composer -d custom/plugins/${CI_PROJECT_NAME} config name)\n    - cd custom/plugins/${CI_PROJECT_NAME}\n  script:\n    - ${SHOPWARE_ROOT}/vendor/bin/phpunit --coverage-text --coverage-cobertura=coverage.cobertura.xml\n\n  cache:\n    - key: $CI_JOB_NAME\n      paths:\n        - $COMPOSER_CACHE_DIR\n  coverage: /^\\s*Lines:\\s*\\d+.\\d+\\%/\n  artifacts:\n    reports:\n      coverage_report:\n        coverage_format: cobertura\n        path: coverage.cobertura.xml\n  rules:\n    - if: $CI_PIPELINE_SOURCE == \"merge_request_event\"\n    - if: $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH\n      when: never\n    - if: $CI_COMMIT_BRANCH\n\ncode-quality:\n   image:\n      name: ghcr.io/shopware/shopware-cli:latest-php-8.2\n      entrypoint: [\"\"]\n   stage: test\n   script:\n      - shopware-cli extension validate --full . | tee report.json\n   artifacts:\n      reports:\n         codequality: report.json\n   rules:\n      - if: $CI_PIPELINE_SOURCE == \"merge_request_event\"\n      - if: $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH\n        when: never\n      - if: $CI_COMMIT_BRANCH\n",[1490],{"type":26,"tag":108,"props":1491,"children":1492},{"__ignoreMap":7},[1493,1504,1515,1522,1533,1548,1559,1574,1593,1604,1623,1638,1649,1664,1679,1690,1705,1720,1735,1742,1757,1772,1787,1802,1809,1824,1835,1846,1857,1868,1879,1890,1901,1912,1923,1934,1945,1956,1967,1974,1985,2004,2015,2026,2041,2052,2063,2074,2089,2104,2115,2134,2153,2168,2187,2195,2207,2219,2235,2255,2271,2283,2295,2307,2319,2335,2347,2367,2387,2403],{"type":26,"tag":140,"props":1494,"children":1495},{"class":142,"line":143},[1496,1500],{"type":26,"tag":140,"props":1497,"children":1498},{"style":248},[1499],{"type":39,"value":251},{"type":26,"tag":140,"props":1501,"children":1502},{"style":254},[1503],{"type":39,"value":257},{"type":26,"tag":140,"props":1505,"children":1506},{"class":142,"line":260},[1507,1511],{"type":26,"tag":140,"props":1508,"children":1509},{"style":254},[1510],{"type":39,"value":654},{"type":26,"tag":140,"props":1512,"children":1513},{"style":152},[1514],{"type":39,"value":271},{"type":26,"tag":140,"props":1516,"children":1517},{"class":142,"line":274},[1518],{"type":26,"tag":140,"props":1519,"children":1520},{"emptyLinePlaceholder":278},[1521],{"type":39,"value":281},{"type":26,"tag":140,"props":1523,"children":1524},{"class":142,"line":284},[1525,1529],{"type":26,"tag":140,"props":1526,"children":1527},{"style":248},[1528],{"type":39,"value":673},{"type":26,"tag":140,"props":1530,"children":1531},{"style":254},[1532],{"type":39,"value":257},{"type":26,"tag":140,"props":1534,"children":1535},{"class":142,"line":296},[1536,1540,1544],{"type":26,"tag":140,"props":1537,"children":1538},{"style":248},[1539],{"type":39,"value":685},{"type":26,"tag":140,"props":1541,"children":1542},{"style":254},[1543],{"type":39,"value":321},{"type":26,"tag":140,"props":1545,"children":1546},{"style":152},[1547],{"type":39,"value":271},{"type":26,"tag":140,"props":1549,"children":1550},{"class":142,"line":231},[1551,1555],{"type":26,"tag":140,"props":1552,"children":1553},{"style":248},[1554],{"type":39,"value":701},{"type":26,"tag":140,"props":1556,"children":1557},{"style":254},[1558],{"type":39,"value":257},{"type":26,"tag":140,"props":1560,"children":1561},{"class":142,"line":329},[1562,1566,1570],{"type":26,"tag":140,"props":1563,"children":1564},{"style":248},[1565],{"type":39,"value":713},{"type":26,"tag":140,"props":1567,"children":1568},{"style":254},[1569],{"type":39,"value":321},{"type":26,"tag":140,"props":1571,"children":1572},{"style":152},[1573],{"type":39,"value":326},{"type":26,"tag":140,"props":1575,"children":1576},{"class":142,"line":353},[1577,1581,1585,1589],{"type":26,"tag":140,"props":1578,"children":1579},{"style":248},[1580],{"type":39,"value":729},{"type":26,"tag":140,"props":1582,"children":1583},{"style":254},[1584],{"type":39,"value":340},{"type":26,"tag":140,"props":1586,"children":1587},{"style":152},[1588],{"type":39,"value":345},{"type":26,"tag":140,"props":1590,"children":1591},{"style":254},[1592],{"type":39,"value":350},{"type":26,"tag":140,"props":1594,"children":1595},{"class":142,"line":370},[1596,1600],{"type":26,"tag":140,"props":1597,"children":1598},{"style":248},[1599],{"type":39,"value":750},{"type":26,"tag":140,"props":1601,"children":1602},{"style":254},[1603],{"type":39,"value":257},{"type":26,"tag":140,"props":1605,"children":1606},{"class":142,"line":383},[1607,1611,1615,1619],{"type":26,"tag":140,"props":1608,"children":1609},{"style":254},[1610],{"type":39,"value":763},{"type":26,"tag":140,"props":1612,"children":1613},{"style":248},[1614],{"type":39,"value":768},{"type":26,"tag":140,"props":1616,"children":1617},{"style":254},[1618],{"type":39,"value":321},{"type":26,"tag":140,"props":1620,"children":1621},{"style":152},[1622],{"type":39,"value":777},{"type":26,"tag":140,"props":1624,"children":1625},{"class":142,"line":397},[1626,1630,1634],{"type":26,"tag":140,"props":1627,"children":1628},{"style":248},[1629],{"type":39,"value":786},{"type":26,"tag":140,"props":1631,"children":1632},{"style":254},[1633],{"type":39,"value":321},{"type":26,"tag":140,"props":1635,"children":1636},{"style":152},[1637],{"type":39,"value":795},{"type":26,"tag":140,"props":1639,"children":1640},{"class":142,"line":410},[1641,1645],{"type":26,"tag":140,"props":1642,"children":1643},{"style":248},[1644],{"type":39,"value":804},{"type":26,"tag":140,"props":1646,"children":1647},{"style":254},[1648],{"type":39,"value":257},{"type":26,"tag":140,"props":1650,"children":1651},{"class":142,"line":423},[1652,1656,1660],{"type":26,"tag":140,"props":1653,"children":1654},{"style":248},[1655],{"type":39,"value":817},{"type":26,"tag":140,"props":1657,"children":1658},{"style":254},[1659],{"type":39,"value":321},{"type":26,"tag":140,"props":1661,"children":1662},{"style":152},[1663],{"type":39,"value":826},{"type":26,"tag":140,"props":1665,"children":1666},{"class":142,"line":232},[1667,1671,1675],{"type":26,"tag":140,"props":1668,"children":1669},{"style":248},[1670],{"type":39,"value":835},{"type":26,"tag":140,"props":1672,"children":1673},{"style":254},[1674],{"type":39,"value":321},{"type":26,"tag":140,"props":1676,"children":1677},{"style":163},[1678],{"type":39,"value":844},{"type":26,"tag":140,"props":1680,"children":1681},{"class":142,"line":233},[1682,1686],{"type":26,"tag":140,"props":1683,"children":1684},{"style":248},[1685],{"type":39,"value":852},{"type":26,"tag":140,"props":1687,"children":1688},{"style":254},[1689],{"type":39,"value":257},{"type":26,"tag":140,"props":1691,"children":1692},{"class":142,"line":234},[1693,1697,1701],{"type":26,"tag":140,"props":1694,"children":1695},{"style":248},[1696],{"type":39,"value":865},{"type":26,"tag":140,"props":1698,"children":1699},{"style":254},[1700],{"type":39,"value":321},{"type":26,"tag":140,"props":1702,"children":1703},{"style":152},[1704],{"type":39,"value":874},{"type":26,"tag":140,"props":1706,"children":1707},{"class":142,"line":235},[1708,1712,1716],{"type":26,"tag":140,"props":1709,"children":1710},{"style":248},[1711],{"type":39,"value":882},{"type":26,"tag":140,"props":1713,"children":1714},{"style":254},[1715],{"type":39,"value":321},{"type":26,"tag":140,"props":1717,"children":1718},{"style":152},[1719],{"type":39,"value":891},{"type":26,"tag":140,"props":1721,"children":1722},{"class":142,"line":236},[1723,1727,1731],{"type":26,"tag":140,"props":1724,"children":1725},{"style":248},[1726],{"type":39,"value":899},{"type":26,"tag":140,"props":1728,"children":1729},{"style":254},[1730],{"type":39,"value":321},{"type":26,"tag":140,"props":1732,"children":1733},{"style":163},[1734],{"type":39,"value":908},{"type":26,"tag":140,"props":1736,"children":1737},{"class":142,"line":911},[1738],{"type":26,"tag":140,"props":1739,"children":1740},{"style":254},[1741],{"type":39,"value":917},{"type":26,"tag":140,"props":1743,"children":1744},{"class":142,"line":617},[1745,1749,1753],{"type":26,"tag":140,"props":1746,"children":1747},{"style":248},[1748],{"type":39,"value":926},{"type":26,"tag":140,"props":1750,"children":1751},{"style":254},[1752],{"type":39,"value":321},{"type":26,"tag":140,"props":1754,"children":1755},{"style":152},[1756],{"type":39,"value":935},{"type":26,"tag":140,"props":1758,"children":1759},{"class":142,"line":618},[1760,1764,1768],{"type":26,"tag":140,"props":1761,"children":1762},{"style":248},[1763],{"type":39,"value":944},{"type":26,"tag":140,"props":1765,"children":1766},{"style":254},[1767],{"type":39,"value":321},{"type":26,"tag":140,"props":1769,"children":1770},{"style":152},[1771],{"type":39,"value":953},{"type":26,"tag":140,"props":1773,"children":1774},{"class":142,"line":619},[1775,1779,1783],{"type":26,"tag":140,"props":1776,"children":1777},{"style":248},[1778],{"type":39,"value":962},{"type":26,"tag":140,"props":1780,"children":1781},{"style":254},[1782],{"type":39,"value":321},{"type":26,"tag":140,"props":1784,"children":1785},{"style":152},[1786],{"type":39,"value":971},{"type":26,"tag":140,"props":1788,"children":1789},{"class":142,"line":620},[1790,1794,1798],{"type":26,"tag":140,"props":1791,"children":1792},{"style":248},[1793],{"type":39,"value":980},{"type":26,"tag":140,"props":1795,"children":1796},{"style":254},[1797],{"type":39,"value":321},{"type":26,"tag":140,"props":1799,"children":1800},{"style":152},[1801],{"type":39,"value":989},{"type":26,"tag":140,"props":1803,"children":1804},{"class":142,"line":992},[1805],{"type":26,"tag":140,"props":1806,"children":1807},{"emptyLinePlaceholder":278},[1808],{"type":39,"value":281},{"type":26,"tag":140,"props":1810,"children":1811},{"class":142,"line":1000},[1812,1816,1820],{"type":26,"tag":140,"props":1813,"children":1814},{"style":248},[1815],{"type":39,"value":1006},{"type":26,"tag":140,"props":1817,"children":1818},{"style":254},[1819],{"type":39,"value":321},{"type":26,"tag":140,"props":1821,"children":1822},{"style":152},[1823],{"type":39,"value":1015},{"type":26,"tag":140,"props":1825,"children":1826},{"class":142,"line":1018},[1827,1831],{"type":26,"tag":140,"props":1828,"children":1829},{"style":248},[1830],{"type":39,"value":1024},{"type":26,"tag":140,"props":1832,"children":1833},{"style":254},[1834],{"type":39,"value":257},{"type":26,"tag":140,"props":1836,"children":1837},{"class":142,"line":621},[1838,1842],{"type":26,"tag":140,"props":1839,"children":1840},{"style":254},[1841],{"type":39,"value":763},{"type":26,"tag":140,"props":1843,"children":1844},{"style":152},[1845],{"type":39,"value":1041},{"type":26,"tag":140,"props":1847,"children":1848},{"class":142,"line":622},[1849,1853],{"type":26,"tag":140,"props":1850,"children":1851},{"style":254},[1852],{"type":39,"value":763},{"type":26,"tag":140,"props":1854,"children":1855},{"style":152},[1856],{"type":39,"value":1054},{"type":26,"tag":140,"props":1858,"children":1859},{"class":142,"line":623},[1860,1864],{"type":26,"tag":140,"props":1861,"children":1862},{"style":254},[1863],{"type":39,"value":763},{"type":26,"tag":140,"props":1865,"children":1866},{"style":152},[1867],{"type":39,"value":1067},{"type":26,"tag":140,"props":1869,"children":1870},{"class":142,"line":624},[1871,1875],{"type":26,"tag":140,"props":1872,"children":1873},{"style":254},[1874],{"type":39,"value":763},{"type":26,"tag":140,"props":1876,"children":1877},{"style":152},[1878],{"type":39,"value":1080},{"type":26,"tag":140,"props":1880,"children":1881},{"class":142,"line":625},[1882,1886],{"type":26,"tag":140,"props":1883,"children":1884},{"style":254},[1885],{"type":39,"value":763},{"type":26,"tag":140,"props":1887,"children":1888},{"style":152},[1889],{"type":39,"value":1093},{"type":26,"tag":140,"props":1891,"children":1892},{"class":142,"line":626},[1893,1897],{"type":26,"tag":140,"props":1894,"children":1895},{"style":254},[1896],{"type":39,"value":763},{"type":26,"tag":140,"props":1898,"children":1899},{"style":152},[1900],{"type":39,"value":1106},{"type":26,"tag":140,"props":1902,"children":1903},{"class":142,"line":627},[1904,1908],{"type":26,"tag":140,"props":1905,"children":1906},{"style":254},[1907],{"type":39,"value":763},{"type":26,"tag":140,"props":1909,"children":1910},{"style":152},[1911],{"type":39,"value":1119},{"type":26,"tag":140,"props":1913,"children":1914},{"class":142,"line":628},[1915,1919],{"type":26,"tag":140,"props":1916,"children":1917},{"style":254},[1918],{"type":39,"value":763},{"type":26,"tag":140,"props":1920,"children":1921},{"style":152},[1922],{"type":39,"value":1132},{"type":26,"tag":140,"props":1924,"children":1925},{"class":142,"line":629},[1926,1930],{"type":26,"tag":140,"props":1927,"children":1928},{"style":254},[1929],{"type":39,"value":763},{"type":26,"tag":140,"props":1931,"children":1932},{"style":152},[1933],{"type":39,"value":1145},{"type":26,"tag":140,"props":1935,"children":1936},{"class":142,"line":630},[1937,1941],{"type":26,"tag":140,"props":1938,"children":1939},{"style":254},[1940],{"type":39,"value":763},{"type":26,"tag":140,"props":1942,"children":1943},{"style":152},[1944],{"type":39,"value":1106},{"type":26,"tag":140,"props":1946,"children":1947},{"class":142,"line":1160},[1948,1952],{"type":26,"tag":140,"props":1949,"children":1950},{"style":248},[1951],{"type":39,"value":1166},{"type":26,"tag":140,"props":1953,"children":1954},{"style":254},[1955],{"type":39,"value":257},{"type":26,"tag":140,"props":1957,"children":1958},{"class":142,"line":631},[1959,1963],{"type":26,"tag":140,"props":1960,"children":1961},{"style":254},[1962],{"type":39,"value":763},{"type":26,"tag":140,"props":1964,"children":1965},{"style":152},[1966],{"type":39,"value":1183},{"type":26,"tag":140,"props":1968,"children":1969},{"class":142,"line":1186},[1970],{"type":26,"tag":140,"props":1971,"children":1972},{"emptyLinePlaceholder":278},[1973],{"type":39,"value":281},{"type":26,"tag":140,"props":1975,"children":1976},{"class":142,"line":1194},[1977,1981],{"type":26,"tag":140,"props":1978,"children":1979},{"style":248},[1980],{"type":39,"value":1200},{"type":26,"tag":140,"props":1982,"children":1983},{"style":254},[1984],{"type":39,"value":257},{"type":26,"tag":140,"props":1986,"children":1987},{"class":142,"line":1207},[1988,1992,1996,2000],{"type":26,"tag":140,"props":1989,"children":1990},{"style":254},[1991],{"type":39,"value":763},{"type":26,"tag":140,"props":1993,"children":1994},{"style":248},[1995],{"type":39,"value":1217},{"type":26,"tag":140,"props":1997,"children":1998},{"style":254},[1999],{"type":39,"value":321},{"type":26,"tag":140,"props":2001,"children":2002},{"style":152},[2003],{"type":39,"value":1226},{"type":26,"tag":140,"props":2005,"children":2006},{"class":142,"line":1229},[2007,2011],{"type":26,"tag":140,"props":2008,"children":2009},{"style":248},[2010],{"type":39,"value":1235},{"type":26,"tag":140,"props":2012,"children":2013},{"style":254},[2014],{"type":39,"value":257},{"type":26,"tag":140,"props":2016,"children":2017},{"class":142,"line":1242},[2018,2022],{"type":26,"tag":140,"props":2019,"children":2020},{"style":254},[2021],{"type":39,"value":1248},{"type":26,"tag":140,"props":2023,"children":2024},{"style":152},[2025],{"type":39,"value":1253},{"type":26,"tag":140,"props":2027,"children":2028},{"class":142,"line":1256},[2029,2033,2037],{"type":26,"tag":140,"props":2030,"children":2031},{"style":248},[2032],{"type":39,"value":1262},{"type":26,"tag":140,"props":2034,"children":2035},{"style":254},[2036],{"type":39,"value":321},{"type":26,"tag":140,"props":2038,"children":2039},{"style":152},[2040],{"type":39,"value":1271},{"type":26,"tag":140,"props":2042,"children":2043},{"class":142,"line":1274},[2044,2048],{"type":26,"tag":140,"props":2045,"children":2046},{"style":248},[2047],{"type":39,"value":1280},{"type":26,"tag":140,"props":2049,"children":2050},{"style":254},[2051],{"type":39,"value":257},{"type":26,"tag":140,"props":2053,"children":2054},{"class":142,"line":1287},[2055,2059],{"type":26,"tag":140,"props":2056,"children":2057},{"style":248},[2058],{"type":39,"value":1293},{"type":26,"tag":140,"props":2060,"children":2061},{"style":254},[2062],{"type":39,"value":257},{"type":26,"tag":140,"props":2064,"children":2065},{"class":142,"line":1300},[2066,2070],{"type":26,"tag":140,"props":2067,"children":2068},{"style":248},[2069],{"type":39,"value":1306},{"type":26,"tag":140,"props":2071,"children":2072},{"style":254},[2073],{"type":39,"value":257},{"type":26,"tag":140,"props":2075,"children":2076},{"class":142,"line":1313},[2077,2081,2085],{"type":26,"tag":140,"props":2078,"children":2079},{"style":248},[2080],{"type":39,"value":1319},{"type":26,"tag":140,"props":2082,"children":2083},{"style":254},[2084],{"type":39,"value":321},{"type":26,"tag":140,"props":2086,"children":2087},{"style":152},[2088],{"type":39,"value":1328},{"type":26,"tag":140,"props":2090,"children":2091},{"class":142,"line":1331},[2092,2096,2100],{"type":26,"tag":140,"props":2093,"children":2094},{"style":248},[2095],{"type":39,"value":1337},{"type":26,"tag":140,"props":2097,"children":2098},{"style":254},[2099],{"type":39,"value":321},{"type":26,"tag":140,"props":2101,"children":2102},{"style":152},[2103],{"type":39,"value":1346},{"type":26,"tag":140,"props":2105,"children":2106},{"class":142,"line":1349},[2107,2111],{"type":26,"tag":140,"props":2108,"children":2109},{"style":248},[2110],{"type":39,"value":1355},{"type":26,"tag":140,"props":2112,"children":2113},{"style":254},[2114],{"type":39,"value":257},{"type":26,"tag":140,"props":2116,"children":2117},{"class":142,"line":1362},[2118,2122,2126,2130],{"type":26,"tag":140,"props":2119,"children":2120},{"style":254},[2121],{"type":39,"value":763},{"type":26,"tag":140,"props":2123,"children":2124},{"style":248},[2125],{"type":39,"value":464},{"type":26,"tag":140,"props":2127,"children":2128},{"style":254},[2129],{"type":39,"value":321},{"type":26,"tag":140,"props":2131,"children":2132},{"style":152},[2133],{"type":39,"value":473},{"type":26,"tag":140,"props":2135,"children":2136},{"class":142,"line":1382},[2137,2141,2145,2149],{"type":26,"tag":140,"props":2138,"children":2139},{"style":254},[2140],{"type":39,"value":763},{"type":26,"tag":140,"props":2142,"children":2143},{"style":248},[2144],{"type":39,"value":464},{"type":26,"tag":140,"props":2146,"children":2147},{"style":254},[2148],{"type":39,"value":321},{"type":26,"tag":140,"props":2150,"children":2151},{"style":152},[2152],{"type":39,"value":494},{"type":26,"tag":140,"props":2154,"children":2155},{"class":142,"line":1402},[2156,2160,2164],{"type":26,"tag":140,"props":2157,"children":2158},{"style":248},[2159],{"type":39,"value":1408},{"type":26,"tag":140,"props":2161,"children":2162},{"style":254},[2163],{"type":39,"value":321},{"type":26,"tag":140,"props":2165,"children":2166},{"style":152},[2167],{"type":39,"value":512},{"type":26,"tag":140,"props":2169,"children":2170},{"class":142,"line":1419},[2171,2175,2179,2183],{"type":26,"tag":140,"props":2172,"children":2173},{"style":254},[2174],{"type":39,"value":763},{"type":26,"tag":140,"props":2176,"children":2177},{"style":248},[2178],{"type":39,"value":464},{"type":26,"tag":140,"props":2180,"children":2181},{"style":254},[2182],{"type":39,"value":321},{"type":26,"tag":140,"props":2184,"children":2185},{"style":152},[2186],{"type":39,"value":533},{"type":26,"tag":140,"props":2188,"children":2190},{"class":142,"line":2189},55,[2191],{"type":26,"tag":140,"props":2192,"children":2193},{"emptyLinePlaceholder":278},[2194],{"type":39,"value":281},{"type":26,"tag":140,"props":2196,"children":2198},{"class":142,"line":2197},56,[2199,2203],{"type":26,"tag":140,"props":2200,"children":2201},{"style":248},[2202],{"type":39,"value":98},{"type":26,"tag":140,"props":2204,"children":2205},{"style":254},[2206],{"type":39,"value":257},{"type":26,"tag":140,"props":2208,"children":2210},{"class":142,"line":2209},57,[2211,2215],{"type":26,"tag":140,"props":2212,"children":2213},{"style":248},[2214],{"type":39,"value":302},{"type":26,"tag":140,"props":2216,"children":2217},{"style":254},[2218],{"type":39,"value":257},{"type":26,"tag":140,"props":2220,"children":2222},{"class":142,"line":2221},58,[2223,2227,2231],{"type":26,"tag":140,"props":2224,"children":2225},{"style":248},[2226],{"type":39,"value":316},{"type":26,"tag":140,"props":2228,"children":2229},{"style":254},[2230],{"type":39,"value":321},{"type":26,"tag":140,"props":2232,"children":2233},{"style":152},[2234],{"type":39,"value":326},{"type":26,"tag":140,"props":2236,"children":2238},{"class":142,"line":2237},59,[2239,2243,2247,2251],{"type":26,"tag":140,"props":2240,"children":2241},{"style":248},[2242],{"type":39,"value":335},{"type":26,"tag":140,"props":2244,"children":2245},{"style":254},[2246],{"type":39,"value":340},{"type":26,"tag":140,"props":2248,"children":2249},{"style":152},[2250],{"type":39,"value":345},{"type":26,"tag":140,"props":2252,"children":2253},{"style":254},[2254],{"type":39,"value":350},{"type":26,"tag":140,"props":2256,"children":2258},{"class":142,"line":2257},60,[2259,2263,2267],{"type":26,"tag":140,"props":2260,"children":2261},{"style":248},[2262],{"type":39,"value":359},{"type":26,"tag":140,"props":2264,"children":2265},{"style":254},[2266],{"type":39,"value":321},{"type":26,"tag":140,"props":2268,"children":2269},{"style":152},[2270],{"type":39,"value":271},{"type":26,"tag":140,"props":2272,"children":2274},{"class":142,"line":2273},61,[2275,2279],{"type":26,"tag":140,"props":2276,"children":2277},{"style":248},[2278],{"type":39,"value":376},{"type":26,"tag":140,"props":2280,"children":2281},{"style":254},[2282],{"type":39,"value":257},{"type":26,"tag":140,"props":2284,"children":2286},{"class":142,"line":2285},62,[2287,2291],{"type":26,"tag":140,"props":2288,"children":2289},{"style":254},[2290],{"type":39,"value":389},{"type":26,"tag":140,"props":2292,"children":2293},{"style":152},[2294],{"type":39,"value":394},{"type":26,"tag":140,"props":2296,"children":2298},{"class":142,"line":2297},63,[2299,2303],{"type":26,"tag":140,"props":2300,"children":2301},{"style":248},[2302],{"type":39,"value":403},{"type":26,"tag":140,"props":2304,"children":2305},{"style":254},[2306],{"type":39,"value":257},{"type":26,"tag":140,"props":2308,"children":2310},{"class":142,"line":2309},64,[2311,2315],{"type":26,"tag":140,"props":2312,"children":2313},{"style":248},[2314],{"type":39,"value":416},{"type":26,"tag":140,"props":2316,"children":2317},{"style":254},[2318],{"type":39,"value":257},{"type":26,"tag":140,"props":2320,"children":2322},{"class":142,"line":2321},65,[2323,2327,2331],{"type":26,"tag":140,"props":2324,"children":2325},{"style":248},[2326],{"type":39,"value":429},{"type":26,"tag":140,"props":2328,"children":2329},{"style":254},[2330],{"type":39,"value":321},{"type":26,"tag":140,"props":2332,"children":2333},{"style":152},[2334],{"type":39,"value":438},{"type":26,"tag":140,"props":2336,"children":2338},{"class":142,"line":2337},66,[2339,2343],{"type":26,"tag":140,"props":2340,"children":2341},{"style":248},[2342],{"type":39,"value":447},{"type":26,"tag":140,"props":2344,"children":2345},{"style":254},[2346],{"type":39,"value":257},{"type":26,"tag":140,"props":2348,"children":2350},{"class":142,"line":2349},67,[2351,2355,2359,2363],{"type":26,"tag":140,"props":2352,"children":2353},{"style":254},[2354],{"type":39,"value":389},{"type":26,"tag":140,"props":2356,"children":2357},{"style":248},[2358],{"type":39,"value":464},{"type":26,"tag":140,"props":2360,"children":2361},{"style":254},[2362],{"type":39,"value":321},{"type":26,"tag":140,"props":2364,"children":2365},{"style":152},[2366],{"type":39,"value":473},{"type":26,"tag":140,"props":2368,"children":2370},{"class":142,"line":2369},68,[2371,2375,2379,2383],{"type":26,"tag":140,"props":2372,"children":2373},{"style":254},[2374],{"type":39,"value":389},{"type":26,"tag":140,"props":2376,"children":2377},{"style":248},[2378],{"type":39,"value":464},{"type":26,"tag":140,"props":2380,"children":2381},{"style":254},[2382],{"type":39,"value":321},{"type":26,"tag":140,"props":2384,"children":2385},{"style":152},[2386],{"type":39,"value":494},{"type":26,"tag":140,"props":2388,"children":2390},{"class":142,"line":2389},69,[2391,2395,2399],{"type":26,"tag":140,"props":2392,"children":2393},{"style":248},[2394],{"type":39,"value":503},{"type":26,"tag":140,"props":2396,"children":2397},{"style":254},[2398],{"type":39,"value":321},{"type":26,"tag":140,"props":2400,"children":2401},{"style":152},[2402],{"type":39,"value":512},{"type":26,"tag":140,"props":2404,"children":2406},{"class":142,"line":2405},70,[2407,2411,2415,2419],{"type":26,"tag":140,"props":2408,"children":2409},{"style":254},[2410],{"type":39,"value":389},{"type":26,"tag":140,"props":2412,"children":2413},{"style":248},[2414],{"type":39,"value":464},{"type":26,"tag":140,"props":2416,"children":2417},{"style":254},[2418],{"type":39,"value":321},{"type":26,"tag":140,"props":2420,"children":2421},{"style":152},[2422],{"type":39,"value":533},{"type":26,"tag":2424,"props":2425,"children":2426},"style",{},[2427],{"type":39,"value":2428},"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 .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}",{"title":7,"searchDepth":260,"depth":260,"links":2430},[2431,2434,2437],{"id":98,"depth":260,"text":101,"children":2432},[2433],{"id":217,"depth":274,"text":220},{"id":551,"depth":260,"text":67,"children":2435},[2436],{"id":597,"depth":274,"text":600},{"id":1477,"depth":260,"text":1480},"markdown","common:en:blog:20.shopware-plugin-gitlab-pipeline-test.md","common","en/blog/20.shopware-plugin-gitlab-pipeline-test.md","en/blog/20.shopware-plugin-gitlab-pipeline-test","md",{"_path":2445,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":2446,"description":2447,"author":10,"image":11,"releaseDate":2448,"blogCategories":2449,"articleTags":2450,"tags":2451,"_type":2438,"_id":2452,"_source":2440,"_file":2453,"_stem":2454,"_extension":2443},"/en/blog/gitops-docker-renovate","Software management with GitLab, Renovate Bot and Docker","Managing software on a server is not easy. Is it?","2025-10-30",[14,15],[15,17],[21],"common:en:blog:17.gitops-docker-renovate.md","en/blog/17.gitops-docker-renovate.md","en/blog/17.gitops-docker-renovate",{"_path":2456,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":2457,"description":2458,"author":10,"image":11,"releaseDate":2459,"blogCategories":2460,"articleTags":2461,"tags":2462,"_type":2438,"_id":2463,"_source":2440,"_file":2464,"_stem":2465,"_extension":2443},"/en/blog/traefik-magic","Using Traefik as reverse proxy - convention over configuration","How to configure Traefik reverse proxy to get rid of any Traefik specific labels and automatically route to subdomains matching compose project name","2026-03-13",[14,15],[15],[21],"common:en:blog:21.traefik-magic.md","en/blog/21.traefik-magic.md","en/blog/21.traefik-magic",{"_path":2467},"/blog/shopware-plugin-gitlab-pipeline-test",{"_path":4},{"_path":2470,"_dir":2471,"_draft":6,"_partial":6,"_locale":7,"slug":10,"teams":2472,"primaryTeam":2474,"firstName":2475,"lastName":2476,"prefixTitle":7,"suffixTitle":2477,"education":2478,"role":2483,"workingSince":2488,"inTheCompanySince":2489,"techSkills":2490,"skills":2529,"projects":2546,"contactDetails":2556,"_image":2560,"image":2561,"_id":2562,"_type":2563,"title":2564,"_source":2471,"_file":2565,"_stem":2566,"_extension":2563},"/employees/robert-juzak","employees",[2473,2474],"appDev","devOps","Robert","Juzak","B.Sc.",[2479],[2480,2481,2482],"Bachelor of Computer Science","Technische Universität Breslau","2016",[2484,2485,2486,2487],"softwareDeveloper","fullstackDeveloper","admin","consultant","2015","2018",[2491,2495,2497,2500,2503,2506,2508,2511,2514,2518,2521,2523,2526],{"name":2492,"level":2493,"icon":2494},"Docker","expert","/images/Docker.svg",{"name":2496,"level":2493},"GitLab",{"name":2498,"level":2493,"icon":2499},"Kubernetes","/images/Kubernetes.svg",{"name":2501,"level":2493,"icon":2502},"PHPUnit","/images/PHP-Unit.svg",{"name":2504,"level":2493,"icon":2505},"Portainer","/images/Portainer.svg",{"name":2507,"level":2493},"Sentry",{"name":2509,"level":2493,"icon":2510},"Sonarqube","/images/Sonarqube.svg",{"name":2512,"level":2493,"icon":2513},"Linux","/images/linux_os-mono.svg",{"name":2515,"level":2516,"icon":2517},"CSS","advanced","/images/css.svg",{"name":2519,"level":2516,"icon":2520},"HTML","/images/html.svg",{"name":2522,"level":2516},"PHP",{"name":2524,"level":2516,"icon":2525},"SQL","/images/SQL.svg",{"name":2527,"level":2516,"icon":2528},"VueJS","/images/vuejs.svg",[2530,2531,2533,2534,2536,2538,2540,2542,2544],{"name":113,"level":2493},{"name":2532,"level":2493},"qualityAssurance",{"name":2474,"level":2493},{"name":2535,"level":2493},"testDrivenBugfix",{"name":2537,"level":2493},"testDrivenDevelopment",{"name":2539,"level":2516},"accessibility",{"name":2541,"level":2516},"databases",{"name":2543,"level":2516},"linuxServerAdministration",{"name":2545,"level":2516},"softwareArchitect",[2547,2552,2554],{"project":2548,"position":2549},"Herole",[2550,2551],"Dev-Ops","Frontend Developer",{"project":2553,"position":2551},"Huawei-Calibration-aaS",{"project":2555,"position":2551},"Huawei-Inspect-3D",{"eMail":2557,"phone":2558,"visibility":2559},"robert.juzak@helmundwalter.de","+49 351 799 035 26","1","images/employees/Portraits/robert_juzak.webp","images/employees/Portraits/RobertJuzak_MS.webp","employees:employees:6.robert-juzak.json","json","Robert Juzak","employees/6.robert-juzak.json","employees/6.robert-juzak",{"_path":2445,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":2446,"description":2447,"author":10,"image":11,"releaseDate":2448,"blogCategories":2568,"articleTags":2569,"tags":2570,"body":2571,"_type":2438,"_id":2452,"_source":2440,"_file":2453,"_stem":2454,"_extension":2443},[14,15],[15,17],[21],{"type":23,"children":2572,"toc":4051},[2573,2578,2583,2588,2593,2598,2617,2622,2664,2670,2683,2696,2701,2707,2720,2733,2742,2748,2753,2866,3390,3426,3437,3441,3446,3609,3622,3628,3633,3711,3716,3722,3727,3738,3751,3785,3812,3824,3845,3858,3861,3866,3879,3897,3902,3907,4029,4047],{"type":26,"tag":35,"props":2574,"children":2575},{},[2576],{"type":39,"value":2577},"Managing a multiservice server can be tricky. Not every software is compatible with another, for example different requirements for database or PHP-versions.",{"type":26,"tag":35,"props":2579,"children":2580},{},[2581],{"type":39,"value":2582},"Additionally, all the software should be in the latest version, to mitigate possible security risks.",{"type":26,"tag":35,"props":2584,"children":2585},{},[2586],{"type":39,"value":2587},"There are many ways to handle this: Ansible, Chef etc.",{"type":26,"tag":35,"props":2589,"children":2590},{},[2591],{"type":39,"value":2592},"Out goal was to have an easy to use, automated and free solution.",{"type":26,"tag":35,"props":2594,"children":2595},{},[2596],{"type":39,"value":2597},"Here are the goals:",{"type":26,"tag":2599,"props":2600,"children":2601},"ul",{},[2602,2607,2612],{"type":26,"tag":46,"props":2603,"children":2604},{},[2605],{"type":39,"value":2606},"use the GitOps approach to store and version control the deployment",{"type":26,"tag":46,"props":2608,"children":2609},{},[2610],{"type":39,"value":2611},"use containers to run the software",{"type":26,"tag":46,"props":2613,"children":2614},{},[2615],{"type":39,"value":2616},"get automated security updates and opt in for minor/major version updates",{"type":26,"tag":35,"props":2618,"children":2619},{},[2620],{"type":39,"value":2621},"This is the stack we ended with:",{"type":26,"tag":2599,"props":2623,"children":2624},{},[2625,2643,2653],{"type":26,"tag":46,"props":2626,"children":2627},{},[2628,2633,2635,2641],{"type":26,"tag":52,"props":2629,"children":2631},{"href":2630},"https://www.docker.com/",[2632],{"type":39,"value":2492},{"type":39,"value":2634}," and ",{"type":26,"tag":52,"props":2636,"children":2638},{"href":2637},"https://docs.docker.com/compose/",[2639],{"type":39,"value":2640},"Docker Compose",{"type":39,"value":2642}," do manage the software",{"type":26,"tag":46,"props":2644,"children":2645},{},[2646,2651],{"type":26,"tag":52,"props":2647,"children":2649},{"href":2648},"https://about.gitlab.com/",[2650],{"type":39,"value":2496},{"type":39,"value":2652}," to store all the compose files",{"type":26,"tag":46,"props":2654,"children":2655},{},[2656,2662],{"type":26,"tag":52,"props":2657,"children":2659},{"href":2658},"https://docs.renovatebot.com",[2660],{"type":39,"value":2661},"Renovate Bot",{"type":39,"value":2663}," to keep the software up to date",{"type":26,"tag":96,"props":2665,"children":2667},{"id":2666},"about-the-stack",[2668],{"type":39,"value":2669},"About the stack",{"type":26,"tag":35,"props":2671,"children":2672},{},[2673,2675,2681],{"type":39,"value":2674},"We have been using Docker in production for some time now. Depending on the situation, we create the compose file directly on the server, manage it over portainer or ",{"type":26,"tag":108,"props":2676,"children":2678},{"className":2677},[],[2679],{"type":39,"value":2680},"scp",{"type":39,"value":2682}," it from a pipeline.",{"type":26,"tag":35,"props":2684,"children":2685},{},[2686,2688,2694],{"type":39,"value":2687},"GitLab is out primary tool for version control. Additionally, a ",{"type":26,"tag":52,"props":2689,"children":2691},{"href":2690},"https://docs.gitlab.com/runner/",[2692],{"type":39,"value":2693},"GitLab Runner",{"type":39,"value":2695}," takes care of running pipelines.",{"type":26,"tag":35,"props":2697,"children":2698},{},[2699],{"type":39,"value":2700},"Renovate automates dependency updates. PHP, Go, Python, Docker - to name a couple. We already use it for various projects.",{"type":26,"tag":96,"props":2702,"children":2704},{"id":2703},"container-with-docker-and-docker-compose",[2705],{"type":39,"value":2706},"Container with Docker and Docker Compose",{"type":26,"tag":35,"props":2708,"children":2709},{},[2710,2712,2718],{"type":39,"value":2711},"The main reason why we chose Docker is the ability to access a remote Docker host and execute Docker commands.\nRefer to ",{"type":26,"tag":52,"props":2713,"children":2715},{"href":2714},"https://docs.docker.com/reference/cli/docker/#host",[2716],{"type":39,"value":2717},"the official documentation",{"type":39,"value":2719}," for more information.",{"type":26,"tag":35,"props":2721,"children":2722},{},[2723,2725,2731],{"type":39,"value":2724},"We are using ",{"type":26,"tag":108,"props":2726,"children":2728},{"className":2727},[],[2729],{"type":39,"value":2730},"ssh",{"type":39,"value":2732}," to access our target server.",{"type":26,"tag":35,"props":2734,"children":2735},{},[2736],{"type":26,"tag":108,"props":2737,"children":2739},{"className":2738},[],[2740],{"type":39,"value":2741},"DOCKER_HOST=ssh://[username@]\u003CIP or host>[:port] docker compose up --wait",{"type":26,"tag":96,"props":2743,"children":2745},{"id":2744},"gitops-with-gitlab",[2746],{"type":39,"value":2747},"GitOps with GitLab",{"type":26,"tag":35,"props":2749,"children":2750},{},[2751],{"type":39,"value":2752},"The idea behind GitOps is to use a git repository to store the configuration. Here is an example:",{"type":26,"tag":130,"props":2754,"children":2756},{"className":134,"code":2755,"language":133,"meta":7,"style":7},".\n├──.gitlab-ci.yml             # pipeline definition\n├── renovate.json             # Renovate configuration\n├── nextcloud\n│   ├── docker-compose.yml   # Nextcloud file hosting and collaboration\n└── traefik\n    └── docker-compose.yml   # Traefik reverse proxy configuration\n\n",[2757],{"type":26,"tag":108,"props":2758,"children":2759},{"__ignoreMap":7},[2760,2769,2783,2801,2813,2836,2849],{"type":26,"tag":140,"props":2761,"children":2762},{"class":142,"line":143},[2763],{"type":26,"tag":140,"props":2764,"children":2766},{"style":2765},"--shiki-default:#79B8FF;--shiki-dark:#79B8FF;--shiki-sepia:#66D9EF",[2767],{"type":39,"value":2768},".\n",{"type":26,"tag":140,"props":2770,"children":2771},{"class":142,"line":260},[2772,2777],{"type":26,"tag":140,"props":2773,"children":2774},{"style":147},[2775],{"type":39,"value":2776},"├──.gitlab-ci.yml",{"type":26,"tag":140,"props":2778,"children":2780},{"style":2779},"--shiki-default:#6A737D;--shiki-dark:#6A737D;--shiki-sepia:#88846F",[2781],{"type":39,"value":2782},"             # pipeline definition\n",{"type":26,"tag":140,"props":2784,"children":2785},{"class":142,"line":274},[2786,2791,2796],{"type":26,"tag":140,"props":2787,"children":2788},{"style":147},[2789],{"type":39,"value":2790},"├──",{"type":26,"tag":140,"props":2792,"children":2793},{"style":152},[2794],{"type":39,"value":2795}," renovate.json",{"type":26,"tag":140,"props":2797,"children":2798},{"style":2779},[2799],{"type":39,"value":2800},"             # Renovate configuration\n",{"type":26,"tag":140,"props":2802,"children":2803},{"class":142,"line":284},[2804,2808],{"type":26,"tag":140,"props":2805,"children":2806},{"style":147},[2807],{"type":39,"value":2790},{"type":26,"tag":140,"props":2809,"children":2810},{"style":152},[2811],{"type":39,"value":2812}," nextcloud\n",{"type":26,"tag":140,"props":2814,"children":2815},{"class":142,"line":296},[2816,2821,2826,2831],{"type":26,"tag":140,"props":2817,"children":2818},{"style":147},[2819],{"type":39,"value":2820},"│",{"type":26,"tag":140,"props":2822,"children":2823},{"style":152},[2824],{"type":39,"value":2825},"   ├──",{"type":26,"tag":140,"props":2827,"children":2828},{"style":152},[2829],{"type":39,"value":2830}," docker-compose.yml",{"type":26,"tag":140,"props":2832,"children":2833},{"style":2779},[2834],{"type":39,"value":2835},"   # Nextcloud file hosting and collaboration\n",{"type":26,"tag":140,"props":2837,"children":2838},{"class":142,"line":231},[2839,2844],{"type":26,"tag":140,"props":2840,"children":2841},{"style":147},[2842],{"type":39,"value":2843},"└──",{"type":26,"tag":140,"props":2845,"children":2846},{"style":152},[2847],{"type":39,"value":2848}," traefik\n",{"type":26,"tag":140,"props":2850,"children":2851},{"class":142,"line":329},[2852,2857,2861],{"type":26,"tag":140,"props":2853,"children":2854},{"style":147},[2855],{"type":39,"value":2856},"    └──",{"type":26,"tag":140,"props":2858,"children":2859},{"style":152},[2860],{"type":39,"value":2830},{"type":26,"tag":140,"props":2862,"children":2863},{"style":2779},[2864],{"type":39,"value":2865},"   # Traefik reverse proxy configuration\n",{"type":26,"tag":130,"props":2867,"children":2870},{"className":238,"code":2868,"filename":2869,"language":237,"meta":7,"style":7},"volumes:\n  nextcloud:\n  db:\n\nservices:\n  db:\n    image: mariadb:11.8\n    restart: unless-stopped\n    volumes:\n      - db:/var/lib/mysql\n    environment:\n      - MARIADB_ROOT_PASSWORD=${NEXTCLOUD_MARIADB_ROOT_PASSWORD:?error}\n      - MARIADB_PASSWORD=${NEXTCLOUD_MARIADB_PASSWORD:?error}\n      - MARIADB_DATABASE=nextcloud\n      - MARIADB_USER=nextcloud\n    command:\n      - --transaction-isolation=READ-COMMITTED\n      - --log-bin=binlog\n      - --binlog-format=ROW\n    healthcheck:\n      test: [\"CMD\", \"healthcheck.sh\", \"--connect\", \"--innodb_initialized\"]\n      interval: 15s\n      timeout: 5s\n      retries: 6\n\n  nextcloud:\n    image: nextcloud:32.0.0\n    restart: unless-stopped\n    depends_on:\n      db:\n        condition: service_healthy\n    volumes:\n      - nextcloud:/var/www/html\n    environment:\n      - MYSQL_PASSWORD=${NEXTCLOUD_MARIADB_PASSWORD:?error}\n      - MYSQL_DATABASE=nextcloud\n      - MYSQL_USER=nextcloud\n      - MYSQL_HOST=db\n","nextcloud/docker-compose.yaml",[2871],{"type":26,"tag":108,"props":2872,"children":2873},{"__ignoreMap":7},[2874,2886,2898,2910,2917,2929,2940,2957,2974,2986,2998,3010,3022,3034,3046,3058,3070,3082,3094,3106,3118,3167,3184,3201,3218,3225,3236,3252,3267,3279,3291,3308,3319,3331,3342,3354,3366,3378],{"type":26,"tag":140,"props":2875,"children":2876},{"class":142,"line":143},[2877,2882],{"type":26,"tag":140,"props":2878,"children":2879},{"style":248},[2880],{"type":39,"value":2881},"volumes",{"type":26,"tag":140,"props":2883,"children":2884},{"style":254},[2885],{"type":39,"value":257},{"type":26,"tag":140,"props":2887,"children":2888},{"class":142,"line":260},[2889,2894],{"type":26,"tag":140,"props":2890,"children":2891},{"style":248},[2892],{"type":39,"value":2893},"  nextcloud",{"type":26,"tag":140,"props":2895,"children":2896},{"style":254},[2897],{"type":39,"value":257},{"type":26,"tag":140,"props":2899,"children":2900},{"class":142,"line":274},[2901,2906],{"type":26,"tag":140,"props":2902,"children":2903},{"style":248},[2904],{"type":39,"value":2905},"  db",{"type":26,"tag":140,"props":2907,"children":2908},{"style":254},[2909],{"type":39,"value":257},{"type":26,"tag":140,"props":2911,"children":2912},{"class":142,"line":284},[2913],{"type":26,"tag":140,"props":2914,"children":2915},{"emptyLinePlaceholder":278},[2916],{"type":39,"value":281},{"type":26,"tag":140,"props":2918,"children":2919},{"class":142,"line":296},[2920,2925],{"type":26,"tag":140,"props":2921,"children":2922},{"style":248},[2923],{"type":39,"value":2924},"services",{"type":26,"tag":140,"props":2926,"children":2927},{"style":254},[2928],{"type":39,"value":257},{"type":26,"tag":140,"props":2930,"children":2931},{"class":142,"line":231},[2932,2936],{"type":26,"tag":140,"props":2933,"children":2934},{"style":248},[2935],{"type":39,"value":2905},{"type":26,"tag":140,"props":2937,"children":2938},{"style":254},[2939],{"type":39,"value":257},{"type":26,"tag":140,"props":2941,"children":2942},{"class":142,"line":329},[2943,2948,2952],{"type":26,"tag":140,"props":2944,"children":2945},{"style":248},[2946],{"type":39,"value":2947},"    image",{"type":26,"tag":140,"props":2949,"children":2950},{"style":254},[2951],{"type":39,"value":321},{"type":26,"tag":140,"props":2953,"children":2954},{"style":152},[2955],{"type":39,"value":2956},"mariadb:11.8\n",{"type":26,"tag":140,"props":2958,"children":2959},{"class":142,"line":353},[2960,2965,2969],{"type":26,"tag":140,"props":2961,"children":2962},{"style":248},[2963],{"type":39,"value":2964},"    restart",{"type":26,"tag":140,"props":2966,"children":2967},{"style":254},[2968],{"type":39,"value":321},{"type":26,"tag":140,"props":2970,"children":2971},{"style":152},[2972],{"type":39,"value":2973},"unless-stopped\n",{"type":26,"tag":140,"props":2975,"children":2976},{"class":142,"line":370},[2977,2982],{"type":26,"tag":140,"props":2978,"children":2979},{"style":248},[2980],{"type":39,"value":2981},"    volumes",{"type":26,"tag":140,"props":2983,"children":2984},{"style":254},[2985],{"type":39,"value":257},{"type":26,"tag":140,"props":2987,"children":2988},{"class":142,"line":383},[2989,2993],{"type":26,"tag":140,"props":2990,"children":2991},{"style":254},[2992],{"type":39,"value":389},{"type":26,"tag":140,"props":2994,"children":2995},{"style":152},[2996],{"type":39,"value":2997},"db:/var/lib/mysql\n",{"type":26,"tag":140,"props":2999,"children":3000},{"class":142,"line":397},[3001,3006],{"type":26,"tag":140,"props":3002,"children":3003},{"style":248},[3004],{"type":39,"value":3005},"    environment",{"type":26,"tag":140,"props":3007,"children":3008},{"style":254},[3009],{"type":39,"value":257},{"type":26,"tag":140,"props":3011,"children":3012},{"class":142,"line":410},[3013,3017],{"type":26,"tag":140,"props":3014,"children":3015},{"style":254},[3016],{"type":39,"value":389},{"type":26,"tag":140,"props":3018,"children":3019},{"style":152},[3020],{"type":39,"value":3021},"MARIADB_ROOT_PASSWORD=${NEXTCLOUD_MARIADB_ROOT_PASSWORD:?error}\n",{"type":26,"tag":140,"props":3023,"children":3024},{"class":142,"line":423},[3025,3029],{"type":26,"tag":140,"props":3026,"children":3027},{"style":254},[3028],{"type":39,"value":389},{"type":26,"tag":140,"props":3030,"children":3031},{"style":152},[3032],{"type":39,"value":3033},"MARIADB_PASSWORD=${NEXTCLOUD_MARIADB_PASSWORD:?error}\n",{"type":26,"tag":140,"props":3035,"children":3036},{"class":142,"line":232},[3037,3041],{"type":26,"tag":140,"props":3038,"children":3039},{"style":254},[3040],{"type":39,"value":389},{"type":26,"tag":140,"props":3042,"children":3043},{"style":152},[3044],{"type":39,"value":3045},"MARIADB_DATABASE=nextcloud\n",{"type":26,"tag":140,"props":3047,"children":3048},{"class":142,"line":233},[3049,3053],{"type":26,"tag":140,"props":3050,"children":3051},{"style":254},[3052],{"type":39,"value":389},{"type":26,"tag":140,"props":3054,"children":3055},{"style":152},[3056],{"type":39,"value":3057},"MARIADB_USER=nextcloud\n",{"type":26,"tag":140,"props":3059,"children":3060},{"class":142,"line":234},[3061,3066],{"type":26,"tag":140,"props":3062,"children":3063},{"style":248},[3064],{"type":39,"value":3065},"    command",{"type":26,"tag":140,"props":3067,"children":3068},{"style":254},[3069],{"type":39,"value":257},{"type":26,"tag":140,"props":3071,"children":3072},{"class":142,"line":235},[3073,3077],{"type":26,"tag":140,"props":3074,"children":3075},{"style":254},[3076],{"type":39,"value":389},{"type":26,"tag":140,"props":3078,"children":3079},{"style":152},[3080],{"type":39,"value":3081},"--transaction-isolation=READ-COMMITTED\n",{"type":26,"tag":140,"props":3083,"children":3084},{"class":142,"line":236},[3085,3089],{"type":26,"tag":140,"props":3086,"children":3087},{"style":254},[3088],{"type":39,"value":389},{"type":26,"tag":140,"props":3090,"children":3091},{"style":152},[3092],{"type":39,"value":3093},"--log-bin=binlog\n",{"type":26,"tag":140,"props":3095,"children":3096},{"class":142,"line":911},[3097,3101],{"type":26,"tag":140,"props":3098,"children":3099},{"style":254},[3100],{"type":39,"value":389},{"type":26,"tag":140,"props":3102,"children":3103},{"style":152},[3104],{"type":39,"value":3105},"--binlog-format=ROW\n",{"type":26,"tag":140,"props":3107,"children":3108},{"class":142,"line":617},[3109,3114],{"type":26,"tag":140,"props":3110,"children":3111},{"style":248},[3112],{"type":39,"value":3113},"    healthcheck",{"type":26,"tag":140,"props":3115,"children":3116},{"style":254},[3117],{"type":39,"value":257},{"type":26,"tag":140,"props":3119,"children":3120},{"class":142,"line":618},[3121,3126,3130,3135,3140,3145,3149,3154,3158,3163],{"type":26,"tag":140,"props":3122,"children":3123},{"style":248},[3124],{"type":39,"value":3125},"      test",{"type":26,"tag":140,"props":3127,"children":3128},{"style":254},[3129],{"type":39,"value":340},{"type":26,"tag":140,"props":3131,"children":3132},{"style":152},[3133],{"type":39,"value":3134},"\"CMD\"",{"type":26,"tag":140,"props":3136,"children":3137},{"style":254},[3138],{"type":39,"value":3139},", ",{"type":26,"tag":140,"props":3141,"children":3142},{"style":152},[3143],{"type":39,"value":3144},"\"healthcheck.sh\"",{"type":26,"tag":140,"props":3146,"children":3147},{"style":254},[3148],{"type":39,"value":3139},{"type":26,"tag":140,"props":3150,"children":3151},{"style":152},[3152],{"type":39,"value":3153},"\"--connect\"",{"type":26,"tag":140,"props":3155,"children":3156},{"style":254},[3157],{"type":39,"value":3139},{"type":26,"tag":140,"props":3159,"children":3160},{"style":152},[3161],{"type":39,"value":3162},"\"--innodb_initialized\"",{"type":26,"tag":140,"props":3164,"children":3165},{"style":254},[3166],{"type":39,"value":350},{"type":26,"tag":140,"props":3168,"children":3169},{"class":142,"line":619},[3170,3175,3179],{"type":26,"tag":140,"props":3171,"children":3172},{"style":248},[3173],{"type":39,"value":3174},"      interval",{"type":26,"tag":140,"props":3176,"children":3177},{"style":254},[3178],{"type":39,"value":321},{"type":26,"tag":140,"props":3180,"children":3181},{"style":152},[3182],{"type":39,"value":3183},"15s\n",{"type":26,"tag":140,"props":3185,"children":3186},{"class":142,"line":620},[3187,3192,3196],{"type":26,"tag":140,"props":3188,"children":3189},{"style":248},[3190],{"type":39,"value":3191},"      timeout",{"type":26,"tag":140,"props":3193,"children":3194},{"style":254},[3195],{"type":39,"value":321},{"type":26,"tag":140,"props":3197,"children":3198},{"style":152},[3199],{"type":39,"value":3200},"5s\n",{"type":26,"tag":140,"props":3202,"children":3203},{"class":142,"line":992},[3204,3209,3213],{"type":26,"tag":140,"props":3205,"children":3206},{"style":248},[3207],{"type":39,"value":3208},"      retries",{"type":26,"tag":140,"props":3210,"children":3211},{"style":254},[3212],{"type":39,"value":321},{"type":26,"tag":140,"props":3214,"children":3215},{"style":163},[3216],{"type":39,"value":3217},"6\n",{"type":26,"tag":140,"props":3219,"children":3220},{"class":142,"line":1000},[3221],{"type":26,"tag":140,"props":3222,"children":3223},{"emptyLinePlaceholder":278},[3224],{"type":39,"value":281},{"type":26,"tag":140,"props":3226,"children":3227},{"class":142,"line":1018},[3228,3232],{"type":26,"tag":140,"props":3229,"children":3230},{"style":248},[3231],{"type":39,"value":2893},{"type":26,"tag":140,"props":3233,"children":3234},{"style":254},[3235],{"type":39,"value":257},{"type":26,"tag":140,"props":3237,"children":3238},{"class":142,"line":621},[3239,3243,3247],{"type":26,"tag":140,"props":3240,"children":3241},{"style":248},[3242],{"type":39,"value":2947},{"type":26,"tag":140,"props":3244,"children":3245},{"style":254},[3246],{"type":39,"value":321},{"type":26,"tag":140,"props":3248,"children":3249},{"style":152},[3250],{"type":39,"value":3251},"nextcloud:32.0.0\n",{"type":26,"tag":140,"props":3253,"children":3254},{"class":142,"line":622},[3255,3259,3263],{"type":26,"tag":140,"props":3256,"children":3257},{"style":248},[3258],{"type":39,"value":2964},{"type":26,"tag":140,"props":3260,"children":3261},{"style":254},[3262],{"type":39,"value":321},{"type":26,"tag":140,"props":3264,"children":3265},{"style":152},[3266],{"type":39,"value":2973},{"type":26,"tag":140,"props":3268,"children":3269},{"class":142,"line":623},[3270,3275],{"type":26,"tag":140,"props":3271,"children":3272},{"style":248},[3273],{"type":39,"value":3274},"    depends_on",{"type":26,"tag":140,"props":3276,"children":3277},{"style":254},[3278],{"type":39,"value":257},{"type":26,"tag":140,"props":3280,"children":3281},{"class":142,"line":624},[3282,3287],{"type":26,"tag":140,"props":3283,"children":3284},{"style":248},[3285],{"type":39,"value":3286},"      db",{"type":26,"tag":140,"props":3288,"children":3289},{"style":254},[3290],{"type":39,"value":257},{"type":26,"tag":140,"props":3292,"children":3293},{"class":142,"line":625},[3294,3299,3303],{"type":26,"tag":140,"props":3295,"children":3296},{"style":248},[3297],{"type":39,"value":3298},"        condition",{"type":26,"tag":140,"props":3300,"children":3301},{"style":254},[3302],{"type":39,"value":321},{"type":26,"tag":140,"props":3304,"children":3305},{"style":152},[3306],{"type":39,"value":3307},"service_healthy\n",{"type":26,"tag":140,"props":3309,"children":3310},{"class":142,"line":626},[3311,3315],{"type":26,"tag":140,"props":3312,"children":3313},{"style":248},[3314],{"type":39,"value":2981},{"type":26,"tag":140,"props":3316,"children":3317},{"style":254},[3318],{"type":39,"value":257},{"type":26,"tag":140,"props":3320,"children":3321},{"class":142,"line":627},[3322,3326],{"type":26,"tag":140,"props":3323,"children":3324},{"style":254},[3325],{"type":39,"value":389},{"type":26,"tag":140,"props":3327,"children":3328},{"style":152},[3329],{"type":39,"value":3330},"nextcloud:/var/www/html\n",{"type":26,"tag":140,"props":3332,"children":3333},{"class":142,"line":628},[3334,3338],{"type":26,"tag":140,"props":3335,"children":3336},{"style":248},[3337],{"type":39,"value":3005},{"type":26,"tag":140,"props":3339,"children":3340},{"style":254},[3341],{"type":39,"value":257},{"type":26,"tag":140,"props":3343,"children":3344},{"class":142,"line":629},[3345,3349],{"type":26,"tag":140,"props":3346,"children":3347},{"style":254},[3348],{"type":39,"value":389},{"type":26,"tag":140,"props":3350,"children":3351},{"style":152},[3352],{"type":39,"value":3353},"MYSQL_PASSWORD=${NEXTCLOUD_MARIADB_PASSWORD:?error}\n",{"type":26,"tag":140,"props":3355,"children":3356},{"class":142,"line":630},[3357,3361],{"type":26,"tag":140,"props":3358,"children":3359},{"style":254},[3360],{"type":39,"value":389},{"type":26,"tag":140,"props":3362,"children":3363},{"style":152},[3364],{"type":39,"value":3365},"MYSQL_DATABASE=nextcloud\n",{"type":26,"tag":140,"props":3367,"children":3368},{"class":142,"line":1160},[3369,3373],{"type":26,"tag":140,"props":3370,"children":3371},{"style":254},[3372],{"type":39,"value":389},{"type":26,"tag":140,"props":3374,"children":3375},{"style":152},[3376],{"type":39,"value":3377},"MYSQL_USER=nextcloud\n",{"type":26,"tag":140,"props":3379,"children":3380},{"class":142,"line":631},[3381,3385],{"type":26,"tag":140,"props":3382,"children":3383},{"style":254},[3384],{"type":39,"value":389},{"type":26,"tag":140,"props":3386,"children":3387},{"style":152},[3388],{"type":39,"value":3389},"MYSQL_HOST=db\n",{"type":26,"tag":130,"props":3391,"children":3395},{"className":3392,"code":3393,"language":3394,"meta":7,"style":7},"language-dotenv shiki shiki-themes github-dark github-dark monokai","NEXTCLOUD_MARIADB_ROOT_PASSWORD=\nNEXTCLOUD_MARIADB_PASSWORD=\n","dotenv",[3396],{"type":26,"tag":108,"props":3397,"children":3398},{"__ignoreMap":7},[3399,3414],{"type":26,"tag":140,"props":3400,"children":3401},{"class":142,"line":143},[3402,3408],{"type":26,"tag":140,"props":3403,"children":3405},{"style":3404},"--shiki-default:#FFAB70;--shiki-dark:#FFAB70;--shiki-sepia:#F8F8F2",[3406],{"type":39,"value":3407},"NEXTCLOUD_MARIADB_ROOT_PASSWORD",{"type":26,"tag":140,"props":3409,"children":3411},{"style":3410},"--shiki-default:#F97583;--shiki-dark:#F97583;--shiki-sepia:#F92672",[3412],{"type":39,"value":3413},"=\n",{"type":26,"tag":140,"props":3415,"children":3416},{"class":142,"line":260},[3417,3422],{"type":26,"tag":140,"props":3418,"children":3419},{"style":3404},[3420],{"type":39,"value":3421},"NEXTCLOUD_MARIADB_PASSWORD",{"type":26,"tag":140,"props":3423,"children":3424},{"style":3410},[3425],{"type":39,"value":3413},{"type":26,"tag":35,"props":3427,"children":3428},{},[3429,3431],{"type":39,"value":3430},"are stored as ",{"type":26,"tag":52,"props":3432,"children":3434},{"href":3433},"https://docs.gitlab.com/ci/variables/",[3435],{"type":39,"value":3436},"CI/CD Variables",{"type":26,"tag":3438,"props":3439,"children":3440},"hr",{},[],{"type":26,"tag":35,"props":3442,"children":3443},{},[3444],{"type":39,"value":3445},"To deploy the stack, we have a pipeline:",{"type":26,"tag":130,"props":3447,"children":3450},{"className":238,"code":3448,"filename":3449,"language":237,"meta":7,"style":7},"stages:\n  - deploy\n\ndeploy:\n  stage: deploy\n  image: docker:28\n  variables:\n    DOCKER_HOST: ssh://[username@]\u003CIP or host>[:port]\n  script:\n    - for file in $(find . -type f -name docker-compose.yml); do docker compose -f $file up --remove-orphans --wait; done\n  rules:\n    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH\n\n",".gitlab-ci.yml",[3451],{"type":26,"tag":108,"props":3452,"children":3453},{"__ignoreMap":7},[3454,3465,3477,3484,3496,3511,3527,3538,3555,3566,3578,3589],{"type":26,"tag":140,"props":3455,"children":3456},{"class":142,"line":143},[3457,3461],{"type":26,"tag":140,"props":3458,"children":3459},{"style":248},[3460],{"type":39,"value":251},{"type":26,"tag":140,"props":3462,"children":3463},{"style":254},[3464],{"type":39,"value":257},{"type":26,"tag":140,"props":3466,"children":3467},{"class":142,"line":260},[3468,3472],{"type":26,"tag":140,"props":3469,"children":3470},{"style":254},[3471],{"type":39,"value":266},{"type":26,"tag":140,"props":3473,"children":3474},{"style":152},[3475],{"type":39,"value":3476},"deploy\n",{"type":26,"tag":140,"props":3478,"children":3479},{"class":142,"line":274},[3480],{"type":26,"tag":140,"props":3481,"children":3482},{"emptyLinePlaceholder":278},[3483],{"type":39,"value":281},{"type":26,"tag":140,"props":3485,"children":3486},{"class":142,"line":284},[3487,3492],{"type":26,"tag":140,"props":3488,"children":3489},{"style":248},[3490],{"type":39,"value":3491},"deploy",{"type":26,"tag":140,"props":3493,"children":3494},{"style":254},[3495],{"type":39,"value":257},{"type":26,"tag":140,"props":3497,"children":3498},{"class":142,"line":296},[3499,3503,3507],{"type":26,"tag":140,"props":3500,"children":3501},{"style":248},[3502],{"type":39,"value":685},{"type":26,"tag":140,"props":3504,"children":3505},{"style":254},[3506],{"type":39,"value":321},{"type":26,"tag":140,"props":3508,"children":3509},{"style":152},[3510],{"type":39,"value":3476},{"type":26,"tag":140,"props":3512,"children":3513},{"class":142,"line":231},[3514,3518,3522],{"type":26,"tag":140,"props":3515,"children":3516},{"style":248},[3517],{"type":39,"value":701},{"type":26,"tag":140,"props":3519,"children":3520},{"style":254},[3521],{"type":39,"value":321},{"type":26,"tag":140,"props":3523,"children":3524},{"style":152},[3525],{"type":39,"value":3526},"docker:28\n",{"type":26,"tag":140,"props":3528,"children":3529},{"class":142,"line":329},[3530,3534],{"type":26,"tag":140,"props":3531,"children":3532},{"style":248},[3533],{"type":39,"value":852},{"type":26,"tag":140,"props":3535,"children":3536},{"style":254},[3537],{"type":39,"value":257},{"type":26,"tag":140,"props":3539,"children":3540},{"class":142,"line":353},[3541,3546,3550],{"type":26,"tag":140,"props":3542,"children":3543},{"style":248},[3544],{"type":39,"value":3545},"    DOCKER_HOST",{"type":26,"tag":140,"props":3547,"children":3548},{"style":254},[3549],{"type":39,"value":321},{"type":26,"tag":140,"props":3551,"children":3552},{"style":152},[3553],{"type":39,"value":3554},"ssh://[username@]\u003CIP or host>[:port]\n",{"type":26,"tag":140,"props":3556,"children":3557},{"class":142,"line":370},[3558,3562],{"type":26,"tag":140,"props":3559,"children":3560},{"style":248},[3561],{"type":39,"value":1166},{"type":26,"tag":140,"props":3563,"children":3564},{"style":254},[3565],{"type":39,"value":257},{"type":26,"tag":140,"props":3567,"children":3568},{"class":142,"line":383},[3569,3573],{"type":26,"tag":140,"props":3570,"children":3571},{"style":254},[3572],{"type":39,"value":763},{"type":26,"tag":140,"props":3574,"children":3575},{"style":152},[3576],{"type":39,"value":3577},"for file in $(find . -type f -name docker-compose.yml); do docker compose -f $file up --remove-orphans --wait; done\n",{"type":26,"tag":140,"props":3579,"children":3580},{"class":142,"line":397},[3581,3585],{"type":26,"tag":140,"props":3582,"children":3583},{"style":248},[3584],{"type":39,"value":1355},{"type":26,"tag":140,"props":3586,"children":3587},{"style":254},[3588],{"type":39,"value":257},{"type":26,"tag":140,"props":3590,"children":3591},{"class":142,"line":410},[3592,3596,3600,3604],{"type":26,"tag":140,"props":3593,"children":3594},{"style":254},[3595],{"type":39,"value":763},{"type":26,"tag":140,"props":3597,"children":3598},{"style":248},[3599],{"type":39,"value":464},{"type":26,"tag":140,"props":3601,"children":3602},{"style":254},[3603],{"type":39,"value":321},{"type":26,"tag":140,"props":3605,"children":3606},{"style":152},[3607],{"type":39,"value":3608},"$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH\n",{"type":26,"tag":35,"props":3610,"children":3611},{},[3612,3614,3620],{"type":39,"value":3613},"The pipeline runs with every commit on the default branch, iterates through all ",{"type":26,"tag":108,"props":3615,"children":3617},{"className":3616},[],[3618],{"type":39,"value":3619},"docker-compose.yml",{"type":39,"value":3621}," files, and deploys them.",{"type":26,"tag":96,"props":3623,"children":3625},{"id":3624},"keep-your-deployments-up-to-date-with-renovate-bot",[3626],{"type":39,"value":3627},"Keep your deployments up-to-date with Renovate Bot",{"type":26,"tag":35,"props":3629,"children":3630},{},[3631],{"type":39,"value":3632},"Here is where Renovate kicks in.",{"type":26,"tag":130,"props":3634,"children":3638},{"className":3635,"code":3636,"filename":3637,"language":2563,"meta":7,"style":7},"language-json shiki shiki-themes github-dark github-dark monokai","{\n  \"$schema\": \"https://docs.renovatebot.com/renovate-schema.json\",\n  \"extends\": [\n    \"config:best-practices\"\n  ]\n}\n","renovate.json",[3639],{"type":26,"tag":108,"props":3640,"children":3641},{"__ignoreMap":7},[3642,3650,3674,3687,3695,3703],{"type":26,"tag":140,"props":3643,"children":3644},{"class":142,"line":143},[3645],{"type":26,"tag":140,"props":3646,"children":3647},{"style":254},[3648],{"type":39,"value":3649},"{\n",{"type":26,"tag":140,"props":3651,"children":3652},{"class":142,"line":260},[3653,3659,3663,3669],{"type":26,"tag":140,"props":3654,"children":3656},{"style":3655},"--shiki-default:#79B8FF;--shiki-default-font-style:inherit;--shiki-dark:#79B8FF;--shiki-dark-font-style:inherit;--shiki-sepia:#66D9EF;--shiki-sepia-font-style:italic",[3657],{"type":39,"value":3658},"  \"$schema\"",{"type":26,"tag":140,"props":3660,"children":3661},{"style":254},[3662],{"type":39,"value":321},{"type":26,"tag":140,"props":3664,"children":3666},{"style":3665},"--shiki-default:#9ECBFF;--shiki-dark:#9ECBFF;--shiki-sepia:#CFCFC2",[3667],{"type":39,"value":3668},"\"https://docs.renovatebot.com/renovate-schema.json\"",{"type":26,"tag":140,"props":3670,"children":3671},{"style":254},[3672],{"type":39,"value":3673},",\n",{"type":26,"tag":140,"props":3675,"children":3676},{"class":142,"line":274},[3677,3682],{"type":26,"tag":140,"props":3678,"children":3679},{"style":3655},[3680],{"type":39,"value":3681},"  \"extends\"",{"type":26,"tag":140,"props":3683,"children":3684},{"style":254},[3685],{"type":39,"value":3686},": [\n",{"type":26,"tag":140,"props":3688,"children":3689},{"class":142,"line":284},[3690],{"type":26,"tag":140,"props":3691,"children":3692},{"style":3665},[3693],{"type":39,"value":3694},"    \"config:best-practices\"\n",{"type":26,"tag":140,"props":3696,"children":3697},{"class":142,"line":296},[3698],{"type":26,"tag":140,"props":3699,"children":3700},{"style":254},[3701],{"type":39,"value":3702},"  ]\n",{"type":26,"tag":140,"props":3704,"children":3705},{"class":142,"line":231},[3706],{"type":26,"tag":140,"props":3707,"children":3708},{"style":254},[3709],{"type":39,"value":3710},"}\n",{"type":26,"tag":35,"props":3712,"children":3713},{},[3714],{"type":39,"value":3715},"Renovate will create Merge Requests for every update. Nice!",{"type":26,"tag":96,"props":3717,"children":3719},{"id":3718},"automated-security-updates-and-opt-in-for-minormajor-version-updates",[3720],{"type":39,"value":3721},"Automated security updates and opt-in for minor/major version updates",{"type":26,"tag":35,"props":3723,"children":3724},{},[3725],{"type":39,"value":3726},"The current configuration creates Merge Requests for every update, but we want security updates to happen without user interaction.",{"type":26,"tag":35,"props":3728,"children":3729},{},[3730,3732],{"type":39,"value":3731},"You need to understand how Docker images are versioned / tagged. It depends on the image, but let's take a look on the official ",{"type":26,"tag":52,"props":3733,"children":3735},{"href":3734},"https://hub.docker.com/_/mariadb",[3736],{"type":39,"value":3737},"MariaDB",{"type":26,"tag":35,"props":3739,"children":3740},{},[3741,3743,3749],{"type":39,"value":3742},"There are ",{"type":26,"tag":108,"props":3744,"children":3746},{"className":3745},[],[3747],{"type":39,"value":3748},"11.8.3-noble, 11.8-noble, 11-noble, lts-noble, 11.8.3, 11.8, 11, lts",{"type":39,"value":3750},", all of which refer to the same image.",{"type":26,"tag":35,"props":3752,"children":3753},{},[3754,3760,3762,3768,3770,3776,3777,3783],{"type":26,"tag":108,"props":3755,"children":3757},{"className":3756},[],[3758],{"type":39,"value":3759},"11.8.3-noble",{"type":39,"value":3761}," means that we get MariaDB in version ",{"type":26,"tag":108,"props":3763,"children":3765},{"className":3764},[],[3766],{"type":39,"value":3767},"11.8.3",{"type":39,"value":3769}," based on Ubuntu Noble.\n",{"type":26,"tag":108,"props":3771,"children":3773},{"className":3772},[],[3774],{"type":39,"value":3775},"11.8-noble",{"type":39,"value":3761},{"type":26,"tag":108,"props":3778,"children":3780},{"className":3779},[],[3781],{"type":39,"value":3782},"11.8.\u003Clatest_path>",{"type":39,"value":3784}," based on Ubuntu Noble.",{"type":26,"tag":35,"props":3786,"children":3787},{},[3788,3790,3796,3798,3803,3805,3810],{"type":39,"value":3789},"When a neu version of MariaDB is released, e.g ",{"type":26,"tag":108,"props":3791,"children":3793},{"className":3792},[],[3794],{"type":39,"value":3795},"11.8.4-noble",{"type":39,"value":3797},", a new ",{"type":26,"tag":108,"props":3799,"children":3801},{"className":3800},[],[3802],{"type":39,"value":3795},{"type":39,"value":3804}," tag will be pushed, but the ",{"type":26,"tag":108,"props":3806,"children":3808},{"className":3807},[],[3809],{"type":39,"value":3775},{"type":39,"value":3811}," will be updated.",{"type":26,"tag":35,"props":3813,"children":3814},{},[3815,3817,3822],{"type":39,"value":3816},"This same is true for the Ubuntu update. The ",{"type":26,"tag":108,"props":3818,"children":3820},{"className":3819},[],[3821],{"type":39,"value":3759},{"type":39,"value":3823}," tag can be updated, if the image is rebuild with the latest Ubuntu image.",{"type":26,"tag":35,"props":3825,"children":3826},{},[3827,3829,3835,3837,3843],{"type":39,"value":3828},"Running ",{"type":26,"tag":108,"props":3830,"children":3832},{"className":3831},[],[3833],{"type":39,"value":3834},"docker compose up",{"type":39,"value":3836}," on ",{"type":26,"tag":108,"props":3838,"children":3840},{"className":3839},[],[3841],{"type":39,"value":3842},"mariadb:11.8-noble",{"type":39,"value":3844}," will do nothing, because Docker is not aware of that change.",{"type":26,"tag":35,"props":3846,"children":3847},{},[3848,3850,3856],{"type":39,"value":3849},"In the example above we reference ",{"type":26,"tag":108,"props":3851,"children":3853},{"className":3852},[],[3854],{"type":39,"value":3855},"mariadb:11.8",{"type":39,"value":3857},", because we want to use the latest patch version based on the most current OS.",{"type":26,"tag":3438,"props":3859,"children":3860},{},[],{"type":26,"tag":35,"props":3862,"children":3863},{},[3864],{"type":39,"value":3865},"How to tell Docker that there is a new version?",{"type":26,"tag":35,"props":3867,"children":3868},{},[3869,3871,3877],{"type":39,"value":3870},"The main idea is to attach a ",{"type":26,"tag":52,"props":3872,"children":3874},{"href":3873},"https://docs.docker.com/dhi/core-concepts/digests/",[3875],{"type":39,"value":3876},"digest",{"type":39,"value":3878}," to the Docker image.",{"type":26,"tag":35,"props":3880,"children":3881},{},[3882,3884,3889,3891],{"type":39,"value":3883},"When running Renovate for the first time, it will find the reference to ",{"type":26,"tag":108,"props":3885,"children":3887},{"className":3886},[],[3888],{"type":39,"value":3855},{"type":39,"value":3890}," and create a merge request to pin the Digest to something like ",{"type":26,"tag":108,"props":3892,"children":3894},{"className":3893},[],[3895],{"type":39,"value":3896},"mariadb:11.8@sha256:ae6119716edac6998ae85508431b3d2e666530ddf4e94c61a10710caec9b0f71",{"type":26,"tag":35,"props":3898,"children":3899},{},[3900],{"type":39,"value":3901},"Renovate will also monitor the upstream changes, so that every time the image get updated, the digest will change and Renovate will create a merge request.",{"type":26,"tag":35,"props":3903,"children":3904},{},[3905],{"type":39,"value":3906},"To merge these updates automatically, we need to do some adjustments.",{"type":26,"tag":130,"props":3908,"children":3911},{"className":3635,"code":3909,"filename":3637,"highlights":3910,"language":2563,"meta":7,"style":7},"{\n  \"$schema\": \"https://docs.renovatebot.com/renovate-schema.json\",\n  \"extends\": [\n    \"config:best-practices\",\n    \"default:automergeDigest\"\n  ],\n  \"automergeType\": \"branch\",\n  \"ignoreTests\": true\n}\n",[296,231,329,353],[3912],{"type":26,"tag":108,"props":3913,"children":3914},{"__ignoreMap":7},[3915,3922,3941,3952,3964,3973,3982,4004,4022],{"type":26,"tag":140,"props":3916,"children":3917},{"class":142,"line":143},[3918],{"type":26,"tag":140,"props":3919,"children":3920},{"style":254},[3921],{"type":39,"value":3649},{"type":26,"tag":140,"props":3923,"children":3924},{"class":142,"line":260},[3925,3929,3933,3937],{"type":26,"tag":140,"props":3926,"children":3927},{"style":3655},[3928],{"type":39,"value":3658},{"type":26,"tag":140,"props":3930,"children":3931},{"style":254},[3932],{"type":39,"value":321},{"type":26,"tag":140,"props":3934,"children":3935},{"style":3665},[3936],{"type":39,"value":3668},{"type":26,"tag":140,"props":3938,"children":3939},{"style":254},[3940],{"type":39,"value":3673},{"type":26,"tag":140,"props":3942,"children":3943},{"class":142,"line":274},[3944,3948],{"type":26,"tag":140,"props":3945,"children":3946},{"style":3655},[3947],{"type":39,"value":3681},{"type":26,"tag":140,"props":3949,"children":3950},{"style":254},[3951],{"type":39,"value":3686},{"type":26,"tag":140,"props":3953,"children":3954},{"class":142,"line":284},[3955,3960],{"type":26,"tag":140,"props":3956,"children":3957},{"style":3665},[3958],{"type":39,"value":3959},"    \"config:best-practices\"",{"type":26,"tag":140,"props":3961,"children":3962},{"style":254},[3963],{"type":39,"value":3673},{"type":26,"tag":140,"props":3965,"children":3967},{"class":3966,"line":296},[142,310],[3968],{"type":26,"tag":140,"props":3969,"children":3970},{"style":3665},[3971],{"type":39,"value":3972},"    \"default:automergeDigest\"\n",{"type":26,"tag":140,"props":3974,"children":3976},{"class":3975,"line":231},[142,310],[3977],{"type":26,"tag":140,"props":3978,"children":3979},{"style":254},[3980],{"type":39,"value":3981},"  ],\n",{"type":26,"tag":140,"props":3983,"children":3985},{"class":3984,"line":329},[142,310],[3986,3991,3995,4000],{"type":26,"tag":140,"props":3987,"children":3988},{"style":3655},[3989],{"type":39,"value":3990},"  \"automergeType\"",{"type":26,"tag":140,"props":3992,"children":3993},{"style":254},[3994],{"type":39,"value":321},{"type":26,"tag":140,"props":3996,"children":3997},{"style":3665},[3998],{"type":39,"value":3999},"\"branch\"",{"type":26,"tag":140,"props":4001,"children":4002},{"style":254},[4003],{"type":39,"value":3673},{"type":26,"tag":140,"props":4005,"children":4007},{"class":4006,"line":353},[142,310],[4008,4013,4017],{"type":26,"tag":140,"props":4009,"children":4010},{"style":3655},[4011],{"type":39,"value":4012},"  \"ignoreTests\"",{"type":26,"tag":140,"props":4014,"children":4015},{"style":254},[4016],{"type":39,"value":321},{"type":26,"tag":140,"props":4018,"children":4019},{"style":163},[4020],{"type":39,"value":4021},"true\n",{"type":26,"tag":140,"props":4023,"children":4024},{"class":142,"line":370},[4025],{"type":26,"tag":140,"props":4026,"children":4027},{"style":254},[4028],{"type":39,"value":3710},{"type":26,"tag":35,"props":4030,"children":4031},{},[4032,4034,4040,4041],{"type":39,"value":4033},"This instructs Renovate to auto-merge the Digest updates without creating a merge request prior. This reduces noise since there is no merge request notification.\nYou can read more about ",{"type":26,"tag":52,"props":4035,"children":4037},{"href":4036},"https://docs.renovatebot.com/key-concepts/automerge/#branch-vs-pr-automerging",[4038],{"type":39,"value":4039},"automergeType",{"type":39,"value":2634},{"type":26,"tag":52,"props":4042,"children":4044},{"href":4043},"https://docs.renovatebot.com/key-concepts/automerge/#absence-of-tests",[4045],{"type":39,"value":4046},"ignoreTests",{"type":26,"tag":2424,"props":4048,"children":4049},{},[4050],{"type":39,"value":2428},{"title":7,"searchDepth":260,"depth":260,"links":4052},[4053,4054,4055,4056,4057],{"id":2666,"depth":260,"text":2669},{"id":2703,"depth":260,"text":2706},{"id":2744,"depth":260,"text":2747},{"id":3624,"depth":260,"text":3627},{"id":3718,"depth":260,"text":3721},{"_path":2456,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":2457,"description":2458,"author":10,"image":11,"releaseDate":2459,"blogCategories":4059,"articleTags":4060,"tags":4061,"body":4062,"_type":2438,"_id":2463,"_source":2440,"_file":2464,"_stem":2465,"_extension":2443},[14,15],[15],[21],{"type":23,"children":4063,"toc":5200},[4064,4075,4080,4205,4210,4283,4289,4316,4321,4345,4358,4376,4382,4399,4500,4505,4510,4535,4581,4584,4589,4929,4933,4969,4975,4983,5034,5038,5045,5073,5079,5084,5090,5095,5107,5115,5121,5133,5196],{"type":26,"tag":35,"props":4065,"children":4066},{},[4067,4073],{"type":26,"tag":52,"props":4068,"children":4070},{"href":4069},"https://doc.traefik.io/traefik/",[4071],{"type":39,"value":4072},"Traefik",{"type":39,"value":4074}," is a reverse proxy with excellent docker integration. It uses labeln attached to containers to route traffic to them.",{"type":26,"tag":35,"props":4076,"children":4077},{},[4078],{"type":39,"value":4079},"A common label set looks similar to this:",{"type":26,"tag":130,"props":4081,"children":4085},{"code":4082,"filename":4083,"highlights":4084,"language":237,"meta":7,"className":238,"style":7},"services:\n  whoami:\n    image: traefik/whoami\n    labels:\n      - \"traefik.enable=true\"\n      - \"traefik.http.routers.whoami.rule=Host(`whoami.example.com`)\"\n      - \"traefik.http.routers.whoami.entrypoints=websecure\"\n      - \"traefik.http.routers.whoami.tls=true\"\n      - \"traefik.http.routers.whoami.tls.certresolver=letsencrypt\"\n","whoami/docker-compose.yaml",[296,231,329,353,370],[4086],{"type":26,"tag":108,"props":4087,"children":4088},{"__ignoreMap":7},[4089,4100,4112,4128,4140,4153,4166,4179,4192],{"type":26,"tag":140,"props":4090,"children":4091},{"class":142,"line":143},[4092,4096],{"type":26,"tag":140,"props":4093,"children":4094},{"style":248},[4095],{"type":39,"value":2924},{"type":26,"tag":140,"props":4097,"children":4098},{"style":254},[4099],{"type":39,"value":257},{"type":26,"tag":140,"props":4101,"children":4102},{"class":142,"line":260},[4103,4108],{"type":26,"tag":140,"props":4104,"children":4105},{"style":248},[4106],{"type":39,"value":4107},"  whoami",{"type":26,"tag":140,"props":4109,"children":4110},{"style":254},[4111],{"type":39,"value":257},{"type":26,"tag":140,"props":4113,"children":4114},{"class":142,"line":274},[4115,4119,4123],{"type":26,"tag":140,"props":4116,"children":4117},{"style":248},[4118],{"type":39,"value":2947},{"type":26,"tag":140,"props":4120,"children":4121},{"style":254},[4122],{"type":39,"value":321},{"type":26,"tag":140,"props":4124,"children":4125},{"style":152},[4126],{"type":39,"value":4127},"traefik/whoami\n",{"type":26,"tag":140,"props":4129,"children":4130},{"class":142,"line":284},[4131,4136],{"type":26,"tag":140,"props":4132,"children":4133},{"style":248},[4134],{"type":39,"value":4135},"    labels",{"type":26,"tag":140,"props":4137,"children":4138},{"style":254},[4139],{"type":39,"value":257},{"type":26,"tag":140,"props":4141,"children":4143},{"class":4142,"line":296},[142,310],[4144,4148],{"type":26,"tag":140,"props":4145,"children":4146},{"style":254},[4147],{"type":39,"value":389},{"type":26,"tag":140,"props":4149,"children":4150},{"style":152},[4151],{"type":39,"value":4152},"\"traefik.enable=true\"\n",{"type":26,"tag":140,"props":4154,"children":4156},{"class":4155,"line":231},[142,310],[4157,4161],{"type":26,"tag":140,"props":4158,"children":4159},{"style":254},[4160],{"type":39,"value":389},{"type":26,"tag":140,"props":4162,"children":4163},{"style":152},[4164],{"type":39,"value":4165},"\"traefik.http.routers.whoami.rule=Host(`whoami.example.com`)\"\n",{"type":26,"tag":140,"props":4167,"children":4169},{"class":4168,"line":329},[142,310],[4170,4174],{"type":26,"tag":140,"props":4171,"children":4172},{"style":254},[4173],{"type":39,"value":389},{"type":26,"tag":140,"props":4175,"children":4176},{"style":152},[4177],{"type":39,"value":4178},"\"traefik.http.routers.whoami.entrypoints=websecure\"\n",{"type":26,"tag":140,"props":4180,"children":4182},{"class":4181,"line":353},[142,310],[4183,4187],{"type":26,"tag":140,"props":4184,"children":4185},{"style":254},[4186],{"type":39,"value":389},{"type":26,"tag":140,"props":4188,"children":4189},{"style":152},[4190],{"type":39,"value":4191},"\"traefik.http.routers.whoami.tls=true\"\n",{"type":26,"tag":140,"props":4193,"children":4195},{"class":4194,"line":370},[142,310],[4196,4200],{"type":26,"tag":140,"props":4197,"children":4198},{"style":254},[4199],{"type":39,"value":389},{"type":26,"tag":140,"props":4201,"children":4202},{"style":152},[4203],{"type":39,"value":4204},"\"traefik.http.routers.whoami.tls.certresolver=letsencrypt\"\n",{"type":26,"tag":35,"props":4206,"children":4207},{},[4208],{"type":39,"value":4209},"In this example:",{"type":26,"tag":2599,"props":4211,"children":4212},{},[4213,4224,4240,4245,4257,4262],{"type":26,"tag":46,"props":4214,"children":4215},{},[4216,4222],{"type":26,"tag":108,"props":4217,"children":4219},{"className":4218},[],[4220],{"type":39,"value":4221},"whoami",{"type":39,"value":4223}," the name of the \"main\" service (2)",{"type":26,"tag":46,"props":4225,"children":4226},{},[4227,4232,4234],{"type":26,"tag":108,"props":4228,"children":4230},{"className":4229},[],[4231],{"type":39,"value":4221},{"type":39,"value":4233}," the also the default ",{"type":26,"tag":52,"props":4235,"children":4237},{"href":4236},"https://docs.docker.com/compose/how-tos/project-name/",[4238],{"type":39,"value":4239},"compose project name",{"type":26,"tag":46,"props":4241,"children":4242},{},[4243],{"type":39,"value":4244},"Traefik is activated (5)",{"type":26,"tag":46,"props":4246,"children":4247},{},[4248,4250,4255],{"type":39,"value":4249},"It is exposed under ",{"type":26,"tag":108,"props":4251,"children":4253},{"className":4252},[],[4254],{"type":39,"value":4221},{"type":39,"value":4256}," subdomain (6)",{"type":26,"tag":46,"props":4258,"children":4259},{},[4260],{"type":39,"value":4261},"It is served over https (7)",{"type":26,"tag":46,"props":4263,"children":4264},{},[4265,4267,4273,4275,4281],{"type":39,"value":4266},"A preconfigured certresolver named ",{"type":26,"tag":108,"props":4268,"children":4270},{"className":4269},[],[4271],{"type":39,"value":4272},"letsencrypt",{"type":39,"value":4274}," is used for ",{"type":26,"tag":108,"props":4276,"children":4278},{"className":4277},[],[4279],{"type":39,"value":4280},"tls",{"type":39,"value":4282}," (8-9)",{"type":26,"tag":96,"props":4284,"children":4286},{"id":4285},"the-problem",[4287],{"type":39,"value":4288},"The problem",{"type":26,"tag":35,"props":4290,"children":4291},{},[4292,4294,4300,4302,4308,4310,4315],{"type":39,"value":4293},"In my ",{"type":26,"tag":52,"props":4295,"children":4297},{"href":4296},"/blog/gitops-docker-renovate",[4298],{"type":39,"value":4299},"other post about gitops with docker",{"type":39,"value":4301}," I introduced a concept of using ",{"type":26,"tag":108,"props":4303,"children":4305},{"className":4304},[],[4306],{"type":39,"value":4307},"git",{"type":39,"value":4309},"\nas the sources of truth for docker deployments using ",{"type":26,"tag":52,"props":4311,"children":4312},{"href":2637},[4313],{"type":39,"value":4314},"docker compose",{"type":39,"value":128},{"type":26,"tag":35,"props":4317,"children":4318},{},[4319],{"type":39,"value":4320},"Now there are some requirements to this approach:",{"type":26,"tag":2599,"props":4322,"children":4323},{},[4324,4335],{"type":26,"tag":46,"props":4325,"children":4326},{},[4327,4329],{"type":39,"value":4328},"every stack is exposer under ",{"type":26,"tag":108,"props":4330,"children":4332},{"className":4331},[],[4333],{"type":39,"value":4334},"\u003Cstack_name>.\u003Cyour_domain>",{"type":26,"tag":46,"props":4336,"children":4337},{},[4338,4340],{"type":39,"value":4339},"every stack is protected with ",{"type":26,"tag":108,"props":4341,"children":4343},{"className":4342},[],[4344],{"type":39,"value":4280},{"type":26,"tag":35,"props":4346,"children":4347},{},[4348,4350,4356],{"type":39,"value":4349},"We could of course edit every ",{"type":26,"tag":108,"props":4351,"children":4353},{"className":4352},[],[4354],{"type":39,"value":4355},"docker-compose.yaml",{"type":39,"value":4357}," file and add the required labels, but it quickly becomes obvious,\nthat all the labels are the same!",{"type":26,"tag":42,"props":4359,"children":4360},{},[4361,4366,4371],{"type":26,"tag":46,"props":4362,"children":4363},{},[4364],{"type":39,"value":4365},"Enable Traefik",{"type":26,"tag":46,"props":4367,"children":4368},{},[4369],{"type":39,"value":4370},"Assign a subdomain",{"type":26,"tag":46,"props":4372,"children":4373},{},[4374],{"type":39,"value":4375},"Enable HTTPS",{"type":26,"tag":96,"props":4377,"children":4379},{"id":4378},"the-solution",[4380],{"type":39,"value":4381},"The solution",{"type":26,"tag":35,"props":4383,"children":4384},{},[4385,4387,4397],{"type":39,"value":4386},"By using ",{"type":26,"tag":52,"props":4388,"children":4390},{"href":4389},"https://docs.docker.com/compose/how-tos/environment-variables/envvars/#compose_project_name",[4391],{"type":26,"tag":108,"props":4392,"children":4394},{"className":4393},[],[4395],{"type":39,"value":4396},"$COMPOSE_PROJECT_NAME",{"type":39,"value":4398},"\nvariable we can create a more generic template.\nThis ensures that the Traefik rules are consistent.",{"type":26,"tag":130,"props":4400,"children":4403},{"code":4401,"filename":4402,"language":237,"meta":7,"className":238,"style":7},"services:\n  \u003Cmain_service>:\n    labels:\n      - \"traefik.enable=true\"\n      - \"traefik.http.routers.$COMPOSE_PROJECT_NAME.rule=Host(`\u003Cstack_name>.example.com`)\"\n      - \"traefik.http.routers.$COMPOSE_PROJECT_NAME.entrypoints=websecure\"\n      - \"traefik.http.routers.$COMPOSE_PROJECT_NAME.tls=true\"\n      - \"traefik.http.routers.$COMPOSE_PROJECT_NAME.tls.certresolver=letsencrypt\"\n","\u003Cstack_name>/docker-compose.yaml",[4404],{"type":26,"tag":108,"props":4405,"children":4406},{"__ignoreMap":7},[4407,4418,4430,4441,4452,4464,4476,4488],{"type":26,"tag":140,"props":4408,"children":4409},{"class":142,"line":143},[4410,4414],{"type":26,"tag":140,"props":4411,"children":4412},{"style":248},[4413],{"type":39,"value":2924},{"type":26,"tag":140,"props":4415,"children":4416},{"style":254},[4417],{"type":39,"value":257},{"type":26,"tag":140,"props":4419,"children":4420},{"class":142,"line":260},[4421,4426],{"type":26,"tag":140,"props":4422,"children":4423},{"style":248},[4424],{"type":39,"value":4425},"  \u003Cmain_service>",{"type":26,"tag":140,"props":4427,"children":4428},{"style":254},[4429],{"type":39,"value":257},{"type":26,"tag":140,"props":4431,"children":4432},{"class":142,"line":274},[4433,4437],{"type":26,"tag":140,"props":4434,"children":4435},{"style":248},[4436],{"type":39,"value":4135},{"type":26,"tag":140,"props":4438,"children":4439},{"style":254},[4440],{"type":39,"value":257},{"type":26,"tag":140,"props":4442,"children":4443},{"class":142,"line":284},[4444,4448],{"type":26,"tag":140,"props":4445,"children":4446},{"style":254},[4447],{"type":39,"value":389},{"type":26,"tag":140,"props":4449,"children":4450},{"style":152},[4451],{"type":39,"value":4152},{"type":26,"tag":140,"props":4453,"children":4454},{"class":142,"line":296},[4455,4459],{"type":26,"tag":140,"props":4456,"children":4457},{"style":254},[4458],{"type":39,"value":389},{"type":26,"tag":140,"props":4460,"children":4461},{"style":152},[4462],{"type":39,"value":4463},"\"traefik.http.routers.$COMPOSE_PROJECT_NAME.rule=Host(`\u003Cstack_name>.example.com`)\"\n",{"type":26,"tag":140,"props":4465,"children":4466},{"class":142,"line":231},[4467,4471],{"type":26,"tag":140,"props":4468,"children":4469},{"style":254},[4470],{"type":39,"value":389},{"type":26,"tag":140,"props":4472,"children":4473},{"style":152},[4474],{"type":39,"value":4475},"\"traefik.http.routers.$COMPOSE_PROJECT_NAME.entrypoints=websecure\"\n",{"type":26,"tag":140,"props":4477,"children":4478},{"class":142,"line":329},[4479,4483],{"type":26,"tag":140,"props":4480,"children":4481},{"style":254},[4482],{"type":39,"value":389},{"type":26,"tag":140,"props":4484,"children":4485},{"style":152},[4486],{"type":39,"value":4487},"\"traefik.http.routers.$COMPOSE_PROJECT_NAME.tls=true\"\n",{"type":26,"tag":140,"props":4489,"children":4490},{"class":142,"line":353},[4491,4495],{"type":26,"tag":140,"props":4492,"children":4493},{"style":254},[4494],{"type":39,"value":389},{"type":26,"tag":140,"props":4496,"children":4497},{"style":152},[4498],{"type":39,"value":4499},"\"traefik.http.routers.$COMPOSE_PROJECT_NAME.tls.certresolver=letsencrypt\"\n",{"type":26,"tag":35,"props":4501,"children":4502},{},[4503],{"type":39,"value":4504},"The good news is that Traefik lets us configure some defaults that will cover the above boilerplate!",{"type":26,"tag":35,"props":4506,"children":4507},{},[4508],{"type":39,"value":4509},"Let's us define two simple conventions:",{"type":26,"tag":42,"props":4511,"children":4512},{},[4513,4524],{"type":26,"tag":46,"props":4514,"children":4515},{},[4516,4522],{"type":26,"tag":108,"props":4517,"children":4519},{"className":4518},[],[4520],{"type":39,"value":4521},"app",{"type":39,"value":4523}," is the main container, where Traefik will route the traffic",{"type":26,"tag":46,"props":4525,"children":4526},{},[4527,4533],{"type":26,"tag":108,"props":4528,"children":4530},{"className":4529},[],[4531],{"type":39,"value":4532},"\u003Cstack_name>",{"type":39,"value":4534}," (the compose project name) is the subdomain",{"type":26,"tag":130,"props":4536,"children":4539},{"code":4537,"filename":4083,"language":237,"meta":4538,"className":238,"style":7},"services:\n  app:\n    image: traefik/whoami\n","(1)",[4540],{"type":26,"tag":108,"props":4541,"children":4542},{"__ignoreMap":7},[4543,4554,4566],{"type":26,"tag":140,"props":4544,"children":4545},{"class":142,"line":143},[4546,4550],{"type":26,"tag":140,"props":4547,"children":4548},{"style":248},[4549],{"type":39,"value":2924},{"type":26,"tag":140,"props":4551,"children":4552},{"style":254},[4553],{"type":39,"value":257},{"type":26,"tag":140,"props":4555,"children":4556},{"class":142,"line":260},[4557,4562],{"type":26,"tag":140,"props":4558,"children":4559},{"style":248},[4560],{"type":39,"value":4561},"  app",{"type":26,"tag":140,"props":4563,"children":4564},{"style":254},[4565],{"type":39,"value":257},{"type":26,"tag":140,"props":4567,"children":4568},{"class":142,"line":274},[4569,4573,4577],{"type":26,"tag":140,"props":4570,"children":4571},{"style":248},[4572],{"type":39,"value":2947},{"type":26,"tag":140,"props":4574,"children":4575},{"style":254},[4576],{"type":39,"value":321},{"type":26,"tag":140,"props":4578,"children":4579},{"style":152},[4580],{"type":39,"value":4127},{"type":26,"tag":3438,"props":4582,"children":4583},{},[],{"type":26,"tag":35,"props":4585,"children":4586},{},[4587],{"type":39,"value":4588},"Now let's configure traefik to do \"the magic\"",{"type":26,"tag":130,"props":4590,"children":4594},{"code":4591,"filename":4592,"language":237,"meta":4593,"className":238,"style":7},"volumes:\n  letsencrypt:\n    \nservices:\n  traefik:\n    container_name: traefik\n    restart: always\n    image: traefik:3\n    network_mode: host\n    command:\n      - --certificatesresolvers.letsencrypt.acme.httpchallenge=true\n      - --certificatesresolvers.letsencrypt.acme.email=\u003Cyour_email_here>\n      - --certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json\n      - --certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web\n\n      - --entrypoints.web.address=:80\n      - --entrypoints.websecure.address=:443\n      \n      - --entrypoints.web.http.redirections.entrypoint.to=websecure\n      - --entrypoints.web.http.redirections.entrypoint.scheme=https\n      - --entrypoints.websecure.http.tls.certresolver=letsencrypt\n\n      - --providers.docker\n      - --providers.docker.defaultrule=Host(`{{ trimPrefix `app-` .Name }}.example.com`)\n      - --providers.docker.constraints=Label(`com.docker.compose.service`,`app`)\n    volumes:\n      - /var/run/docker.sock:/var/run/docker.sock:ro\n      - /letsencrypt:/letsencrypt\n","traefik/docker-compose.yaml","(2)",[4595],{"type":26,"tag":108,"props":4596,"children":4597},{"__ignoreMap":7},[4598,4609,4621,4628,4639,4651,4668,4684,4700,4717,4728,4740,4752,4764,4776,4783,4795,4807,4815,4827,4839,4851,4858,4870,4882,4894,4905,4917],{"type":26,"tag":140,"props":4599,"children":4600},{"class":142,"line":143},[4601,4605],{"type":26,"tag":140,"props":4602,"children":4603},{"style":248},[4604],{"type":39,"value":2881},{"type":26,"tag":140,"props":4606,"children":4607},{"style":254},[4608],{"type":39,"value":257},{"type":26,"tag":140,"props":4610,"children":4611},{"class":142,"line":260},[4612,4617],{"type":26,"tag":140,"props":4613,"children":4614},{"style":248},[4615],{"type":39,"value":4616},"  letsencrypt",{"type":26,"tag":140,"props":4618,"children":4619},{"style":254},[4620],{"type":39,"value":257},{"type":26,"tag":140,"props":4622,"children":4623},{"class":142,"line":274},[4624],{"type":26,"tag":140,"props":4625,"children":4626},{"style":254},[4627],{"type":39,"value":917},{"type":26,"tag":140,"props":4629,"children":4630},{"class":142,"line":284},[4631,4635],{"type":26,"tag":140,"props":4632,"children":4633},{"style":248},[4634],{"type":39,"value":2924},{"type":26,"tag":140,"props":4636,"children":4637},{"style":254},[4638],{"type":39,"value":257},{"type":26,"tag":140,"props":4640,"children":4641},{"class":142,"line":296},[4642,4647],{"type":26,"tag":140,"props":4643,"children":4644},{"style":248},[4645],{"type":39,"value":4646},"  traefik",{"type":26,"tag":140,"props":4648,"children":4649},{"style":254},[4650],{"type":39,"value":257},{"type":26,"tag":140,"props":4652,"children":4653},{"class":142,"line":231},[4654,4659,4663],{"type":26,"tag":140,"props":4655,"children":4656},{"style":248},[4657],{"type":39,"value":4658},"    container_name",{"type":26,"tag":140,"props":4660,"children":4661},{"style":254},[4662],{"type":39,"value":321},{"type":26,"tag":140,"props":4664,"children":4665},{"style":152},[4666],{"type":39,"value":4667},"traefik\n",{"type":26,"tag":140,"props":4669,"children":4670},{"class":142,"line":329},[4671,4675,4679],{"type":26,"tag":140,"props":4672,"children":4673},{"style":248},[4674],{"type":39,"value":2964},{"type":26,"tag":140,"props":4676,"children":4677},{"style":254},[4678],{"type":39,"value":321},{"type":26,"tag":140,"props":4680,"children":4681},{"style":152},[4682],{"type":39,"value":4683},"always\n",{"type":26,"tag":140,"props":4685,"children":4686},{"class":142,"line":353},[4687,4691,4695],{"type":26,"tag":140,"props":4688,"children":4689},{"style":248},[4690],{"type":39,"value":2947},{"type":26,"tag":140,"props":4692,"children":4693},{"style":254},[4694],{"type":39,"value":321},{"type":26,"tag":140,"props":4696,"children":4697},{"style":152},[4698],{"type":39,"value":4699},"traefik:3\n",{"type":26,"tag":140,"props":4701,"children":4702},{"class":142,"line":370},[4703,4708,4712],{"type":26,"tag":140,"props":4704,"children":4705},{"style":248},[4706],{"type":39,"value":4707},"    network_mode",{"type":26,"tag":140,"props":4709,"children":4710},{"style":254},[4711],{"type":39,"value":321},{"type":26,"tag":140,"props":4713,"children":4714},{"style":152},[4715],{"type":39,"value":4716},"host\n",{"type":26,"tag":140,"props":4718,"children":4719},{"class":142,"line":383},[4720,4724],{"type":26,"tag":140,"props":4721,"children":4722},{"style":248},[4723],{"type":39,"value":3065},{"type":26,"tag":140,"props":4725,"children":4726},{"style":254},[4727],{"type":39,"value":257},{"type":26,"tag":140,"props":4729,"children":4730},{"class":142,"line":397},[4731,4735],{"type":26,"tag":140,"props":4732,"children":4733},{"style":254},[4734],{"type":39,"value":389},{"type":26,"tag":140,"props":4736,"children":4737},{"style":152},[4738],{"type":39,"value":4739},"--certificatesresolvers.letsencrypt.acme.httpchallenge=true\n",{"type":26,"tag":140,"props":4741,"children":4742},{"class":142,"line":410},[4743,4747],{"type":26,"tag":140,"props":4744,"children":4745},{"style":254},[4746],{"type":39,"value":389},{"type":26,"tag":140,"props":4748,"children":4749},{"style":152},[4750],{"type":39,"value":4751},"--certificatesresolvers.letsencrypt.acme.email=\u003Cyour_email_here>\n",{"type":26,"tag":140,"props":4753,"children":4754},{"class":142,"line":423},[4755,4759],{"type":26,"tag":140,"props":4756,"children":4757},{"style":254},[4758],{"type":39,"value":389},{"type":26,"tag":140,"props":4760,"children":4761},{"style":152},[4762],{"type":39,"value":4763},"--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json\n",{"type":26,"tag":140,"props":4765,"children":4766},{"class":142,"line":232},[4767,4771],{"type":26,"tag":140,"props":4768,"children":4769},{"style":254},[4770],{"type":39,"value":389},{"type":26,"tag":140,"props":4772,"children":4773},{"style":152},[4774],{"type":39,"value":4775},"--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web\n",{"type":26,"tag":140,"props":4777,"children":4778},{"class":142,"line":233},[4779],{"type":26,"tag":140,"props":4780,"children":4781},{"emptyLinePlaceholder":278},[4782],{"type":39,"value":281},{"type":26,"tag":140,"props":4784,"children":4785},{"class":142,"line":234},[4786,4790],{"type":26,"tag":140,"props":4787,"children":4788},{"style":254},[4789],{"type":39,"value":389},{"type":26,"tag":140,"props":4791,"children":4792},{"style":152},[4793],{"type":39,"value":4794},"--entrypoints.web.address=:80\n",{"type":26,"tag":140,"props":4796,"children":4797},{"class":142,"line":235},[4798,4802],{"type":26,"tag":140,"props":4799,"children":4800},{"style":254},[4801],{"type":39,"value":389},{"type":26,"tag":140,"props":4803,"children":4804},{"style":152},[4805],{"type":39,"value":4806},"--entrypoints.websecure.address=:443\n",{"type":26,"tag":140,"props":4808,"children":4809},{"class":142,"line":236},[4810],{"type":26,"tag":140,"props":4811,"children":4812},{"style":254},[4813],{"type":39,"value":4814},"      \n",{"type":26,"tag":140,"props":4816,"children":4817},{"class":142,"line":911},[4818,4822],{"type":26,"tag":140,"props":4819,"children":4820},{"style":254},[4821],{"type":39,"value":389},{"type":26,"tag":140,"props":4823,"children":4824},{"style":152},[4825],{"type":39,"value":4826},"--entrypoints.web.http.redirections.entrypoint.to=websecure\n",{"type":26,"tag":140,"props":4828,"children":4829},{"class":142,"line":617},[4830,4834],{"type":26,"tag":140,"props":4831,"children":4832},{"style":254},[4833],{"type":39,"value":389},{"type":26,"tag":140,"props":4835,"children":4836},{"style":152},[4837],{"type":39,"value":4838},"--entrypoints.web.http.redirections.entrypoint.scheme=https\n",{"type":26,"tag":140,"props":4840,"children":4841},{"class":142,"line":618},[4842,4846],{"type":26,"tag":140,"props":4843,"children":4844},{"style":254},[4845],{"type":39,"value":389},{"type":26,"tag":140,"props":4847,"children":4848},{"style":152},[4849],{"type":39,"value":4850},"--entrypoints.websecure.http.tls.certresolver=letsencrypt\n",{"type":26,"tag":140,"props":4852,"children":4853},{"class":142,"line":619},[4854],{"type":26,"tag":140,"props":4855,"children":4856},{"emptyLinePlaceholder":278},[4857],{"type":39,"value":281},{"type":26,"tag":140,"props":4859,"children":4860},{"class":142,"line":620},[4861,4865],{"type":26,"tag":140,"props":4862,"children":4863},{"style":254},[4864],{"type":39,"value":389},{"type":26,"tag":140,"props":4866,"children":4867},{"style":152},[4868],{"type":39,"value":4869},"--providers.docker\n",{"type":26,"tag":140,"props":4871,"children":4872},{"class":142,"line":992},[4873,4877],{"type":26,"tag":140,"props":4874,"children":4875},{"style":254},[4876],{"type":39,"value":389},{"type":26,"tag":140,"props":4878,"children":4879},{"style":152},[4880],{"type":39,"value":4881},"--providers.docker.defaultrule=Host(`{{ trimPrefix `app-` .Name }}.example.com`)\n",{"type":26,"tag":140,"props":4883,"children":4884},{"class":142,"line":1000},[4885,4889],{"type":26,"tag":140,"props":4886,"children":4887},{"style":254},[4888],{"type":39,"value":389},{"type":26,"tag":140,"props":4890,"children":4891},{"style":152},[4892],{"type":39,"value":4893},"--providers.docker.constraints=Label(`com.docker.compose.service`,`app`)\n",{"type":26,"tag":140,"props":4895,"children":4896},{"class":142,"line":1018},[4897,4901],{"type":26,"tag":140,"props":4898,"children":4899},{"style":248},[4900],{"type":39,"value":2981},{"type":26,"tag":140,"props":4902,"children":4903},{"style":254},[4904],{"type":39,"value":257},{"type":26,"tag":140,"props":4906,"children":4907},{"class":142,"line":621},[4908,4912],{"type":26,"tag":140,"props":4909,"children":4910},{"style":254},[4911],{"type":39,"value":389},{"type":26,"tag":140,"props":4913,"children":4914},{"style":152},[4915],{"type":39,"value":4916},"/var/run/docker.sock:/var/run/docker.sock:ro\n",{"type":26,"tag":140,"props":4918,"children":4919},{"class":142,"line":622},[4920,4924],{"type":26,"tag":140,"props":4921,"children":4922},{"style":254},[4923],{"type":39,"value":389},{"type":26,"tag":140,"props":4925,"children":4926},{"style":152},[4927],{"type":39,"value":4928},"/letsencrypt:/letsencrypt\n",{"type":26,"tag":35,"props":4930,"children":4931},{},[4932],{"type":39,"value":1441},{"type":26,"tag":42,"props":4934,"children":4935},{},[4936,4949,4954,4959,4964],{"type":26,"tag":46,"props":4937,"children":4938},{},[4939,4941,4947],{"type":39,"value":4940},"We configure a ",{"type":26,"tag":52,"props":4942,"children":4944},{"href":4943},"https://letsencrypt.org/",[4945],{"type":39,"value":4946},"Let's Encrypt",{"type":39,"value":4948}," certificates resolvers (11-14)",{"type":26,"tag":46,"props":4950,"children":4951},{},[4952],{"type":39,"value":4953},"We listen on port 80 and 443 (16-17)",{"type":26,"tag":46,"props":4955,"children":4956},{},[4957],{"type":39,"value":4958},"We redirect all http traffic to https (19-20)",{"type":26,"tag":46,"props":4960,"children":4961},{},[4962],{"type":39,"value":4963},"and attach the configured certificates resolvers to it (21)",{"type":26,"tag":46,"props":4965,"children":4966},{},[4967],{"type":39,"value":4968},"We setup the docker provider (23-25)",{"type":26,"tag":215,"props":4970,"children":4972},{"id":4971},"explanation-for-lines-24-25",[4973],{"type":39,"value":4974},"Explanation for lines 24-25",{"type":26,"tag":130,"props":4976,"children":4978},{"code":4977},"--providers.docker.defaultrule=Host(`{{ trimPrefix 'app-'.Name }}.example.com`)\n",[4979],{"type":26,"tag":108,"props":4980,"children":4981},{"__ignoreMap":7},[4982],{"type":39,"value":4977},{"type":26,"tag":2599,"props":4984,"children":4985},{},[4986,5018],{"type":26,"tag":46,"props":4987,"children":4988},{},[4989,4995,4997,5003,5005,5010,5012],{"type":26,"tag":108,"props":4990,"children":4992},{"className":4991},[],[4993],{"type":39,"value":4994},".Name",{"type":39,"value":4996}," is autogenerated as ",{"type":26,"tag":108,"props":4998,"children":5000},{"className":4999},[],[5001],{"type":39,"value":5002},"\u003Cservice-name>-\u003Cstack_name>",{"type":39,"value":5004}," so for your ",{"type":26,"tag":108,"props":5006,"children":5008},{"className":5007},[],[5009],{"type":39,"value":4221},{"type":39,"value":5011}," example it would be ",{"type":26,"tag":108,"props":5013,"children":5015},{"className":5014},[],[5016],{"type":39,"value":5017},"app-whoami",{"type":26,"tag":46,"props":5019,"children":5020},{},[5021,5027,5029],{"type":26,"tag":108,"props":5022,"children":5024},{"className":5023},[],[5025],{"type":39,"value":5026},"trimPrefix 'app-'.Name",{"type":39,"value":5028}," resolves in ",{"type":26,"tag":108,"props":5030,"children":5032},{"className":5031},[],[5033],{"type":39,"value":4221},{"type":26,"tag":5035,"props":5036,"children":5037},"br",{},[],{"type":26,"tag":130,"props":5039,"children":5040},{"code":4893},[5041],{"type":26,"tag":108,"props":5042,"children":5043},{"__ignoreMap":7},[5044],{"type":39,"value":4893},{"type":26,"tag":35,"props":5046,"children":5047},{},[5048,5050,5055,5057,5063,5065,5071],{"type":39,"value":5049},"All services should be exposed by default but should be filtered down, only to ",{"type":26,"tag":108,"props":5051,"children":5053},{"className":5052},[],[5054],{"type":39,"value":4521},{"type":39,"value":5056}," services. The label ",{"type":26,"tag":108,"props":5058,"children":5060},{"className":5059},[],[5061],{"type":39,"value":5062},"com.docker.compose.service",{"type":39,"value":5064}," is added by ",{"type":26,"tag":108,"props":5066,"children":5068},{"className":5067},[],[5069],{"type":39,"value":5070},"docker-compose",{"type":39,"value":5072}," to all containers .",{"type":26,"tag":96,"props":5074,"children":5076},{"id":5075},"bonus-configuration",[5077],{"type":39,"value":5078},"Bonus configuration",{"type":26,"tag":35,"props":5080,"children":5081},{},[5082],{"type":39,"value":5083},"We can tweak this configuration even more.",{"type":26,"tag":215,"props":5085,"children":5087},{"id":5086},"exposing-other-services-in-the-stack",[5088],{"type":39,"value":5089},"Exposing other services in the stack",{"type":26,"tag":35,"props":5091,"children":5092},{},[5093],{"type":39,"value":5094},"Sometimes you want to expose more than just the app container.",{"type":26,"tag":35,"props":5096,"children":5097},{},[5098,5100,5105],{"type":39,"value":5099},"The current configuration won't route traffic to any other services other than ",{"type":26,"tag":108,"props":5101,"children":5103},{"className":5102},[],[5104],{"type":39,"value":4521},{"type":39,"value":5106},".\nTo still be able to use the default configuration method, we need to re-enable it.",{"type":26,"tag":130,"props":5108,"children":5110},{"code":5109},"--providers.docker.constraints=Label(`com.docker.compose.service`,`app`) || Label(`traefik.enable`, `true`)\n",[5111],{"type":26,"tag":108,"props":5112,"children":5113},{"__ignoreMap":7},[5114],{"type":39,"value":5109},{"type":26,"tag":215,"props":5116,"children":5118},{"id":5117},"stack-name-other-than-directory-name",[5119],{"type":39,"value":5120},"Stack name other than directory name",{"type":26,"tag":35,"props":5122,"children":5123},{},[5124,5126,5131],{"type":39,"value":5125},"When deploying the stacks, the name is generated based on directory name where the ",{"type":26,"tag":108,"props":5127,"children":5129},{"className":5128},[],[5130],{"type":39,"value":4355},{"type":39,"value":5132}," file is located.\nWe can change the make in several ways, but here is the easiest one:",{"type":26,"tag":130,"props":5134,"children":5138},{"code":5135,"filename":5136,"highlights":5137,"language":237,"meta":7,"className":238,"style":7},"name: whoami\nservices:\n  app:\n    image: traefik/whoami\n","whoami-example/docker-compose.yaml",[143],[5139],{"type":26,"tag":108,"props":5140,"children":5141},{"__ignoreMap":7},[5142,5159,5170,5181],{"type":26,"tag":140,"props":5143,"children":5145},{"class":5144,"line":143},[142,310],[5146,5150,5154],{"type":26,"tag":140,"props":5147,"children":5148},{"style":248},[5149],{"type":39,"value":768},{"type":26,"tag":140,"props":5151,"children":5152},{"style":254},[5153],{"type":39,"value":321},{"type":26,"tag":140,"props":5155,"children":5156},{"style":152},[5157],{"type":39,"value":5158},"whoami\n",{"type":26,"tag":140,"props":5160,"children":5161},{"class":142,"line":260},[5162,5166],{"type":26,"tag":140,"props":5163,"children":5164},{"style":248},[5165],{"type":39,"value":2924},{"type":26,"tag":140,"props":5167,"children":5168},{"style":254},[5169],{"type":39,"value":257},{"type":26,"tag":140,"props":5171,"children":5172},{"class":142,"line":274},[5173,5177],{"type":26,"tag":140,"props":5174,"children":5175},{"style":248},[5176],{"type":39,"value":4561},{"type":26,"tag":140,"props":5178,"children":5179},{"style":254},[5180],{"type":39,"value":257},{"type":26,"tag":140,"props":5182,"children":5183},{"class":142,"line":284},[5184,5188,5192],{"type":26,"tag":140,"props":5185,"children":5186},{"style":248},[5187],{"type":39,"value":2947},{"type":26,"tag":140,"props":5189,"children":5190},{"style":254},[5191],{"type":39,"value":321},{"type":26,"tag":140,"props":5193,"children":5194},{"style":152},[5195],{"type":39,"value":4127},{"type":26,"tag":2424,"props":5197,"children":5198},{},[5199],{"type":39,"value":2428},{"title":7,"searchDepth":260,"depth":260,"links":5201},[5202,5203,5206],{"id":4285,"depth":260,"text":4288},{"id":4378,"depth":260,"text":4381,"children":5204},[5205],{"id":4971,"depth":274,"text":4974},{"id":5075,"depth":260,"text":5078,"children":5207},[5208,5209],{"id":5086,"depth":274,"text":5089},{"id":5117,"depth":274,"text":5120},{"_path":2470,"_dir":2471,"_draft":6,"_partial":6,"_locale":7,"slug":10,"teams":5211,"primaryTeam":2474,"firstName":2475,"lastName":2476,"prefixTitle":7,"suffixTitle":2477,"education":5212,"role":5214,"workingSince":2488,"inTheCompanySince":2489,"techSkills":5215,"skills":5229,"projects":5239,"contactDetails":5244,"_image":2560,"image":2561,"_id":2562,"_type":2563,"title":2564,"_source":2471,"_file":2565,"_stem":2566,"_extension":2563},[2473,2474],[5213],[2480,2481,2482],[2484,2485,2486,2487],[5216,5217,5218,5219,5220,5221,5222,5223,5224,5225,5226,5227,5228],{"name":2492,"level":2493,"icon":2494},{"name":2496,"level":2493},{"name":2498,"level":2493,"icon":2499},{"name":2501,"level":2493,"icon":2502},{"name":2504,"level":2493,"icon":2505},{"name":2507,"level":2493},{"name":2509,"level":2493,"icon":2510},{"name":2512,"level":2493,"icon":2513},{"name":2515,"level":2516,"icon":2517},{"name":2519,"level":2516,"icon":2520},{"name":2522,"level":2516},{"name":2524,"level":2516,"icon":2525},{"name":2527,"level":2516,"icon":2528},[5230,5231,5232,5233,5234,5235,5236,5237,5238],{"name":113,"level":2493},{"name":2532,"level":2493},{"name":2474,"level":2493},{"name":2535,"level":2493},{"name":2537,"level":2493},{"name":2539,"level":2516},{"name":2541,"level":2516},{"name":2543,"level":2516},{"name":2545,"level":2516},[5240,5242,5243],{"project":2548,"position":5241},[2550,2551],{"project":2553,"position":2551},{"project":2555,"position":2551},{"eMail":2557,"phone":2558,"visibility":2559},[5246,5260,5272,5284],{"_path":5247,"_dir":5248,"_draft":6,"_partial":278,"_locale":7,"name":5249,"slug":5248,"text":5250,"hoverText":5251,"image":5252,"customer":5253,"tags":5254,"_id":5256,"_type":237,"title":5257,"_source":2440,"_file":5258,"_stem":5259,"_extension":237},"/en/portfolio/bitburger/_teaser","bitburger","Bitburger B2B and Brand Shops","Relaunch of Bitburger Brewery Group online shops with new platform","Migration of all brand shops and B2B shop of Bitburger Brewery Group to a unified platform. Ongoing support for redesign initiatives and integration of additional subsystems.","/images/portfolio/bitburger/Bitburger_HuW_Glaeser.jpg","Bitburger",[20,5255],"e-commerce","common:en:portfolio:900.bitburger:_teaser.yaml","Teaser","en/portfolio/900.bitburger/_teaser.yaml","en/portfolio/900.bitburger/_teaser",{"_path":5261,"_dir":5262,"_draft":6,"_partial":278,"_locale":7,"name":5263,"slug":5262,"text":5264,"hoverText":5265,"image":5266,"customer":5267,"tags":5268,"_id":5269,"_type":237,"title":5257,"_source":2440,"_file":5270,"_stem":5271,"_extension":237},"/en/portfolio/purize/_teaser","purize","Purize Filters","E-Commerce for high-quality activated carbon filters","We support PURIZE® Filters with a complete e-commerce service including fulfillment solution and connection to shipping service providers. Our expertise in online retail enables the manufacturer of high-quality activated carbon filters \"Made in Germany\" to process their orders smoothly.","/images/portfolio/barcode-machine-verdandijpg.jpg","PURIZE® Filters",[20,5255],"common:en:portfolio:9000.purize:_teaser.yaml","en/portfolio/9000.purize/_teaser.yaml","en/portfolio/9000.purize/_teaser",{"_path":5273,"_dir":5274,"_draft":6,"_partial":278,"_locale":7,"name":5275,"slug":5274,"text":5276,"hoverText":5277,"image":5278,"customer":5275,"tags":5279,"_id":5281,"_type":237,"title":5257,"_source":2440,"_file":5282,"_stem":5283,"_extension":237},"/en/portfolio/pixelx/_teaser","pixelx","PixelX","IT Security with Precision and Expertise","For PixelX, we conducted a targeted security analysis where, thanks to our deep technical understanding, we were able to identify a critical SQL injection vulnerability. With minimal time investment, we achieved maximum security gain.","/images/portfolio/pixelx/pixelx_secured.png",[5280,21],"security","common:en:portfolio:9010.pixelx:_teaser.yaml","en/portfolio/9010.pixelx/_teaser.yaml","en/portfolio/9010.pixelx/_teaser",{"_path":5285,"_dir":5286,"_draft":6,"_partial":278,"_locale":7,"name":5287,"slug":5286,"text":5288,"hoverText":5289,"image":5290,"customer":5287,"tags":5291,"_id":5292,"_type":237,"title":5257,"_source":2440,"_file":5293,"_stem":5294,"_extension":237},"/en/portfolio/slimspots/_teaser","slimspots","SlimSpots","Processing large amounts of data in real-time","For SlimSpots, a global provider of ad marketing solutions, we developed a highly scalable infrastructure that enables the processing of trillions of data records in real-time.","/images/portfolio/slimspots/slim_spots_prtfolio.png",[2541,21],"common:en:portfolio:9020.slimspots:_teaser.yaml","en/portfolio/9020.slimspots/_teaser.yaml","en/portfolio/9020.slimspots/_teaser",1782284058031]