From 3728e6e4023203ea119f3f57da2d283907d5b22a Mon Sep 17 00:00:00 2001
From: Christoph Witzko <github@christophwitzko.com>
Date: Wed, 12 Aug 2020 20:47:02 +0200
Subject: [PATCH] feat: initial

---
 cmd/provider-gitlab/main.go |  15 +++
 go.mod                      |  10 ++
 go.sum                      | 157 ++++++++++++++++++++++++++++++
 pkg/provider/gitlab.go      | 159 ++++++++++++++++++++++++++++++
 pkg/provider/gitlab_test.go | 186 ++++++++++++++++++++++++++++++++++++
 5 files changed, 527 insertions(+)
 create mode 100644 cmd/provider-gitlab/main.go
 create mode 100644 go.mod
 create mode 100644 go.sum
 create mode 100644 pkg/provider/gitlab.go
 create mode 100644 pkg/provider/gitlab_test.go

diff --git a/cmd/provider-gitlab/main.go b/cmd/provider-gitlab/main.go
new file mode 100644
index 0000000..47cc193
--- /dev/null
+++ b/cmd/provider-gitlab/main.go
@@ -0,0 +1,15 @@
+package main
+
+import (
+	gitlabProvider "github.com/go-semantic-release/provider-gitlab/pkg/provider"
+	"github.com/go-semantic-release/semantic-release/v2/pkg/plugin"
+	"github.com/go-semantic-release/semantic-release/v2/pkg/provider"
+)
+
+func main() {
+	plugin.Serve(&plugin.ServeOpts{
+		Provider: func() provider.Provider {
+			return &gitlabProvider.GitLabRepository{}
+		},
+	})
+}
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..5069fd5
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,10 @@
+module github.com/go-semantic-release/provider-gitlab
+
+go 1.14
+
+require (
+	github.com/Masterminds/semver/v3 v3.1.0
+	github.com/go-semantic-release/semantic-release/v2 v2.1.0
+	github.com/stretchr/testify v1.6.1
+	github.com/xanzy/go-gitlab v0.34.2
+)
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..88aadf7
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,157 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/Masterminds/semver/v3 v3.1.0 h1:Y2lUDsFKVRSYGojLJ1yLxSXdMmMYTYls0rCvoqmMUQk=
+github.com/Masterminds/semver/v3 v3.1.0/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
+github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
+github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
+github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
+github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
+github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
+github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
+github.com/go-semantic-release/semantic-release/v2 v2.1.0 h1:pK2l3e4TMjvOh67yt/1P7p9HDOhgnT18n4Tf+TV0agw=
+github.com/go-semantic-release/semantic-release/v2 v2.1.0/go.mod h1:VSiaMm0rYu2A2OOmmgaXFFVHVAyrQQDMlpLQO0lEZ4k=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
+github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
+github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
+github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
+github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
+github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
+github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
+github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
+github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-github/v32 v32.1.0/go.mod h1:rIEpZD9CTDQwDK9GDrtMTycQNA4JU3qBsCizh3q2WCI=
+github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
+github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
+github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
+github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=
+github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
+github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI=
+github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
+github.com/hashicorp/go-hclog v0.14.1 h1:nQcJDQwIAGnmoUWp8ubocEX40cCml/17YkF6csQLReU=
+github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
+github.com/hashicorp/go-plugin v1.3.0 h1:4d/wJojzvHV1I4i/rrjVaeuyxWrLzDE1mDCyDy8fXS8=
+github.com/hashicorp/go-plugin v1.3.0/go.mod h1:F9eH4LrE/ZsRdbwhfjs9k9HoDUwAHnYtXdgmf1AVNs0=
+github.com/hashicorp/go-retryablehttp v0.6.4 h1:BbgctKO892xEyOXnGiaAwIoSq1QZ/SS4AhjoAh9DnfY=
+github.com/hashicorp/go-retryablehttp v0.6.4/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
+github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb h1:b5rjCoWHc7eqmAS4/qyk21ZsHyb6Mxv/jykxvNTkU4M=
+github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
+github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE=
+github.com/jhump/protoreflect v1.6.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74=
+github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
+github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
+github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
+github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW10=
+github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
+github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77 h1:7GoSOOW2jpsfkntVKaS2rAr1TJqfcxotyaUcuxoZSzg=
+github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
+github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
+github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
+github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/shuheiktgw/go-travis v0.2.4/go.mod h1:RtODX49bvgHTvfzFvGEPFtU0dKVk0D3PyvUQR/63hT0=
+github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
+github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/urfave/cli/v2 v2.2.0 h1:JTTnM6wKzdA0Jqodd966MVj4vWbbquZykeX1sKbe2C4=
+github.com/urfave/cli/v2 v2.2.0/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
+github.com/xanzy/go-gitlab v0.34.2 h1:CU6QAuv+rW7jHri4ebEYgRrpkvuMWkN1syDkZxCoqz0=
+github.com/xanzy/go-gitlab v0.34.2/go.mod h1:sPLojNBn68fMUWSxIJtdVVIP8uSBYqesTfDUseX11Ug=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
+golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181108082009-03003ca0c849/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
+golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20191008105621-543471e840be h1:QAcqgptGM8IQBC9K/RC4o+O9YmqEm0diQn9QmZw/0mU=
+golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
+golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
+google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY=
+google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
+google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
+google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
+google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.31.0 h1:T7P4R73V3SSDPhH7WW7ATbfViLtmamH0DKrP3f9AuDI=
+google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
+google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
+google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
+google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
+google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
+google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
+google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
+google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
diff --git a/pkg/provider/gitlab.go b/pkg/provider/gitlab.go
new file mode 100644
index 0000000..ed53fd5
--- /dev/null
+++ b/pkg/provider/gitlab.go
@@ -0,0 +1,159 @@
+package provider
+
+import (
+	"fmt"
+	"regexp"
+
+	"github.com/Masterminds/semver/v3"
+	"github.com/go-semantic-release/semantic-release/v2/pkg/provider"
+	"github.com/go-semantic-release/semantic-release/v2/pkg/semrel"
+	"github.com/xanzy/go-gitlab"
+)
+
+type GitLabRepository struct {
+	projectID string
+	branch    string
+	client    *gitlab.Client
+}
+
+func (repo *GitLabRepository) Init(config map[string]string) error {
+	gitlabBaseUrl := config["gitlabBaseUrl"]
+	token := config["token"]
+	branch := config["gitlabBranch"]
+	projectID := config["gitlabProjectID"]
+	if projectID == "" {
+		return fmt.Errorf("project id is required")
+	}
+
+	repo.projectID = projectID
+	repo.branch = branch
+
+	var (
+		client *gitlab.Client
+		err    error
+	)
+
+	if gitlabBaseUrl != "" {
+		client, err = gitlab.NewClient(token, gitlab.WithBaseURL(gitlabBaseUrl))
+	} else {
+		client, err = gitlab.NewClient(token)
+	}
+
+	if err != nil {
+		return fmt.Errorf("failed to create client: %w", err)
+	}
+
+	repo.client = client
+	return nil
+}
+
+func (repo *GitLabRepository) GetInfo() (*provider.RepositoryInfo, error) {
+	project, _, err := repo.client.Projects.GetProject(repo.projectID, nil)
+
+	if err != nil {
+		return nil, err
+	}
+	return &provider.RepositoryInfo{
+		Owner:         "",
+		Repo:          "",
+		DefaultBranch: project.DefaultBranch,
+		Private:       project.Visibility == gitlab.PrivateVisibility,
+	}, nil
+}
+
+func (repo *GitLabRepository) GetCommits(sha string) ([]*semrel.RawCommit, error) {
+	opts := &gitlab.ListCommitsOptions{
+		ListOptions: gitlab.ListOptions{
+			Page:    1,
+			PerPage: 100,
+		},
+		RefName: gitlab.String(fmt.Sprintf("%s...%s", repo.branch, sha)),
+		All:     gitlab.Bool(true),
+	}
+
+	allCommits := make([]*semrel.RawCommit, 0)
+
+	for {
+		commits, resp, err := repo.client.Commits.ListCommits(repo.projectID, opts)
+
+		if err != nil {
+			return nil, err
+		}
+
+		for _, commit := range commits {
+			allCommits = append(allCommits, &semrel.RawCommit{
+				SHA:        commit.ID,
+				RawMessage: commit.Message,
+			})
+		}
+
+		if resp.CurrentPage >= resp.TotalPages {
+			break
+		}
+
+		opts.Page = resp.NextPage
+	}
+
+	return allCommits, nil
+}
+
+func (repo *GitLabRepository) GetReleases(rawRe string) ([]*semrel.Release, error) {
+	re := regexp.MustCompile(rawRe)
+	allReleases := make([]*semrel.Release, 0)
+
+	opts := &gitlab.ListTagsOptions{
+		ListOptions: gitlab.ListOptions{
+			Page:    1,
+			PerPage: 100,
+		},
+	}
+
+	for {
+		tags, resp, err := repo.client.Tags.ListTags(repo.projectID, opts)
+		if err != nil {
+			return nil, err
+		}
+
+		for _, tag := range tags {
+			if rawRe != "" && !re.MatchString(tag.Name) {
+				continue
+			}
+
+			version, err := semver.NewVersion(tag.Name)
+			if err != nil {
+				continue
+			}
+
+			allReleases = append(allReleases, &semrel.Release{
+				SHA:     tag.Commit.ID,
+				Version: version.String(),
+			})
+		}
+
+		if resp.CurrentPage >= resp.TotalPages {
+			break
+		}
+
+		opts.Page = resp.NextPage
+	}
+
+	return allReleases, nil
+}
+
+func (repo *GitLabRepository) CreateRelease(release *provider.CreateReleaseConfig) error {
+	tag := fmt.Sprintf("v%s", release.NewVersion)
+
+	// Gitlab does not have any notion of pre-releases
+	_, _, err := repo.client.Releases.CreateRelease(repo.projectID, &gitlab.CreateReleaseOptions{
+		TagName: &tag,
+		Ref:     &release.SHA,
+		// TODO: this may been to be wrapped in ```
+		Description: &release.Changelog,
+	})
+
+	return err
+}
+
+func (repo *GitLabRepository) Name() string {
+	return "GitLab"
+}
diff --git a/pkg/provider/gitlab_test.go b/pkg/provider/gitlab_test.go
new file mode 100644
index 0000000..09faa49
--- /dev/null
+++ b/pkg/provider/gitlab_test.go
@@ -0,0 +1,186 @@
+package provider
+
+import (
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"net/http/httptest"
+	"strconv"
+	"testing"
+
+	"github.com/go-semantic-release/semantic-release/v2/pkg/provider"
+	"github.com/go-semantic-release/semantic-release/v2/pkg/semrel"
+	"github.com/stretchr/testify/require"
+	"github.com/xanzy/go-gitlab"
+)
+
+func TestNewGitlabRepository(t *testing.T) {
+	require := require.New(t)
+
+	var repo *GitLabRepository
+	repo = &GitLabRepository{}
+	err := repo.Init(map[string]string{})
+	require.EqualError(err, "project id is required")
+
+	repo = &GitLabRepository{}
+	err = repo.Init(map[string]string{
+		"token":           "token",
+		"gitlabProjectID": "1",
+	})
+	require.NoError(err)
+
+	repo = &GitLabRepository{}
+	err = repo.Init(map[string]string{
+		"gitlabBaseUrl":   "https://mygitlab.com",
+		"token":           "token",
+		"gitlabProjectID": "1",
+	})
+	require.NoError(err)
+	require.Equal("https://mygitlab.com/api/v4/", repo.client.BaseURL().String(), "invalid custom instance initialization")
+}
+
+func createGitlabCommit(sha, message string) *gitlab.Commit {
+	return &gitlab.Commit{ID: sha, Message: message}
+}
+
+func createGitlabTag(name, sha string) *gitlab.Tag {
+	return &gitlab.Tag{Name: name, Commit: &gitlab.Commit{
+		ID: sha,
+	}}
+}
+
+var (
+	GITLAB_PROJECT_ID    = 12324322
+	GITLAB_DEFAULTBRANCH = "master"
+	GITLAB_PROJECT       = gitlab.Project{DefaultBranch: GITLAB_DEFAULTBRANCH, Visibility: gitlab.PrivateVisibility, ID: GITLAB_PROJECT_ID}
+	GITLAB_COMMITS       = []*gitlab.Commit{
+		createGitlabCommit("abcd", "feat(app): new feature"),
+		createGitlabCommit("dcba", "Fix: bug"),
+		createGitlabCommit("cdba", "Initial commit"),
+		createGitlabCommit("efcd", "chore: break\nBREAKING CHANGE: breaks everything"),
+	}
+	GITLAB_TAGS = []*gitlab.Tag{
+		createGitlabTag("test-tag", "deadbeef"),
+		createGitlabTag("v1.0.0", "deadbeef"),
+		createGitlabTag("v2.0.0", "deadbeef"),
+		createGitlabTag("v2.1.0-beta", "deadbeef"),
+		createGitlabTag("v3.0.0-beta.2", "deadbeef"),
+		createGitlabTag("v3.0.0-beta.1", "deadbeef"),
+		createGitlabTag("2020.04.19", "deadbeef"),
+	}
+)
+
+//nolint:errcheck
+func GitlabHandler(w http.ResponseWriter, r *http.Request) {
+	// Rate Limit headers
+	if r.Method == "GET" && r.URL.Path == "/api/v4/" {
+		json.NewEncoder(w).Encode(struct{}{})
+		return
+	}
+
+	if r.Header.Get("PRIVATE-TOKEN") == "" {
+		http.Error(w, "unauthorized", http.StatusUnauthorized)
+		return
+	}
+
+	if r.Method == "GET" && r.URL.Path == fmt.Sprintf("/api/v4/projects/%d", GITLAB_PROJECT_ID) {
+		json.NewEncoder(w).Encode(GITLAB_PROJECT)
+		return
+	}
+
+	if r.Method == "GET" && r.URL.Path == fmt.Sprintf("/api/v4/projects/%d/repository/commits", GITLAB_PROJECT_ID) {
+		json.NewEncoder(w).Encode(GITLAB_COMMITS)
+		return
+	}
+
+	if r.Method == "GET" && r.URL.Path == fmt.Sprintf("/api/v4/projects/%d/repository/tags", GITLAB_PROJECT_ID) {
+		json.NewEncoder(w).Encode(GITLAB_TAGS)
+		return
+	}
+
+	if r.Method == "POST" && r.URL.Path == fmt.Sprintf("/api/v4/projects/%d/releases", GITLAB_PROJECT_ID) {
+		var data map[string]string
+		json.NewDecoder(r.Body).Decode(&data)
+		r.Body.Close()
+		if data["tag_name"] != "v2.0.0" {
+			http.Error(w, "invalid tag name", http.StatusBadRequest)
+			return
+		}
+		fmt.Fprint(w, "{}")
+		return
+	}
+
+	http.Error(w, "invalid route", http.StatusNotImplemented)
+}
+
+func getNewGitlabTestRepo(t *testing.T) (*GitLabRepository, *httptest.Server) {
+	ts := httptest.NewServer(http.HandlerFunc(GitlabHandler))
+	repo := &GitLabRepository{}
+	err := repo.Init(map[string]string{
+		"gitlabBaseUrl":   ts.URL,
+		"token":           "gitlab-examples-ci",
+		"gitlabBranch":    "",
+		"gitlabProjectID": strconv.Itoa(GITLAB_PROJECT_ID),
+	})
+	require.NoError(t, err)
+
+	return repo, ts
+}
+
+func TestGitlabGetInfo(t *testing.T) {
+	repo, ts := getNewGitlabTestRepo(t)
+	defer ts.Close()
+	repoInfo, err := repo.GetInfo()
+	require.NoError(t, err)
+	require.Equal(t, GITLAB_DEFAULTBRANCH, repoInfo.DefaultBranch)
+	require.True(t, repoInfo.Private)
+}
+
+func TestGitlabGetCommits(t *testing.T) {
+	repo, ts := getNewGitlabTestRepo(t)
+	defer ts.Close()
+	commits, err := repo.GetCommits("")
+	require.NoError(t, err)
+	require.Len(t, commits, 4)
+
+	for i, c := range commits {
+		require.Equal(t, c.SHA, GITLAB_COMMITS[i].ID)
+		require.Equal(t, c.RawMessage, GITLAB_COMMITS[i].Message)
+	}
+}
+
+func TestGitlabGetReleases(t *testing.T) {
+	repo, ts := getNewGitlabTestRepo(t)
+	defer ts.Close()
+
+	testCases := []struct {
+		vrange          string
+		re              string
+		expectedSHA     string
+		expectedVersion string
+	}{
+		{"", "", "deadbeef", "2020.4.19"},
+		{"", "^v[0-9]*", "deadbeef", "2.0.0"},
+		{"2-beta", "", "deadbeef", "2.1.0-beta"},
+		{"3-beta", "", "deadbeef", "3.0.0-beta.2"},
+		{"4-beta", "", "deadbeef", "4.0.0-beta"},
+	}
+
+	for _, tc := range testCases {
+		t.Run(fmt.Sprintf("VersionRange: %s, RE: %s", tc.vrange, tc.re), func(t *testing.T) {
+			releases, err := repo.GetReleases(tc.re)
+			require.NoError(t, err)
+			release, err := semrel.GetLatestReleaseFromReleases(releases, tc.vrange)
+			require.NoError(t, err)
+			require.Equal(t, tc.expectedSHA, release.SHA)
+			require.Equal(t, tc.expectedVersion, release.Version)
+		})
+	}
+}
+
+func TestGitlabCreateRelease(t *testing.T) {
+	repo, ts := getNewGitlabTestRepo(t)
+	defer ts.Close()
+	err := repo.CreateRelease(&provider.CreateReleaseConfig{NewVersion: "2.0.0", SHA: "deadbeef"})
+	require.NoError(t, err)
+}
-- 
GitLab