CLOVER🍀

That was when it all began.

TerraformでGitLabのDefault branchとProtected branchesを設定する

これは、なにをしたくて書いたもの?

GitLabのDefault branchとProtected branchesを、Terraformで設定してみましょう、ということで。

Default branch

Default branchはこちらの話です。

Default branch | GitLab Docs

新しいプロジェクトを作成した時に作成されるブランチで、以下のように特別扱いされます。

  • 削除不可
  • デフォルトではforce push不可
  • Merge Requestでissueをクローズすると、Default branchにマージされる

GitLab Terraform Providerではgitlab_projectで設定します。

gitlab_project (Resource)

Protected branches

Protected branchesはこちらの話です。

Protected branches | GitLab Docs

このようなブランチのことです。

  • コードの変更をmergeおよびpushできるユーザーを制限できる
  • 削除保護を設定できる
  • コードレビューを承認プロセスを設定できる(PremiumまたはUltimateのみ)
  • コード所有者の承認とファイルの制限を管理できる
  • force pushを制限できる
  • UIおよびAPIの両方でアクセス制御ができる

GitLab Terraform Providerではこちらで設定します。

gitlab_branch_protection (Resource)

環境

今回の環境はこちら。GitLabはすでに構築済みで、192.168.0.6で動作しているものとします。

$ sudo gitlab-rake gitlab:env:info

System information
System:         Ubuntu 24.04
Current User:   git
Using RVM:      no
Ruby Version:   3.2.5
Gem Version:    3.6.5
Bundler Version:2.6.5
Rake Version:   13.0.6
Redis Version:  7.0.15
Sidekiq Version:7.2.4
Go Version:     unknown

GitLab information
Version:        17.10.4
Revision:       17c5705edda
Directory:      /opt/gitlab/embedded/service/gitlab-rails
DB Adapter:     PostgreSQL
DB Version:     14.17
URL:            http://192.168.0.6
HTTP Clone URL: http://192.168.0.6/some-group/some-project.git
SSH Clone URL:  git@192.168.0.6:some-group/some-project.git
Using LDAP:     no
Using Omniauth: yes
Omniauth Providers:

GitLab Shell
Version:        14.41.0
Repository storages:
- default:      unix:/var/opt/gitlab/gitaly/gitaly.socket
GitLab Shell path:              /opt/gitlab/embedded/service/gitlab-shell

Gitaly
- default Address:      unix:/var/opt/gitlab/gitaly/gitaly.socket
- default Version:      17.10.4
- default Git Version:  2.48.1.gl1

Terraform。

$ terraform version
Terraform v1.11.4
on linux_amd64

Default branchとProtected branchesを設定したGitLabプロジェクトをTerraformで構築する

それでは、Default branchとProtected branchesを設定したGitLabプロジェクトをTerraformで構築してみます。

Terraformでのリソース定義。

terraform.tf

terraform {
  required_version = "1.11.4"

  required_providers {
    gitlab = {
      source  = "gitlabhq/gitlab"
      version = "17.10.0"
    }
  }
}

main.tf

variable "root_access_token" {
  type      = string
  ephemeral = true
}

provider "gitlab" {
  token    = var.root_access_token
  base_url = "http://192.168.0.6/"
}

resource "gitlab_group" "sample_group" {
  name = "sample group"
  path = "sample-group"
}

resource "gitlab_project" "sample_app" {
  name         = "sample-app"
  namespace_id = gitlab_group.sample_group.id

  default_branch = "main"

  visibility_level = "private"

  auto_devops_enabled = false ## 今回はGitLab CI/CDを使わないのでオフ

  only_allow_merge_if_pipeline_succeeds            = false ## 今回はGitLab CI/CDを使わないのでオフ
  only_allow_merge_if_all_discussions_are_resolved = true
}

resource "gitlab_branch_protection" "main_branch" {
  project = gitlab_project.sample_app.id
  branch  = "main"

  allow_force_push = false

  merge_access_level     = "maintainer"
  push_access_level      = "no one"
  unprotect_access_level = "maintainer"
}

resource "gitlab_group_membership" "sample_owner" {
  group_id     = gitlab_group.sample_group.id
  user_id      = gitlab_user.sample_owner.id
  access_level = "owner"
}

resource "gitlab_user" "sample_owner" {
  name     = "sample-owner"
  username = "sample-owner"
  password = "P@ssw0rd"
  email    = "sample-owner@example.com"
}

resource "gitlab_group_membership" "sample_developer" {
  group_id     = gitlab_group.sample_group.id
  user_id      = gitlab_user.sample_developer.id
  access_level = "developer"
}

resource "gitlab_user" "sample_developer" {
  name     = "sample-developer"
  username = "sample-developer"
  password = "P@ssw0rd"
  email    = "sample-developer@example.com"
}

Defaut branchはgitlab_projectdefault_branchで設定します。ひとまずmainにしておきました。

resource "gitlab_project" "sample_app" {
  name         = "sample-app"
  namespace_id = gitlab_group.sample_group.id

  default_branch = "main"

  visibility_level = "private"

  auto_devops_enabled = false ## 今回はGitLab CI/CDを使わないのでオフ

  only_allow_merge_if_pipeline_succeeds            = false ## 今回はGitLab CI/CDを使わないのでオフ
  only_allow_merge_if_all_discussions_are_resolved = true
}

今回は簡単のためにCI/CDを使わないので、Auto DevOpsとマージの条件にパイプラインが成功していることの
2つを無効にしています。

Protected branchの設定はこちら。

resource "gitlab_branch_protection" "main_branch" {
  project = gitlab_project.sample_app.id
  branch  = "main"

  allow_force_push = false

  merge_access_level     = "maintainer"
  push_access_level      = "no one"
  unprotect_access_level = "maintainer"
}

force push無効、mergeはmaintainerのみ、pushは許可しない、unprotectはmaintainerのみが可能という設定です。

ユーザーはownerとdeveloperの2人を用意しました。

アクセストークンを環境変数で設定して

$ export TF_VAR_root_access_token=...

リソース構築。

$ terraform init
$ terraform apply

できあがったプロジェクトのSettings → Repositoryで現在のブランチ設定を確認。

mainブランチがprotectedになっているのみですね。Default branchは設定が見えません。ブランチがないので。

Protected branchに関しては、存在していないブランチに対しても追加ができます。

ソースコードを登録する

では、このプロジェクトにソースコードを登録します。

登録する内容はあまり関係がないので、今回はGitLabの新規プロジェクト作成に表示されている手順にそのまま
習うことにします。

$ git init --initial-branch=main
$ git remote add origin http://192.168.0.6/sample-group/sample-app.git
$ git add .

$ git config --local user.name 'sample-owner'
$ git config --local user.email 'sample-owner@example.com'

$ git commit -m 'Initial commit'

$ git push --set-upstream origin main

push後、Default branchがmainになりました。

Terraformの設定が反映されているかに見えますが、最初にpushされたブランチがデフォルトになっているだけな
気もします…。

Protected branchを追加して、Default branchを変更する

ではここにdevelopブランチを追加してみます。

$ git switch -c develop
$ git push --set-upstream origin develop

そしてTerraformもリソース定義でDefault branchをdevelopに変更し

resource "gitlab_project" "sample_app" {
  name         = "sample-app"
  namespace_id = gitlab_group.sample_group.id

  default_branch = "develop"

  visibility_level = "private"

  auto_devops_enabled = false ## 今回はGitLab CI/CDを使わないのでオフ

  only_allow_merge_if_pipeline_succeeds            = false ## 今回はGitLab CI/CDを使わないのでオフ
  only_allow_merge_if_all_discussions_are_resolved = true
}

Protected branchとしても設定します。

resource "gitlab_branch_protection" "develop_branch" {
  project = gitlab_project.sample_app.id
  branch  = "develop"

  allow_force_push = false

  merge_access_level     = "maintainer"
  push_access_level      = "no one"
  unprotect_access_level = "maintainer"
}

反映。

$ terraform apply

Default branchがdevelopになり、Protected branchesにも追加されました。

確認

少しコードを変更して、developブランチにpushしてみます。

$ git commit -m 'edit'
$ git commit -m 'edit'
[develop 0ac9ac3] edit
 1 file changed, 1 insertion(+)
ubuntu@60e0d4fad550:/host$ git push origin HEAD
Username for 'http://192.168.0.6': sample-owner
Password for 'http://sample-owner@192.168.0.6':
Enumerating objects: 17, done.
Counting objects: 100% (17/17), done.
Delta compression using up to 16 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (9/9), 536 bytes | 536.00 KiB/s, done.
Total 9 (delta 2), reused 0 (delta 0), pack-reused 0
remote: GitLab: You are not allowed to push code to protected branches on this project.
To http://192.168.0.6/sample-group/sample-app.git
 ! [remote rejected] HEAD -> develop (pre-receive hook declined)
error: failed to push some refs to 'http://192.168.0.6/sample-group/sample-app.git'

Protected branchなのでNGです。

では、別ブランチにしてMerge Requestにしてみましょう。

$ git switch -c feature1
$ git push origin HEAD

Merge Requestを作ってdeveloperで見てみると、マージができません。

ownerで見ると、マージができるようになっています。

そして、実際マージが可能です。

こんなところでしょうか。

おわりに

TerraformでGitLabのDefault branchとProtected branchesを設定してみました。

UIで操作すると簡単に終わる内容なのですが、Terraformでいざやろうとするとまあまあハマりました…。

まあ、今後いろいろ確認するのに慣れておいた方がいいと思うので、これはこれで押さえておいた方がいいと
思うことにしておきましょう。