[{"data":1,"prerenderedAt":719},["ShallowReactive",2],{"/en-us/blog/how-to-bring-devops-to-the-database-with-gitlab-and-liquibase/":3,"navigation-en-us":36,"banner-en-us":464,"footer-en-us":481,"Tsvi Zandany":691,"next-steps-en-us":704},{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"seo":8,"content":16,"config":26,"_id":29,"_type":30,"title":31,"_source":32,"_file":33,"_stem":34,"_extension":35},"/en-us/blog/how-to-bring-devops-to-the-database-with-gitlab-and-liquibase","blog",false,"",{"title":9,"description":10,"ogTitle":9,"ogDescription":10,"noIndex":6,"ogImage":11,"ogUrl":12,"ogSiteName":13,"ogType":14,"canonicalUrls":12,"schema":15},"How to bring DevOps to the database with GitLab and Liquibase","Learn how to build a continuous delivery pipeline for database code changes with this tutorial.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1749672677/Blog/Hero%20Images/metalgears_databasecasestudy.jpg","https://about.gitlab.com/blog/how-to-bring-devops-to-the-database-with-gitlab-and-liquibase","https://about.gitlab.com","article","\n                        {\n        \"@context\": \"https://schema.org\",\n        \"@type\": \"Article\",\n        \"headline\": \"How to bring DevOps to the database with GitLab and Liquibase\",\n        \"author\": [{\"@type\":\"Person\",\"name\":\"Tsvi Zandany\"}],\n        \"datePublished\": \"2022-01-05\",\n      }",{"title":9,"description":10,"authors":17,"heroImage":11,"date":19,"body":20,"category":21,"tags":22},[18],"Tsvi Zandany","2022-01-05","In the [Accelerate State of DevOps 2021\nReport](https://cloud.google.com/devops/state-of-devops/), the DevOps\nResearch and Assessment (DORA) team reveals “elite DevOps performers are 3.4\ntimes more likely to exercise database change management compared to their\nlow-performing counterparts.” Tracking changes with version control is not\njust for application code, though. It’s crucial for managing changes for one\nof your most important assets: your database.   \n\n\nThe GitLab DevOps platform enables database management teams to leverage\nCI/CD to track, manage, and deploy database changes, along with application\ndevelopment and automation and infrastructure as code. Database change\nmanagement tools have become more advanced in recent years, supporting\neasier collaboration and communication, which are the keys to successful\nDevOps. In this blog post, I’ll take you through a tutorial using\n[Liquibase](https://www.liquibase.com), a tool that integrates seamlessly\ninto the GitLab DevOps platform so your teams can deliver database code\nchanges as fast as application code changes (without compromising on quality\nand security). \n\n\n## What is Liquibase?\n\n\nLiquibase was founded as an open source project over 15 years ago to address\ngetting database changes into version control. With more than 75 million\ndownloads, the company behind Liquibase expanded to paid editions and\nsupport to help teams release software faster and safer by bringing the\ndatabase change process into their existing CI/CD automation.  \n\n\nIntegrating Liquibase with GitLab CI/CD enables database teams to leverage\nDevOps automation and best practices for database management. Liquibase\nhelps teams build automated database scripts and gain insights into when,\nwhere, and how database changes are deployed. In this tutorial, we’ll\ndemonstrate how to check database scripts for security and compliance\nissues, speed up database code reviews, perform easy rollbacks, and provide\ndatabase snapshots to check for malware.\n\n\n## Adding Liquibase to GitLab’s DevOps Platform\n\n\nTeams can add Liquibase to GitLab to enable true CI/CD for the database.\nIt’s easy to integrate Liquibase into your GitLab CI/CD pipeline. Before\njumping into the tutorial, let’s take a look at the [example Liquibase\nGitLab project\nrepository](https://gitlab.com/gitlab-com/alliances/liquibase/sandbox-projects/sql_server)\nyou’ll be using.\n\n\n### Understanding the example Liquibase GitLab project repository\n\n\n![A CI/CD pipeline\ndiagram](https://about.gitlab.com/images/blogimages/1_CICD_Pipeline_Diagram.png){:\n.shadow.small.center}\n\n\nFor this example, the GitLab CI/CD pipeline environments include DEV, QA,\nand PROD. This pipeline goes through several stages: build, test, deploy,\nand compare. A post stage comes into play later to capture a snapshot of\nyour database in Production.\n\n\nStages:\n  - build\n  - test\n  - deploy\n  - compare\n\n### Liquibase commands in the pipeline\n\n\nFor each of the predefined jobs in the GitLab repository, you’ll be using\nseveral Liquibase commands to help manage database changes quickly and\nsafely:\n\n\n- liquibase_job:\n\n  before_script:\n    - functions\n    - isUpToDate\n    - liquibase checks run\n    - liquibase updateSQL\n    - liquibase update\n    - liquibase rollbackOneUpdate --force\n    - liquibase tag $CI_PIPELINE_ID\n    - liquibase --logFile=${CI_JOB_NAME}_${CI_PIPELINE_ID}.log --logLevel=info update\n    - liquibase history\n\n  script:\n    - echo \"Comparing databases DEV --> QA\"\n    - liquibase diff\n    - liquibase --outputFile=diff_between_DEV_QA.json diff --format=json\n\n  script:\n    - echo \"Snapshotting database PROD\"\n    - liquibase --outputFile=snapshot_PROD.json snapshot --snapshotFormat=json\n\nLearn more about each of these commands in the [README file in the GitLab\nrepository](https://gitlab.com/gitlab-com/alliances/liquibase/sandbox-projects/sql_server/-/blob/main/README.md). \n\n\n## Tutorial\n\n\nThe following tutorial demonstrates how to run Liquibase in a GitLab CI/CD\npipeline. Follow along by watching this companion video:\n\n\n\u003C!-- blank line -->\n\n\u003Cfigure class=\"video_container\">\n  \u003Ciframe src=\"https://www.youtube.com/embed/ZBFhDayoRYo\" frameborder=\"0\" allowfullscreen=\"true\"> \u003C/iframe>\n\u003C/figure>\n\n\u003C!-- blank line -->\n\n\n### Prerequisites\n\n\nTo start, I’m using a Linux machine with the following:\n\n\n- [A GitLab account](https://www.gitlab.com)\n\n- Self-managed Runner on a Linux machine\n\n- Git\n\n- Java 11\n\n- Access to a SQL Server database with multiple environments\n\n\n### Download, install, and configure Liquibase\n\n\n[Download Liquibase v4.6.1+](https://www.liquibase.org/download)\n\n\n[Install\nLiquibase](https://docs.liquibase.com/concepts/installation/installation-linux-unix-mac.html)\n\n\n[Get a free Liquibase Pro license key](https://www.liquibase.com/trial). No\ncredit card is required, so you can play with all the advanced features and\nget support for 30 days. You’ll use this key later when you configure\nenvironment variables within GitLab.\n\n\nEnsure Liquibase is installed properly by running the liquibase --version\ncommand. If everything is good you’ll see the following:\n\n\nStarting Liquibase at 18:10:06 (version 4.6.1 #98 built at 2021-11-04\n20:16+0000)\n\nRunning Java under /usr/lib/jvm/java-11-openjdk-11.0.13.0.8-1.el7_9.x86_64\n(Version 11.0.13)\n\n\nLiquibase Version: 4.6.1\n\nLiquibase Community 4.6.1 by Liquibase\n\n\n### Prepare your GitLab project\n\n\nFork this [example GitLab project\nrepository](https://gitlab.com/gitlab-com/alliances/liquibase/sandbox-projects/sql_server).\n([See more information about forking a\nrepository](https://docs.gitlab.com/ee/user/project/repository/forking_workflow.html).)\n\n\n[Create a self-managed GitLab Runner](https://docs.gitlab.com/runner/) on\nyour Linux instance with your newly forked GitLab project.\n\n\nClone your newly forked project repository:\n\ngit clone https://gitlab.com/\u003Cusername>/sql_server.git\n\n\nGo to the “sql_server” project folder.\n\ncd sql_server\n\n\nRun the following command to change your git branch to staging:\n\ngit checkout staging\n\n\nConfigure the GitLab CI/CD pipeline environment variables.\n\n\nYour configuration will include [CI/CD\nvariables](https://docs.gitlab.com/ee/ci/variables/#add-a-cicd-variable-to-a-project),\n[Liquibase\nproperties](https://www.liquibase.com/blog/secure-database-developer-flow-using-gitlab-pipelines),\ndatabase credentials, and the Liquibase Pro trial license key so you can use\nall the advanced Liquibase commands.\n\n\nFrom the main sql_server project, go to Settings → CI/CD\n\n\nUnder Variables, click Expand and add the following variables:\n\n\n![A CI/CD pipeline\ndiagram](https://about.gitlab.com/images/blogimages/liquibasevariables.png){:\n.shadow.small.center}\n\n\n![A CI/CD pipeline\ndiagram](https://about.gitlab.com/images/blogimages/liquibasevariables2.png){:\n.shadow.small.center}\n\n\n### Configure the self-managed GitLab runner\n\n\nFrom the main sql_server project, go to Settings → CI/CD\n\n\nExpand the runners section, click the pencil edit icon, and add the\nfollowing runner tags (comma separated):\n\n\ndev_db,prod_db,test_db\n\n\nNote: Tags are created to help choose which runner will do the job. In this\nexample, we are associating all tags to one runner. Learn more about\n[configuring\nrunners](https://docs.gitlab.com/ee/ci/runners/configure_runners.html). \n\n\n### Make changes to the database\n\n\nEdit the changelog.sql file and add the following changeset after \n\n\n```\n\nliquibase formatted sql:\n\n-- changeset SteveZ:createTable_salesTableZ\n\nCREATE TABLE salesTableZ (\n   ID int NOT NULL,\n   NAME varchar(20) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,\n   REGION varchar(20) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,\n   MARKET varchar(20) COLLATE SQL_Latin1_General_CP1_CI_AS NULL\n)\n\n--rollback DROP TABLE salesTableZ\n\nAdd, commit, and push all new database changes.\n\ngit add changelog.sql\n\ngit commit -m “added changelog id and a create table salesTableZ changeset”\n\ngit push -u origin staging\n\n```\n\n\n### Merge the changes and run the pipeline\n\n\nLet’s merge the changes from branch staging → main to trigger the pipeline\nto run all jobs.\n\n\nClick Merge requests → New merge request\n\n\nSelect staging as Source branch and main as Target branch\n\n\nClick Compare branches and continue\n\n\nOn the next screen, click Create merge request\n\n\nClick Merge to finish merging the changes\n\n\n![A look at the merge\nrequest](https://about.gitlab.com/images/blogimages/2_Merge_Request1.png){:\n.shadow.small.center}\n\n\n![Another look at the merge\nrequestt](https://about.gitlab.com/images/blogimages/3_Merge_Request2.png){:\n.shadow.small.center}\n\n\nOnce these steps are completed, the code is merged into main and the\npipeline is triggered to run.\n\n\n![The pipeline is\ntriggered](https://about.gitlab.com/images/blogimages/4_Merge_Request3.png){:\n.shadow.small.center}\n\n\nTo see the pipeline running, click Pipelines.\n\n\nTo view the pipeline progress, click the pipeline ID link. You can view each\njob’s log output by clicking on each job name.\n\n\n![The pipeline in\nprogress](https://about.gitlab.com/images/blogimages/5_Pipeline_Progress.png){:\n.shadow.small.center}\n\n\nClicking into the build-job example:\n\n\nThe liquibase checks run command validates the SQL for any violations.\n\n\n```\n\n57Starting Liquibase at 22:19:14 (version 4.6.1 #98 built at 2021-11-04\n20:16+0000)\n\n58Liquibase Version: 4.6.1\n\n59Liquibase Pro 4.6.1 by Liquibase licensed to customersuccess until Mon Jun\n27 04:59:59 UTC 2022\n\n60Executing Quality Checks against changelog.sql\n\n61Executing all checks because a valid Liquibase Pro license was found!\n\n62Changesets Validated:\n\n63  ID: createTable_salesTableZ; Author: SteveZ; File path: changelog.sql\n\n64Checks run against each changeset:\n\n65  Warn on Detection of 'GRANT' Statements\n\n66  Warn on Detection of 'REVOKE' Statements\n\n67  Warn when 'DROP TABLE' detected\n\n68  Warn when 'DROP COLUMN' detected\n\n69  Check for specific patterns in sql (Short Name: SqlCreateRoleCheck)\n\n70  Warn when 'TRUNCATE TABLE' detected\n\n71  Warn on Detection of grant that contains 'WITH ADMIN OPTION'\n\n72Liquibase command 'checks run' was executed successfully.\n\n```\n\n\nThe liquibase update command deploys the changes. If you choose, you can\nview a full report of your changes in [Liquibase\nHub](https://docs.liquibase.com/tools-integrations/liquibase-hub/setup.html).\nThe update command also saves the deployment log output file as an artifact.\n\n\n```\n\n227Starting Liquibase at 22:19:34 (version 4.6.1 #98 built at 2021-11-04\n20:16+0000)\n\n228Liquibase Version: 4.6.1\n\n229Liquibase Pro 4.6.1 by Liquibase licensed to customersuccess until Mon\nJun 27 04:59:59 UTC 2022\n\n230----------------------------------------------------------------------\n\n231View a report of this operation at https://hub.liquibase.com/r/I7ens13ooM\n\n232* IMPORTANT: New users of Hub first need to Sign In to your account\n\n233with the one-time password sent to your email, which also serves as\n\n234your username.\n\n235----------------------------------------------------------------------\n\n236Logs saved to\n/home/gitlab-runner/builds/3-UvD4aX/0/szandany/sql_server/build-job_405710044.log\n\n237Liquibase command 'update' was executed successfully.\n\n```\n\n\nHere’s what your Liquibase Hub report will look like:\n\n\n![The hub report, part\none](https://about.gitlab.com/images/blogimages/6_LiquibaseHub_Report.png){:\n.shadow.small.center}\n\n\n![The hub report, part\ntwot](https://about.gitlab.com/images/blogimages/7_LiquibaseHub_Report.png){:\n.shadow.small.center}\n\n\nThe Liquibase history command will show what changes are currently in the\ndatabase.\n\n\n```\n\n255Starting Liquibase at 22:19:40 (version 4.6.1 #98 built at 2021-11-04\n20:16+0000)\n\n256Liquibase Version: 4.6.1\n\n257Liquibase Pro 4.6.1 by Liquibase licensed to customersuccess until Mon\nJun 27 04:59:59 UTC 2022\n\n258Liquibase History for\njdbc:sqlserver://localhost:1433;sendTemporalDataTypesAsStringForBulkCopy=true;delayLoadingLobs=true;useFmtOnly=false;useBulkCopyForBatchInsert=false;cancelQueryTimeout=-1;sslProtocol=TLS;jaasConfigurationName=SQLJDBCDriver;statementPoolingCacheSize=0;serverPreparedStatementDiscardThreshold=10;enablePrepareOnFirstPreparedStatementCall=false;fips=false;socketTimeout=0;authentication=NotSpecified;authenticationScheme=nativeAuthentication;xopenStates=false;sendTimeAsDatetime=true;trustStoreType=JKS;trustServerCertificate=false;TransparentNetworkIPResolution=true;serverNameAsACE=false;sendStringParametersAsUnicode=true;selectMethod=direct;responseBuffering=adaptive;queryTimeout=-1;packetSize=8000;multiSubnetFailover=false;loginTimeout=15;lockTimeout=-1;lastUpdateCount=true;encrypt=false;disableStatementPooling=true;databaseName=DEV;columnEncryptionSetting=Disabled;applicationName=Microsoft\nJDBC Driver for SQL Server;applicationIntent=readwrite;\n\n259- Database updated at 11/9/21, 10:19 PM. Applied 1 changeset(s),\nDeploymentId: 6496372605\n\n260  liquibase-internal::1636496372758::liquibase\n\n261- Database updated at 11/9/21, 10:19 PM. Applied 1 changeset(s),\nDeploymentId: 6496375151\n\n262  changelog.sql::createTable_salesTableZ::SteveZ\n\n263Liquibase command 'history' was executed successfully.\n\n```\n\n\n### Clicking into the DEV->QA job example from your pipeline\n\n\nWe run the liquibase diff command to compare the DEV and QA databases. This\nhelps detect any drift between the databases.\n\n\nNotice in the log output that there are some unexpected changes: \n\n\ntable named bad_table\n\n\nprocedure named bad_proc\n\n\n![The diff\nreport](https://about.gitlab.com/images/blogimages/8_LiquibaseDiff_Report.png){:\n.shadow.small.center}\n\n\nBy using the [Liquibase Pro trial license\nkey](https://www.liquibase.com/trial), you’re able to detect any stored\nlogic objects included in the diff report. Liquibase Pro also allows you to\ngenerate a parsable JSON output file and save it as an artifact for later\nuse.\n\n\n```\n\n137Starting Liquibase at 22:21:10 (version 4.6.1 #98 built at 2021-11-04\n20:16+0000)\n\n138Liquibase Version: 4.6.1\n\n139Liquibase Pro 4.6.1 by Liquibase licensed to customersuccess until Mon\nJun 27 04:59:59 UTC 2022\n\n140Output saved to\n/home/gitlab-runner/builds/3-UvD4aX/0/szandany/sql_server/diff_between_DEV_QA.json\n\n141Liquibase command 'diff' was executed successfully.\n\n```\n\n\nJSON artifact output file example:\n\n\n```\n\n{\n    \"diff\": {\n        \"diffFormat\": 1,\n        \"created\": \"Wed Dec 08 20:16:53 UTC 2021\",\n        \"databases\": {\n            \"reference\": {\n                \"majorVersion\": \"14\",\n                \"minorVersion\": \"00\",\n                \"name\": \"Microsoft SQL Server\",\n                \"url\": \"jdbc:sqlserver://localhost:1433;databaseName=DEV; ...\"\n            },\n            \"target\": {\n                \"majorVersion\": \"14\",\n                \"minorVersion\": \"00\",\n                \"name\": \"Microsoft SQL Server\",\n                \"url\": \"jdbc:sqlserver://localhost:1433;databaseName=QA; ...\"\n            }\n        },\n        \"unexpectedObjects\": [\n            {\n                \"unexpectedObject\": {\n                    \"name\": \"bad_proc\",\n                    \"type\": \"storedProcedure\",\n                    \"schemaName\": \"dbo\",\n                    \"catalogName\": \"QA\"\n                }\n            },\n            {\n                \"unexpectedObject\": {\n                    \"name\": \"bad_table\",\n                    \"type\": \"table\",\n                    \"schemaName\": \"dbo\",\n                    \"catalogName\": \"QA\"\n                }\n            },\n            {\n                \"unexpectedObject\": {\n                    \"name\": \"MARKET\",\n                    \"type\": \"column\",\n                    \"relationName\": \"bad_table\",\n                    \"schemaName\": \"dbo\",\n                    \"catalogName\": \"QA\"\n                }\n            },\n            {\n                \"unexpectedObject\": {\n                    \"name\": \"ID\",\n                    \"type\": \"column\",\n                    \"relationName\": \"bad_table\",\n                    \"schemaName\": \"dbo\",\n                    \"catalogName\": \"QA\"\n                }\n            },\n            {\n                \"unexpectedObject\": {\n                    \"name\": \"NAME\",\n                    \"type\": \"column\",\n                    \"relationName\": \"bad_table\",\n                    \"schemaName\": \"dbo\",\n                    \"catalogName\": \"QA\"\n                }\n            },\n            {\n                \"unexpectedObject\": {\n                    \"name\": \"REGION\",\n                    \"type\": \"column\",\n                    \"relationName\": \"bad_table\",\n                    \"schemaName\": \"dbo\",\n                    \"catalogName\": \"QA\"\n                }\n            }\n        ],\n        \"changedObjects\": [\n            {\n                \"changedObject\": {\n                    \"name\": \"QA\",\n                    \"type\": \"catalog\",\n                    \"differences\": [\n                        {\n                            \"difference\": {\n                                \"comparedValue\": \"QA\",\n                                \"field\": \"name\",\n                                \"message\": \"name changed from 'DEV' to 'QA'\",\n                                \"referenceValue\": \"DEV\"\n                            }\n                        }\n                    ]\n                }\n            }\n        ]\n    }\n}\n\n\n```\n\n\nNote that the [Liquibase\ndiffChangelog](https://docs.liquibase.com/commands/diffchangelog.html) can\nhelp any baseline environments that have drifted. \n\n\nClicking into the snapshot PROD job example, the snapshot file contains all\nthe current schema changes represented in a JSON file. You can obtain the\nPROD database snapshot file to compare two states of the same database to\nprotect against malware with drift detection.\n\n\n```\n\n58Starting Liquibase at 22:21:32 (version 4.6.1 #98 built at 2021-11-04\n20:16+0000)\n\n59Liquibase Version: 4.6.1\n\n60Liquibase Pro 4.6.1 by Liquibase licensed to customersuccess until Mon Jun\n27 04:59:59 UTC 2022\n\n61Output saved to\n/home/gitlab-runner/builds/3-UvD4aX/0/szandany/sql_server/snapshot_PROD.json\n\n62Liquibase command 'snapshot' was executed successfully. \n\n64Uploading artifacts for successful job00:01\n\n70Cleaning up project directory and file based variables00:00\n\n72Job succeeded\n\n```\n\n\n### Congratulations! The pipeline ran successfully.\n\n\nIf all the jobs are successful, you’ll see a green checkmark right next to\neach one.\n\n\nHere’s what your database changes will look like with a database SQL query\ntool.\n\n\n![The\ndatabase](https://about.gitlab.com/images/blogimages/9_Database_Changes_SQL_Query_Tool.png){:\n.shadow.small.center}\n\n\n## Summing it up\n\n\nYou’ve now successfully run Liquibase in a GitLab pipeline to enable true\nCI/CD for the database. You can easily keep adding more changes to the\ndatabase by adding more Liquibase changesets to the changelog, commit them\nto GitLab version control, and repeat the merge request process described in\nthis tutorial to add the changes. \n\n\nStill have questions or want support integrating Liquibase with your Gitlab\nCI/CD Pipeline? Our team of database DevOps experts is happy to help! \n\n\n[Contact Liquibase](https://www.liquibase.com/contact)\n\n\n[Contact GitLab](/sales/)\n\n\nContact a [certified GitLab channel\npartner](https://www.google.com/url?q=https://partners.gitlab.com/English/directory/&sa=D&source=docs&ust=1641393355697069&usg=AOvVaw0R5mPukwMBR2dKsn3eQzqp)\n\n\nContact a [Liquibase channel partner](https://www.liquibase.com/partners)\n\n\nOther useful links: \n\n\n[Gitlab CI/CD setup Liquibase\ndocumentation](https://docs.liquibase.com/concepts/installation/setup-gitlab-cicd.html)\n\n\n[GitLab - Liquibase\nrepository](https://gitlab.com/gitlab-com/alliances/liquibase/sandbox-projects/liquibasegitlabcicd/-/blob/master/README.md) \n\n\nGet a [speedy, secure database developer\nflow](https://www.liquibase.com/blog/secure-database-developer-flow-using-gitlab-pipelines)\nusing GitLab pipelines & Liquibase\n\n\n_Author Tsvi Zandany is a Senior Solutions Architect at Liquibase_\n","engineering",[23,24,25],"integrations","CI","CD",{"slug":27,"featured":6,"template":28},"how-to-bring-devops-to-the-database-with-gitlab-and-liquibase","BlogPost","content:en-us:blog:how-to-bring-devops-to-the-database-with-gitlab-and-liquibase.yml","yaml","How To Bring Devops To The Database With Gitlab And Liquibase","content","en-us/blog/how-to-bring-devops-to-the-database-with-gitlab-and-liquibase.yml","en-us/blog/how-to-bring-devops-to-the-database-with-gitlab-and-liquibase","yml",{"_path":37,"_dir":38,"_draft":6,"_partial":6,"_locale":7,"data":39,"_id":460,"_type":30,"title":461,"_source":32,"_file":462,"_stem":463,"_extension":35},"/shared/en-us/main-navigation","en-us",{"logo":40,"freeTrial":45,"sales":50,"login":55,"items":60,"search":391,"minimal":422,"duo":441,"pricingDeployment":450},{"config":41},{"href":42,"dataGaName":43,"dataGaLocation":44},"/","gitlab logo","header",{"text":46,"config":47},"Get free trial",{"href":48,"dataGaName":49,"dataGaLocation":44},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com&glm_content=default-saas-trial/","free trial",{"text":51,"config":52},"Talk to sales",{"href":53,"dataGaName":54,"dataGaLocation":44},"/sales/","sales",{"text":56,"config":57},"Sign in",{"href":58,"dataGaName":59,"dataGaLocation":44},"https://gitlab.com/users/sign_in/","sign in",[61,105,203,208,312,372],{"text":62,"config":63,"cards":65,"footer":88},"Platform",{"dataNavLevelOne":64},"platform",[66,72,80],{"title":62,"description":67,"link":68},"The most comprehensive AI-powered DevSecOps Platform",{"text":69,"config":70},"Explore our Platform",{"href":71,"dataGaName":64,"dataGaLocation":44},"/platform/",{"title":73,"description":74,"link":75},"GitLab Duo (AI)","Build software faster with AI at every stage of development",{"text":76,"config":77},"Meet GitLab Duo",{"href":78,"dataGaName":79,"dataGaLocation":44},"/gitlab-duo/","gitlab duo ai",{"title":81,"description":82,"link":83},"Why GitLab","10 reasons why Enterprises choose GitLab",{"text":84,"config":85},"Learn more",{"href":86,"dataGaName":87,"dataGaLocation":44},"/why-gitlab/","why gitlab",{"title":89,"items":90},"Get started with",[91,96,101],{"text":92,"config":93},"Platform Engineering",{"href":94,"dataGaName":95,"dataGaLocation":44},"/solutions/platform-engineering/","platform engineering",{"text":97,"config":98},"Developer Experience",{"href":99,"dataGaName":100,"dataGaLocation":44},"/developer-experience/","Developer experience",{"text":102,"config":103},"MLOps",{"href":104,"dataGaName":102,"dataGaLocation":44},"/topics/devops/the-role-of-ai-in-devops/",{"text":106,"left":107,"config":108,"link":110,"lists":114,"footer":185},"Product",true,{"dataNavLevelOne":109},"solutions",{"text":111,"config":112},"View all Solutions",{"href":113,"dataGaName":109,"dataGaLocation":44},"/solutions/",[115,140,164],{"title":116,"description":117,"link":118,"items":123},"Automation","CI/CD and automation to accelerate deployment",{"config":119},{"icon":120,"href":121,"dataGaName":122,"dataGaLocation":44},"AutomatedCodeAlt","/solutions/delivery-automation/","automated software delivery",[124,128,132,136],{"text":125,"config":126},"CI/CD",{"href":127,"dataGaLocation":44,"dataGaName":125},"/solutions/continuous-integration/",{"text":129,"config":130},"AI-Assisted Development",{"href":78,"dataGaLocation":44,"dataGaName":131},"AI assisted development",{"text":133,"config":134},"Source Code Management",{"href":135,"dataGaLocation":44,"dataGaName":133},"/solutions/source-code-management/",{"text":137,"config":138},"Automated Software Delivery",{"href":121,"dataGaLocation":44,"dataGaName":139},"Automated software delivery",{"title":141,"description":142,"link":143,"items":148},"Security","Deliver code faster without compromising security",{"config":144},{"href":145,"dataGaName":146,"dataGaLocation":44,"icon":147},"/solutions/security-compliance/","security and compliance","ShieldCheckLight",[149,154,159],{"text":150,"config":151},"Application Security Testing",{"href":152,"dataGaName":153,"dataGaLocation":44},"/solutions/application-security-testing/","Application security testing",{"text":155,"config":156},"Software Supply Chain Security",{"href":157,"dataGaLocation":44,"dataGaName":158},"/solutions/supply-chain/","Software supply chain security",{"text":160,"config":161},"Software Compliance",{"href":162,"dataGaName":163,"dataGaLocation":44},"/solutions/software-compliance/","software compliance",{"title":165,"link":166,"items":171},"Measurement",{"config":167},{"icon":168,"href":169,"dataGaName":170,"dataGaLocation":44},"DigitalTransformation","/solutions/visibility-measurement/","visibility and measurement",[172,176,180],{"text":173,"config":174},"Visibility & Measurement",{"href":169,"dataGaLocation":44,"dataGaName":175},"Visibility and Measurement",{"text":177,"config":178},"Value Stream Management",{"href":179,"dataGaLocation":44,"dataGaName":177},"/solutions/value-stream-management/",{"text":181,"config":182},"Analytics & Insights",{"href":183,"dataGaLocation":44,"dataGaName":184},"/solutions/analytics-and-insights/","Analytics and insights",{"title":186,"items":187},"GitLab for",[188,193,198],{"text":189,"config":190},"Enterprise",{"href":191,"dataGaLocation":44,"dataGaName":192},"/enterprise/","enterprise",{"text":194,"config":195},"Small Business",{"href":196,"dataGaLocation":44,"dataGaName":197},"/small-business/","small business",{"text":199,"config":200},"Public Sector",{"href":201,"dataGaLocation":44,"dataGaName":202},"/solutions/public-sector/","public sector",{"text":204,"config":205},"Pricing",{"href":206,"dataGaName":207,"dataGaLocation":44,"dataNavLevelOne":207},"/pricing/","pricing",{"text":209,"config":210,"link":212,"lists":216,"feature":299},"Resources",{"dataNavLevelOne":211},"resources",{"text":213,"config":214},"View all resources",{"href":215,"dataGaName":211,"dataGaLocation":44},"/resources/",[217,249,271],{"title":218,"items":219},"Getting started",[220,225,230,235,240,245],{"text":221,"config":222},"Install",{"href":223,"dataGaName":224,"dataGaLocation":44},"/install/","install",{"text":226,"config":227},"Quick start guides",{"href":228,"dataGaName":229,"dataGaLocation":44},"/get-started/","quick setup checklists",{"text":231,"config":232},"Learn",{"href":233,"dataGaLocation":44,"dataGaName":234},"https://university.gitlab.com/","learn",{"text":236,"config":237},"Product documentation",{"href":238,"dataGaName":239,"dataGaLocation":44},"https://docs.gitlab.com/","product documentation",{"text":241,"config":242},"Best practice videos",{"href":243,"dataGaName":244,"dataGaLocation":44},"/getting-started-videos/","best practice videos",{"text":246,"config":247},"Integrations",{"href":248,"dataGaName":23,"dataGaLocation":44},"/integrations/",{"title":250,"items":251},"Discover",[252,257,261,266],{"text":253,"config":254},"Customer success stories",{"href":255,"dataGaName":256,"dataGaLocation":44},"/customers/","customer success stories",{"text":258,"config":259},"Blog",{"href":260,"dataGaName":5,"dataGaLocation":44},"/blog/",{"text":262,"config":263},"Remote",{"href":264,"dataGaName":265,"dataGaLocation":44},"https://handbook.gitlab.com/handbook/company/culture/all-remote/","remote",{"text":267,"config":268},"TeamOps",{"href":269,"dataGaName":270,"dataGaLocation":44},"/teamops/","teamops",{"title":272,"items":273},"Connect",[274,279,284,289,294],{"text":275,"config":276},"GitLab Services",{"href":277,"dataGaName":278,"dataGaLocation":44},"/services/","services",{"text":280,"config":281},"Community",{"href":282,"dataGaName":283,"dataGaLocation":44},"/community/","community",{"text":285,"config":286},"Forum",{"href":287,"dataGaName":288,"dataGaLocation":44},"https://forum.gitlab.com/","forum",{"text":290,"config":291},"Events",{"href":292,"dataGaName":293,"dataGaLocation":44},"/events/","events",{"text":295,"config":296},"Partners",{"href":297,"dataGaName":298,"dataGaLocation":44},"/partners/","partners",{"backgroundColor":300,"textColor":301,"text":302,"image":303,"link":307},"#2f2a6b","#fff","Insights for the future of software development",{"altText":304,"config":305},"the source promo card",{"src":306},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758208064/dzl0dbift9xdizyelkk4.svg",{"text":308,"config":309},"Read the latest",{"href":310,"dataGaName":311,"dataGaLocation":44},"/the-source/","the source",{"text":313,"config":314,"lists":316},"Company",{"dataNavLevelOne":315},"company",[317],{"items":318},[319,324,330,332,337,342,347,352,357,362,367],{"text":320,"config":321},"About",{"href":322,"dataGaName":323,"dataGaLocation":44},"/company/","about",{"text":325,"config":326,"footerGa":329},"Jobs",{"href":327,"dataGaName":328,"dataGaLocation":44},"/jobs/","jobs",{"dataGaName":328},{"text":290,"config":331},{"href":292,"dataGaName":293,"dataGaLocation":44},{"text":333,"config":334},"Leadership",{"href":335,"dataGaName":336,"dataGaLocation":44},"/company/team/e-group/","leadership",{"text":338,"config":339},"Team",{"href":340,"dataGaName":341,"dataGaLocation":44},"/company/team/","team",{"text":343,"config":344},"Handbook",{"href":345,"dataGaName":346,"dataGaLocation":44},"https://handbook.gitlab.com/","handbook",{"text":348,"config":349},"Investor relations",{"href":350,"dataGaName":351,"dataGaLocation":44},"https://ir.gitlab.com/","investor relations",{"text":353,"config":354},"Trust Center",{"href":355,"dataGaName":356,"dataGaLocation":44},"/security/","trust center",{"text":358,"config":359},"AI Transparency Center",{"href":360,"dataGaName":361,"dataGaLocation":44},"/ai-transparency-center/","ai transparency center",{"text":363,"config":364},"Newsletter",{"href":365,"dataGaName":366,"dataGaLocation":44},"/company/contact/","newsletter",{"text":368,"config":369},"Press",{"href":370,"dataGaName":371,"dataGaLocation":44},"/press/","press",{"text":373,"config":374,"lists":375},"Contact us",{"dataNavLevelOne":315},[376],{"items":377},[378,381,386],{"text":51,"config":379},{"href":53,"dataGaName":380,"dataGaLocation":44},"talk to sales",{"text":382,"config":383},"Get help",{"href":384,"dataGaName":385,"dataGaLocation":44},"/support/","get help",{"text":387,"config":388},"Customer portal",{"href":389,"dataGaName":390,"dataGaLocation":44},"https://customers.gitlab.com/customers/sign_in/","customer portal",{"close":392,"login":393,"suggestions":400},"Close",{"text":394,"link":395},"To search repositories and projects, login to",{"text":396,"config":397},"gitlab.com",{"href":58,"dataGaName":398,"dataGaLocation":399},"search login","search",{"text":401,"default":402},"Suggestions",[403,405,409,411,415,419],{"text":73,"config":404},{"href":78,"dataGaName":73,"dataGaLocation":399},{"text":406,"config":407},"Code Suggestions (AI)",{"href":408,"dataGaName":406,"dataGaLocation":399},"/solutions/code-suggestions/",{"text":125,"config":410},{"href":127,"dataGaName":125,"dataGaLocation":399},{"text":412,"config":413},"GitLab on AWS",{"href":414,"dataGaName":412,"dataGaLocation":399},"/partners/technology-partners/aws/",{"text":416,"config":417},"GitLab on Google Cloud",{"href":418,"dataGaName":416,"dataGaLocation":399},"/partners/technology-partners/google-cloud-platform/",{"text":420,"config":421},"Why GitLab?",{"href":86,"dataGaName":420,"dataGaLocation":399},{"freeTrial":423,"mobileIcon":428,"desktopIcon":433,"secondaryButton":436},{"text":424,"config":425},"Start free trial",{"href":426,"dataGaName":49,"dataGaLocation":427},"https://gitlab.com/-/trials/new/","nav",{"altText":429,"config":430},"Gitlab Icon",{"src":431,"dataGaName":432,"dataGaLocation":427},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203874/jypbw1jx72aexsoohd7x.svg","gitlab icon",{"altText":429,"config":434},{"src":435,"dataGaName":432,"dataGaLocation":427},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1758203875/gs4c8p8opsgvflgkswz9.svg",{"text":437,"config":438},"Get Started",{"href":439,"dataGaName":440,"dataGaLocation":427},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com/compare/gitlab-vs-github/","get started",{"freeTrial":442,"mobileIcon":446,"desktopIcon":448},{"text":443,"config":444},"Learn more about GitLab Duo",{"href":78,"dataGaName":445,"dataGaLocation":427},"gitlab duo",{"altText":429,"config":447},{"src":431,"dataGaName":432,"dataGaLocation":427},{"altText":429,"config":449},{"src":435,"dataGaName":432,"dataGaLocation":427},{"freeTrial":451,"mobileIcon":456,"desktopIcon":458},{"text":452,"config":453},"Back to pricing",{"href":206,"dataGaName":454,"dataGaLocation":427,"icon":455},"back to pricing","GoBack",{"altText":429,"config":457},{"src":431,"dataGaName":432,"dataGaLocation":427},{"altText":429,"config":459},{"src":435,"dataGaName":432,"dataGaLocation":427},"content:shared:en-us:main-navigation.yml","Main Navigation","shared/en-us/main-navigation.yml","shared/en-us/main-navigation",{"_path":465,"_dir":38,"_draft":6,"_partial":6,"_locale":7,"title":466,"button":467,"image":472,"config":476,"_id":478,"_type":30,"_source":32,"_file":479,"_stem":480,"_extension":35},"/shared/en-us/banner","is now in public beta!",{"text":468,"config":469},"Try the Beta",{"href":470,"dataGaName":471,"dataGaLocation":44},"/gitlab-duo/agent-platform/","duo banner",{"altText":473,"config":474},"GitLab Duo Agent Platform",{"src":475},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1753720689/somrf9zaunk0xlt7ne4x.svg",{"layout":477},"release","content:shared:en-us:banner.yml","shared/en-us/banner.yml","shared/en-us/banner",{"_path":482,"_dir":38,"_draft":6,"_partial":6,"_locale":7,"data":483,"_id":687,"_type":30,"title":688,"_source":32,"_file":689,"_stem":690,"_extension":35},"/shared/en-us/main-footer",{"text":484,"source":485,"edit":491,"contribute":496,"config":501,"items":506,"minimal":679},"Git is a trademark of Software Freedom Conservancy and our use of 'GitLab' is under license",{"text":486,"config":487},"View page source",{"href":488,"dataGaName":489,"dataGaLocation":490},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/","page source","footer",{"text":492,"config":493},"Edit this page",{"href":494,"dataGaName":495,"dataGaLocation":490},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/content/","web ide",{"text":497,"config":498},"Please contribute",{"href":499,"dataGaName":500,"dataGaLocation":490},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/CONTRIBUTING.md/","please contribute",{"twitter":502,"facebook":503,"youtube":504,"linkedin":505},"https://twitter.com/gitlab","https://www.facebook.com/gitlab","https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg","https://www.linkedin.com/company/gitlab-com",[507,530,586,615,649],{"title":62,"links":508,"subMenu":513},[509],{"text":510,"config":511},"DevSecOps platform",{"href":71,"dataGaName":512,"dataGaLocation":490},"devsecops platform",[514],{"title":204,"links":515},[516,520,525],{"text":517,"config":518},"View plans",{"href":206,"dataGaName":519,"dataGaLocation":490},"view plans",{"text":521,"config":522},"Why Premium?",{"href":523,"dataGaName":524,"dataGaLocation":490},"/pricing/premium/","why premium",{"text":526,"config":527},"Why Ultimate?",{"href":528,"dataGaName":529,"dataGaLocation":490},"/pricing/ultimate/","why ultimate",{"title":531,"links":532},"Solutions",[533,538,540,542,547,552,556,559,563,568,570,573,576,581],{"text":534,"config":535},"Digital transformation",{"href":536,"dataGaName":537,"dataGaLocation":490},"/topics/digital-transformation/","digital transformation",{"text":150,"config":539},{"href":152,"dataGaName":150,"dataGaLocation":490},{"text":139,"config":541},{"href":121,"dataGaName":122,"dataGaLocation":490},{"text":543,"config":544},"Agile development",{"href":545,"dataGaName":546,"dataGaLocation":490},"/solutions/agile-delivery/","agile delivery",{"text":548,"config":549},"Cloud transformation",{"href":550,"dataGaName":551,"dataGaLocation":490},"/topics/cloud-native/","cloud transformation",{"text":553,"config":554},"SCM",{"href":135,"dataGaName":555,"dataGaLocation":490},"source code management",{"text":125,"config":557},{"href":127,"dataGaName":558,"dataGaLocation":490},"continuous integration & delivery",{"text":560,"config":561},"Value stream management",{"href":179,"dataGaName":562,"dataGaLocation":490},"value stream management",{"text":564,"config":565},"GitOps",{"href":566,"dataGaName":567,"dataGaLocation":490},"/solutions/gitops/","gitops",{"text":189,"config":569},{"href":191,"dataGaName":192,"dataGaLocation":490},{"text":571,"config":572},"Small business",{"href":196,"dataGaName":197,"dataGaLocation":490},{"text":574,"config":575},"Public sector",{"href":201,"dataGaName":202,"dataGaLocation":490},{"text":577,"config":578},"Education",{"href":579,"dataGaName":580,"dataGaLocation":490},"/solutions/education/","education",{"text":582,"config":583},"Financial services",{"href":584,"dataGaName":585,"dataGaLocation":490},"/solutions/finance/","financial services",{"title":209,"links":587},[588,590,592,594,597,599,601,603,605,607,609,611,613],{"text":221,"config":589},{"href":223,"dataGaName":224,"dataGaLocation":490},{"text":226,"config":591},{"href":228,"dataGaName":229,"dataGaLocation":490},{"text":231,"config":593},{"href":233,"dataGaName":234,"dataGaLocation":490},{"text":236,"config":595},{"href":238,"dataGaName":596,"dataGaLocation":490},"docs",{"text":258,"config":598},{"href":260,"dataGaName":5,"dataGaLocation":490},{"text":253,"config":600},{"href":255,"dataGaName":256,"dataGaLocation":490},{"text":262,"config":602},{"href":264,"dataGaName":265,"dataGaLocation":490},{"text":275,"config":604},{"href":277,"dataGaName":278,"dataGaLocation":490},{"text":267,"config":606},{"href":269,"dataGaName":270,"dataGaLocation":490},{"text":280,"config":608},{"href":282,"dataGaName":283,"dataGaLocation":490},{"text":285,"config":610},{"href":287,"dataGaName":288,"dataGaLocation":490},{"text":290,"config":612},{"href":292,"dataGaName":293,"dataGaLocation":490},{"text":295,"config":614},{"href":297,"dataGaName":298,"dataGaLocation":490},{"title":313,"links":616},[617,619,621,623,625,627,629,633,638,640,642,644],{"text":320,"config":618},{"href":322,"dataGaName":315,"dataGaLocation":490},{"text":325,"config":620},{"href":327,"dataGaName":328,"dataGaLocation":490},{"text":333,"config":622},{"href":335,"dataGaName":336,"dataGaLocation":490},{"text":338,"config":624},{"href":340,"dataGaName":341,"dataGaLocation":490},{"text":343,"config":626},{"href":345,"dataGaName":346,"dataGaLocation":490},{"text":348,"config":628},{"href":350,"dataGaName":351,"dataGaLocation":490},{"text":630,"config":631},"Sustainability",{"href":632,"dataGaName":630,"dataGaLocation":490},"/sustainability/",{"text":634,"config":635},"Diversity, inclusion and belonging (DIB)",{"href":636,"dataGaName":637,"dataGaLocation":490},"/diversity-inclusion-belonging/","Diversity, inclusion and belonging",{"text":353,"config":639},{"href":355,"dataGaName":356,"dataGaLocation":490},{"text":363,"config":641},{"href":365,"dataGaName":366,"dataGaLocation":490},{"text":368,"config":643},{"href":370,"dataGaName":371,"dataGaLocation":490},{"text":645,"config":646},"Modern Slavery Transparency Statement",{"href":647,"dataGaName":648,"dataGaLocation":490},"https://handbook.gitlab.com/handbook/legal/modern-slavery-act-transparency-statement/","modern slavery transparency statement",{"title":650,"links":651},"Contact Us",[652,655,657,659,664,669,674],{"text":653,"config":654},"Contact an expert",{"href":53,"dataGaName":54,"dataGaLocation":490},{"text":382,"config":656},{"href":384,"dataGaName":385,"dataGaLocation":490},{"text":387,"config":658},{"href":389,"dataGaName":390,"dataGaLocation":490},{"text":660,"config":661},"Status",{"href":662,"dataGaName":663,"dataGaLocation":490},"https://status.gitlab.com/","status",{"text":665,"config":666},"Terms of use",{"href":667,"dataGaName":668,"dataGaLocation":490},"/terms/","terms of use",{"text":670,"config":671},"Privacy statement",{"href":672,"dataGaName":673,"dataGaLocation":490},"/privacy/","privacy statement",{"text":675,"config":676},"Cookie preferences",{"dataGaName":677,"dataGaLocation":490,"id":678,"isOneTrustButton":107},"cookie preferences","ot-sdk-btn",{"items":680},[681,683,685],{"text":665,"config":682},{"href":667,"dataGaName":668,"dataGaLocation":490},{"text":670,"config":684},{"href":672,"dataGaName":673,"dataGaLocation":490},{"text":675,"config":686},{"dataGaName":677,"dataGaLocation":490,"id":678,"isOneTrustButton":107},"content:shared:en-us:main-footer.yml","Main Footer","shared/en-us/main-footer.yml","shared/en-us/main-footer",[692],{"_path":693,"_dir":694,"_draft":6,"_partial":6,"_locale":7,"content":695,"config":699,"_id":701,"_type":30,"title":18,"_source":32,"_file":702,"_stem":703,"_extension":35},"/en-us/blog/authors/tsvi-zandany","authors",{"name":18,"config":696},{"headshot":697,"ctfId":698},"https://res.cloudinary.com/about-gitlab-com/image/upload/v1749659488/Blog/Author%20Headshots/gitlab-logo-extra-whitespace.png","Tsvi-Zandany",{"template":700},"BlogAuthor","content:en-us:blog:authors:tsvi-zandany.yml","en-us/blog/authors/tsvi-zandany.yml","en-us/blog/authors/tsvi-zandany",{"_path":705,"_dir":38,"_draft":6,"_partial":6,"_locale":7,"header":706,"eyebrow":707,"blurb":708,"button":709,"secondaryButton":713,"_id":715,"_type":30,"title":716,"_source":32,"_file":717,"_stem":718,"_extension":35},"/shared/en-us/next-steps","Start shipping better software faster","50%+ of the Fortune 100 trust GitLab","See what your team can do with the intelligent\n\n\nDevSecOps platform.\n",{"text":46,"config":710},{"href":711,"dataGaName":49,"dataGaLocation":712},"https://gitlab.com/-/trial_registrations/new?glm_content=default-saas-trial&glm_source=about.gitlab.com/","feature",{"text":51,"config":714},{"href":53,"dataGaName":54,"dataGaLocation":712},"content:shared:en-us:next-steps.yml","Next Steps","shared/en-us/next-steps.yml","shared/en-us/next-steps",1758326264697]