[{"data":1,"prerenderedAt":719},["ShallowReactive",2],{"/en-us/blog/go-tools-and-gitlab-how-to-do-continuous-integration-like-a-boss/":3,"navigation-en-us":36,"banner-en-us":464,"footer-en-us":481,"Julien Andrieux":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/go-tools-and-gitlab-how-to-do-continuous-integration-like-a-boss","blog",false,"",{"title":9,"description":10,"ogTitle":9,"ogDescription":10,"noIndex":6,"ogImage":11,"ogUrl":12,"ogSiteName":13,"ogType":14,"canonicalUrls":12,"schema":15},"Go tools and GitLab: How to do continuous integration like a boss","How the team at Pantomath makes their lives easier with GitLab CI.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1749667978/Blog/Hero%20Images/go-tools-and-gitlab.jpg","https://about.gitlab.com/blog/go-tools-and-gitlab-how-to-do-continuous-integration-like-a-boss","https://about.gitlab.com","article","\n                        {\n        \"@context\": \"https://schema.org\",\n        \"@type\": \"Article\",\n        \"headline\": \"Go tools and GitLab: How to do continuous integration like a boss\",\n        \"author\": [{\"@type\":\"Person\",\"name\":\"Julien Andrieux\"}],\n        \"datePublished\": \"2017-11-27\",\n      }",{"title":9,"description":10,"authors":17,"heroImage":11,"date":19,"body":20,"category":21,"tags":22},[18],"Julien Andrieux","2017-11-27","At [Pantomath](https://pantomath.io/), we use [GitLab](/) for all our\ndevelopment work. The purpose of this paper is not to present GitLab and all\n[its features](/pricing/feature-comparison/), but to introduce how we use these tools to ease\nour lives. So what is it all about? To automate everything that is related\nto your development project, and let you focus on your code.\n\n\n\u003C!-- more -->\n\n\nWe’ll cover the [lint](https://en.wikipedia.org/wiki/Lint_(software)), [unit\ntests](https://en.wikipedia.org/wiki/Unit_testing), [data\nrace](https://en.wikipedia.org/wiki/Race_condition), [memory\nsanitizer](https://clang.llvm.org/docs/MemorySanitizer.html), [code\ncoverage](https://en.wikipedia.org/wiki/Code_coverage), and build.\n\n\nAll the source code shown in this post is available at\n[gitlab.com/pantomath-io/demo-tools](https://gitlab.com/pantomath-io/demo-tools).\nSo feel free to get the repository, and use the tags to navigate in it. The\nrepository should be placed in the `src` folder of your `$GOPATH`:\n\n\n```bash\n\n$ go get -v -d gitlab.com/pantomath-io/demo-tools\n\n$ cd $GOPATH/src/gitlab.com/pantomath-io/demo-tools\n\n```\n\n\n### Go tools\n\n\nLuckily, `Go` — the open source programming language also known as golang —\ncomes with a [lot of useful tools](https://golang.org/cmd/go/), to build,\ntest, and check your code. In fact, it’s all there. We’ll just add extra\ntools to glue them together. But before we go there, we need to take them\none by one, and see what they do.\n\n\n#### Package list\n\n\nYour Go project is a collection of packages, as described in the [official\ndoc](https://golang.org/doc/code.html). Most of the following tools will be\nfed with these packages, and thus the first command we need is a way to list\nthe packages. Hopefully, the `Go` language covers our back with the `list`\nsubcommand ([read the fine\nmanual](https://golang.org/cmd/go/#hdr-List_packages) and this [excellent\npost from Dave\nCheney](https://dave.cheney.net/2014/09/14/go-list-your-swiss-army-knife)):\n\n\n```bash\n\n$ go list ./...\n\n```\n\n\nNote that we want to avoid applying our tools on external packages or\nresources, and restrict it to **our** code. So we need to get rid of the\n[vendor directories](https://golang.org/cmd/go/#hdr-Vendor_Directories):\n\n\n```bash\n\n$ go list ./... | grep -v /vendor/\n\n```\n\n\n#### Lint\n\n\nThis is the very first tool we use on the code: the linter. Its role is to\nmake sure that the code respects the code style. This may sounds like an\noptional tool, or at least a “nice-to-have” but it really helps to keep\nconsistent style over your project.\n\n\nThis linter is not part of Go *per se*, so you need to grab it and install\nit by hand (see [official doc](https://github.com/golang/lint)).\n\n\nThe usage is fairly simple: you just run it on the packages of your code\n(you can also point the `.go` files):\n\n\n```bash\n\n$ golint -set_exit_status $(go list ./... | grep -v /vendor/)\n\n```\n\n\nNote the `-set_exit_status` option. By default, `golint` only prints the\nstyle issues, and returns (with a 0 return code), so the CI never considers\nsomething went wrong. If you specify the `-set_exit_status`, the return code\nfrom `golint` will be different from 0 if any style issue is encountered.\n\n\n#### Unit test\n\n\nThese are the most common tests you can run on your code. For each `.go`\nfile, we need to have an associated `_test.go` file holding the unit tests.\nYou can run the tests for all the packages with the following command:\n\n\n```bash\n\n$ go test -short $(go list ./... | grep -v /vendor/)\n\n```\n\n\n#### Data race\n\n\nThis is usually a hard subject to cover, but the `Go` tool has it by default\n(but only available on `linux/amd64`, `freebsd/amd64`, `darwin/amd64` and\n`windows/amd64`). For more information about data race, see [this\narticle](https://golang.org/doc/articles/race_detector.html). Meanwhile,\nhere is how to run it:\n\n\n```bash\n\n$ go test -race -short $(go list ./... | grep -v /vendor/)\n\n```\n\n\n#### Memory sanitizer\n\n\nClang has a nice detector for uninitialized reads called\n[MemorySanitizer](https://clang.llvm.org/docs/MemorySanitizer.html). The `go\ntest` tool is kind enough to interact with this Clang module (as soon as you\nare on `linux/amd64` host and using a recent version of Clang/LLVM\n(`>=3.8.0`). This command is how to run it:\n\n\n```bash\n\n$ go test -msan -short $(go list ./... | grep -v /vendor/)\n\n```\n\n\n#### Code coverage\n\n\nThis is also a must have to evaluate the health of your code, and see what\nthe part of code is under unit tests and what part is not. [Rob\nPike](https://twitter.com/rob_pike) wrote a [full post on that very\nsubject](https://blog.golang.org/cover).\n\n\nTo calculate the code coverage ratio, we need to run the following script:\n\n\n```bash\n\n$ PKG_LIST=$(go list ./... | grep -v /vendor/)\n\n$ for package in ${PKG_LIST}; do\n    go test -covermode=count -coverprofile \"cover/${package##*/}.cov\" \"$package\" ;\ndone\n\n$ tail -q -n +2 cover/*.cov >> cover/coverage.cov\n\n$ go tool cover -func=cover/coverage.cov\n\n```\n\n\nIf we want to get the coverage report in HTML format, we need to add the\nfollowing command:\n\n\n```bash\n\n$ go tool cover -html=cover/coverage.cov -o coverage.html\n\n```\n\n\n#### Build\n\n\nLast but not least, once the code has been fully tested, we might want to\ncompile it to make sure we can build a working binary.\n\n\n```bash\n\n$ go build -i -v gitlab.com/pantomath-io/demo-tools\n\n```\n\n\n### Makefile\n\n\n*git tag:*\n[init-makefile](https://gitlab.com/pantomath-io/demo-tools/tags/init-makefile)\n\n\n![](https://cdn-images-1.medium.com/max/1600/1*Ip_q_6I-kNpUjuPMOutuTA.jpeg)\n\n*\u003Csmall>Photo by [Matt\nArtz](https://unsplash.com/photos/qJE5Svhs2ek?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)\non\n[Unsplash](https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)\u003C/small>*\n\n\nNow we have all the tools that we may use in the context of continuous\nintegration, we can wrap them all in a\n[Makefile](https://gitlab.com/pantomath-io/demo-tools/blob/init-makefile/Makefile),\nand have a consistent way to call them.\n\n\nThe purpose of this doc is not to present `make`, but you can refer to\n[official documentation](https://www.gnu.org/software/make/manual/make.html)\nto learn more about it.\n\n    PROJECT_NAME := \"demo-tools\"\n    PKG := \"gitlab.com/pantomath-io/$(PROJECT_NAME)\"\n    PKG_LIST := $(shell go list ${PKG}/... | grep -v /vendor/)\n    GO_FILES := $(shell find . -name '*.go' | grep -v /vendor/ | grep -v _test.go)\n\n    .PHONY: all dep build clean test coverage coverhtml lint\n\n    all: build\n\n    lint: ## Lint the files\n      @golint -set_exit_status ${PKG_LIST}\n\n    test: ## Run unittests\n      @go test -short ${PKG_LIST}\n\n    race: dep ## Run data race detector\n      @go test -race -short ${PKG_LIST}\n\n    msan: dep ## Run memory sanitizer\n      @go test -msan -short ${PKG_LIST}\n\n    coverage: ## Generate global code coverage report\n      ./tools/coverage.sh;\n\n    coverhtml: ## Generate global code coverage report in HTML\n      ./tools/coverage.sh html;\n\n    dep: ## Get the dependencies\n      @go get -v -d ./...\n\n    build: dep ## Build the binary file\n      @go build -i -v $(PKG)\n\n    clean: ## Remove previous build\n      @rm -f $(PROJECT_NAME)\n\n    help: ## Display this help screen\n      @grep -h -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = \":.*?## \"}; {printf \"\\033[36m%-30s\\033[0m %s\\n\", $$1, $$2}'\n\nWhat do we have now? One target for any tool previously presented, and three\nmore targets for:\n\n\n* installation of dependencies (`dep`);\n\n* housekeeping of the project (`clean`);\n\n* some nice and shiny help (`help`).\n\n\nNote that we also had to create a script for the code coverage work. This is\nbecause implementing loops over files in a Makefile is a pain. So the work\nis done in a `bash` script, and the Makefile only triggers this script.\n\n\nYou can try the Makefile with the following commands:\n\n    $ make help\n    $ make lint\n    $ make coverage\n\n### Continuous integration\n\n\n*git tag:*\n[init-ci](https://gitlab.com/pantomath-io/demo-tools/tags/init-ci)\n\n\nNow the tools are in place, and we can run various tests on our code, we’d\nlike to automate these, on your repository. Luckily, GitLab offers [CI\npipelines](/solutions/continuous-integration/) just for this. And the setup\nfor this is pretty straightforward: all you create is a `.gitlab-ci.yml`\nfile at the root of the repository.\n\n\nThe [full documentation](https://docs.gitlab.com/ee/ci/yaml/) on this Yaml\nfile presents all the options, but you can start with this `.gitlab-ci.yml`:\n\n\n```yaml\n\nimage: golang:1.9\n\n\ncache:\n  paths:\n    - /apt-cache\n    - /go/src/github.com\n    - /go/src/golang.org\n    - /go/src/google.golang.org\n    - /go/src/gopkg.in\n\nstages:\n  - test\n  - build\n\nbefore_script:\n  - mkdir -p /go/src/gitlab.com/pantomath-io /go/src/_/builds\n  - cp -r $CI_PROJECT_DIR /go/src/gitlab.com/pantomath-io/pantomath\n  - ln -s /go/src/gitlab.com/pantomath-io /go/src/_/builds/pantomath-io\n  - make dep\n\nunit_tests:\n  stage: test\n  script:\n    - make test\n\nrace_detector:\n  stage: test\n  script:\n    - make race\n\nmemory_sanitizer:\n  stage: test\n  script:\n    - make msan\n\ncode_coverage:\n  stage: test\n  script:\n    - make coverage\n\ncode_coverage_report:\n  stage: test\n  script:\n    - make coverhtml\n  only:\n  - master\n\nlint_code:\n  stage: test\n  script:\n    - make lint\n\nbuild:\n  stage: build\n  script:\n    - make\n```\n\n\nIf you break down the file, here are some explanations on its content:\n\n\n* The first thing is to choose what Docker image will be used to run the CI.\nHead to the [Docker Hub](https://hub.docker.com/) to choose the right image\nfor your project.\n\n* Then, you specify some folders of this image [to be\ncached](https://docs.gitlab.com/ee/ci/yaml/#cache). The goal here is to\navoid downloading the same content several times. Once a job is completed,\nthe listed paths will be archived, and next job will use the same archive.\n\n* You define the different `stages` that will group your jobs. In our case,\nwe have two [stages](https://docs.gitlab.com/ee/ci/yaml/#stages) (to be\nprocessed in that order): `test` and `build`. We could have other stages,\nsuch as `deploy`.\n\n* The `before_script`\n[section](https://docs.gitlab.com/ee/ci/yaml/#before_script) defines the\ncommands to run in the Docker container right before the job is actually\ndone. In our context, the commands just copy or link the repository deployed\nin the `$GOPATH`, and install dependencies.\n\n* Then come the actual [jobs](https://docs.gitlab.com/ee/ci/jobs/), using\nthe `Makefile` targets. Note the special case for `code_coverage_report`\nwhere execution is restricted to the `master` branch (we don’t want to\nupdate the code coverage report from feature branches for instance).\n\n\nAs we commit/push the `.gitlab-ci.yml` file in the repository, the CI is\n[automatically\ntriggered](https://gitlab.com/pantomath-io/demo-tools/pipelines/13481935).\nAnd the pipeline fails. Howcome?\n\n\nThe `lint_code`\n[job](https://gitlab.com/pantomath-io/demo-tools/-/jobs/38690212) fails\nbecause it can’t find the `golint` binary:\n\n\n```bash\n\n$ make lint\n\nmake: golint: Command not found\n\nMakefile:11: recipe for target 'lint' failed\n\nmake: *** [lint] Error 127\n\n```\n\n\nSo,\n[update](https://gitlab.com/pantomath-io/demo-toolscommit/17a0206eb626504e559f56773e2d81c7b5808dbe)\nyour `Makefile` to install `golint` as part of the `dep` target.\n\n\nThe `memory_sanitizer`\n[job](https://gitlab.com/pantomath-io/demo-tools/-/jobs/38690209) fails\nbecause `gcc` complains:\n\n\n```bash\n\n$ make msan\n\n# runtime/cgo\n\ngcc: error: unrecognized argument to -fsanitize= option: 'memory'\n\nMakefile:20: recipe for target 'msan' failed\n\nmake: *** [msan] Error 2\n\n```\n\n\nBut remember we need to use Clang/LLVM `>=3.8.0` to enjoy the `-msan` option\nin `go test` command.\n\n\nWe have two options here:\n\n\n* either we set up Clang in the job (using `before_script`);\n\n* or we use a Docker image with Clang installed by default.\n\n\nThe first option is nice, but that implies to have this setup done **for\nevery single job**. This is going to be so long, we should do it once and\nfor all. So we prefer the second option, which is a good way to play with\n[GitLab\nRegistry](https://docs.gitlab.com/ee/user/packages/container_registry/index.html).\n\n\n*git tag:*\n[use-own-docker](https://gitlab.com/pantomath-io/demo-tools/tags/use-own-docker)\n\n\nWe need to create a\n[Dockerfile](https://gitlab.com/pantomath-io/demo-tools/blob/use-own-docker/Dockerfile)\nfor the container (as usual: read the [official\ndocumentation](https://docs.docker.com/engine/reference/builder) for more\noptions about it):\n\n    # Base image:\n    FROM golang:1.9\n    MAINTAINER Julien Andrieux \u003Cjulien@pantomath.io>\n\n    # Install golint\n    ENV GOPATH /go\n    ENV PATH ${GOPATH}/bin:$PATH\n    RUN go get -u github.com/golang/lint/golint\n\n    # Add apt key for LLVM repository\n    RUN wget -O -\n     | apt-key add -\n\n    # Add LLVM apt repository\n    RUN echo \"deb\n     llvm-toolchain-stretch-5.0 main\" | tee -a /etc/apt/sources.list\n\n    # Install clang from LLVM repository\n    RUN apt-get update && apt-get install -y --no-install-recommends \\\n        clang-5.0 \\\n        && apt-get clean \\\n        && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*\n\n    # Set Clang as default CC\n    ENV set_clang /etc/profile.d/set-clang-cc.sh\n    RUN echo \"export CC=clang-5.0\" | tee -a ${set_clang} && chmod a+x ${set_clang}\n\nThe container built out of this Dockerfile will be based on\n[golang:1.9](https://hub.docker.com/_/golang/) image (the one referenced in\nthe `.gitlab-ci.yml` file).\n\n\nWhile we’re at it, we install `golint` in the container, so we have it\navailable. Then we follow [official way](http://apt.llvm.org/) of installing\nClang 5.0 from LLVM repository.\n\n\nNow we have the Dockerfile in place, we need to build the container image\nand make it available for GitLab:\n\n\n```bash\n\n$ docker login registry.gitlab.com\n\n$ docker build -t registry.gitlab.com/pantomath-io/demo-tools .\n\n$ docker push registry.gitlab.com/pantomath-io/demo-tools\n\n```\n\n\nThe first command connects you to the GitLab Registry. Then you build the\ncontainer image described in the Dockerfile. And finally, you push it to the\nGitLab Registry.\n\n\nTake a look at the [Registry for your\nrepository](https://gitlab.com/pantomath-io/demo-tools/container_registry),\nyou’ll see your image, ready to be used. And to have the CI using your\nimage, you just need to update the `.gitlab-ci.yml` file:\n\n    image: golang:1.9\n\nbecomes\n\n    image: registry.gitlab.com/pantomath-io/demo-tools:latest\n\nOne last detail: you need to tell the CI to use the proper compiler (i.e.\nthe `CC` environment variable), so we add the variable initialization in the\n`.gitlab-ci.yml` file:\n\n    export CC=clang-5.0\n\nOnce the modification are done, next commit will trigger the pipeline, which\nnow works:\n\n\n[gitlab.com/pantomath-io/demo-tools/pipelines/13497136](https://gitlab.com/pantomath-io/demo-tools/pipelines/13497136)\n\n\n### Badges\n\n\n*git tag:*\n[init-badges](https://gitlab.com/pantomath-io/demo-tools/tags/init-badges)\n\n\n![](https://cdn-images-1.medium.com/max/1600/1*0pY_6oCiHZ_eLh0vfg5rDA.jpeg)\n\n\n*\u003Csmall>Photo by [Jakob\nOwens](https://unsplash.com/photos/ZBadHaTUkP0?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)\non\n[Unsplash](https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)\u003C/small>*\n\n\nNow the tools are in place, every commit will launch a test suite, and you\nprobably want to show it, and that’s legitimate :) The best way to do so is\nto use badges, and the best place for it is the `README`\n[file](https://gitlab.com/pantomath-io/demo-tools/blob/init-badges/README.md).\n\n\nEdit it and add the four following badges:\n\n\n* Build Status: the status of the last pipeline on the `master` branch:\n\n\n```\n\n[![Build\nStatus](https://gitlab.com/pantomath-io/demo-tools/badges/master/build.svg)](https://gitlab.com/pantomath-io/demo-tools/commits/master)\n\n```\n\n\n* Coverage Report: the percentage of source code covered by tests\n\n\n```\n\n[![Coverage\nReport](https://gitlab.com/pantomath-io/demo-tools/badges/master/coverage.svg)](https://gitlab.com/pantomath-io/demo-tools/commits/master)\n\n```\n\n\n* Go Report Card:\n\n\n```\n\n[![Go Report\nCard](https://goreportcard.com/badge/gitlab.com/pantomath-io/demo-tools)](https://goreportcard.com/report/gitlab.com/pantomath-io/demo-tools)\n\n```\n\n\n* License:\n\n\n```\n\n[![License\nMIT](https://img.shields.io/badge/License-MIT-brightgreen.svg)](https://img.shields.io/badge/License-MIT-brightgreen.svg)\n\n```\n\n\nThe coverage report needs a special configuration. You need to tell GitLab\nhow to get that information, considering that there is a job in the CI that\n*displays* it when it runs.\u003Cbr> There is a\n[configuration](https://gitlab.com/help/user/project/pipelines/settings#test-coverage-parsing)\nto provide GitLab with a regexp, used in any job’ output. If the regexp\nmatches, GitLab consider the match to be the code coverage result.\n\n\nSo head to `Settings > CI/CD` in your repository, scroll down to the `Test\ncoverage parsing` setting in the `General pipelines settings` section, and\nuse the following regexp:\n\n    total:\\s+\\(statements\\)\\s+(\\d+.\\d+\\%)\n\nYou’re all set! Head to the [overview of your\nrepository](https://gitlab.com/pantomath-io/demo-tools/tree/init-badges),\nand look at your `README`:\n\n\n### Conclusion\n\n\nWhat’s next? Probably more tests in your CI. You can also look at the CD\n([Continuous\nDeployment](https://docs.gitlab.com/ee/ci/environments/index.html)) to\nautomate the deployment of your builds. The documentation can be done using\n[GoDoc](https://godoc.org/-/about). Note that you generate a coverage report\nwith the `code_coverage_report`, but don’t use it in the CI. You can make\nthe job copy the HTML file to a web server, using `scp` (see this\n[documentation](https://docs.gitlab.com/ee/ci/ssh_keys/) on how to use SSH\nkeys).\n\n\nMany thanks to [Charles Francoise](https://dev.to/loderunner) who co-wrote\nthis paper and\n[gitlab.com/pantomath-io/demo-tools](https://gitlab.com/pantomath-io/demo-tools).\n\n\n## About the Guest Author\n\n\nJulien Andrieux is currently working on Pantomath. Pantomath is a modern,\nopen source monitoring solution, built for performance, that bridges the\ngaps across all levels of your company. The wellbeing of your infrastructure\nis everyone’s business. [Keep up with the project](http://goo.gl/tcxtXq).\n\n *[Go tools & GitLab - how to do Continuous Integration like a boss](https://medium.com/pantomath/go-tools-gitlab-how-to-do-continuous-integration-like-a-boss-941a3a9ad0b6) was originally published on Medium.*\n\n*Cover photo by [Todd\nQuackenbush](https://unsplash.com/photos/IClZBVw5W5A?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)\non\n[Unsplash](https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)*\n\n{: .note}\n","engineering",[23,24,25],"CI/CD","user stories","tutorial",{"slug":27,"featured":6,"template":28},"go-tools-and-gitlab-how-to-do-continuous-integration-like-a-boss","BlogPost","content:en-us:blog:go-tools-and-gitlab-how-to-do-continuous-integration-like-a-boss.yml","yaml","Go Tools And Gitlab How To Do Continuous Integration Like A Boss","content","en-us/blog/go-tools-and-gitlab-how-to-do-continuous-integration-like-a-boss.yml","en-us/blog/go-tools-and-gitlab-how-to-do-continuous-integration-like-a-boss","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,202,207,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":184},"Product",true,{"dataNavLevelOne":109},"solutions",{"text":111,"config":112},"View all Solutions",{"href":113,"dataGaName":109,"dataGaLocation":44},"/solutions/",[115,139,163],{"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,127,131,135],{"text":23,"config":125},{"href":126,"dataGaLocation":44,"dataGaName":23},"/solutions/continuous-integration/",{"text":128,"config":129},"AI-Assisted Development",{"href":78,"dataGaLocation":44,"dataGaName":130},"AI assisted development",{"text":132,"config":133},"Source Code Management",{"href":134,"dataGaLocation":44,"dataGaName":132},"/solutions/source-code-management/",{"text":136,"config":137},"Automated Software Delivery",{"href":121,"dataGaLocation":44,"dataGaName":138},"Automated software delivery",{"title":140,"description":141,"link":142,"items":147},"Security","Deliver code faster without compromising security",{"config":143},{"href":144,"dataGaName":145,"dataGaLocation":44,"icon":146},"/solutions/security-compliance/","security and compliance","ShieldCheckLight",[148,153,158],{"text":149,"config":150},"Application Security Testing",{"href":151,"dataGaName":152,"dataGaLocation":44},"/solutions/application-security-testing/","Application security testing",{"text":154,"config":155},"Software Supply Chain Security",{"href":156,"dataGaLocation":44,"dataGaName":157},"/solutions/supply-chain/","Software supply chain security",{"text":159,"config":160},"Software Compliance",{"href":161,"dataGaName":162,"dataGaLocation":44},"/solutions/software-compliance/","software compliance",{"title":164,"link":165,"items":170},"Measurement",{"config":166},{"icon":167,"href":168,"dataGaName":169,"dataGaLocation":44},"DigitalTransformation","/solutions/visibility-measurement/","visibility and measurement",[171,175,179],{"text":172,"config":173},"Visibility & Measurement",{"href":168,"dataGaLocation":44,"dataGaName":174},"Visibility and Measurement",{"text":176,"config":177},"Value Stream Management",{"href":178,"dataGaLocation":44,"dataGaName":176},"/solutions/value-stream-management/",{"text":180,"config":181},"Analytics & Insights",{"href":182,"dataGaLocation":44,"dataGaName":183},"/solutions/analytics-and-insights/","Analytics and insights",{"title":185,"items":186},"GitLab for",[187,192,197],{"text":188,"config":189},"Enterprise",{"href":190,"dataGaLocation":44,"dataGaName":191},"/enterprise/","enterprise",{"text":193,"config":194},"Small Business",{"href":195,"dataGaLocation":44,"dataGaName":196},"/small-business/","small business",{"text":198,"config":199},"Public Sector",{"href":200,"dataGaLocation":44,"dataGaName":201},"/solutions/public-sector/","public sector",{"text":203,"config":204},"Pricing",{"href":205,"dataGaName":206,"dataGaLocation":44,"dataNavLevelOne":206},"/pricing/","pricing",{"text":208,"config":209,"link":211,"lists":215,"feature":299},"Resources",{"dataNavLevelOne":210},"resources",{"text":212,"config":213},"View all resources",{"href":214,"dataGaName":210,"dataGaLocation":44},"/resources/",[216,249,271],{"title":217,"items":218},"Getting started",[219,224,229,234,239,244],{"text":220,"config":221},"Install",{"href":222,"dataGaName":223,"dataGaLocation":44},"/install/","install",{"text":225,"config":226},"Quick start guides",{"href":227,"dataGaName":228,"dataGaLocation":44},"/get-started/","quick setup checklists",{"text":230,"config":231},"Learn",{"href":232,"dataGaLocation":44,"dataGaName":233},"https://university.gitlab.com/","learn",{"text":235,"config":236},"Product documentation",{"href":237,"dataGaName":238,"dataGaLocation":44},"https://docs.gitlab.com/","product documentation",{"text":240,"config":241},"Best practice videos",{"href":242,"dataGaName":243,"dataGaLocation":44},"/getting-started-videos/","best practice videos",{"text":245,"config":246},"Integrations",{"href":247,"dataGaName":248,"dataGaLocation":44},"/integrations/","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":23,"config":410},{"href":126,"dataGaName":23,"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":205,"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":203,"links":515},[516,520,525],{"text":517,"config":518},"View plans",{"href":205,"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":149,"config":539},{"href":151,"dataGaName":149,"dataGaLocation":490},{"text":138,"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":134,"dataGaName":555,"dataGaLocation":490},"source code management",{"text":23,"config":557},{"href":126,"dataGaName":558,"dataGaLocation":490},"continuous integration & delivery",{"text":560,"config":561},"Value stream management",{"href":178,"dataGaName":562,"dataGaLocation":490},"value stream management",{"text":564,"config":565},"GitOps",{"href":566,"dataGaName":567,"dataGaLocation":490},"/solutions/gitops/","gitops",{"text":188,"config":569},{"href":190,"dataGaName":191,"dataGaLocation":490},{"text":571,"config":572},"Small business",{"href":195,"dataGaName":196,"dataGaLocation":490},{"text":574,"config":575},"Public sector",{"href":200,"dataGaName":201,"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":208,"links":587},[588,590,592,594,597,599,601,603,605,607,609,611,613],{"text":220,"config":589},{"href":222,"dataGaName":223,"dataGaLocation":490},{"text":225,"config":591},{"href":227,"dataGaName":228,"dataGaLocation":490},{"text":230,"config":593},{"href":232,"dataGaName":233,"dataGaLocation":490},{"text":235,"config":595},{"href":237,"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/julien-andrieux","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","Julien-Andrieux",{"template":700},"BlogAuthor","content:en-us:blog:authors:julien-andrieux.yml","en-us/blog/authors/julien-andrieux.yml","en-us/blog/authors/julien-andrieux",{"_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",1758326257375]