[{"data":1,"prerenderedAt":1505},["ShallowReactive",2],{"/blog/solving-gitlabs-changelog-conflict-crisis":3,"navigation-en-us":719,"banner-en-us":1146,"footer-en-us":1156,"blog-post-authors-en-us-Robert Speicher":1401,"blog-related-posts-en-us-solving-gitlabs-changelog-conflict-crisis":1415,"blog-promotions-en-us":1441,"next-steps-en-us":1495},{"id":4,"title":5,"authors":6,"body":8,"category":699,"date":700,"description":701,"extension":702,"externalUrl":703,"faq":703,"featured":704,"heroImage":705,"meta":706,"navigation":114,"path":707,"seo":708,"slug":712,"stem":713,"tags":714,"template":717,"updatedDate":703,"__hash__":718},"blogPosts/en-us/blog/solving-gitlabs-changelog-conflict-crisis.md","How we solved GitLab's CHANGELOG conflict crisis",[7],"Robert Speicher",{"type":9,"value":10,"toc":690},"minimark",[11,28,31,34,42,180,187,260,268,325,328,413,416,423,431,434,437,448,453,472,483,490,494,501,523,530,537,541,544,568,575,579,582,592,596,602,605,626,630,638,650,654,661,672,686],[12,13,14,15,19,20,23,24,27],"p",{},"Since its ",[16,17,18],"span",{},"very first commit"," more than six years ago, GitLab has had a changelog\ndetailing the noteworthy changes in each release. Shortly after ",[16,21,22],{},"Enterprise\nEdition (EE) was introduced",", it ",[16,25,26],{},"got a changelog of its own",". Whenever anyone\n– whether it was a community contributor or a GitLab employee – contributed a\nnew feature or fix to the project, a changelog entry would be added to let users\nknow what improved.",[12,29,30],{},"As GitLab gained in popularity and started receiving more contributions, we'd\nconstantly see merge conflicts in the changelog when multiple merge requests\nattempted to add an entry to the list. This quickly became a major source of\ndelays in development, as contributors would have to rebase their branch in order\nto resolve the conflicts.",[12,32,33],{},"This post outlines how we completely eliminated changelog-related merge\nconflicts, removed bottlenecks for contributions, and automated a crucial part\nof our release process.",[12,35,36,37,41],{},"At the beginning, GitLab's ",[38,39,40],"code",{},"CHANGELOG"," file would look something like this:",[43,44,49],"pre",{"className":45,"code":46,"language":47,"meta":48,"style":48},"language-markdown shiki shiki-themes github-light","v 8.0.0 (unreleased)\n  - Prevent anchors from being hidden by header (Stan Hu)\n  - Remove satellites\n  - Better performance for web editor (switched from satellites to rugged)\n  - Faster merge\n  - ...\n  - Ability to fetch merge requests from refs/merge-requests/:id\n\nv 7.14.1\n  - Improve abuse reports management from admin area\n  - Ability to enable SSL verification for Webhooks\n\nv 7.14.0\n  - Fix bug where non-project members of the target project could set labels on new merge requests.\n  - Upgrade gitlab_git to 7.2.14 to ignore CRLFs in .gitmodules (Stan Hu)\n  - ...\n  - Fix broken code import and display error messages if something went wrong with creating project (Stan Hu)\n\n","markdown","",[38,50,51,59,69,77,85,93,101,109,116,122,130,138,143,149,157,165,172],{"__ignoreMap":48},[16,52,55],{"class":53,"line":54},"line",1,[16,56,58],{"class":57},"sgsFI","v 8.0.0 (unreleased)\n",[16,60,62,66],{"class":53,"line":61},2,[16,63,65],{"class":64},"sqxcx","  -",[16,67,68],{"class":57}," Prevent anchors from being hidden by header (Stan Hu)\n",[16,70,72,74],{"class":53,"line":71},3,[16,73,65],{"class":64},[16,75,76],{"class":57}," Remove satellites\n",[16,78,80,82],{"class":53,"line":79},4,[16,81,65],{"class":64},[16,83,84],{"class":57}," Better performance for web editor (switched from satellites to rugged)\n",[16,86,88,90],{"class":53,"line":87},5,[16,89,65],{"class":64},[16,91,92],{"class":57}," Faster merge\n",[16,94,96,98],{"class":53,"line":95},6,[16,97,65],{"class":64},[16,99,100],{"class":57}," ...\n",[16,102,104,106],{"class":53,"line":103},7,[16,105,65],{"class":64},[16,107,108],{"class":57}," Ability to fetch merge requests from refs/merge-requests/:id\n",[16,110,112],{"class":53,"line":111},8,[16,113,115],{"emptyLinePlaceholder":114},true,"\n",[16,117,119],{"class":53,"line":118},9,[16,120,121],{"class":57},"v 7.14.1\n",[16,123,125,127],{"class":53,"line":124},10,[16,126,65],{"class":64},[16,128,129],{"class":57}," Improve abuse reports management from admin area\n",[16,131,133,135],{"class":53,"line":132},11,[16,134,65],{"class":64},[16,136,137],{"class":57}," Ability to enable SSL verification for Webhooks\n",[16,139,141],{"class":53,"line":140},12,[16,142,115],{"emptyLinePlaceholder":114},[16,144,146],{"class":53,"line":145},13,[16,147,148],{"class":57},"v 7.14.0\n",[16,150,152,154],{"class":53,"line":151},14,[16,153,65],{"class":64},[16,155,156],{"class":57}," Fix bug where non-project members of the target project could set labels on new merge requests.\n",[16,158,160,162],{"class":53,"line":159},15,[16,161,65],{"class":64},[16,163,164],{"class":57}," Upgrade gitlab_git to 7.2.14 to ignore CRLFs in .gitmodules (Stan Hu)\n",[16,166,168,170],{"class":53,"line":167},16,[16,169,65],{"class":64},[16,171,100],{"class":57},[16,173,175,177],{"class":53,"line":174},17,[16,176,65],{"class":64},[16,178,179],{"class":57}," Fix broken code import and display error messages if something went wrong with creating project (Stan Hu)\n",[12,181,182,183,186],{},"When a developer made a change in the upcoming release, ",[38,184,185],{},"8.0.0"," in this example,\nthey would add a changelog entry at the bottom:",[43,188,192],{"className":189,"code":190,"language":191,"meta":48,"style":48},"language-diff shiki shiki-themes github-light","diff --git a/CHANGELOG b/CHANGELOG\nindex de2066f..0fc2c18 100644\n--- a/CHANGELOG\n+++ b/CHANGELOG\n@@ -5,6 +5,7 @@ v 8.0.0 (unreleased)\n   - Faster merge\n   - ...\n   - Ability to fetch merge requests from refs/merge-requests/:id\n+  - Made literally everything better. Evvvvverything!\n\n v 7.14.1\n   - Improve abuse reports management from admin area\n\n","diff",[38,193,194,200,205,211,217,226,231,236,241,246,250,255],{"__ignoreMap":48},[16,195,196],{"class":53,"line":54},[16,197,199],{"class":198},"sYu0t","diff --git a/CHANGELOG b/CHANGELOG\n",[16,201,202],{"class":53,"line":61},[16,203,204],{"class":57},"index de2066f..0fc2c18 100644\n",[16,206,207],{"class":53,"line":71},[16,208,210],{"class":209},"s9hmT","--- a/CHANGELOG\n",[16,212,213],{"class":53,"line":79},[16,214,216],{"class":215},"shJU0","+++ b/CHANGELOG\n",[16,218,219,223],{"class":53,"line":87},[16,220,222],{"class":221},"soKTg","@@ -5,6 +5,7 @@",[16,224,225],{"class":57}," v 8.0.0 (unreleased)\n",[16,227,228],{"class":53,"line":95},[16,229,230],{"class":57},"   - Faster merge\n",[16,232,233],{"class":53,"line":103},[16,234,235],{"class":57},"   - ...\n",[16,237,238],{"class":53,"line":111},[16,239,240],{"class":57},"   - Ability to fetch merge requests from refs/merge-requests/:id\n",[16,242,243],{"class":53,"line":118},[16,244,245],{"class":215},"+  - Made literally everything better. Evvvvverything!\n",[16,247,248],{"class":53,"line":124},[16,249,115],{"emptyLinePlaceholder":114},[16,251,252],{"class":53,"line":132},[16,253,254],{"class":57}," v 7.14.1\n",[16,256,257],{"class":53,"line":140},[16,258,259],{"class":57},"   - Improve abuse reports management from admin area\n",[12,261,262,263,267],{},"At the same time, another developer might have made a similar change in ",[264,265,266],"em",{},"their","\nbranch:",[43,269,271],{"className":189,"code":270,"language":191,"meta":48,"style":48},"diff --git a/CHANGELOG b/CHANGELOG\nindex de2066f..5f81cfd 100644\n--- a/CHANGELOG\n+++ b/CHANGELOG\n@@ -5,6 +5,7 @@ v 8.0.0 (unreleased)\n   - Faster merge\n   - ...\n   - Ability to fetch merge requests from refs/merge-requests/:id\n+  - Made a few things worse. Woops!\n\n v 7.14.1\n   - Improve abuse reports management from admin area\n\n",[38,272,273,277,282,286,290,296,300,304,308,313,317,321],{"__ignoreMap":48},[16,274,275],{"class":53,"line":54},[16,276,199],{"class":198},[16,278,279],{"class":53,"line":61},[16,280,281],{"class":57},"index de2066f..5f81cfd 100644\n",[16,283,284],{"class":53,"line":71},[16,285,210],{"class":209},[16,287,288],{"class":53,"line":79},[16,289,216],{"class":215},[16,291,292,294],{"class":53,"line":87},[16,293,222],{"class":221},[16,295,225],{"class":57},[16,297,298],{"class":53,"line":95},[16,299,230],{"class":57},[16,301,302],{"class":53,"line":103},[16,303,235],{"class":57},[16,305,306],{"class":53,"line":111},[16,307,240],{"class":57},[16,309,310],{"class":53,"line":118},[16,311,312],{"class":215},"+  - Made a few things worse. Woops!\n",[16,314,315],{"class":53,"line":124},[16,316,115],{"emptyLinePlaceholder":114},[16,318,319],{"class":53,"line":132},[16,320,254],{"class":57},[16,322,323],{"class":53,"line":140},[16,324,259],{"class":57},[12,326,327],{},"Now when one branch was merged, it'd create a conflict in the other:",[43,329,331],{"className":189,"code":330,"language":191,"meta":48,"style":48},"diff --cc CHANGELOG\nindex 5f81cfd,0fc2c18..0000000\n--- a/CHANGELOG\n+++ b/CHANGELOG\n@@@ -5,7 -5,7 +5,11 @@@ v 8.0.0 (unreleased\n    - Faster merge\n    - ...\n    - Ability to fetch merge requests from refs/merge-requests/:id\n++\u003C\u003C\u003C\u003C\u003C\u003C\u003C HEAD\n +  - Made a few things worse. Woops!\n++=======\n+   - Made literally everything better. Evvvvverything!\n++>>>>>>> developer-1\n\n  v 7.14.1\n    - Improve abuse reports management from admin area\n\n",[38,332,333,338,343,347,351,359,364,369,374,379,384,389,394,399,403,408],{"__ignoreMap":48},[16,334,335],{"class":53,"line":54},[16,336,337],{"class":198},"diff --cc CHANGELOG\n",[16,339,340],{"class":53,"line":61},[16,341,342],{"class":57},"index 5f81cfd,0fc2c18..0000000\n",[16,344,345],{"class":53,"line":71},[16,346,210],{"class":209},[16,348,349],{"class":53,"line":79},[16,350,216],{"class":215},[16,352,353,356],{"class":53,"line":87},[16,354,355],{"class":221},"@@@ -5,7 -5,7 +5,11 @@",[16,357,358],{"class":57},"@ v 8.0.0 (unreleased\n",[16,360,361],{"class":53,"line":95},[16,362,363],{"class":57},"    - Faster merge\n",[16,365,366],{"class":53,"line":103},[16,367,368],{"class":57},"    - ...\n",[16,370,371],{"class":53,"line":111},[16,372,373],{"class":57},"    - Ability to fetch merge requests from refs/merge-requests/:id\n",[16,375,376],{"class":53,"line":118},[16,377,378],{"class":215},"++\u003C\u003C\u003C\u003C\u003C\u003C\u003C HEAD\n",[16,380,381],{"class":53,"line":124},[16,382,383],{"class":57}," +  - Made a few things worse. Woops!\n",[16,385,386],{"class":53,"line":132},[16,387,388],{"class":215},"++=======\n",[16,390,391],{"class":53,"line":140},[16,392,393],{"class":215},"+   - Made literally everything better. Evvvvverything!\n",[16,395,396],{"class":53,"line":145},[16,397,398],{"class":215},"++>>>>>>> developer-1\n",[16,400,401],{"class":53,"line":151},[16,402,115],{"emptyLinePlaceholder":114},[16,404,405],{"class":53,"line":159},[16,406,407],{"class":57},"  v 7.14.1\n",[16,409,410],{"class":53,"line":167},[16,411,412],{"class":57},"    - Improve abuse reports management from admin area\n",[12,414,415],{},"This resulted in a ton of wasted time as something would get merged, and then\nevery other open branch adding a changelog entry would need to be rebased. The\nsituation only got worse as the number of contributors to GitLab grew over time.",[12,417,418,419,422],{},"Our initial, ",[16,420,421],{},"boring solution"," to the problem was to begin adding empty\nplaceholder entries at the beginning of each monthly release cycle. The\nchangelog for the upcoming unreleased version might look like this:",[43,424,429],{"className":425,"code":427,"language":428,"meta":48},[426],"language-text","v8.1.0 (unreleased)\n  -\n  -\n  -\n  -\n  -\n  -\n  -\n  - (and so on)\n\n","text",[38,430,427],{"__ignoreMap":48},[12,432,433],{},"A developer would make their change and then choose a random spot in the list to\nadd a changelog entry. This worked for a while, until the placeholders began to\nbe filled out as we got closer to the release date. Eventually two (or more)\nmerge requests would attempt to add different entries at the same placeholder,\nand one being merged created a conflict in the others.",[12,435,436],{},"The problem was lessened, but not solved.",[12,438,439,440,443,444,447],{},"Not only was this a huge waste of time for developers, it created an additional\nheadache for ",[16,441,442],{},"release managers"," when they cherry-picked a commit into a stable\nbranch for a patch release. If the commit included a changelog entry, which any\nchange intended for a patch release ",[264,445,446],{},"should"," have, cherry-picking that commit\nwould bring in the contents of the changelog at the point of that commit, often\nincluding dozens of unrelated changes. The release manager would have to\nmanually remove the unrelated entries, often doing this multiple times per\nrelease. This was compounded when we had to release multiple patch versions at\nonce due to a security issue.",[449,450,452],"h2",{"id":451},"brainstorming-solutions","Brainstorming solutions",[12,454,455,456,459,460,463,464,467,468,471],{},"Frustrations with the process finally reached a tipping point, and ",[16,457,458],{},"an issue was\ncreated"," to discuss a solution. Yorick had the ",[16,461,462],{},"original idea"," that would\nultimately form the foundation of our solution. During a ",[16,465,466],{},"trip around the\nworld",", myself, Douwe, and Marin were in Brooklyn, NY, and during a walk\naround the city one beautiful summer evening we ended up ",[16,469,470],{},"with a proposal"," to\nfinally solve the problem.",[12,473,474,475,478,479,482],{},"Each changelog entry would be its own YAML file in a ",[38,476,477],{},"CHANGELOG/unreleased","\nfolder. When a release manager went to cherry-pick a merge into a stable branch\nin preparation for a release, they'd use a custom script that would perform the\ncherry-pick and then move any changelog entry added by that action to a\nversion-specific subfolder, such as ",[38,480,481],{},"CHANGELOG/8.9.4",". At the time of release,\nany entries in the version's subfolder would be compiled into a single Markdown\nchangelog file, and then deleted.",[12,484,485,486,489],{},"With an idea of where we wanted to end up but no idea how to get there, I\nstarted with a ",[16,487,488],{},"spike",".",[449,491,493],{"id":492},"a-turning-point","A turning point",[12,495,496,497,500],{},"After a few days of working on the spike, I ",[16,498,499],{},"had a realization"," that we didn't\nneed the cherry-picking concept at all:",[502,503,504],"blockquote",{},[12,505,506,507,510,511,514,515,518,519,522],{},"Cherry picking a merge commit into a stable branch will add that merge's\n",[38,508,509],{},"CHANGELOG/unreleased/whatever-its-called.yml"," file to the stable branch. Upon\ntagging a release with release-tools, we can consider ",[264,512,513],{},"everything"," in that\nstable branch's \"unreleased\" folder as part of the tagged release. We collect\nthose files, compile them to Markdown, remove them from the stable branch\n",[264,516,517],{},"and"," ",[38,520,521],{},"master",", and that's our changelog for the release.",[12,524,525,526,529],{},"This was a major \"aha\" moment, as it greatly simplified the\nworkflow for release managers. They could continue their existing workflow, and\nthe release flow would transparently handle the rest. It also meant we could\nhandle everything in our ",[16,527,528],{},"release-tools"," project, which is responsible\nfor tagging a release and kicking off our packaging.",[12,531,532,533,536],{},"Even though we ended up not using a lot of the work that went into it, my\noriginal spike was still valuable. It allowed us to see pain points early on,\nrefine the process, and find a better solution. It also gave me additional\nexperience interacting with Git repositories programmatically via ",[16,534,535],{},"Rugged",", and\nthat would go on to be especially useful as we implemented the final tooling.",[449,538,540],{"id":539},"building-the-building-blocks","Building the building blocks",[12,542,543],{},"We knew there were several components that we'd need to build:",[545,546,547,551,554,561],"ol",{},[548,549,550],"li",{},"Something to read and represent the individual YAML data files",[548,552,553],{},"Something to compile individual entries into a Markdown list",[548,555,556,557,560],{},"Something to insert the compiled Markdown into the ",[264,558,559],{},"correct spot"," in an\nexisting list of releases",[548,562,563,564,567],{},"Something to remove the files that had been compiled, and then commit the\nupdated ",[38,565,566],{},"CHANGELOG.md"," file to the repository",[12,569,570,571,574],{},"All of these components were created in a ",[16,572,573],{},"single merge request"," and refined\nthrough several code review cycles. The commits listed there are all fairly\natomic and may be interesting to read through on their own. The code review that\nhappened in the merge request was incredibly valuable, and allowed us to really\nsimplify some code that was hard to wrap one's head around, even for me as the\noriginal author!",[449,576,578],{"id":577},"automated-testing","Automated testing",[12,580,581],{},"Of course, we wouldn't consider this solution complete until we had automated\ntests guaranteeing the behavior and consistency of the automated compilation,\nincluding reading from and writing to multiple branches across multiple\nrepositories.",[12,583,584,585,588,589,489],{},"I ended up using Rugged to create ",[16,586,587],{},"fixture repositories"," that would create a\nrepeatable testing environment, which we could then verify with ",[16,590,591],{},"custom RSpec\nmatchers",[449,593,595],{"id":594},"hooking-into-the-release-process","Hooking into the release process",[12,597,598,599,489],{},"At this point we were fairly confident the changelog compilation worked, so it\nwas time to ",[16,600,601],{},"hook it into our existing release process",[12,603,604],{},"While testing this integration on a real release, we uncovered a pretty\nhilarious (but dangerous) oversight. I'll let the commit that fixed it speak for\nitself:",[502,606,607,615],{},[12,608,609],{},[610,611,614],"a",{"href":612,"rel":613},"https://gitlab.com/gitlab-org/release-tools/merge_requests/47/diffs?commit_id=5b3fe48a7697bda856b6bed1fedc4c210439849b",[],"Protect against deleting everything when there are no changelog entries",[12,616,617,618,621,622,625],{},"On a stable branch with no changelog entry files, the resulting empty\narray was passed to ",[38,619,620],{},"Rugged::Index#remove_all"," which, when given an\nempty array, removes ",[623,624,513],"strong",{},". This was not ideal.",[449,627,629],{"id":628},"developer-tooling","Developer tooling",[12,631,632,633,489],{},"The final pieces of the puzzle were creating a tool to help developers create\nvalid changelog entries easily, and adding documentation. Both were handled in\n",[610,634,637],{"href":635,"rel":636},"https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7098",[],"this merge request",[12,639,640,641,644,645,489],{},"This tool allows developers to run ",[38,642,643],{},"bin/changelog",", passing it the title of\ntheir change, to generate a valid changelog entry file. Additional options are\n",[610,646,649],{"href":647,"rel":648},"https://docs.gitlab.com/development/changelog/",[],"in the documentation",[449,651,653],{"id":652},"future-plans","Future plans",[12,655,656,657,660],{},"This changelog process has worked beautifully for us since it was introduced,\nand we know it might be just as useful to other projects. We're ",[16,658,659],{},"investigating a\nway to make it more generic"," so that it can remove a tedious chore for more\ndevelopers.",[12,662,663,664,667,668,489],{},"I worked on this project as part of our Edge team, now known as the ",[16,665,666],{},"Quality\nteam",". If you're interested in this kind of internal tooling or other\nautomation, we're hiring! Check out our ",[610,669,671],{"href":670},"/jobs/","open positions",[12,673,674,675,680,681],{},"Photo by ",[610,676,679],{"href":677,"rel":678},"https://unsplash.com/photos/1S-PanVaJmU?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText",[],"Patrick Tomasso"," on ",[610,682,685],{"href":683,"rel":684},"https://unsplash.com/search/photos/abstract?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText",[],"Unsplash",[687,688,689],"style",{},"html pre.shiki code .sgsFI, html code.shiki .sgsFI{--shiki-default:#24292E}html pre.shiki code .sqxcx, html code.shiki .sqxcx{--shiki-default:#E36209}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 pre.shiki code .sYu0t, html code.shiki .sYu0t{--shiki-default:#005CC5}html pre.shiki code .s9hmT, html code.shiki .s9hmT{--shiki-default:#B31D28}html pre.shiki code .shJU0, html code.shiki .shJU0{--shiki-default:#22863A}html pre.shiki code .soKTg, html code.shiki .soKTg{--shiki-default:#6F42C1;--shiki-default-font-weight:bold}",{"title":48,"searchDepth":61,"depth":61,"links":691},[692,693,694,695,696,697,698],{"id":451,"depth":61,"text":452},{"id":492,"depth":61,"text":493},{"id":539,"depth":61,"text":540},{"id":577,"depth":61,"text":578},{"id":594,"depth":61,"text":595},{"id":628,"depth":61,"text":629},{"id":652,"depth":61,"text":653},"engineering","2018-07-03","How we eliminated changelog-related merge conflicts and automated a crucial part of our release process.","md",null,false,"https://res.cloudinary.com/about-gitlab-com/image/upload/v1749672139/Blog/Hero%20Images/solving-gitlab-changelog-crisis.jpg",{},"/en-us/blog/solving-gitlabs-changelog-conflict-crisis",{"title":5,"description":701,"ogTitle":5,"ogDescription":701,"noIndex":704,"ogImage":705,"ogUrl":709,"ogSiteName":710,"ogType":711,"canonicalUrls":709},"https://about.gitlab.com/blog/solving-gitlabs-changelog-conflict-crisis","https://about.gitlab.com","article","solving-gitlabs-changelog-conflict-crisis","en-us/blog/solving-gitlabs-changelog-conflict-crisis",[715,716],"inside GitLab","workflow","BlogPost","ZQ8oQh-7o4uU5FfctLF23yN3E7KhkBQ7zK0X7Dj5ooI",{"logo":720,"freeTrial":725,"sales":730,"login":735,"items":740,"search":1066,"minimal":1097,"duo":1116,"switchNav":1125,"pricingDeployment":1136},{"config":721},{"href":722,"dataGaName":723,"dataGaLocation":724},"/","gitlab logo","header",{"text":726,"config":727},"Get free trial",{"href":728,"dataGaName":729,"dataGaLocation":724},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com&glm_content=default-saas-trial/","free trial",{"text":731,"config":732},"Request a demo",{"href":733,"dataGaName":734,"dataGaLocation":724},"/sales/?contact-topic=request-demo","sales",{"text":736,"config":737},"Sign in",{"href":738,"dataGaName":739,"dataGaLocation":724},"https://gitlab.com/users/sign_in/","sign in",[741,770,870,875,989,1044],{"text":742,"config":743,"menu":745},"Platform",{"dataNavLevelOne":744},"platform",{"type":746,"columns":747},"cards",[748,754,762],{"title":742,"description":749,"link":750},"The intelligent orchestration platform for DevSecOps",{"text":751,"config":752},"Explore our Platform",{"href":753,"dataGaName":744,"dataGaLocation":724},"/platform/",{"title":755,"description":756,"link":757},"GitLab Duo Agent Platform","Agentic AI for the entire software lifecycle",{"text":758,"config":759},"Meet GitLab Duo",{"href":760,"dataGaName":761,"dataGaLocation":724},"/gitlab-duo-agent-platform/","gitlab duo agent platform",{"title":763,"description":764,"link":765},"Why GitLab","See the top reasons enterprises choose GitLab",{"text":766,"config":767},"Learn more",{"href":768,"dataGaName":769,"dataGaLocation":724},"/why-gitlab/","why gitlab",{"text":771,"left":114,"config":772,"menu":774},"Product",{"dataNavLevelOne":773},"solutions",{"type":775,"link":776,"columns":780,"feature":849},"lists",{"text":777,"config":778},"View all Solutions",{"href":779,"dataGaName":773,"dataGaLocation":724},"/solutions/",[781,805,828],{"title":782,"description":783,"link":784,"items":789},"Automation","CI/CD and automation to accelerate deployment",{"config":785},{"icon":786,"href":787,"dataGaName":788,"dataGaLocation":724},"AutomatedCodeAlt","/solutions/delivery-automation/","automated software delivery",[790,794,797,801],{"text":791,"config":792},"CI/CD",{"href":793,"dataGaLocation":724,"dataGaName":791},"/solutions/continuous-integration/",{"text":755,"config":795},{"href":760,"dataGaLocation":724,"dataGaName":796},"gitlab duo agent platform - product menu",{"text":798,"config":799},"Source Code Management",{"href":800,"dataGaLocation":724,"dataGaName":798},"/solutions/source-code-management/",{"text":802,"config":803},"Automated Software Delivery",{"href":787,"dataGaLocation":724,"dataGaName":804},"Automated software delivery",{"title":806,"description":807,"link":808,"items":813},"Security","Deliver code faster without compromising security",{"config":809},{"href":810,"dataGaName":811,"dataGaLocation":724,"icon":812},"/solutions/application-security-testing/","security and compliance","ShieldCheckLight",[814,818,823],{"text":815,"config":816},"Application Security Testing",{"href":810,"dataGaName":817,"dataGaLocation":724},"Application security testing",{"text":819,"config":820},"Software Supply Chain Security",{"href":821,"dataGaLocation":724,"dataGaName":822},"/solutions/supply-chain/","Software supply chain security",{"text":824,"config":825},"Software Compliance",{"href":826,"dataGaName":827,"dataGaLocation":724},"/solutions/software-compliance/","software compliance",{"title":829,"link":830,"items":835},"Measurement",{"config":831},{"icon":832,"href":833,"dataGaName":834,"dataGaLocation":724},"DigitalTransformation","/solutions/visibility-measurement/","visibility and measurement",[836,840,844],{"text":837,"config":838},"Visibility & Measurement",{"href":833,"dataGaLocation":724,"dataGaName":839},"Visibility and Measurement",{"text":841,"config":842},"Value Stream Management",{"href":843,"dataGaLocation":724,"dataGaName":841},"/solutions/value-stream-management/",{"text":845,"config":846},"Analytics & Insights",{"href":847,"dataGaLocation":724,"dataGaName":848},"/solutions/analytics-and-insights/","Analytics and insights",{"title":850,"type":775,"items":851},"GitLab for",[852,858,864],{"text":853,"config":854},"Enterprise",{"icon":855,"href":856,"dataGaLocation":724,"dataGaName":857},"Building","/enterprise/","enterprise",{"text":859,"config":860},"Small Business",{"icon":861,"href":862,"dataGaLocation":724,"dataGaName":863},"Work","/small-business/","small business",{"text":865,"config":866},"Public Sector",{"icon":867,"href":868,"dataGaLocation":724,"dataGaName":869},"Organization","/solutions/public-sector/","public sector",{"text":871,"config":872},"Pricing",{"href":873,"dataGaName":874,"dataGaLocation":724,"dataNavLevelOne":874},"/pricing/","pricing",{"text":876,"config":877,"menu":879},"Resources",{"dataNavLevelOne":878},"resources",{"type":775,"link":880,"columns":884,"feature":978},{"text":881,"config":882},"View all resources",{"href":883,"dataGaName":878,"dataGaLocation":724},"/resources/",[885,918,945],{"title":886,"items":887},"Getting started",[888,893,898,903,908,913],{"text":889,"config":890},"Install",{"href":891,"dataGaName":892,"dataGaLocation":724},"/install/","install",{"text":894,"config":895},"Quick start guides",{"href":896,"dataGaName":897,"dataGaLocation":724},"/get-started/","quick setup checklists",{"text":899,"config":900},"Learn",{"href":901,"dataGaLocation":724,"dataGaName":902},"https://university.gitlab.com/","learn",{"text":904,"config":905},"Product documentation",{"href":906,"dataGaName":907,"dataGaLocation":724},"https://docs.gitlab.com/","product documentation",{"text":909,"config":910},"Best practice videos",{"href":911,"dataGaName":912,"dataGaLocation":724},"/getting-started-videos/","best practice videos",{"text":914,"config":915},"Integrations",{"href":916,"dataGaName":917,"dataGaLocation":724},"/integrations/","integrations",{"title":919,"items":920},"Discover",[921,926,931,936,940],{"text":922,"config":923},"Customer success stories",{"href":924,"dataGaName":925,"dataGaLocation":724},"/customers/","customer success stories",{"text":927,"config":928},"Blog",{"href":929,"dataGaName":930,"dataGaLocation":724},"/blog/","blog",{"text":932,"config":933},"Demo Hub",{"href":934,"dataGaName":935,"dataGaLocation":724},"/demo-hub/","demo hub",{"text":937,"config":938},"The Source",{"href":939,"dataGaName":930,"dataGaLocation":724},"/the-source/",{"text":941,"config":942},"Remote",{"href":943,"dataGaName":944,"dataGaLocation":724},"https://handbook.gitlab.com/handbook/company/culture/all-remote/","remote",{"title":946,"items":947},"Connect",[948,953,958,963,968,973],{"text":949,"config":950},"GitLab Services",{"href":951,"dataGaName":952,"dataGaLocation":724},"/services/","services",{"text":954,"config":955},"Contribute",{"href":956,"dataGaName":957,"dataGaLocation":724},"https://contributors.gitlab.com","contribute",{"text":959,"config":960},"Community",{"href":961,"dataGaName":962,"dataGaLocation":724},"/community/","community",{"text":964,"config":965},"Forum",{"href":966,"dataGaName":967,"dataGaLocation":724},"https://forum.gitlab.com/","forum",{"text":969,"config":970},"Events",{"href":971,"dataGaName":972,"dataGaLocation":724},"/events/","events",{"text":974,"config":975},"Partners",{"href":976,"dataGaName":977,"dataGaLocation":724},"/partners/","partners",{"config":979,"title":982,"text":983,"link":984},{"background":980,"textColor":981},"url('https://res.cloudinary.com/about-gitlab-com/image/upload/v1777322348/qpq8yrgn8knii57omj0c.png')","#000","What’s new in GitLab","Stay updated with our latest features and improvements.",{"text":985,"config":986},"Read the latest",{"href":987,"dataGaName":988,"dataGaLocation":724},"/whats-new/","whats new",{"text":990,"config":991,"menu":993},"Company",{"dataNavLevelOne":992},"company",{"type":775,"columns":994},[995],{"items":996},[997,1002,1007,1009,1014,1019,1024,1029,1034,1039],{"text":998,"config":999},"About",{"href":1000,"dataGaName":1001,"dataGaLocation":724},"/company/","about",{"text":1003,"config":1004,"footerGa":1006},"Jobs",{"href":670,"dataGaName":1005,"dataGaLocation":724},"jobs",{"dataGaName":1005},{"text":969,"config":1008},{"href":971,"dataGaName":972,"dataGaLocation":724},{"text":1010,"config":1011},"Leadership",{"href":1012,"dataGaName":1013,"dataGaLocation":724},"/company/team/e-group/","leadership",{"text":1015,"config":1016},"Handbook",{"href":1017,"dataGaName":1018,"dataGaLocation":724},"https://handbook.gitlab.com/","handbook",{"text":1020,"config":1021},"Investor relations",{"href":1022,"dataGaName":1023,"dataGaLocation":724},"https://ir.gitlab.com/overview/default.aspx","investor relations",{"text":1025,"config":1026},"Trust Center",{"href":1027,"dataGaName":1028,"dataGaLocation":724},"/security/","trust center",{"text":1030,"config":1031},"AI Transparency Center",{"href":1032,"dataGaName":1033,"dataGaLocation":724},"/ai-transparency-center/","ai transparency center",{"text":1035,"config":1036},"Newsletter",{"href":1037,"dataGaName":1038,"dataGaLocation":724},"/company/contact/#contact-forms","newsletter",{"text":1040,"config":1041},"Press",{"href":1042,"dataGaName":1043,"dataGaLocation":724},"/press/","press",{"text":1045,"config":1046,"menu":1047},"Contact us",{"dataNavLevelOne":992},{"type":775,"columns":1048},[1049],{"items":1050},[1051,1056,1061],{"text":1052,"config":1053},"Talk to sales",{"href":1054,"dataGaName":1055,"dataGaLocation":724},"/sales/","talk to sales",{"text":1057,"config":1058},"Support portal",{"href":1059,"dataGaName":1060,"dataGaLocation":724},"https://support.gitlab.com/hc/en-us","support portal",{"text":1062,"config":1063},"Customer portal",{"href":1064,"dataGaName":1065,"dataGaLocation":724},"https://customers.gitlab.com/customers/sign_in/","customer portal",{"close":1067,"login":1068,"suggestions":1075},"Close",{"text":1069,"link":1070},"To search repositories and projects, login to",{"text":1071,"config":1072},"gitlab.com",{"href":738,"dataGaName":1073,"dataGaLocation":1074},"search login","search",{"text":1076,"default":1077},"Suggestions",[1078,1080,1084,1086,1090,1094],{"text":755,"config":1079},{"href":760,"dataGaName":755,"dataGaLocation":1074},{"text":1081,"config":1082},"Code Suggestions (AI)",{"href":1083,"dataGaName":1081,"dataGaLocation":1074},"/solutions/code-suggestions/",{"text":791,"config":1085},{"href":793,"dataGaName":791,"dataGaLocation":1074},{"text":1087,"config":1088},"GitLab on AWS",{"href":1089,"dataGaName":1087,"dataGaLocation":1074},"/partners/technology-partners/aws/",{"text":1091,"config":1092},"GitLab on Google Cloud",{"href":1093,"dataGaName":1091,"dataGaLocation":1074},"/partners/technology-partners/google-cloud-platform/",{"text":1095,"config":1096},"Why GitLab?",{"href":768,"dataGaName":1095,"dataGaLocation":1074},{"freeTrial":1098,"mobileIcon":1103,"desktopIcon":1108,"secondaryButton":1111},{"text":1099,"config":1100},"Start free trial",{"href":1101,"dataGaName":729,"dataGaLocation":1102},"https://gitlab.com/-/trials/new/","nav",{"altText":1104,"config":1105},"Gitlab Icon",{"src":1106,"dataGaName":1107,"dataGaLocation":1102},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203874/jypbw1jx72aexsoohd7x.svg","gitlab icon",{"altText":1104,"config":1109},{"src":1110,"dataGaName":1107,"dataGaLocation":1102},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203875/gs4c8p8opsgvflgkswz9.svg",{"text":1112,"config":1113},"Get Started",{"href":1114,"dataGaName":1115,"dataGaLocation":1102},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com/get-started/","get started",{"freeTrial":1117,"mobileIcon":1121,"desktopIcon":1123},{"text":1118,"config":1119},"Learn more about GitLab Duo",{"href":760,"dataGaName":1120,"dataGaLocation":1102},"gitlab duo",{"altText":1104,"config":1122},{"src":1106,"dataGaName":1107,"dataGaLocation":1102},{"altText":1104,"config":1124},{"src":1110,"dataGaName":1107,"dataGaLocation":1102},{"button":1126,"mobileIcon":1131,"desktopIcon":1133},{"text":1127,"config":1128},"/switch",{"href":1129,"dataGaName":1130,"dataGaLocation":1102},"#contact","switch",{"altText":1104,"config":1132},{"src":1106,"dataGaName":1107,"dataGaLocation":1102},{"altText":1104,"config":1134},{"src":1135,"dataGaName":1107,"dataGaLocation":1102},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1773335277/ohhpiuoxoldryzrnhfrh.png",{"freeTrial":1137,"mobileIcon":1142,"desktopIcon":1144},{"text":1138,"config":1139},"Back to pricing",{"href":873,"dataGaName":1140,"dataGaLocation":1102,"icon":1141},"back to pricing","GoBack",{"altText":1104,"config":1143},{"src":1106,"dataGaName":1107,"dataGaLocation":1102},{"altText":1104,"config":1145},{"src":1110,"dataGaName":1107,"dataGaLocation":1102},{"title":1147,"titleMobile":1148,"button":1149,"config":1154},"Duo Agent Platform delivers 400% ROI, per new Forrester Consulting study.","400% ROI: Forrester TEI for GitLab Duo",{"text":766,"config":1150},{"href":1151,"dataGaName":1152,"dataGaLocation":1153},"https://about.gitlab.com/blog/gitlab-duo-agent-platform-delivers-400-percent-roi/","forrester-tei-dap-banner","global-banner",{"layout":1155,"disabled":704},"release",{"data":1157},{"text":1158,"source":1159,"edit":1165,"contribute":1170,"config":1175,"items":1180,"minimal":1390},"Git is a trademark of Software Freedom Conservancy and our use of 'GitLab' is under license",{"text":1160,"config":1161},"View page source",{"href":1162,"dataGaName":1163,"dataGaLocation":1164},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/","page source","footer",{"text":1166,"config":1167},"Edit this page",{"href":1168,"dataGaName":1169,"dataGaLocation":1164},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/content/","web ide",{"text":1171,"config":1172},"Please contribute",{"href":1173,"dataGaName":1174,"dataGaLocation":1164},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/CONTRIBUTING.md/","please contribute",{"twitter":1176,"facebook":1177,"youtube":1178,"linkedin":1179},"https://twitter.com/gitlab","https://www.facebook.com/gitlab","https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg","https://www.linkedin.com/company/gitlab-com",[1181,1228,1282,1326,1358],{"title":871,"links":1182,"subMenu":1197},[1183,1187,1192],{"text":1184,"config":1185},"View plans",{"href":873,"dataGaName":1186,"dataGaLocation":1164},"view plans",{"text":1188,"config":1189},"Why Premium?",{"href":1190,"dataGaName":1191,"dataGaLocation":1164},"/pricing/premium/","why premium",{"text":1193,"config":1194},"Why Ultimate?",{"href":1195,"dataGaName":1196,"dataGaLocation":1164},"/pricing/ultimate/","why ultimate",[1198],{"title":1199,"links":1200},"Contact Us",[1201,1204,1206,1208,1213,1218,1223],{"text":1202,"config":1203},"Contact sales",{"href":1054,"dataGaName":734,"dataGaLocation":1164},{"text":1057,"config":1205},{"href":1059,"dataGaName":1060,"dataGaLocation":1164},{"text":1062,"config":1207},{"href":1064,"dataGaName":1065,"dataGaLocation":1164},{"text":1209,"config":1210},"Status",{"href":1211,"dataGaName":1212,"dataGaLocation":1164},"https://status.gitlab.com/","status",{"text":1214,"config":1215},"Terms of use",{"href":1216,"dataGaName":1217,"dataGaLocation":1164},"/terms/","terms of use",{"text":1219,"config":1220},"Privacy statement",{"href":1221,"dataGaName":1222,"dataGaLocation":1164},"/privacy/","privacy statement",{"text":1224,"config":1225},"Cookie preferences",{"dataGaName":1226,"dataGaLocation":1164,"id":1227,"isOneTrustButton":114},"cookie preferences","ot-sdk-btn",{"title":771,"links":1229,"subMenu":1238},[1230,1234],{"text":1231,"config":1232},"DevSecOps platform",{"href":753,"dataGaName":1233,"dataGaLocation":1164},"devsecops platform",{"text":1235,"config":1236},"AI-Assisted Development",{"href":760,"dataGaName":1237,"dataGaLocation":1164},"ai-assisted development",[1239],{"title":1240,"links":1241},"Topics",[1242,1247,1252,1257,1262,1267,1272,1277],{"text":1243,"config":1244},"CICD",{"href":1245,"dataGaName":1246,"dataGaLocation":1164},"/topics/ci-cd/","cicd",{"text":1248,"config":1249},"GitOps",{"href":1250,"dataGaName":1251,"dataGaLocation":1164},"/topics/gitops/","gitops",{"text":1253,"config":1254},"DevOps",{"href":1255,"dataGaName":1256,"dataGaLocation":1164},"/topics/devops/","devops",{"text":1258,"config":1259},"Version Control",{"href":1260,"dataGaName":1261,"dataGaLocation":1164},"/topics/version-control/","version control",{"text":1263,"config":1264},"DevSecOps",{"href":1265,"dataGaName":1266,"dataGaLocation":1164},"/topics/devsecops/","devsecops",{"text":1268,"config":1269},"Cloud Native",{"href":1270,"dataGaName":1271,"dataGaLocation":1164},"/topics/cloud-native/","cloud native",{"text":1273,"config":1274},"AI for Coding",{"href":1275,"dataGaName":1276,"dataGaLocation":1164},"/topics/devops/ai-for-coding/","ai for coding",{"text":1278,"config":1279},"Agentic AI",{"href":1280,"dataGaName":1281,"dataGaLocation":1164},"/topics/agentic-ai/","agentic ai",{"title":1283,"links":1284},"Solutions",[1285,1287,1289,1294,1298,1301,1305,1308,1310,1313,1316,1321],{"text":815,"config":1286},{"href":810,"dataGaName":815,"dataGaLocation":1164},{"text":804,"config":1288},{"href":787,"dataGaName":788,"dataGaLocation":1164},{"text":1290,"config":1291},"Agile development",{"href":1292,"dataGaName":1293,"dataGaLocation":1164},"/solutions/agile-delivery/","agile delivery",{"text":1295,"config":1296},"SCM",{"href":800,"dataGaName":1297,"dataGaLocation":1164},"source code management",{"text":1243,"config":1299},{"href":793,"dataGaName":1300,"dataGaLocation":1164},"continuous integration & delivery",{"text":1302,"config":1303},"Value stream management",{"href":843,"dataGaName":1304,"dataGaLocation":1164},"value stream management",{"text":1248,"config":1306},{"href":1307,"dataGaName":1251,"dataGaLocation":1164},"/solutions/gitops/",{"text":853,"config":1309},{"href":856,"dataGaName":857,"dataGaLocation":1164},{"text":1311,"config":1312},"Small business",{"href":862,"dataGaName":863,"dataGaLocation":1164},{"text":1314,"config":1315},"Public sector",{"href":868,"dataGaName":869,"dataGaLocation":1164},{"text":1317,"config":1318},"Education",{"href":1319,"dataGaName":1320,"dataGaLocation":1164},"/solutions/education/","education",{"text":1322,"config":1323},"Financial services",{"href":1324,"dataGaName":1325,"dataGaLocation":1164},"/solutions/finance/","financial services",{"title":876,"links":1327},[1328,1330,1332,1334,1337,1339,1342,1344,1346,1348,1350,1352,1354,1356],{"text":889,"config":1329},{"href":891,"dataGaName":892,"dataGaLocation":1164},{"text":894,"config":1331},{"href":896,"dataGaName":897,"dataGaLocation":1164},{"text":899,"config":1333},{"href":901,"dataGaName":902,"dataGaLocation":1164},{"text":904,"config":1335},{"href":906,"dataGaName":1336,"dataGaLocation":1164},"docs",{"text":927,"config":1338},{"href":929,"dataGaName":930,"dataGaLocation":1164},{"text":1340,"config":1341},"What's new",{"href":987,"dataGaName":988,"dataGaLocation":1164},{"text":922,"config":1343},{"href":924,"dataGaName":925,"dataGaLocation":1164},{"text":941,"config":1345},{"href":943,"dataGaName":944,"dataGaLocation":1164},{"text":949,"config":1347},{"href":951,"dataGaName":952,"dataGaLocation":1164},{"text":954,"config":1349},{"href":956,"dataGaName":957,"dataGaLocation":1164},{"text":959,"config":1351},{"href":961,"dataGaName":962,"dataGaLocation":1164},{"text":964,"config":1353},{"href":966,"dataGaName":967,"dataGaLocation":1164},{"text":969,"config":1355},{"href":971,"dataGaName":972,"dataGaLocation":1164},{"text":974,"config":1357},{"href":976,"dataGaName":977,"dataGaLocation":1164},{"title":990,"links":1359},[1360,1362,1364,1366,1368,1370,1374,1379,1381,1383,1385],{"text":998,"config":1361},{"href":1000,"dataGaName":992,"dataGaLocation":1164},{"text":1003,"config":1363},{"href":670,"dataGaName":1005,"dataGaLocation":1164},{"text":1010,"config":1365},{"href":1012,"dataGaName":1013,"dataGaLocation":1164},{"text":1015,"config":1367},{"href":1017,"dataGaName":1018,"dataGaLocation":1164},{"text":1020,"config":1369},{"href":1022,"dataGaName":1023,"dataGaLocation":1164},{"text":1371,"config":1372},"Sustainability",{"href":1373,"dataGaName":1371,"dataGaLocation":1164},"/sustainability/",{"text":1375,"config":1376},"Diversity, inclusion and belonging (DIB)",{"href":1377,"dataGaName":1378,"dataGaLocation":1164},"/diversity-inclusion-belonging/","Diversity, inclusion and belonging",{"text":1025,"config":1380},{"href":1027,"dataGaName":1028,"dataGaLocation":1164},{"text":1035,"config":1382},{"href":1037,"dataGaName":1038,"dataGaLocation":1164},{"text":1040,"config":1384},{"href":1042,"dataGaName":1043,"dataGaLocation":1164},{"text":1386,"config":1387},"Modern Slavery Transparency Statement",{"href":1388,"dataGaName":1389,"dataGaLocation":1164},"https://handbook.gitlab.com/handbook/legal/modern-slavery-act-transparency-statement/","modern slavery transparency statement",{"items":1391},[1392,1395,1398],{"text":1393,"config":1394},"Terms",{"href":1216,"dataGaName":1217,"dataGaLocation":1164},{"text":1396,"config":1397},"Cookies",{"dataGaName":1226,"dataGaLocation":1164,"id":1227,"isOneTrustButton":114},{"text":1399,"config":1400},"Privacy",{"href":1221,"dataGaName":1222,"dataGaLocation":1164},[1402],{"id":1403,"title":7,"body":703,"config":1404,"content":1406,"description":703,"extension":1409,"meta":1410,"navigation":114,"path":1411,"seo":1412,"stem":1413,"__hash__":1414},"blogAuthors/en-us/blog/authors/robert-speicher.yml",{"template":1405},"BlogAuthor",{"name":7,"config":1407},{"headshot":48,"ctfId":1408},"rspeicher","yml",{},"/en-us/blog/authors/robert-speicher",{},"en-us/blog/authors/robert-speicher","7DoEEXTBZFobqYAqIhMGZY_r6Uej_MvOCTH1SlHhdAw",[1416,1425,1433],{"title":1417,"description":1418,"heroImage":1419,"category":699,"date":1420,"authors":1421,"slug":1424,"externalUrl":703},"Confidential AI for GitLab Self-Hosted","Give developers AI coding agents in GitLab Duo without source code leaving a hardware-encrypted boundary — no GPUs needed.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1773866173/vte9qh8rriznvyclhkes.png","2026-08-06",[1422,1423],"Mathias Ewald","Martin Paloncy, Edgeless Systems","confidential-ai-for-gitlab-self-hosted",{"title":1426,"description":1427,"heroImage":1428,"category":699,"date":1429,"authors":1430,"slug":1432,"externalUrl":703},"Green DevOps: Why carbon measurement belongs in your CI/CD pipeline","CI/CD pipelines have a hidden carbon cost. Here's why measuring it matters, and how you can get started with Eco CI and Carmen in GitLab.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1765809212/noh0mdfn9o94ry9ykura.png","2026-07-09",[1431],"Lysanne Pinto","green-devops-carbon-measurement-cicd-pipeline",{"title":1434,"description":1435,"heroImage":1436,"category":699,"date":1437,"authors":1438,"slug":1440,"externalUrl":703},"How to build CI/CD observability at scale","This practical guide to GitLab pipeline analytics helps self-managed users gain operational insights using Prometheus and Grafana.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1774465167/n5hlvrsrheadeccyr1oz.png","2026-04-28",[1439],"Paul Meresanu","how-to-build-ci-cd-observability-at-scale",{"promotions":1442},[1443,1457,1469,1481],{"id":1444,"categories":1445,"header":1447,"text":1448,"button":1449,"image":1454},"ai-modernization",[1446],"ai","Is AI achieving its promise at scale?","Quiz will take 5 minutes or less",{"text":1450,"config":1451},"Get your AI maturity score",{"href":1452,"dataGaName":1453,"dataGaLocation":930},"/assessments/ai-modernization-assessment/","modernization assessment",{"config":1455},{"src":1456},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138786/qix0m7kwnd8x2fh1zq49.png",{"id":1458,"categories":1459,"header":1461,"text":1448,"button":1462,"image":1466},"devops-modernization",[1460,1266],"product","Are you just managing tools or shipping innovation?",{"text":1463,"config":1464},"Get your DevOps maturity score",{"href":1465,"dataGaName":1453,"dataGaLocation":930},"/assessments/devops-modernization-assessment/",{"config":1467},{"src":1468},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138785/eg818fmakweyuznttgid.png",{"id":1470,"categories":1471,"header":1473,"text":1448,"button":1474,"image":1478},"security-modernization",[1472],"security","Are you trading speed for security?",{"text":1475,"config":1476},"Get your security maturity score",{"href":1477,"dataGaName":1453,"dataGaLocation":930},"/assessments/security-modernization-assessment/",{"config":1479},{"src":1480},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1772138786/p4pbqd9nnjejg5ds6mdk.png",{"id":1482,"paths":1483,"header":1486,"text":1487,"button":1488,"image":1493},"github-azure-migration",[1484,1485],"migration-from-azure-devops-to-gitlab","integrating-azure-devops-scm-and-gitlab","Is your team ready for GitHub's Azure move?","GitHub is already rebuilding around Azure. Find out what it means for you.",{"text":1489,"config":1490},"See how GitLab compares to GitHub",{"href":1491,"dataGaName":1492,"dataGaLocation":930},"/compare/gitlab-vs-github/github-azure-migration/","github azure migration",{"config":1494},{"src":1468},{"header":1496,"blurb":1497,"button":1498,"secondaryButton":1503},"Start building faster today","See what your team can do with the intelligent orchestration platform for DevSecOps.\n",{"text":1499,"config":1500},"Get your free trial",{"href":1501,"dataGaName":729,"dataGaLocation":1502},"https://gitlab.com/-/trial_registrations/new?glm_content=default-saas-trial&glm_source=about.gitlab.com/","feature",{"text":1202,"config":1504},{"href":1054,"dataGaName":734,"dataGaLocation":1502},1786803773173]