[Vendor] Update xanzy/go-gitlab v0.31.0 => v0.37.0 (#12701)
* update github.com/xanzy/go-gitlab v0.31.0 => v0.37.0 * vendor * adapt changes Co-authored-by: techknowlogick <techknowlogick@gitea.io>
This commit is contained in:
parent
0ed5e103fe
commit
0c6a802731
8
go.mod
8
go.mod
|
@ -50,7 +50,7 @@ require (
|
||||||
github.com/google/go-github/v32 v32.1.0
|
github.com/google/go-github/v32 v32.1.0
|
||||||
github.com/google/uuid v1.1.1
|
github.com/google/uuid v1.1.1
|
||||||
github.com/gorilla/context v1.1.1
|
github.com/gorilla/context v1.1.1
|
||||||
github.com/hashicorp/go-retryablehttp v0.6.6 // indirect
|
github.com/hashicorp/go-retryablehttp v0.6.7 // indirect
|
||||||
github.com/hashicorp/go-version v0.0.0-00010101000000-000000000000
|
github.com/hashicorp/go-version v0.0.0-00010101000000-000000000000
|
||||||
github.com/huandu/xstrings v1.3.0
|
github.com/huandu/xstrings v1.3.0
|
||||||
github.com/issue9/assert v1.3.2 // indirect
|
github.com/issue9/assert v1.3.2 // indirect
|
||||||
|
@ -94,7 +94,7 @@ require (
|
||||||
github.com/unknwon/i18n v0.0.0-20190805065654-5c6446a380b6
|
github.com/unknwon/i18n v0.0.0-20190805065654-5c6446a380b6
|
||||||
github.com/unknwon/paginater v0.0.0-20151104151617-7748a72e0141
|
github.com/unknwon/paginater v0.0.0-20151104151617-7748a72e0141
|
||||||
github.com/urfave/cli v1.20.0
|
github.com/urfave/cli v1.20.0
|
||||||
github.com/xanzy/go-gitlab v0.31.0
|
github.com/xanzy/go-gitlab v0.37.0
|
||||||
github.com/yohcop/openid-go v1.0.0
|
github.com/yohcop/openid-go v1.0.0
|
||||||
github.com/yuin/goldmark v1.2.1
|
github.com/yuin/goldmark v1.2.1
|
||||||
github.com/yuin/goldmark-highlighting v0.0.0-20200307114337-60d527fdb691
|
github.com/yuin/goldmark-highlighting v0.0.0-20200307114337-60d527fdb691
|
||||||
|
@ -104,8 +104,8 @@ require (
|
||||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
|
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
|
||||||
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae
|
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae
|
||||||
golang.org/x/text v0.3.3
|
golang.org/x/text v0.3.3
|
||||||
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 // indirect
|
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e // indirect
|
||||||
golang.org/x/tools v0.0.0-20200814230902-9882f1d1823d
|
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d
|
||||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||||
gopkg.in/asn1-ber.v1 v1.0.0-20150924051756-4e86f4367175 // indirect
|
gopkg.in/asn1-ber.v1 v1.0.0-20150924051756-4e86f4367175 // indirect
|
||||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
|
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
|
||||||
|
|
21
go.sum
21
go.sum
|
@ -477,8 +477,8 @@ github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjh
|
||||||
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||||
github.com/hashicorp/go-retryablehttp v0.6.4/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
|
github.com/hashicorp/go-retryablehttp v0.6.4/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
|
||||||
github.com/hashicorp/go-retryablehttp v0.6.6 h1:HJunrbHTDDbBb/ay4kxa1n+dLmttUlnP3V9oNE4hmsM=
|
github.com/hashicorp/go-retryablehttp v0.6.7 h1:8/CAEZt/+F7kR7GevNHulKkUjLht3CPmn7egmhieNKo=
|
||||||
github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
|
github.com/hashicorp/go-retryablehttp v0.6.7/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
|
||||||
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
|
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
|
||||||
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
||||||
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
||||||
|
@ -888,8 +888,8 @@ github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyC
|
||||||
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
|
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
|
||||||
github.com/willf/bitset v1.1.10 h1:NotGKqX0KwQ72NUzqrjZq5ipPNDQex9lo3WpaS8L2sc=
|
github.com/willf/bitset v1.1.10 h1:NotGKqX0KwQ72NUzqrjZq5ipPNDQex9lo3WpaS8L2sc=
|
||||||
github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
|
github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
|
||||||
github.com/xanzy/go-gitlab v0.31.0 h1:+nHztQuCXGSMluKe5Q9IRaPdz6tO8O0gMkQ0vqGpiBk=
|
github.com/xanzy/go-gitlab v0.37.0 h1:Z/CQkjj5VwbWVYVL7S70kS/TFj5H/pJumV7xbJ0YUQ8=
|
||||||
github.com/xanzy/go-gitlab v0.31.0/go.mod h1:sPLojNBn68fMUWSxIJtdVVIP8uSBYqesTfDUseX11Ug=
|
github.com/xanzy/go-gitlab v0.37.0/go.mod h1:sPLojNBn68fMUWSxIJtdVVIP8uSBYqesTfDUseX11Ug=
|
||||||
github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70=
|
github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70=
|
||||||
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
|
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
|
||||||
github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I=
|
github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I=
|
||||||
|
@ -1072,15 +1072,14 @@ golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae h1:Ih9Yo4hSPImZOpfGuA4bR/ORK
|
||||||
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 h1:NusfzzA6yGQ+ua51ck7E3omNUX/JuqbFSaRGqU8CcLI=
|
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e h1:EHBhcS0mlXEAVwNyO2dLfjToGsyY4j24pTs2ScHnX7s=
|
||||||
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
@ -1111,11 +1110,10 @@ golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtn
|
||||||
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20200225230052-807dcd883420/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
golang.org/x/tools v0.0.0-20200225230052-807dcd883420/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
golang.org/x/tools v0.0.0-20200325010219-a49f79bcc224 h1:azwY/v0y0K4mFHVsg5+UrTgchqALYWpqVo6vL5OmkmI=
|
|
||||||
golang.org/x/tools v0.0.0-20200325010219-a49f79bcc224/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
golang.org/x/tools v0.0.0-20200325010219-a49f79bcc224/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||||
golang.org/x/tools v0.0.0-20200717024301-6ddee64345a6/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
golang.org/x/tools v0.0.0-20200717024301-6ddee64345a6/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||||
golang.org/x/tools v0.0.0-20200814230902-9882f1d1823d h1:XZxUC4/ZNKTjrT4/Oc9gCgIYnzPW3/CefdPjsndrVWM=
|
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d h1:W07d4xkoAUSNOkOzdzXCdFGxT7o2rW4q8M34tB2i//k=
|
||||||
golang.org/x/tools v0.0.0-20200814230902-9882f1d1823d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||||
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
@ -1160,7 +1158,6 @@ google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ
|
||||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
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.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.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||||
google.golang.org/protobuf v1.22.0 h1:cJv5/xdbk1NnMPR1VP9+HU6gupuG9MLBoH1r6RHZ2MY=
|
|
||||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
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.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.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
@ -1187,9 +1184,7 @@ gopkg.in/ini.v1 v1.44.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
gopkg.in/ini.v1 v1.44.2/go.mod h1:M3Cogqpuv0QCi3ExAY5V4uOt4qb/R3xZubo9m8lK5wg=
|
gopkg.in/ini.v1 v1.44.2/go.mod h1:M3Cogqpuv0QCi3ExAY5V4uOt4qb/R3xZubo9m8lK5wg=
|
||||||
gopkg.in/ini.v1 v1.46.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
gopkg.in/ini.v1 v1.46.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
gopkg.in/ini.v1 v1.57.0 h1:9unxIsFcTt4I55uWluz+UmL95q4kdJ0buvQ1ZIqVQww=
|
|
||||||
gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
gopkg.in/ini.v1 v1.60.1 h1:P5y5shSkb0CFe44qEeMBgn8JLow09MP17jlJHanke5g=
|
|
||||||
gopkg.in/ini.v1 v1.60.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
gopkg.in/ini.v1 v1.60.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
gopkg.in/ini.v1 v1.60.2 h1:7i8mqModL63zqi8nQn8Q3+0zvSCZy1AxhBgthKfi4WU=
|
gopkg.in/ini.v1 v1.60.2 h1:7i8mqModL63zqi8nQn8Q3+0zvSCZy1AxhBgthKfi4WU=
|
||||||
gopkg.in/ini.v1 v1.60.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
gopkg.in/ini.v1 v1.60.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
|
|
|
@ -234,10 +234,10 @@ func (g *GitlabDownloader) GetLabels() ([]*base.Label, error) {
|
||||||
var perPage = 100
|
var perPage = 100
|
||||||
var labels = make([]*base.Label, 0, perPage)
|
var labels = make([]*base.Label, 0, perPage)
|
||||||
for i := 1; ; i++ {
|
for i := 1; ; i++ {
|
||||||
ls, _, err := g.client.Labels.ListLabels(g.repoID, &gitlab.ListLabelsOptions{
|
ls, _, err := g.client.Labels.ListLabels(g.repoID, &gitlab.ListLabelsOptions{ListOptions: gitlab.ListOptions{
|
||||||
Page: i,
|
Page: i,
|
||||||
PerPage: perPage,
|
PerPage: perPage,
|
||||||
}, nil, gitlab.WithContext(g.ctx))
|
}}, nil, gitlab.WithContext(g.ctx))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
sudo: false
|
|
||||||
|
|
||||||
language: go
|
|
||||||
|
|
||||||
go:
|
|
||||||
- 1.12.4
|
|
||||||
|
|
||||||
branches:
|
|
||||||
only:
|
|
||||||
- master
|
|
||||||
|
|
||||||
script: make updatedeps test
|
|
|
@ -35,11 +35,12 @@ import (
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/hashicorp/go-cleanhttp"
|
cleanhttp "github.com/hashicorp/go-cleanhttp"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -276,12 +277,16 @@ type Logger interface {
|
||||||
Printf(string, ...interface{})
|
Printf(string, ...interface{})
|
||||||
}
|
}
|
||||||
|
|
||||||
// LeveledLogger interface implements the basic methods that a logger library needs
|
// LeveledLogger is an interface that can be implemented by any logger or a
|
||||||
|
// logger wrapper to provide leveled logging. The methods accept a message
|
||||||
|
// string and a variadic number of key-value pairs. For log.Printf style
|
||||||
|
// formatting where message string contains a format specifier, use Logger
|
||||||
|
// interface.
|
||||||
type LeveledLogger interface {
|
type LeveledLogger interface {
|
||||||
Error(string, ...interface{})
|
Error(msg string, keysAndValues ...interface{})
|
||||||
Info(string, ...interface{})
|
Info(msg string, keysAndValues ...interface{})
|
||||||
Debug(string, ...interface{})
|
Debug(msg string, keysAndValues ...interface{})
|
||||||
Warn(string, ...interface{})
|
Warn(msg string, keysAndValues ...interface{})
|
||||||
}
|
}
|
||||||
|
|
||||||
// hookLogger adapts an LeveledLogger to Logger for use by the existing hook functions
|
// hookLogger adapts an LeveledLogger to Logger for use by the existing hook functions
|
||||||
|
@ -357,6 +362,7 @@ type Client struct {
|
||||||
ErrorHandler ErrorHandler
|
ErrorHandler ErrorHandler
|
||||||
|
|
||||||
loggerInit sync.Once
|
loggerInit sync.Once
|
||||||
|
clientInit sync.Once
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewClient creates a new Client with default settings.
|
// NewClient creates a new Client with default settings.
|
||||||
|
@ -420,6 +426,13 @@ func DefaultRetryPolicy(ctx context.Context, resp *http.Response, err error) (bo
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 429 Too Many Requests is recoverable. Sometimes the server puts
|
||||||
|
// a Retry-After response header to indicate when the server is
|
||||||
|
// available to start processing request from client.
|
||||||
|
if resp.StatusCode == http.StatusTooManyRequests {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Check the response code. We retry on 500-range responses to allow
|
// Check the response code. We retry on 500-range responses to allow
|
||||||
// the server time to recover, as 500's are typically not permanent
|
// the server time to recover, as 500's are typically not permanent
|
||||||
// errors and may relate to outages on the server side. This will catch
|
// errors and may relate to outages on the server side. This will catch
|
||||||
|
@ -431,10 +444,66 @@ func DefaultRetryPolicy(ctx context.Context, resp *http.Response, err error) (bo
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrorPropagatedRetryPolicy is the same as DefaultRetryPolicy, except it
|
||||||
|
// propagates errors back instead of returning nil. This allows you to inspect
|
||||||
|
// why it decided to retry or not.
|
||||||
|
func ErrorPropagatedRetryPolicy(ctx context.Context, resp *http.Response, err error) (bool, error) {
|
||||||
|
// do not retry on context.Canceled or context.DeadlineExceeded
|
||||||
|
if ctx.Err() != nil {
|
||||||
|
return false, ctx.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if v, ok := err.(*url.Error); ok {
|
||||||
|
// Don't retry if the error was due to too many redirects.
|
||||||
|
if redirectsErrorRe.MatchString(v.Error()) {
|
||||||
|
return false, v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't retry if the error was due to an invalid protocol scheme.
|
||||||
|
if schemeErrorRe.MatchString(v.Error()) {
|
||||||
|
return false, v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't retry if the error was due to TLS cert verification failure.
|
||||||
|
if _, ok := v.Err.(x509.UnknownAuthorityError); ok {
|
||||||
|
return false, v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The error is likely recoverable so retry.
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the response code. We retry on 500-range responses to allow
|
||||||
|
// the server time to recover, as 500's are typically not permanent
|
||||||
|
// errors and may relate to outages on the server side. This will catch
|
||||||
|
// invalid response codes as well, like 0 and 999.
|
||||||
|
if resp.StatusCode == 0 || (resp.StatusCode >= 500 && resp.StatusCode != 501) {
|
||||||
|
return true, fmt.Errorf("unexpected HTTP status %s", resp.Status)
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
// DefaultBackoff provides a default callback for Client.Backoff which
|
// DefaultBackoff provides a default callback for Client.Backoff which
|
||||||
// will perform exponential backoff based on the attempt number and limited
|
// will perform exponential backoff based on the attempt number and limited
|
||||||
// by the provided minimum and maximum durations.
|
// by the provided minimum and maximum durations.
|
||||||
|
//
|
||||||
|
// It also tries to parse Retry-After response header when a http.StatusTooManyRequests
|
||||||
|
// (HTTP Code 429) is found in the resp parameter. Hence it will return the number of
|
||||||
|
// seconds the server states it may be ready to process more requests from this client.
|
||||||
func DefaultBackoff(min, max time.Duration, attemptNum int, resp *http.Response) time.Duration {
|
func DefaultBackoff(min, max time.Duration, attemptNum int, resp *http.Response) time.Duration {
|
||||||
|
if resp != nil {
|
||||||
|
if resp.StatusCode == http.StatusTooManyRequests {
|
||||||
|
if s, ok := resp.Header["Retry-After"]; ok {
|
||||||
|
if sleep, err := strconv.ParseInt(s[0], 10, 64); err == nil {
|
||||||
|
return time.Second * time.Duration(sleep)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mult := math.Pow(2, float64(attemptNum)) * float64(min)
|
mult := math.Pow(2, float64(attemptNum)) * float64(min)
|
||||||
sleep := time.Duration(mult)
|
sleep := time.Duration(mult)
|
||||||
if float64(sleep) != mult || sleep > max {
|
if float64(sleep) != mult || sleep > max {
|
||||||
|
@ -490,25 +559,31 @@ func PassthroughErrorHandler(resp *http.Response, err error, _ int) (*http.Respo
|
||||||
|
|
||||||
// Do wraps calling an HTTP method with retries.
|
// Do wraps calling an HTTP method with retries.
|
||||||
func (c *Client) Do(req *Request) (*http.Response, error) {
|
func (c *Client) Do(req *Request) (*http.Response, error) {
|
||||||
if c.HTTPClient == nil {
|
c.clientInit.Do(func() {
|
||||||
c.HTTPClient = cleanhttp.DefaultPooledClient()
|
if c.HTTPClient == nil {
|
||||||
}
|
c.HTTPClient = cleanhttp.DefaultPooledClient()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
logger := c.logger()
|
logger := c.logger()
|
||||||
|
|
||||||
if logger != nil {
|
if logger != nil {
|
||||||
switch v := logger.(type) {
|
switch v := logger.(type) {
|
||||||
case Logger:
|
|
||||||
v.Printf("[DEBUG] %s %s", req.Method, req.URL)
|
|
||||||
case LeveledLogger:
|
case LeveledLogger:
|
||||||
v.Debug("performing request", "method", req.Method, "url", req.URL)
|
v.Debug("performing request", "method", req.Method, "url", req.URL)
|
||||||
|
case Logger:
|
||||||
|
v.Printf("[DEBUG] %s %s", req.Method, req.URL)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var resp *http.Response
|
var resp *http.Response
|
||||||
var err error
|
var attempt int
|
||||||
|
var shouldRetry bool
|
||||||
|
var doErr, checkErr error
|
||||||
|
|
||||||
for i := 0; ; i++ {
|
for i := 0; ; i++ {
|
||||||
|
attempt++
|
||||||
|
|
||||||
var code int // HTTP response code
|
var code int // HTTP response code
|
||||||
|
|
||||||
// Always rewind the request body when non-nil.
|
// Always rewind the request body when non-nil.
|
||||||
|
@ -527,30 +602,30 @@ func (c *Client) Do(req *Request) (*http.Response, error) {
|
||||||
|
|
||||||
if c.RequestLogHook != nil {
|
if c.RequestLogHook != nil {
|
||||||
switch v := logger.(type) {
|
switch v := logger.(type) {
|
||||||
case Logger:
|
|
||||||
c.RequestLogHook(v, req.Request, i)
|
|
||||||
case LeveledLogger:
|
case LeveledLogger:
|
||||||
c.RequestLogHook(hookLogger{v}, req.Request, i)
|
c.RequestLogHook(hookLogger{v}, req.Request, i)
|
||||||
|
case Logger:
|
||||||
|
c.RequestLogHook(v, req.Request, i)
|
||||||
default:
|
default:
|
||||||
c.RequestLogHook(nil, req.Request, i)
|
c.RequestLogHook(nil, req.Request, i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attempt the request
|
// Attempt the request
|
||||||
resp, err = c.HTTPClient.Do(req.Request)
|
resp, doErr = c.HTTPClient.Do(req.Request)
|
||||||
if resp != nil {
|
if resp != nil {
|
||||||
code = resp.StatusCode
|
code = resp.StatusCode
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if we should continue with retries.
|
// Check if we should continue with retries.
|
||||||
checkOK, checkErr := c.CheckRetry(req.Context(), resp, err)
|
shouldRetry, checkErr = c.CheckRetry(req.Context(), resp, doErr)
|
||||||
|
|
||||||
if err != nil {
|
if doErr != nil {
|
||||||
switch v := logger.(type) {
|
switch v := logger.(type) {
|
||||||
case Logger:
|
|
||||||
v.Printf("[ERR] %s %s request failed: %v", req.Method, req.URL, err)
|
|
||||||
case LeveledLogger:
|
case LeveledLogger:
|
||||||
v.Error("request failed", "error", err, "method", req.Method, "url", req.URL)
|
v.Error("request failed", "error", doErr, "method", req.Method, "url", req.URL)
|
||||||
|
case Logger:
|
||||||
|
v.Printf("[ERR] %s %s request failed: %v", req.Method, req.URL, doErr)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Call this here to maintain the behavior of logging all requests,
|
// Call this here to maintain the behavior of logging all requests,
|
||||||
|
@ -558,23 +633,18 @@ func (c *Client) Do(req *Request) (*http.Response, error) {
|
||||||
if c.ResponseLogHook != nil {
|
if c.ResponseLogHook != nil {
|
||||||
// Call the response logger function if provided.
|
// Call the response logger function if provided.
|
||||||
switch v := logger.(type) {
|
switch v := logger.(type) {
|
||||||
case Logger:
|
|
||||||
c.ResponseLogHook(v, resp)
|
|
||||||
case LeveledLogger:
|
case LeveledLogger:
|
||||||
c.ResponseLogHook(hookLogger{v}, resp)
|
c.ResponseLogHook(hookLogger{v}, resp)
|
||||||
|
case Logger:
|
||||||
|
c.ResponseLogHook(v, resp)
|
||||||
default:
|
default:
|
||||||
c.ResponseLogHook(nil, resp)
|
c.ResponseLogHook(nil, resp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now decide if we should continue.
|
if !shouldRetry {
|
||||||
if !checkOK {
|
break
|
||||||
if checkErr != nil {
|
|
||||||
err = checkErr
|
|
||||||
}
|
|
||||||
c.HTTPClient.CloseIdleConnections()
|
|
||||||
return resp, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// We do this before drainBody because there's no need for the I/O if
|
// We do this before drainBody because there's no need for the I/O if
|
||||||
|
@ -585,7 +655,7 @@ func (c *Client) Do(req *Request) (*http.Response, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// We're going to retry, consume any response to reuse the connection.
|
// We're going to retry, consume any response to reuse the connection.
|
||||||
if err == nil && resp != nil {
|
if doErr == nil {
|
||||||
c.drainBody(resp.Body)
|
c.drainBody(resp.Body)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -596,10 +666,10 @@ func (c *Client) Do(req *Request) (*http.Response, error) {
|
||||||
}
|
}
|
||||||
if logger != nil {
|
if logger != nil {
|
||||||
switch v := logger.(type) {
|
switch v := logger.(type) {
|
||||||
case Logger:
|
|
||||||
v.Printf("[DEBUG] %s: retrying in %s (%d left)", desc, wait, remain)
|
|
||||||
case LeveledLogger:
|
case LeveledLogger:
|
||||||
v.Debug("retrying request", "request", desc, "timeout", wait, "remaining", remain)
|
v.Debug("retrying request", "request", desc, "timeout", wait, "remaining", remain)
|
||||||
|
case Logger:
|
||||||
|
v.Printf("[DEBUG] %s: retrying in %s (%d left)", desc, wait, remain)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
select {
|
select {
|
||||||
|
@ -608,21 +678,44 @@ func (c *Client) Do(req *Request) (*http.Response, error) {
|
||||||
return nil, req.Context().Err()
|
return nil, req.Context().Err()
|
||||||
case <-time.After(wait):
|
case <-time.After(wait):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make shallow copy of http Request so that we can modify its body
|
||||||
|
// without racing against the closeBody call in persistConn.writeLoop.
|
||||||
|
httpreq := *req.Request
|
||||||
|
req.Request = &httpreq
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is the closest we have to success criteria
|
||||||
|
if doErr == nil && checkErr == nil && !shouldRetry {
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
defer c.HTTPClient.CloseIdleConnections()
|
||||||
|
|
||||||
|
err := doErr
|
||||||
|
if checkErr != nil {
|
||||||
|
err = checkErr
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.ErrorHandler != nil {
|
if c.ErrorHandler != nil {
|
||||||
c.HTTPClient.CloseIdleConnections()
|
return c.ErrorHandler(resp, err, attempt)
|
||||||
return c.ErrorHandler(resp, err, c.RetryMax+1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// By default, we close the response body and return an error without
|
// By default, we close the response body and return an error without
|
||||||
// returning the response
|
// returning the response
|
||||||
if resp != nil {
|
if resp != nil {
|
||||||
resp.Body.Close()
|
c.drainBody(resp.Body)
|
||||||
}
|
}
|
||||||
c.HTTPClient.CloseIdleConnections()
|
|
||||||
return nil, fmt.Errorf("%s %s giving up after %d attempts",
|
// this means CheckRetry thought the request was a failure, but didn't
|
||||||
req.Method, req.URL, c.RetryMax+1)
|
// communicate why
|
||||||
|
if err == nil {
|
||||||
|
return nil, fmt.Errorf("%s %s giving up after %d attempt(s)",
|
||||||
|
req.Method, req.URL, attempt)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("%s %s giving up after %d attempt(s): %w",
|
||||||
|
req.Method, req.URL, attempt, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to read the response body so we can reuse this connection.
|
// Try to read the response body so we can reuse this connection.
|
||||||
|
@ -632,10 +725,10 @@ func (c *Client) drainBody(body io.ReadCloser) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if c.logger() != nil {
|
if c.logger() != nil {
|
||||||
switch v := c.logger().(type) {
|
switch v := c.logger().(type) {
|
||||||
case Logger:
|
|
||||||
v.Printf("[ERR] error reading response body: %v", err)
|
|
||||||
case LeveledLogger:
|
case LeveledLogger:
|
||||||
v.Error("error reading response body", "error", err)
|
v.Error("error reading response body", "error", err)
|
||||||
|
case Logger:
|
||||||
|
v.Printf("[ERR] error reading response body: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
package retryablehttp
|
package retryablehttp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -39,5 +41,12 @@ func (rt *RoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute the request.
|
// Execute the request.
|
||||||
return rt.Client.Do(retryableReq)
|
resp, err := rt.Client.Do(retryableReq)
|
||||||
|
// If we got an error returned by standard library's `Do` method, unwrap it
|
||||||
|
// otherwise we will wind up erroneously re-nesting the error.
|
||||||
|
if _, ok := err.(*url.Error); ok {
|
||||||
|
return resp, errors.Unwrap(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,7 @@ to add new and/or missing endpoints. Currently the following services are suppor
|
||||||
- [x] Group Milestones
|
- [x] Group Milestones
|
||||||
- [x] Group-Level Variables
|
- [x] Group-Level Variables
|
||||||
- [x] Groups
|
- [x] Groups
|
||||||
|
- [x] Instance Clusters
|
||||||
- [x] Issue Boards
|
- [x] Issue Boards
|
||||||
- [x] Issues
|
- [x] Issues
|
||||||
- [x] Jobs
|
- [x] Jobs
|
||||||
|
@ -161,7 +162,7 @@ func main() {
|
||||||
s := &gitlab.CreateProjectSnippetOptions{
|
s := &gitlab.CreateProjectSnippetOptions{
|
||||||
Title: gitlab.String("Dummy Snippet"),
|
Title: gitlab.String("Dummy Snippet"),
|
||||||
FileName: gitlab.String("snippet.go"),
|
FileName: gitlab.String("snippet.go"),
|
||||||
Code: gitlab.String("package main...."),
|
Content: gitlab.String("package main...."),
|
||||||
Visibility: gitlab.Visibility(gitlab.PublicVisibility),
|
Visibility: gitlab.Visibility(gitlab.PublicVisibility),
|
||||||
}
|
}
|
||||||
_, _, err = git.ProjectSnippets.CreateSnippet(project.ID, s)
|
_, _, err = git.ProjectSnippets.CreateSnippet(project.ID, s)
|
||||||
|
|
|
@ -38,8 +38,10 @@ type Branch struct {
|
||||||
Protected bool `json:"protected"`
|
Protected bool `json:"protected"`
|
||||||
Merged bool `json:"merged"`
|
Merged bool `json:"merged"`
|
||||||
Default bool `json:"default"`
|
Default bool `json:"default"`
|
||||||
|
CanPush bool `json:"can_push"`
|
||||||
DevelopersCanPush bool `json:"developers_can_push"`
|
DevelopersCanPush bool `json:"developers_can_push"`
|
||||||
DevelopersCanMerge bool `json:"developers_can_merge"`
|
DevelopersCanMerge bool `json:"developers_can_merge"`
|
||||||
|
WebURL string `json:"web_url"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b Branch) String() string {
|
func (b Branch) String() string {
|
||||||
|
|
|
@ -24,6 +24,16 @@ func WithCustomBackoff(backoff retryablehttp.Backoff) ClientOptionFunc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithCustomLimiter injects a custom rate limiter to the client.
|
||||||
|
func WithCustomLimiter(limiter RateLimiter) ClientOptionFunc {
|
||||||
|
return func(c *Client) error {
|
||||||
|
c.configureLimiterOnce.Do(func() {
|
||||||
|
c.limiter = limiter
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// WithCustomRetry can be used to configure a custom retry policy.
|
// WithCustomRetry can be used to configure a custom retry policy.
|
||||||
func WithCustomRetry(checkRetry retryablehttp.CheckRetry) ClientOptionFunc {
|
func WithCustomRetry(checkRetry retryablehttp.CheckRetry) ClientOptionFunc {
|
||||||
return func(c *Client) error {
|
return func(c *Client) error {
|
||||||
|
|
|
@ -50,6 +50,7 @@ type Commit struct {
|
||||||
Status *BuildStateValue `json:"status"`
|
Status *BuildStateValue `json:"status"`
|
||||||
LastPipeline *PipelineInfo `json:"last_pipeline"`
|
LastPipeline *PipelineInfo `json:"last_pipeline"`
|
||||||
ProjectID int `json:"project_id"`
|
ProjectID int `json:"project_id"`
|
||||||
|
WebURL string `json:"web_url"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// CommitStats represents the number of added and deleted files in a commit.
|
// CommitStats represents the number of added and deleted files in a commit.
|
||||||
|
@ -118,11 +119,13 @@ const (
|
||||||
|
|
||||||
// CommitAction represents a single file action within a commit.
|
// CommitAction represents a single file action within a commit.
|
||||||
type CommitAction struct {
|
type CommitAction struct {
|
||||||
Action FileAction `url:"action" json:"action"`
|
Action FileAction `url:"action" json:"action"`
|
||||||
FilePath string `url:"file_path" json:"file_path"`
|
FilePath string `url:"file_path" json:"file_path"`
|
||||||
PreviousPath string `url:"previous_path,omitempty" json:"previous_path,omitempty"`
|
PreviousPath string `url:"previous_path,omitempty" json:"previous_path,omitempty"`
|
||||||
Content string `url:"content,omitempty" json:"content,omitempty"`
|
Content string `url:"content,omitempty" json:"content,omitempty"`
|
||||||
Encoding string `url:"encoding,omitempty" json:"encoding,omitempty"`
|
Encoding string `url:"encoding,omitempty" json:"encoding,omitempty"`
|
||||||
|
LastCommitID string `url:"last_commit_id,omitempty" json:"last_commit_id,omitempty"`
|
||||||
|
ExecuteFilemode bool `url:"execute_filemode,omitempty" json:"execute_filemode,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// CommitRef represents the reference of branches/tags in a commit.
|
// CommitRef represents the reference of branches/tags in a commit.
|
||||||
|
|
|
@ -35,6 +35,7 @@ type Deployment struct {
|
||||||
Ref string `json:"ref"`
|
Ref string `json:"ref"`
|
||||||
SHA string `json:"sha"`
|
SHA string `json:"sha"`
|
||||||
CreatedAt *time.Time `json:"created_at"`
|
CreatedAt *time.Time `json:"created_at"`
|
||||||
|
UpdatedAt *time.Time `json:"updated_at"`
|
||||||
User *ProjectUser `json:"user"`
|
User *ProjectUser `json:"user"`
|
||||||
Environment *Environment `json:"environment"`
|
Environment *Environment `json:"environment"`
|
||||||
Deployable struct {
|
Deployable struct {
|
||||||
|
|
|
@ -0,0 +1,133 @@
|
||||||
|
package gitlab
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// EpicIssuesService handles communication with the epic issue related methods
|
||||||
|
// of the GitLab API.
|
||||||
|
//
|
||||||
|
// GitLab API docs: https://docs.gitlab.com/ee/api/epic_issues.html
|
||||||
|
type EpicIssuesService struct {
|
||||||
|
client *Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// EpicIssueAssignment contains both the epic and issue objects returned from
|
||||||
|
// Gitlab with the assignment ID.
|
||||||
|
//
|
||||||
|
// GitLab API docs: https://docs.gitlab.com/ee/api/epic_issues.html
|
||||||
|
type EpicIssueAssignment struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Epic *Epic `json:"epic"`
|
||||||
|
Issue *Issue `json:"issue"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListEpicIssues get a list of epic issues.
|
||||||
|
//
|
||||||
|
// Gitlab API docs:
|
||||||
|
// https://docs.gitlab.com/ee/api/epic_issues.html#list-issues-for-an-epic
|
||||||
|
func (s *EpicIssuesService) ListEpicIssues(gid interface{}, epic int, opt *ListOptions, options ...RequestOptionFunc) ([]*Issue, *Response, error) {
|
||||||
|
group, err := parseID(gid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
u := fmt.Sprintf("groups/%s/epics/%d/issues", pathEscape(group), epic)
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest("GET", u, opt, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var is []*Issue
|
||||||
|
resp, err := s.client.Do(req, &is)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return is, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssignEpicIssue assigns an existing issue to an epic.
|
||||||
|
//
|
||||||
|
// Gitlab API Docs:
|
||||||
|
// https://docs.gitlab.com/ee/api/epic_issues.html#assign-an-issue-to-the-epic
|
||||||
|
func (s *EpicIssuesService) AssignEpicIssue(gid interface{}, epic, issue int, options ...RequestOptionFunc) (*EpicIssueAssignment, *Response, error) {
|
||||||
|
group, err := parseID(gid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
u := fmt.Sprintf("groups/%s/epics/%d/issues/%d", pathEscape(group), epic, issue)
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest("POST", u, nil, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
a := new(EpicIssueAssignment)
|
||||||
|
resp, err := s.client.Do(req, a)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return a, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveEpicIssue removes an issue from an epic.
|
||||||
|
//
|
||||||
|
// Gitlab API Docs:
|
||||||
|
// https://docs.gitlab.com/ee/api/epic_issues.html#remove-an-issue-from-the-epic
|
||||||
|
func (s *EpicIssuesService) RemoveEpicIssue(gid interface{}, epic, epicIssue int, options ...RequestOptionFunc) (*EpicIssueAssignment, *Response, error) {
|
||||||
|
group, err := parseID(gid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
u := fmt.Sprintf("groups/%s/epics/%d/issues/%d", pathEscape(group), epic, epicIssue)
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest("DELETE", u, nil, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
a := new(EpicIssueAssignment)
|
||||||
|
resp, err := s.client.Do(req, a)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return a, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateEpicIsssueAssignmentOptions describes the UpdateEpicIssueAssignment()
|
||||||
|
// options.
|
||||||
|
//
|
||||||
|
// Gitlab API Docs:
|
||||||
|
// https://docs.gitlab.com/ee/api/epic_issues.html#update-epic---issue-association
|
||||||
|
type UpdateEpicIsssueAssignmentOptions struct {
|
||||||
|
*ListOptions
|
||||||
|
MoveBeforeID *int `url:"move_before_id,omitempty" json:"move_before_id,omitempty"`
|
||||||
|
MoveAfterID *int `url:"move_after_id,omitempty" json:"move_after_id,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateEpicIssueAssignment moves an issue before or after another issue in an
|
||||||
|
// epic issue list.
|
||||||
|
//
|
||||||
|
// Gitlab API Docs:
|
||||||
|
// https://docs.gitlab.com/ee/api/epic_issues.html#update-epic---issue-association
|
||||||
|
func (s *EpicIssuesService) UpdateEpicIssueAssignment(gid interface{}, epic, epicIssue int, opt *UpdateEpicIsssueAssignmentOptions, options ...RequestOptionFunc) ([]*Issue, *Response, error) {
|
||||||
|
group, err := parseID(gid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
u := fmt.Sprintf("groups/%s/epics/%d/issues/%d", pathEscape(group), epic, epicIssue)
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest("PUT", u, opt, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var is []*Issue
|
||||||
|
resp, err := s.client.Do(req, &is)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return is, resp, err
|
||||||
|
}
|
|
@ -30,16 +30,12 @@ type Epic struct {
|
||||||
ID int `json:"id"`
|
ID int `json:"id"`
|
||||||
IID int `json:"iid"`
|
IID int `json:"iid"`
|
||||||
GroupID int `json:"group_id"`
|
GroupID int `json:"group_id"`
|
||||||
Author *EpicAuthor `json:"author"`
|
ParentID int `json:"parent_id"`
|
||||||
|
Title string `json:"title"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
State string `json:"state"`
|
State string `json:"state"`
|
||||||
Upvotes int `json:"upvotes"`
|
WebURL string `json:"web_url"`
|
||||||
Downvotes int `json:"downvotes"`
|
Author *EpicAuthor `json:"author"`
|
||||||
Labels []string `json:"labels"`
|
|
||||||
Title string `json:"title"`
|
|
||||||
UpdatedAt *time.Time `json:"updated_at"`
|
|
||||||
CreatedAt *time.Time `json:"created_at"`
|
|
||||||
UserNotesCount int `json:"user_notes_count"`
|
|
||||||
StartDate *ISOTime `json:"start_date"`
|
StartDate *ISOTime `json:"start_date"`
|
||||||
StartDateIsFixed bool `json:"start_date_is_fixed"`
|
StartDateIsFixed bool `json:"start_date_is_fixed"`
|
||||||
StartDateFixed *ISOTime `json:"start_date_fixed"`
|
StartDateFixed *ISOTime `json:"start_date_fixed"`
|
||||||
|
@ -48,6 +44,13 @@ type Epic struct {
|
||||||
DueDateIsFixed bool `json:"due_date_is_fixed"`
|
DueDateIsFixed bool `json:"due_date_is_fixed"`
|
||||||
DueDateFixed *ISOTime `json:"due_date_fixed"`
|
DueDateFixed *ISOTime `json:"due_date_fixed"`
|
||||||
DueDateFromMilestones *ISOTime `json:"due_date_from_milestones"`
|
DueDateFromMilestones *ISOTime `json:"due_date_from_milestones"`
|
||||||
|
CreatedAt *time.Time `json:"created_at"`
|
||||||
|
UpdatedAt *time.Time `json:"updated_at"`
|
||||||
|
Labels []string `json:"labels"`
|
||||||
|
Upvotes int `json:"upvotes"`
|
||||||
|
Downvotes int `json:"downvotes"`
|
||||||
|
UserNotesCount int `json:"user_notes_count"`
|
||||||
|
URL string `json:"url"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e Epic) String() string {
|
func (e Epic) String() string {
|
||||||
|
@ -59,12 +62,20 @@ func (e Epic) String() string {
|
||||||
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#list-epics-for-a-group
|
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#list-epics-for-a-group
|
||||||
type ListGroupEpicsOptions struct {
|
type ListGroupEpicsOptions struct {
|
||||||
ListOptions
|
ListOptions
|
||||||
State *string `url:"state,omitempty" json:"state,omitempty"`
|
AuthorID *int `url:"author_id,omitempty" json:"author_id,omitempty"`
|
||||||
Labels Labels `url:"labels,comma,omitempty" json:"labels,omitempty"`
|
Labels Labels `url:"labels,comma,omitempty" json:"labels,omitempty"`
|
||||||
AuthorID *int `url:"author_id,omitempty" json:"author_id,omitempty"`
|
WithLabelDetails *bool `url:"with_labels_details,omitempty" json:"with_labels_details,omitempty"`
|
||||||
OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"`
|
OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"`
|
||||||
Sort *string `url:"sort,omitempty" json:"sort,omitempty"`
|
Sort *string `url:"sort,omitempty" json:"sort,omitempty"`
|
||||||
Search *string `url:"search,omitempty" json:"search,omitempty"`
|
Search *string `url:"search,omitempty" json:"search,omitempty"`
|
||||||
|
State *string `url:"state,omitempty" json:"state,omitempty"`
|
||||||
|
CreatedAfter *time.Time `url:"created_after,omitempty" json:"created_after,omitempty"`
|
||||||
|
CreatedBefore *time.Time `url:"created_before,omitempty" json:"created_before,omitempty"`
|
||||||
|
UpdatedAfter *time.Time `url:"updated_after,omitempty" json:"updated_after,omitempty"`
|
||||||
|
UpdatedBefore *time.Time `url:"updated_before,omitempty" json:"updated_before,omitempty"`
|
||||||
|
IncludeAncestorGroups *bool `url:"include_ancestor_groups,omitempty" json:"include_ancestor_groups,omitempty"`
|
||||||
|
IncludeDescendantGroups *bool `url:"include_descendant_groups,omitempty" json:"include_descendant_groups,omitempty"`
|
||||||
|
MyReactionEmoji *string `url:"my_reaction_emoji,omitempty" json:"my_reaction_emoji,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListGroupEpics gets a list of group epics. This function accepts pagination
|
// ListGroupEpics gets a list of group epics. This function accepts pagination
|
||||||
|
@ -116,6 +127,30 @@ func (s *EpicsService) GetEpic(gid interface{}, epic int, options ...RequestOpti
|
||||||
return e, resp, err
|
return e, resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetEpicLinks gets all child epics of an epic.
|
||||||
|
//
|
||||||
|
// GitLab API docs: https://docs.gitlab.com/ee/api/epic_links.html
|
||||||
|
func (s *EpicsService) GetEpicLinks(gid interface{}, epic int, options ...RequestOptionFunc) ([]*Epic, *Response, error) {
|
||||||
|
group, err := parseID(gid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
u := fmt.Sprintf("groups/%s/epics/%d/epics", pathEscape(group), epic)
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest("GET", u, nil, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var e []*Epic
|
||||||
|
resp, err := s.client.Do(req, &e)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return e, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
// CreateEpicOptions represents the available CreateEpic() options.
|
// CreateEpicOptions represents the available CreateEpic() options.
|
||||||
//
|
//
|
||||||
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#new-epic
|
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#new-epic
|
||||||
|
|
|
@ -85,6 +85,7 @@ type TagEvent struct {
|
||||||
UserID int `json:"user_id"`
|
UserID int `json:"user_id"`
|
||||||
UserName string `json:"user_name"`
|
UserName string `json:"user_name"`
|
||||||
UserAvatar string `json:"user_avatar"`
|
UserAvatar string `json:"user_avatar"`
|
||||||
|
UserEmail string `json:"user_email"`
|
||||||
ProjectID int `json:"project_id"`
|
ProjectID int `json:"project_id"`
|
||||||
Message string `json:"message"`
|
Message string `json:"message"`
|
||||||
Project struct {
|
Project struct {
|
||||||
|
@ -443,8 +444,8 @@ type IssueCommentEvent struct {
|
||||||
TimeEstimate int `json:"time_estimate"`
|
TimeEstimate int `json:"time_estimate"`
|
||||||
Confidential bool `json:"confidential"`
|
Confidential bool `json:"confidential"`
|
||||||
TotalTimeSpent int `json:"total_time_spent"`
|
TotalTimeSpent int `json:"total_time_spent"`
|
||||||
HumanTotalTimeSpent int `json:"human_total_time_spent"`
|
HumanTotalTimeSpent string `json:"human_total_time_spent"`
|
||||||
HumanTimeEstimate int `json:"human_time_estimate"`
|
HumanTimeEstimate string `json:"human_time_estimate"`
|
||||||
AssigneeIDs []int `json:"assignee_ids"`
|
AssigneeIDs []int `json:"assignee_ids"`
|
||||||
AssigneeID int `json:"assignee_id"`
|
AssigneeID int `json:"assignee_id"`
|
||||||
} `json:"issue"`
|
} `json:"issue"`
|
||||||
|
@ -593,6 +594,10 @@ type MergeEvent struct {
|
||||||
Previous int `json:"previous"`
|
Previous int `json:"previous"`
|
||||||
Current int `json:"current"`
|
Current int `json:"current"`
|
||||||
} `json:"source_project_id"`
|
} `json:"source_project_id"`
|
||||||
|
StateID struct {
|
||||||
|
Previous int `json:"previous"`
|
||||||
|
Current int `json:"current"`
|
||||||
|
} `json:"state_id"`
|
||||||
TargetBranch struct {
|
TargetBranch struct {
|
||||||
Previous string `json:"previous"`
|
Previous string `json:"previous"`
|
||||||
Current string `json:"current"`
|
Current string `json:"current"`
|
||||||
|
|
|
@ -20,7 +20,6 @@ package gitlab
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
@ -63,268 +62,6 @@ const (
|
||||||
privateToken
|
privateToken
|
||||||
)
|
)
|
||||||
|
|
||||||
// AccessLevelValue represents a permission level within GitLab.
|
|
||||||
//
|
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/permissions/permissions.html
|
|
||||||
type AccessLevelValue int
|
|
||||||
|
|
||||||
// List of available access levels
|
|
||||||
//
|
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/permissions/permissions.html
|
|
||||||
const (
|
|
||||||
NoPermissions AccessLevelValue = 0
|
|
||||||
GuestPermissions AccessLevelValue = 10
|
|
||||||
ReporterPermissions AccessLevelValue = 20
|
|
||||||
DeveloperPermissions AccessLevelValue = 30
|
|
||||||
MaintainerPermissions AccessLevelValue = 40
|
|
||||||
OwnerPermissions AccessLevelValue = 50
|
|
||||||
|
|
||||||
// These are deprecated and should be removed in a future version
|
|
||||||
MasterPermissions AccessLevelValue = 40
|
|
||||||
OwnerPermission AccessLevelValue = 50
|
|
||||||
)
|
|
||||||
|
|
||||||
// BuildStateValue represents a GitLab build state.
|
|
||||||
type BuildStateValue string
|
|
||||||
|
|
||||||
// These constants represent all valid build states.
|
|
||||||
const (
|
|
||||||
Pending BuildStateValue = "pending"
|
|
||||||
Created BuildStateValue = "created"
|
|
||||||
Running BuildStateValue = "running"
|
|
||||||
Success BuildStateValue = "success"
|
|
||||||
Failed BuildStateValue = "failed"
|
|
||||||
Canceled BuildStateValue = "canceled"
|
|
||||||
Skipped BuildStateValue = "skipped"
|
|
||||||
Manual BuildStateValue = "manual"
|
|
||||||
)
|
|
||||||
|
|
||||||
// DeploymentStatusValue represents a Gitlab deployment status.
|
|
||||||
type DeploymentStatusValue string
|
|
||||||
|
|
||||||
// These constants represent all valid deployment statuses.
|
|
||||||
const (
|
|
||||||
DeploymentStatusCreated DeploymentStatusValue = "created"
|
|
||||||
DeploymentStatusRunning DeploymentStatusValue = "running"
|
|
||||||
DeploymentStatusSuccess DeploymentStatusValue = "success"
|
|
||||||
DeploymentStatusFailed DeploymentStatusValue = "failed"
|
|
||||||
DeploymentStatusCanceled DeploymentStatusValue = "canceled"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ISOTime represents an ISO 8601 formatted date
|
|
||||||
type ISOTime time.Time
|
|
||||||
|
|
||||||
// ISO 8601 date format
|
|
||||||
const iso8601 = "2006-01-02"
|
|
||||||
|
|
||||||
// MarshalJSON implements the json.Marshaler interface
|
|
||||||
func (t ISOTime) MarshalJSON() ([]byte, error) {
|
|
||||||
if y := time.Time(t).Year(); y < 0 || y >= 10000 {
|
|
||||||
// ISO 8901 uses 4 digits for the years
|
|
||||||
return nil, errors.New("json: ISOTime year outside of range [0,9999]")
|
|
||||||
}
|
|
||||||
|
|
||||||
b := make([]byte, 0, len(iso8601)+2)
|
|
||||||
b = append(b, '"')
|
|
||||||
b = time.Time(t).AppendFormat(b, iso8601)
|
|
||||||
b = append(b, '"')
|
|
||||||
|
|
||||||
return b, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalJSON implements the json.Unmarshaler interface
|
|
||||||
func (t *ISOTime) UnmarshalJSON(data []byte) error {
|
|
||||||
// Ignore null, like in the main JSON package
|
|
||||||
if string(data) == "null" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
isotime, err := time.Parse(`"`+iso8601+`"`, string(data))
|
|
||||||
*t = ISOTime(isotime)
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// EncodeValues implements the query.Encoder interface
|
|
||||||
func (t *ISOTime) EncodeValues(key string, v *url.Values) error {
|
|
||||||
if t == nil || (time.Time(*t)).IsZero() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
v.Add(key, t.String())
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// String implements the Stringer interface
|
|
||||||
func (t ISOTime) String() string {
|
|
||||||
return time.Time(t).Format(iso8601)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NotificationLevelValue represents a notification level.
|
|
||||||
type NotificationLevelValue int
|
|
||||||
|
|
||||||
// String implements the fmt.Stringer interface.
|
|
||||||
func (l NotificationLevelValue) String() string {
|
|
||||||
return notificationLevelNames[l]
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalJSON implements the json.Marshaler interface.
|
|
||||||
func (l NotificationLevelValue) MarshalJSON() ([]byte, error) {
|
|
||||||
return json.Marshal(l.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalJSON implements the json.Unmarshaler interface.
|
|
||||||
func (l *NotificationLevelValue) UnmarshalJSON(data []byte) error {
|
|
||||||
var raw interface{}
|
|
||||||
if err := json.Unmarshal(data, &raw); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
switch raw := raw.(type) {
|
|
||||||
case float64:
|
|
||||||
*l = NotificationLevelValue(raw)
|
|
||||||
case string:
|
|
||||||
*l = notificationLevelTypes[raw]
|
|
||||||
case nil:
|
|
||||||
// No action needed.
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("json: cannot unmarshal %T into Go value of type %T", raw, *l)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// List of valid notification levels.
|
|
||||||
const (
|
|
||||||
DisabledNotificationLevel NotificationLevelValue = iota
|
|
||||||
ParticipatingNotificationLevel
|
|
||||||
WatchNotificationLevel
|
|
||||||
GlobalNotificationLevel
|
|
||||||
MentionNotificationLevel
|
|
||||||
CustomNotificationLevel
|
|
||||||
)
|
|
||||||
|
|
||||||
var notificationLevelNames = [...]string{
|
|
||||||
"disabled",
|
|
||||||
"participating",
|
|
||||||
"watch",
|
|
||||||
"global",
|
|
||||||
"mention",
|
|
||||||
"custom",
|
|
||||||
}
|
|
||||||
|
|
||||||
var notificationLevelTypes = map[string]NotificationLevelValue{
|
|
||||||
"disabled": DisabledNotificationLevel,
|
|
||||||
"participating": ParticipatingNotificationLevel,
|
|
||||||
"watch": WatchNotificationLevel,
|
|
||||||
"global": GlobalNotificationLevel,
|
|
||||||
"mention": MentionNotificationLevel,
|
|
||||||
"custom": CustomNotificationLevel,
|
|
||||||
}
|
|
||||||
|
|
||||||
// VisibilityValue represents a visibility level within GitLab.
|
|
||||||
//
|
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/api/
|
|
||||||
type VisibilityValue string
|
|
||||||
|
|
||||||
// List of available visibility levels.
|
|
||||||
//
|
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/api/
|
|
||||||
const (
|
|
||||||
PrivateVisibility VisibilityValue = "private"
|
|
||||||
InternalVisibility VisibilityValue = "internal"
|
|
||||||
PublicVisibility VisibilityValue = "public"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ProjectCreationLevelValue represents a project creation level within GitLab.
|
|
||||||
//
|
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/api/
|
|
||||||
type ProjectCreationLevelValue string
|
|
||||||
|
|
||||||
// List of available project creation levels.
|
|
||||||
//
|
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/api/
|
|
||||||
const (
|
|
||||||
NoOneProjectCreation ProjectCreationLevelValue = "noone"
|
|
||||||
MaintainerProjectCreation ProjectCreationLevelValue = "maintainer"
|
|
||||||
DeveloperProjectCreation ProjectCreationLevelValue = "developer"
|
|
||||||
)
|
|
||||||
|
|
||||||
// SubGroupCreationLevelValue represents a sub group creation level within GitLab.
|
|
||||||
//
|
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/api/
|
|
||||||
type SubGroupCreationLevelValue string
|
|
||||||
|
|
||||||
// List of available sub group creation levels.
|
|
||||||
//
|
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/api/
|
|
||||||
const (
|
|
||||||
OwnerSubGroupCreationLevelValue SubGroupCreationLevelValue = "owner"
|
|
||||||
MaintainerSubGroupCreationLevelValue SubGroupCreationLevelValue = "maintainer"
|
|
||||||
)
|
|
||||||
|
|
||||||
// VariableTypeValue represents a variable type within GitLab.
|
|
||||||
//
|
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/api/
|
|
||||||
type VariableTypeValue string
|
|
||||||
|
|
||||||
// List of available variable types.
|
|
||||||
//
|
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/api/
|
|
||||||
const (
|
|
||||||
EnvVariableType VariableTypeValue = "env_var"
|
|
||||||
FileVariableType VariableTypeValue = "file"
|
|
||||||
)
|
|
||||||
|
|
||||||
// MergeMethodValue represents a project merge type within GitLab.
|
|
||||||
//
|
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#project-merge-method
|
|
||||||
type MergeMethodValue string
|
|
||||||
|
|
||||||
// List of available merge type
|
|
||||||
//
|
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#project-merge-method
|
|
||||||
const (
|
|
||||||
NoFastForwardMerge MergeMethodValue = "merge"
|
|
||||||
FastForwardMerge MergeMethodValue = "ff"
|
|
||||||
RebaseMerge MergeMethodValue = "rebase_merge"
|
|
||||||
)
|
|
||||||
|
|
||||||
// EventTypeValue represents actions type for contribution events
|
|
||||||
type EventTypeValue string
|
|
||||||
|
|
||||||
// List of available action type
|
|
||||||
//
|
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/api/events.html#action-types
|
|
||||||
const (
|
|
||||||
CreatedEventType EventTypeValue = "created"
|
|
||||||
UpdatedEventType EventTypeValue = "updated"
|
|
||||||
ClosedEventType EventTypeValue = "closed"
|
|
||||||
ReopenedEventType EventTypeValue = "reopened"
|
|
||||||
PushedEventType EventTypeValue = "pushed"
|
|
||||||
CommentedEventType EventTypeValue = "commented"
|
|
||||||
MergedEventType EventTypeValue = "merged"
|
|
||||||
JoinedEventType EventTypeValue = "joined"
|
|
||||||
LeftEventType EventTypeValue = "left"
|
|
||||||
DestroyedEventType EventTypeValue = "destroyed"
|
|
||||||
ExpiredEventType EventTypeValue = "expired"
|
|
||||||
)
|
|
||||||
|
|
||||||
// EventTargetTypeValue represents actions type value for contribution events
|
|
||||||
type EventTargetTypeValue string
|
|
||||||
|
|
||||||
// List of available action type
|
|
||||||
//
|
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/api/events.html#target-types
|
|
||||||
const (
|
|
||||||
IssueEventTargetType EventTargetTypeValue = "issue"
|
|
||||||
MilestoneEventTargetType EventTargetTypeValue = "milestone"
|
|
||||||
MergeRequestEventTargetType EventTargetTypeValue = "merge_request"
|
|
||||||
NoteEventTargetType EventTargetTypeValue = "note"
|
|
||||||
ProjectEventTargetType EventTargetTypeValue = "project"
|
|
||||||
SnippetEventTargetType EventTargetTypeValue = "snippet"
|
|
||||||
UserEventTargetType EventTargetTypeValue = "user"
|
|
||||||
)
|
|
||||||
|
|
||||||
// A Client manages communication with the GitLab API.
|
// A Client manages communication with the GitLab API.
|
||||||
type Client struct {
|
type Client struct {
|
||||||
// HTTP client used to communicate with the API.
|
// HTTP client used to communicate with the API.
|
||||||
|
@ -338,12 +75,12 @@ type Client struct {
|
||||||
// disableRetries is used to disable the default retry logic.
|
// disableRetries is used to disable the default retry logic.
|
||||||
disableRetries bool
|
disableRetries bool
|
||||||
|
|
||||||
// configLimiter is used to make sure the limiter is configured exactly
|
// configureLimiterOnce is used to make sure the limiter is configured exactly
|
||||||
// once and block all other calls until the initial (one) call is done.
|
// once and block all other calls until the initial (one) call is done.
|
||||||
configureLimiterOnce sync.Once
|
configureLimiterOnce sync.Once
|
||||||
|
|
||||||
// Limiter is used to limit API calls and prevent 429 responses.
|
// Limiter is used to limit API calls and prevent 429 responses.
|
||||||
limiter *rate.Limiter
|
limiter RateLimiter
|
||||||
|
|
||||||
// Token type used to make authenticated API calls.
|
// Token type used to make authenticated API calls.
|
||||||
authType authType
|
authType authType
|
||||||
|
@ -354,6 +91,9 @@ type Client struct {
|
||||||
// Token used to make authenticated API calls.
|
// Token used to make authenticated API calls.
|
||||||
token string
|
token string
|
||||||
|
|
||||||
|
// Protects the token field from concurrent read/write accesses.
|
||||||
|
tokenLock sync.RWMutex
|
||||||
|
|
||||||
// User agent used when communicating with the GitLab API.
|
// User agent used when communicating with the GitLab API.
|
||||||
UserAgent string
|
UserAgent string
|
||||||
|
|
||||||
|
@ -373,6 +113,7 @@ type Client struct {
|
||||||
Deployments *DeploymentsService
|
Deployments *DeploymentsService
|
||||||
Discussions *DiscussionsService
|
Discussions *DiscussionsService
|
||||||
Environments *EnvironmentsService
|
Environments *EnvironmentsService
|
||||||
|
EpicIssues *EpicIssuesService
|
||||||
Epics *EpicsService
|
Epics *EpicsService
|
||||||
Events *EventsService
|
Events *EventsService
|
||||||
Features *FeaturesService
|
Features *FeaturesService
|
||||||
|
@ -385,8 +126,11 @@ type Client struct {
|
||||||
GroupMilestones *GroupMilestonesService
|
GroupMilestones *GroupMilestonesService
|
||||||
GroupVariables *GroupVariablesService
|
GroupVariables *GroupVariablesService
|
||||||
Groups *GroupsService
|
Groups *GroupsService
|
||||||
|
InstanceCluster *InstanceClustersService
|
||||||
|
InstanceVariables *InstanceVariablesService
|
||||||
IssueLinks *IssueLinksService
|
IssueLinks *IssueLinksService
|
||||||
Issues *IssuesService
|
Issues *IssuesService
|
||||||
|
IssuesStatistics *IssuesStatisticsService
|
||||||
Jobs *JobsService
|
Jobs *JobsService
|
||||||
Keys *KeysService
|
Keys *KeysService
|
||||||
Labels *LabelsService
|
Labels *LabelsService
|
||||||
|
@ -406,6 +150,7 @@ type Client struct {
|
||||||
ProjectCluster *ProjectClustersService
|
ProjectCluster *ProjectClustersService
|
||||||
ProjectImportExport *ProjectImportExportService
|
ProjectImportExport *ProjectImportExportService
|
||||||
ProjectMembers *ProjectMembersService
|
ProjectMembers *ProjectMembersService
|
||||||
|
ProjectMirrors *ProjectMirrorService
|
||||||
ProjectSnippets *ProjectSnippetsService
|
ProjectSnippets *ProjectSnippetsService
|
||||||
ProjectVariables *ProjectVariablesService
|
ProjectVariables *ProjectVariablesService
|
||||||
Projects *ProjectsService
|
Projects *ProjectsService
|
||||||
|
@ -441,6 +186,11 @@ type ListOptions struct {
|
||||||
PerPage int `url:"per_page,omitempty" json:"per_page,omitempty"`
|
PerPage int `url:"per_page,omitempty" json:"per_page,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RateLimiter describes the interface that all (custom) rate limiters must implement.
|
||||||
|
type RateLimiter interface {
|
||||||
|
Wait(context.Context) error
|
||||||
|
}
|
||||||
|
|
||||||
// NewClient returns a new GitLab API client. To use API methods which require
|
// NewClient returns a new GitLab API client. To use API methods which require
|
||||||
// authentication, provide a valid private or personal token.
|
// authentication, provide a valid private or personal token.
|
||||||
func NewClient(token string, options ...ClientOptionFunc) (*Client, error) {
|
func NewClient(token string, options ...ClientOptionFunc) (*Client, error) {
|
||||||
|
@ -465,11 +215,6 @@ func NewBasicAuthClient(username, password string, options ...ClientOptionFunc)
|
||||||
client.username = username
|
client.username = username
|
||||||
client.password = password
|
client.password = password
|
||||||
|
|
||||||
err = client.requestOAuthToken(context.Background())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return client, nil
|
return client, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -485,22 +230,6 @@ func NewOAuthClient(token string, options ...ClientOptionFunc) (*Client, error)
|
||||||
return client, nil
|
return client, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) requestOAuthToken(ctx context.Context) error {
|
|
||||||
config := &oauth2.Config{
|
|
||||||
Endpoint: oauth2.Endpoint{
|
|
||||||
AuthURL: fmt.Sprintf("%s://%s/oauth/authorize", c.BaseURL().Scheme, c.BaseURL().Host),
|
|
||||||
TokenURL: fmt.Sprintf("%s://%s/oauth/token", c.BaseURL().Scheme, c.BaseURL().Host),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
ctx = context.WithValue(ctx, oauth2.HTTPClient, c.client)
|
|
||||||
t, err := config.PasswordCredentialsToken(ctx, c.username, c.password)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
c.token = t.AccessToken
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func newClient(options ...ClientOptionFunc) (*Client, error) {
|
func newClient(options ...ClientOptionFunc) (*Client, error) {
|
||||||
c := &Client{UserAgent: userAgent}
|
c := &Client{UserAgent: userAgent}
|
||||||
|
|
||||||
|
@ -547,6 +276,7 @@ func newClient(options ...ClientOptionFunc) (*Client, error) {
|
||||||
c.Deployments = &DeploymentsService{client: c}
|
c.Deployments = &DeploymentsService{client: c}
|
||||||
c.Discussions = &DiscussionsService{client: c}
|
c.Discussions = &DiscussionsService{client: c}
|
||||||
c.Environments = &EnvironmentsService{client: c}
|
c.Environments = &EnvironmentsService{client: c}
|
||||||
|
c.EpicIssues = &EpicIssuesService{client: c}
|
||||||
c.Epics = &EpicsService{client: c}
|
c.Epics = &EpicsService{client: c}
|
||||||
c.Events = &EventsService{client: c}
|
c.Events = &EventsService{client: c}
|
||||||
c.Features = &FeaturesService{client: c}
|
c.Features = &FeaturesService{client: c}
|
||||||
|
@ -559,8 +289,10 @@ func newClient(options ...ClientOptionFunc) (*Client, error) {
|
||||||
c.GroupMilestones = &GroupMilestonesService{client: c}
|
c.GroupMilestones = &GroupMilestonesService{client: c}
|
||||||
c.GroupVariables = &GroupVariablesService{client: c}
|
c.GroupVariables = &GroupVariablesService{client: c}
|
||||||
c.Groups = &GroupsService{client: c}
|
c.Groups = &GroupsService{client: c}
|
||||||
|
c.InstanceCluster = &InstanceClustersService{client: c}
|
||||||
c.IssueLinks = &IssueLinksService{client: c}
|
c.IssueLinks = &IssueLinksService{client: c}
|
||||||
c.Issues = &IssuesService{client: c, timeStats: timeStats}
|
c.Issues = &IssuesService{client: c, timeStats: timeStats}
|
||||||
|
c.IssuesStatistics = &IssuesStatisticsService{client: c}
|
||||||
c.Jobs = &JobsService{client: c}
|
c.Jobs = &JobsService{client: c}
|
||||||
c.Keys = &KeysService{client: c}
|
c.Keys = &KeysService{client: c}
|
||||||
c.Labels = &LabelsService{client: c}
|
c.Labels = &LabelsService{client: c}
|
||||||
|
@ -580,6 +312,7 @@ func newClient(options ...ClientOptionFunc) (*Client, error) {
|
||||||
c.ProjectCluster = &ProjectClustersService{client: c}
|
c.ProjectCluster = &ProjectClustersService{client: c}
|
||||||
c.ProjectImportExport = &ProjectImportExportService{client: c}
|
c.ProjectImportExport = &ProjectImportExportService{client: c}
|
||||||
c.ProjectMembers = &ProjectMembersService{client: c}
|
c.ProjectMembers = &ProjectMembersService{client: c}
|
||||||
|
c.ProjectMirrors = &ProjectMirrorService{client: c}
|
||||||
c.ProjectSnippets = &ProjectSnippetsService{client: c}
|
c.ProjectSnippets = &ProjectSnippetsService{client: c}
|
||||||
c.ProjectVariables = &ProjectVariablesService{client: c}
|
c.ProjectVariables = &ProjectVariablesService{client: c}
|
||||||
c.Projects = &ProjectsService{client: c}
|
c.Projects = &ProjectsService{client: c}
|
||||||
|
@ -755,13 +488,6 @@ func (c *Client) NewRequest(method, path string, opt interface{}, options []Requ
|
||||||
reqHeaders := make(http.Header)
|
reqHeaders := make(http.Header)
|
||||||
reqHeaders.Set("Accept", "application/json")
|
reqHeaders.Set("Accept", "application/json")
|
||||||
|
|
||||||
switch c.authType {
|
|
||||||
case basicAuth, oAuthToken:
|
|
||||||
reqHeaders.Set("Authorization", "Bearer "+c.token)
|
|
||||||
case privateToken:
|
|
||||||
reqHeaders.Set("PRIVATE-TOKEN", c.token)
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.UserAgent != "" {
|
if c.UserAgent != "" {
|
||||||
reqHeaders.Set("User-Agent", c.UserAgent)
|
reqHeaders.Set("User-Agent", c.UserAgent)
|
||||||
}
|
}
|
||||||
|
@ -875,23 +601,47 @@ func (c *Client) Do(req *retryablehttp.Request, v interface{}) (*Response, error
|
||||||
c.configureLimiterOnce.Do(func() { c.configureLimiter() })
|
c.configureLimiterOnce.Do(func() { c.configureLimiter() })
|
||||||
|
|
||||||
// Wait will block until the limiter can obtain a new token.
|
// Wait will block until the limiter can obtain a new token.
|
||||||
if err := c.limiter.Wait(req.Context()); err != nil {
|
err := c.limiter.Wait(req.Context())
|
||||||
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set the correct authentication header. If using basic auth, then check
|
||||||
|
// if we already have a token and if not first authenticate and get one.
|
||||||
|
var basicAuthToken string
|
||||||
|
switch c.authType {
|
||||||
|
case basicAuth:
|
||||||
|
c.tokenLock.RLock()
|
||||||
|
basicAuthToken = c.token
|
||||||
|
c.tokenLock.RUnlock()
|
||||||
|
if basicAuthToken == "" {
|
||||||
|
// If we don't have a token yet, we first need to request one.
|
||||||
|
basicAuthToken, err = c.requestOAuthToken(req.Context(), basicAuthToken)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
req.Header.Set("Authorization", "Bearer "+basicAuthToken)
|
||||||
|
case oAuthToken:
|
||||||
|
req.Header.Set("Authorization", "Bearer "+c.token)
|
||||||
|
case privateToken:
|
||||||
|
req.Header.Set("PRIVATE-TOKEN", c.token)
|
||||||
|
}
|
||||||
|
|
||||||
resp, err := c.client.Do(req)
|
resp, err := c.client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
if resp.StatusCode == http.StatusUnauthorized && c.authType == basicAuth {
|
if resp.StatusCode == http.StatusUnauthorized && c.authType == basicAuth {
|
||||||
err = c.requestOAuthToken(req.Context())
|
resp.Body.Close()
|
||||||
if err != nil {
|
// The token most likely expired, so we need to request a new one and try again.
|
||||||
|
if _, err := c.requestOAuthToken(req.Context(), basicAuthToken); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return c.Do(req, v)
|
return c.Do(req, v)
|
||||||
}
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
response := newResponse(resp)
|
response := newResponse(resp)
|
||||||
|
|
||||||
|
@ -913,6 +663,32 @@ func (c *Client) Do(req *retryablehttp.Request, v interface{}) (*Response, error
|
||||||
return response, err
|
return response, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Client) requestOAuthToken(ctx context.Context, token string) (string, error) {
|
||||||
|
c.tokenLock.Lock()
|
||||||
|
defer c.tokenLock.Unlock()
|
||||||
|
|
||||||
|
// Return early if the token was updated while waiting for the lock.
|
||||||
|
if c.token != token {
|
||||||
|
return c.token, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
config := &oauth2.Config{
|
||||||
|
Endpoint: oauth2.Endpoint{
|
||||||
|
AuthURL: strings.TrimSuffix(c.baseURL.String(), apiVersionPath) + "oauth/authorize",
|
||||||
|
TokenURL: strings.TrimSuffix(c.baseURL.String(), apiVersionPath) + "oauth/token",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = context.WithValue(ctx, oauth2.HTTPClient, c.client.HTTPClient)
|
||||||
|
t, err := config.PasswordCredentialsToken(ctx, c.username, c.password)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
c.token = t.AccessToken
|
||||||
|
|
||||||
|
return c.token, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Helper function to accept and format both the project ID or name as project
|
// Helper function to accept and format both the project ID or name as project
|
||||||
// identifier for all API calls.
|
// identifier for all API calls.
|
||||||
func parseID(id interface{}) (string, error) {
|
func parseID(id interface{}) (string, error) {
|
||||||
|
@ -1012,129 +788,3 @@ func parseError(raw interface{}) string {
|
||||||
return fmt.Sprintf("failed to parse unexpected error type: %T", raw)
|
return fmt.Sprintf("failed to parse unexpected error type: %T", raw)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bool is a helper routine that allocates a new bool value
|
|
||||||
// to store v and returns a pointer to it.
|
|
||||||
func Bool(v bool) *bool {
|
|
||||||
p := new(bool)
|
|
||||||
*p = v
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
// Int is a helper routine that allocates a new int32 value
|
|
||||||
// to store v and returns a pointer to it, but unlike Int32
|
|
||||||
// its argument value is an int.
|
|
||||||
func Int(v int) *int {
|
|
||||||
p := new(int)
|
|
||||||
*p = v
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
// String is a helper routine that allocates a new string value
|
|
||||||
// to store v and returns a pointer to it.
|
|
||||||
func String(v string) *string {
|
|
||||||
p := new(string)
|
|
||||||
*p = v
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
// Time is a helper routine that allocates a new time.Time value
|
|
||||||
// to store v and returns a pointer to it.
|
|
||||||
func Time(v time.Time) *time.Time {
|
|
||||||
p := new(time.Time)
|
|
||||||
*p = v
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
// AccessLevel is a helper routine that allocates a new AccessLevelValue
|
|
||||||
// to store v and returns a pointer to it.
|
|
||||||
func AccessLevel(v AccessLevelValue) *AccessLevelValue {
|
|
||||||
p := new(AccessLevelValue)
|
|
||||||
*p = v
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
// BuildState is a helper routine that allocates a new BuildStateValue
|
|
||||||
// to store v and returns a pointer to it.
|
|
||||||
func BuildState(v BuildStateValue) *BuildStateValue {
|
|
||||||
p := new(BuildStateValue)
|
|
||||||
*p = v
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeploymentStatus is a helper routine that allocates a new
|
|
||||||
// DeploymentStatusValue to store v and returns a pointer to it.
|
|
||||||
func DeploymentStatus(v DeploymentStatusValue) *DeploymentStatusValue {
|
|
||||||
p := new(DeploymentStatusValue)
|
|
||||||
*p = v
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
// NotificationLevel is a helper routine that allocates a new NotificationLevelValue
|
|
||||||
// to store v and returns a pointer to it.
|
|
||||||
func NotificationLevel(v NotificationLevelValue) *NotificationLevelValue {
|
|
||||||
p := new(NotificationLevelValue)
|
|
||||||
*p = v
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
// VariableType is a helper routine that allocates a new VariableTypeValue
|
|
||||||
// to store v and returns a pointer to it.
|
|
||||||
func VariableType(v VariableTypeValue) *VariableTypeValue {
|
|
||||||
p := new(VariableTypeValue)
|
|
||||||
*p = v
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
// Visibility is a helper routine that allocates a new VisibilityValue
|
|
||||||
// to store v and returns a pointer to it.
|
|
||||||
func Visibility(v VisibilityValue) *VisibilityValue {
|
|
||||||
p := new(VisibilityValue)
|
|
||||||
*p = v
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
// ProjectCreationLevel is a helper routine that allocates a new ProjectCreationLevelValue
|
|
||||||
// to store v and returns a pointer to it.
|
|
||||||
func ProjectCreationLevel(v ProjectCreationLevelValue) *ProjectCreationLevelValue {
|
|
||||||
p := new(ProjectCreationLevelValue)
|
|
||||||
*p = v
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
// SubGroupCreationLevel is a helper routine that allocates a new SubGroupCreationLevelValue
|
|
||||||
// to store v and returns a pointer to it.
|
|
||||||
func SubGroupCreationLevel(v SubGroupCreationLevelValue) *SubGroupCreationLevelValue {
|
|
||||||
p := new(SubGroupCreationLevelValue)
|
|
||||||
*p = v
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
// MergeMethod is a helper routine that allocates a new MergeMethod
|
|
||||||
// to sotre v and returns a pointer to it.
|
|
||||||
func MergeMethod(v MergeMethodValue) *MergeMethodValue {
|
|
||||||
p := new(MergeMethodValue)
|
|
||||||
*p = v
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
// BoolValue is a boolean value with advanced json unmarshaling features.
|
|
||||||
type BoolValue bool
|
|
||||||
|
|
||||||
// UnmarshalJSON allows 1 and 0 to be considered as boolean values
|
|
||||||
// Needed for https://gitlab.com/gitlab-org/gitlab-ce/issues/50122
|
|
||||||
func (t *BoolValue) UnmarshalJSON(b []byte) error {
|
|
||||||
switch string(b) {
|
|
||||||
case `"1"`:
|
|
||||||
*t = true
|
|
||||||
return nil
|
|
||||||
case `"0"`:
|
|
||||||
*t = false
|
|
||||||
return nil
|
|
||||||
default:
|
|
||||||
var v bool
|
|
||||||
err := json.Unmarshal(b, &v)
|
|
||||||
*t = BoolValue(v)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -93,13 +93,13 @@ func (s *GroupClustersService) GetCluster(pid interface{}, cluster int, options
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
pc := new(GroupCluster)
|
gc := new(GroupCluster)
|
||||||
resp, err := s.client.Do(req, &pc)
|
resp, err := s.client.Do(req, &gc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, resp, err
|
return nil, resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return pc, resp, err
|
return gc, resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddGroupClusterOptions represents the available AddCluster() options.
|
// AddGroupClusterOptions represents the available AddCluster() options.
|
||||||
|
@ -141,13 +141,13 @@ func (s *GroupClustersService) AddCluster(pid interface{}, opt *AddGroupClusterO
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
pc := new(GroupCluster)
|
gc := new(GroupCluster)
|
||||||
resp, err := s.client.Do(req, pc)
|
resp, err := s.client.Do(req, gc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, resp, err
|
return nil, resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return pc, resp, err
|
return gc, resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// EditGroupClusterOptions represents the available EditCluster() options.
|
// EditGroupClusterOptions represents the available EditCluster() options.
|
||||||
|
@ -185,13 +185,13 @@ func (s *GroupClustersService) EditCluster(pid interface{}, cluster int, opt *Ed
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
pc := new(GroupCluster)
|
gc := new(GroupCluster)
|
||||||
resp, err := s.client.Do(req, pc)
|
resp, err := s.client.Do(req, gc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, resp, err
|
return nil, resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return pc, resp, err
|
return gc, resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteCluster deletes an existing group cluster.
|
// DeleteCluster deletes an existing group cluster.
|
||||||
|
|
|
@ -51,6 +51,35 @@ func (s *GroupLabelsService) ListGroupLabels(gid interface{}, opt *ListGroupLabe
|
||||||
return l, resp, err
|
return l, resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetGroupLabel get a single label for a given group.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ce/api/group_labels.html#get-a-single-group-label
|
||||||
|
func (s *GroupLabelsService) GetGroupLabel(gid interface{}, labelID interface{}, options ...RequestOptionFunc) (*GroupLabel, *Response, error) {
|
||||||
|
group, err := parseID(gid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
label, err := parseID(labelID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
u := fmt.Sprintf("groups/%s/labels/%s", pathEscape(group), label)
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest("GET", u, nil, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var l *GroupLabel
|
||||||
|
resp, err := s.client.Do(req, &l)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return l, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
// CreateGroupLabelOptions represents the available CreateGroupLabel() options.
|
// CreateGroupLabelOptions represents the available CreateGroupLabel() options.
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
|
|
|
@ -176,6 +176,50 @@ func (s *GroupMembersService) AddGroupMember(gid interface{}, opt *AddGroupMembe
|
||||||
return gm, resp, err
|
return gm, resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ShareWithGroup shares a group with the group.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ce/api/groups.html#share-groups-with-groups
|
||||||
|
func (s *GroupMembersService) ShareWithGroup(gid interface{}, opt *ShareWithGroupOptions, options ...RequestOptionFunc) (*Group, *Response, error) {
|
||||||
|
group, err := parseID(gid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
u := fmt.Sprintf("groups/%s/share", pathEscape(group))
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest("POST", u, opt, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
g := new(Group)
|
||||||
|
resp, err := s.client.Do(req, g)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return g, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteShareWithGroup allows to unshare a group from a group.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ce/api/groups.html#delete-link-sharing-group-with-another-group
|
||||||
|
func (s *GroupMembersService) DeleteShareWithGroup(gid interface{}, groupID int, options ...RequestOptionFunc) (*Response, error) {
|
||||||
|
group, err := parseID(gid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
u := fmt.Sprintf("groups/%s/share/%d", pathEscape(group), groupID)
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest("DELETE", u, nil, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.client.Do(req, nil)
|
||||||
|
}
|
||||||
|
|
||||||
// EditGroupMemberOptions represents the available EditGroupMember()
|
// EditGroupMemberOptions represents the available EditGroupMember()
|
||||||
// options.
|
// options.
|
||||||
//
|
//
|
||||||
|
|
|
@ -247,3 +247,42 @@ func (s *GroupMilestonesService) GetGroupMilestoneMergeRequests(gid interface{},
|
||||||
|
|
||||||
return mr, resp, err
|
return mr, resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type BurndownChartEvent struct {
|
||||||
|
CreatedAt *time.Time `json:"created_at"`
|
||||||
|
Weight *int `json:"weight"`
|
||||||
|
Action *string `json:"action"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetGroupMilestoneBurndownChartEventsOptions represents the available
|
||||||
|
// GetGroupMilestoneBurndownChartEventsOptions() options.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ee/api/group_milestones.html#get-all-burndown-chart-events-for-a-single-milestone-starter
|
||||||
|
type GetGroupMilestoneBurndownChartEventsOptions ListOptions
|
||||||
|
|
||||||
|
// GetGroupMilestoneBurndownChartEvents gets all merge requests assigned to a
|
||||||
|
// single group milestone.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ee/api/group_milestones.html#get-all-burndown-chart-events-for-a-single-milestone-starter
|
||||||
|
func (s *GroupMilestonesService) GetGroupMilestoneBurndownChartEvents(gid interface{}, milestone int, opt *GetGroupMilestoneBurndownChartEventsOptions, options ...RequestOptionFunc) ([]*BurndownChartEvent, *Response, error) {
|
||||||
|
group, err := parseID(gid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
u := fmt.Sprintf("groups/%s/milestones/%d/burndown_events", pathEscape(group), milestone)
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest("GET", u, opt, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var be []*BurndownChartEvent
|
||||||
|
resp, err := s.client.Do(req, &be)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return be, resp, err
|
||||||
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ package gitlab
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GroupsService handles communication with the group related methods of
|
// GroupsService handles communication with the group related methods of
|
||||||
|
@ -32,38 +33,46 @@ type GroupsService struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/api/groups.html
|
// GitLab API docs: https://docs.gitlab.com/ce/api/groups.html
|
||||||
type Group struct {
|
type Group struct {
|
||||||
ID int `json:"id"`
|
ID int `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Path string `json:"path"`
|
Path string `json:"path"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
MembershipLock bool `json:"membership_lock"`
|
MembershipLock bool `json:"membership_lock"`
|
||||||
Visibility VisibilityValue `json:"visibility"`
|
Visibility VisibilityValue `json:"visibility"`
|
||||||
LFSEnabled bool `json:"lfs_enabled"`
|
LFSEnabled bool `json:"lfs_enabled"`
|
||||||
AvatarURL string `json:"avatar_url"`
|
AvatarURL string `json:"avatar_url"`
|
||||||
WebURL string `json:"web_url"`
|
WebURL string `json:"web_url"`
|
||||||
RequestAccessEnabled bool `json:"request_access_enabled"`
|
RequestAccessEnabled bool `json:"request_access_enabled"`
|
||||||
FullName string `json:"full_name"`
|
FullName string `json:"full_name"`
|
||||||
FullPath string `json:"full_path"`
|
FullPath string `json:"full_path"`
|
||||||
ParentID int `json:"parent_id"`
|
ParentID int `json:"parent_id"`
|
||||||
Projects []*Project `json:"projects"`
|
Projects []*Project `json:"projects"`
|
||||||
Statistics *StorageStatistics `json:"statistics"`
|
Statistics *StorageStatistics `json:"statistics"`
|
||||||
CustomAttributes []*CustomAttribute `json:"custom_attributes"`
|
CustomAttributes []*CustomAttribute `json:"custom_attributes"`
|
||||||
ShareWithGroupLock bool `json:"share_with_group_lock"`
|
ShareWithGroupLock bool `json:"share_with_group_lock"`
|
||||||
RequireTwoFactorAuth bool `json:"require_two_factor_authentication"`
|
RequireTwoFactorAuth bool `json:"require_two_factor_authentication"`
|
||||||
TwoFactorGracePeriod int `json:"two_factor_grace_period"`
|
TwoFactorGracePeriod int `json:"two_factor_grace_period"`
|
||||||
ProjectCreationLevel ProjectCreationLevelValue `json:"project_creation_level"`
|
ProjectCreationLevel ProjectCreationLevelValue `json:"project_creation_level"`
|
||||||
AutoDevopsEnabled bool `json:"auto_devops_enabled"`
|
AutoDevopsEnabled bool `json:"auto_devops_enabled"`
|
||||||
SubGroupCreationLevel SubGroupCreationLevelValue `json:"subgroup_creation_level"`
|
SubGroupCreationLevel SubGroupCreationLevelValue `json:"subgroup_creation_level"`
|
||||||
EmailsDisabled bool `json:"emails_disabled"`
|
EmailsDisabled bool `json:"emails_disabled"`
|
||||||
MentionsDisabled bool `json:"mentions_disabled"`
|
MentionsDisabled bool `json:"mentions_disabled"`
|
||||||
RunnersToken string `json:"runners_token"`
|
RunnersToken string `json:"runners_token"`
|
||||||
SharedProjects []*Project `json:"shared_projects"`
|
SharedProjects []*Project `json:"shared_projects"`
|
||||||
LDAPCN string `json:"ldap_cn"`
|
SharedWithGroups []struct {
|
||||||
LDAPAccess AccessLevelValue `json:"ldap_access"`
|
GroupID int `json:"group_id"`
|
||||||
LDAPGroupLinks []*LDAPGroupLink `json:"ldap_group_links"`
|
GroupName string `json:"group_name"`
|
||||||
SharedRunnersMinutesLimit int `json:"shared_runners_minutes_limit"`
|
GroupFullPath string `json:"group_full_path"`
|
||||||
ExtraSharedRunnersMinutesLimit int `json:"extra_shared_runners_minutes_limit"`
|
GroupAccessLevel int `json:"group_access_level"`
|
||||||
MarkedForDeletionOn *ISOTime `json:"marked_for_deletion_on"`
|
ExpiresAt *ISOTime `json:"expires_at"`
|
||||||
|
} `json:"shared_with_groups"`
|
||||||
|
LDAPCN string `json:"ldap_cn"`
|
||||||
|
LDAPAccess AccessLevelValue `json:"ldap_access"`
|
||||||
|
LDAPGroupLinks []*LDAPGroupLink `json:"ldap_group_links"`
|
||||||
|
SharedRunnersMinutesLimit int `json:"shared_runners_minutes_limit"`
|
||||||
|
ExtraSharedRunnersMinutesLimit int `json:"extra_shared_runners_minutes_limit"`
|
||||||
|
MarkedForDeletionOn *ISOTime `json:"marked_for_deletion_on"`
|
||||||
|
CreatedAt *time.Time `json:"created_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type LDAPGroupLink struct {
|
type LDAPGroupLink struct {
|
||||||
|
@ -85,6 +94,7 @@ type ListGroupsOptions struct {
|
||||||
SkipGroups []int `url:"skip_groups,omitempty" json:"skip_groups,omitempty"`
|
SkipGroups []int `url:"skip_groups,omitempty" json:"skip_groups,omitempty"`
|
||||||
Sort *string `url:"sort,omitempty" json:"sort,omitempty"`
|
Sort *string `url:"sort,omitempty" json:"sort,omitempty"`
|
||||||
Statistics *bool `url:"statistics,omitempty" json:"statistics,omitempty"`
|
Statistics *bool `url:"statistics,omitempty" json:"statistics,omitempty"`
|
||||||
|
TopLevelOnly *bool `url:"top_level_only,omitempty" json:"top_level_only,omitempty"`
|
||||||
WithCustomAttributes *bool `url:"with_custom_attributes,omitempty" json:"with_custom_attributes,omitempty"`
|
WithCustomAttributes *bool `url:"with_custom_attributes,omitempty" json:"with_custom_attributes,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -388,7 +398,7 @@ func (s *GroupsService) ListGroupLDAPLinks(gid interface{}, options ...RequestOp
|
||||||
type AddGroupLDAPLinkOptions struct {
|
type AddGroupLDAPLinkOptions struct {
|
||||||
CN *string `url:"cn,omitempty" json:"cn,omitempty"`
|
CN *string `url:"cn,omitempty" json:"cn,omitempty"`
|
||||||
GroupAccess *int `url:"group_access,omitempty" json:"group_access,omitempty"`
|
GroupAccess *int `url:"group_access,omitempty" json:"group_access,omitempty"`
|
||||||
Provider *string `url:"provider,omitempty" json:"provider,ommitempty"`
|
Provider *string `url:"provider,omitempty" json:"provider,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddGroupLDAPLink creates a new group LDAP link. Available only for users who
|
// AddGroupLDAPLink creates a new group LDAP link. Available only for users who
|
||||||
|
|
|
@ -0,0 +1,151 @@
|
||||||
|
//
|
||||||
|
// Copyright 2020, Serena Fang
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
//
|
||||||
|
|
||||||
|
package gitlab
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// InstanceClustersService handles communication with the
|
||||||
|
// instance clusters related methods of the GitLab API.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ee/api/instance_clusters.html
|
||||||
|
type InstanceClustersService struct {
|
||||||
|
client *Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstanceCluster represents a GitLab Instance Cluster.
|
||||||
|
//
|
||||||
|
// GitLab API docs: https://docs.gitlab.com/ee/api/instance_clusters.html
|
||||||
|
type InstanceCluster struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Domain string `json:"domain"`
|
||||||
|
CreatedAt *time.Time `json:"created_at"`
|
||||||
|
ProviderType string `json:"provider_type"`
|
||||||
|
PlatformType string `json:"platform_type"`
|
||||||
|
EnvironmentScope string `json:"environment_scope"`
|
||||||
|
ClusterType string `json:"cluster_type"`
|
||||||
|
User *User `json:"user"`
|
||||||
|
PlatformKubernetes *PlatformKubernetes `json:"platform_kubernetes"`
|
||||||
|
ManagementProject *ManagementProject `json:"management_project"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v InstanceCluster) String() string {
|
||||||
|
return Stringify(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListClusters gets a list of all instance clusters.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ee/api/instance_clusters.html#list-instance-clusters
|
||||||
|
func (s *InstanceClustersService) ListClusters(options ...RequestOptionFunc) ([]*InstanceCluster, *Response, error) {
|
||||||
|
u := "admin/clusters"
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest("GET", u, nil, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var ics []*InstanceCluster
|
||||||
|
resp, err := s.client.Do(req, &ics)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ics, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCluster gets an instance cluster.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ee/api/instance_clusters.html#get-a-single-instance-cluster
|
||||||
|
func (s *InstanceClustersService) GetCluster(cluster int, options ...RequestOptionFunc) (*InstanceCluster, *Response, error) {
|
||||||
|
u := fmt.Sprintf("admin/clusters/%d", cluster)
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest("GET", u, nil, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ic := new(InstanceCluster)
|
||||||
|
resp, err := s.client.Do(req, &ic)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ic, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddCluster adds an existing cluster to the instance.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ee/api/instance_clusters.html#add-existing-instance-cluster
|
||||||
|
func (s *InstanceClustersService) AddCluster(opt *AddClusterOptions, options ...RequestOptionFunc) (*InstanceCluster, *Response, error) {
|
||||||
|
u := "admin/clusters/add"
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest("POST", u, opt, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ic := new(InstanceCluster)
|
||||||
|
resp, err := s.client.Do(req, ic)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ic, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// EditCluster updates an existing instance cluster.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ee/api/instance_clusters.html#edit-instance-cluster
|
||||||
|
func (s *InstanceClustersService) EditCluster(cluster int, opt *EditClusterOptions, options ...RequestOptionFunc) (*InstanceCluster, *Response, error) {
|
||||||
|
u := fmt.Sprintf("admin/clusters/%d", cluster)
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest("PUT", u, opt, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ic := new(InstanceCluster)
|
||||||
|
resp, err := s.client.Do(req, ic)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ic, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteCluster deletes an existing instance cluster.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ee/api/instance_clusters.html#delete-instance-cluster
|
||||||
|
func (s *InstanceClustersService) DeleteCluster(cluster int, options ...RequestOptionFunc) (*Response, error) {
|
||||||
|
u := fmt.Sprintf("admin/clusters/%d", cluster)
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest("DELETE", u, nil, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.client.Do(req, nil)
|
||||||
|
}
|
|
@ -0,0 +1,179 @@
|
||||||
|
//
|
||||||
|
// Copyright 2018, Patrick Webster
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
//
|
||||||
|
|
||||||
|
package gitlab
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
)
|
||||||
|
|
||||||
|
// InstanceVariablesService handles communication with the
|
||||||
|
// instance level CI variables related methods of the GitLab API.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ee/api/instance_level_ci_variables.html
|
||||||
|
type InstanceVariablesService struct {
|
||||||
|
client *Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstanceVariable represents a GitLab instance level CI Variable.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ee/api/instance_level_ci_variables.html
|
||||||
|
type InstanceVariable struct {
|
||||||
|
Key string `json:"key"`
|
||||||
|
Value string `json:"value"`
|
||||||
|
VariableType VariableTypeValue `json:"variable_type"`
|
||||||
|
Protected bool `json:"protected"`
|
||||||
|
Masked bool `json:"masked"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v InstanceVariable) String() string {
|
||||||
|
return Stringify(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListInstanceVariablesOptions represents the available options for listing variables
|
||||||
|
// for an instance.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ee/api/instance_level_ci_variables.html#list-all-instance-variables
|
||||||
|
type ListInstanceVariablesOptions ListOptions
|
||||||
|
|
||||||
|
// ListVariables gets a list of all variables for an instance.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ee/api/instance_level_ci_variables.html#list-all-instance-variables
|
||||||
|
func (s *InstanceVariablesService) ListVariables(opt *ListInstanceVariablesOptions, options ...RequestOptionFunc) ([]*InstanceVariable, *Response, error) {
|
||||||
|
u := "admin/ci/variables"
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest("GET", u, opt, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var vs []*InstanceVariable
|
||||||
|
resp, err := s.client.Do(req, &vs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return vs, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetVariable gets a variable.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ee/api/instance_level_ci_variables.html#show-instance-variable-details
|
||||||
|
func (s *InstanceVariablesService) GetVariable(key string, options ...RequestOptionFunc) (*InstanceVariable, *Response, error) {
|
||||||
|
u := fmt.Sprintf("admin/ci/variables/%s", url.PathEscape(key))
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest("GET", u, nil, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
v := new(InstanceVariable)
|
||||||
|
resp, err := s.client.Do(req, v)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return v, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateInstanceVariableOptions represents the available CreateVariable()
|
||||||
|
// options.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ee/api/instance_level_ci_variables.html#create-instance-variable
|
||||||
|
type CreateInstanceVariableOptions struct {
|
||||||
|
Key *string `url:"key,omitempty" json:"key,omitempty"`
|
||||||
|
Value *string `url:"value,omitempty" json:"value,omitempty"`
|
||||||
|
VariableType *VariableTypeValue `url:"variable_type,omitempty" json:"variable_type,omitempty"`
|
||||||
|
Protected *bool `url:"protected,omitempty" json:"protected,omitempty"`
|
||||||
|
Masked *bool `url:"masked,omitempty" json:"masked,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateVariable creates a new instance level CI variable.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ee/api/instance_level_ci_variables.html#create-instance-variable
|
||||||
|
func (s *InstanceVariablesService) CreateVariable(opt *CreateInstanceVariableOptions, options ...RequestOptionFunc) (*InstanceVariable, *Response, error) {
|
||||||
|
u := "admin/ci/variables"
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest("POST", u, opt, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
v := new(InstanceVariable)
|
||||||
|
resp, err := s.client.Do(req, v)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return v, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateInstanceVariableOptions represents the available UpdateVariable()
|
||||||
|
// options.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ee/api/instance_level_ci_variables.html#update-instance-variable
|
||||||
|
type UpdateInstanceVariableOptions struct {
|
||||||
|
Value *string `url:"value,omitempty" json:"value,omitempty"`
|
||||||
|
VariableType *VariableTypeValue `url:"variable_type,omitempty" json:"variable_type,omitempty"`
|
||||||
|
Protected *bool `url:"protected,omitempty" json:"protected,omitempty"`
|
||||||
|
Masked *bool `url:"masked,omitempty" json:"masked,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateVariable updates the position of an existing
|
||||||
|
// instance level CI variable.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ee/api/instance_level_ci_variables.html#update-instance-variable
|
||||||
|
func (s *InstanceVariablesService) UpdateVariable(key string, opt *UpdateInstanceVariableOptions, options ...RequestOptionFunc) (*InstanceVariable, *Response, error) {
|
||||||
|
u := fmt.Sprintf("admin/ci/variables/%s", url.PathEscape(key))
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest("PUT", u, opt, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
v := new(InstanceVariable)
|
||||||
|
resp, err := s.client.Do(req, v)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return v, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveVariable removes an instance level CI variable.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ee/api/instance_level_ci_variables.html#remove-instance-variable
|
||||||
|
func (s *InstanceVariablesService) RemoveVariable(key string, options ...RequestOptionFunc) (*Response, error) {
|
||||||
|
u := fmt.Sprintf("admin/ci/variables/%s", url.PathEscape(key))
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest("DELETE", u, nil, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.client.Do(req, nil)
|
||||||
|
}
|
|
@ -17,6 +17,7 @@
|
||||||
package gitlab
|
package gitlab
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
@ -60,6 +61,16 @@ type IssueReferences struct {
|
||||||
Full string `json:"full"`
|
Full string `json:"full"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IssueCloser represents a closer of the issue.
|
||||||
|
type IssueCloser struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
State string `json:"state"`
|
||||||
|
WebURL string `json:"web_url"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
AvatarURL string `json:"avatar_url"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
}
|
||||||
|
|
||||||
// IssueLinks represents links of the issue.
|
// IssueLinks represents links of the issue.
|
||||||
type IssueLinks struct {
|
type IssueLinks struct {
|
||||||
Self string `json:"self"`
|
Self string `json:"self"`
|
||||||
|
@ -83,9 +94,11 @@ type Issue struct {
|
||||||
Assignee *IssueAssignee `json:"assignee"`
|
Assignee *IssueAssignee `json:"assignee"`
|
||||||
UpdatedAt *time.Time `json:"updated_at"`
|
UpdatedAt *time.Time `json:"updated_at"`
|
||||||
ClosedAt *time.Time `json:"closed_at"`
|
ClosedAt *time.Time `json:"closed_at"`
|
||||||
|
ClosedBy *IssueCloser `json:"closed_by"`
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
CreatedAt *time.Time `json:"created_at"`
|
CreatedAt *time.Time `json:"created_at"`
|
||||||
Labels Labels `json:"labels"`
|
Labels Labels `json:"labels"`
|
||||||
|
LabelDetails []*LabelDetails `json:"label_details"`
|
||||||
Upvotes int `json:"upvotes"`
|
Upvotes int `json:"upvotes"`
|
||||||
Downvotes int `json:"downvotes"`
|
Downvotes int `json:"downvotes"`
|
||||||
DueDate *ISOTime `json:"due_date"`
|
DueDate *ISOTime `json:"due_date"`
|
||||||
|
@ -100,6 +113,8 @@ type Issue struct {
|
||||||
Links *IssueLinks `json:"_links"`
|
Links *IssueLinks `json:"_links"`
|
||||||
IssueLinkID int `json:"issue_link_id"`
|
IssueLinkID int `json:"issue_link_id"`
|
||||||
MergeRequestCount int `json:"merge_requests_count"`
|
MergeRequestCount int `json:"merge_requests_count"`
|
||||||
|
EpicIssueID int `json:"epic_issue_id"`
|
||||||
|
Epic *Epic `json:"epic"`
|
||||||
TaskCompletionStatus struct {
|
TaskCompletionStatus struct {
|
||||||
Count int `json:"count"`
|
Count int `json:"count"`
|
||||||
CompletedCount int `json:"completed_count"`
|
CompletedCount int `json:"completed_count"`
|
||||||
|
@ -110,42 +125,104 @@ func (i Issue) String() string {
|
||||||
return Stringify(i)
|
return Stringify(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *Issue) UnmarshalJSON(data []byte) error {
|
||||||
|
type alias Issue
|
||||||
|
|
||||||
|
raw := make(map[string]interface{})
|
||||||
|
err := json.Unmarshal(data, &raw)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
labelDetails, ok := raw["labels"].([]interface{})
|
||||||
|
if ok && len(labelDetails) > 0 {
|
||||||
|
// We only want to change anything if we got label details.
|
||||||
|
if _, ok := labelDetails[0].(map[string]interface{}); !ok {
|
||||||
|
return json.Unmarshal(data, (*alias)(i))
|
||||||
|
}
|
||||||
|
|
||||||
|
labels := make([]interface{}, len(labelDetails))
|
||||||
|
for i, details := range labelDetails {
|
||||||
|
labels[i] = details.(map[string]interface{})["name"]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the correct values
|
||||||
|
raw["labels"] = labels
|
||||||
|
raw["label_details"] = labelDetails
|
||||||
|
|
||||||
|
data, err = json.Marshal(raw)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.Unmarshal(data, (*alias)(i))
|
||||||
|
}
|
||||||
|
|
||||||
// Labels is a custom type with specific marshaling characteristics.
|
// Labels is a custom type with specific marshaling characteristics.
|
||||||
type Labels []string
|
type Labels []string
|
||||||
|
|
||||||
// MarshalJSON implements the json.Marshaler interface.
|
// MarshalJSON implements the json.Marshaler interface.
|
||||||
func (l *Labels) MarshalJSON() ([]byte, error) {
|
func (l *Labels) MarshalJSON() ([]byte, error) {
|
||||||
|
if *l == nil {
|
||||||
|
return []byte(`null`), nil
|
||||||
|
}
|
||||||
return json.Marshal(strings.Join(*l, ","))
|
return json.Marshal(strings.Join(*l, ","))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON implements the json.Unmarshaler interface.
|
||||||
|
func (l *Labels) UnmarshalJSON(data []byte) error {
|
||||||
|
type alias Labels
|
||||||
|
if !bytes.HasPrefix(data, []byte("[")) {
|
||||||
|
data = []byte(fmt.Sprintf("[%s]", string(data)))
|
||||||
|
}
|
||||||
|
return json.Unmarshal(data, (*alias)(l))
|
||||||
|
}
|
||||||
|
|
||||||
// EncodeValues implements the query.EncodeValues interface
|
// EncodeValues implements the query.EncodeValues interface
|
||||||
func (l *Labels) EncodeValues(key string, v *url.Values) error {
|
func (l *Labels) EncodeValues(key string, v *url.Values) error {
|
||||||
v.Set(key, strings.Join(*l, ","))
|
v.Set(key, strings.Join(*l, ","))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LabelDetails represents detailed label information.
|
||||||
|
type LabelDetails struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Color string `json:"color"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
DescriptionHTML string `json:"description_html"`
|
||||||
|
TextColor string `json:"text_color"`
|
||||||
|
}
|
||||||
|
|
||||||
// ListIssuesOptions represents the available ListIssues() options.
|
// ListIssuesOptions represents the available ListIssues() options.
|
||||||
//
|
//
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/api/issues.html#list-issues
|
// GitLab API docs: https://docs.gitlab.com/ce/api/issues.html#list-issues
|
||||||
type ListIssuesOptions struct {
|
type ListIssuesOptions struct {
|
||||||
ListOptions
|
ListOptions
|
||||||
State *string `url:"state,omitempty" json:"state,omitempty"`
|
State *string `url:"state,omitempty" json:"state,omitempty"`
|
||||||
Labels Labels `url:"labels,comma,omitempty" json:"labels,omitempty"`
|
Labels Labels `url:"labels,comma,omitempty" json:"labels,omitempty"`
|
||||||
WithLabelDetails *bool `url:"with_labels_details,omitempty" json:"with_labels_details,omitempty"`
|
NotLabels Labels `url:"not[labels],comma,omitempty" json:"not[labels],omitempty"`
|
||||||
Milestone *string `url:"milestone,omitempty" json:"milestone,omitempty"`
|
WithLabelDetails *bool `url:"with_labels_details,omitempty" json:"with_labels_details,omitempty"`
|
||||||
Scope *string `url:"scope,omitempty" json:"scope,omitempty"`
|
Milestone *string `url:"milestone,omitempty" json:"milestone,omitempty"`
|
||||||
AuthorID *int `url:"author_id,omitempty" json:"author_id,omitempty"`
|
NotMilestone *string `url:"not[milestone],omitempty" json:"not[milestone],omitempty"`
|
||||||
AssigneeID *int `url:"assignee_id,omitempty" json:"assignee_id,omitempty"`
|
Scope *string `url:"scope,omitempty" json:"scope,omitempty"`
|
||||||
MyReactionEmoji *string `url:"my_reaction_emoji,omitempty" json:"my_reaction_emoji,omitempty"`
|
AuthorID *int `url:"author_id,omitempty" json:"author_id,omitempty"`
|
||||||
IIDs []int `url:"iids[],omitempty" json:"iids,omitempty"`
|
NotAuthorID []int `url:"not[author_id],omitempty" json:"not[author_id],omitempty"`
|
||||||
OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"`
|
AssigneeID *int `url:"assignee_id,omitempty" json:"assignee_id,omitempty"`
|
||||||
Sort *string `url:"sort,omitempty" json:"sort,omitempty"`
|
NotAssigneeID []int `url:"not[assignee_id],omitempty" json:"not[assignee_id],omitempty"`
|
||||||
Search *string `url:"search,omitempty" json:"search,omitempty"`
|
AssigneeUsername *string `url:"assignee_username,omitempty" json:"assignee_username,omitempty"`
|
||||||
CreatedAfter *time.Time `url:"created_after,omitempty" json:"created_after,omitempty"`
|
MyReactionEmoji *string `url:"my_reaction_emoji,omitempty" json:"my_reaction_emoji,omitempty"`
|
||||||
CreatedBefore *time.Time `url:"created_before,omitempty" json:"created_before,omitempty"`
|
NotMyReactionEmoji []string `url:"not[my_reaction_emoji],omitempty" json:"not[my_reaction_emoji],omitempty"`
|
||||||
UpdatedAfter *time.Time `url:"updated_after,omitempty" json:"updated_after,omitempty"`
|
IIDs []int `url:"iids[],omitempty" json:"iids,omitempty"`
|
||||||
UpdatedBefore *time.Time `url:"updated_before,omitempty" json:"updated_before,omitempty"`
|
OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"`
|
||||||
Confidential *bool `url:"confidential,omitempty" json:"confidential,omitempty"`
|
Sort *string `url:"sort,omitempty" json:"sort,omitempty"`
|
||||||
|
Search *string `url:"search,omitempty" json:"search,omitempty"`
|
||||||
|
CreatedAfter *time.Time `url:"created_after,omitempty" json:"created_after,omitempty"`
|
||||||
|
CreatedBefore *time.Time `url:"created_before,omitempty" json:"created_before,omitempty"`
|
||||||
|
UpdatedAfter *time.Time `url:"updated_after,omitempty" json:"updated_after,omitempty"`
|
||||||
|
UpdatedBefore *time.Time `url:"updated_before,omitempty" json:"updated_before,omitempty"`
|
||||||
|
Confidential *bool `url:"confidential,omitempty" json:"confidential,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListIssues gets all issues created by authenticated user. This function
|
// ListIssues gets all issues created by authenticated user. This function
|
||||||
|
@ -172,22 +249,30 @@ func (s *IssuesService) ListIssues(opt *ListIssuesOptions, options ...RequestOpt
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/api/issues.html#list-group-issues
|
// GitLab API docs: https://docs.gitlab.com/ce/api/issues.html#list-group-issues
|
||||||
type ListGroupIssuesOptions struct {
|
type ListGroupIssuesOptions struct {
|
||||||
ListOptions
|
ListOptions
|
||||||
State *string `url:"state,omitempty" json:"state,omitempty"`
|
State *string `url:"state,omitempty" json:"state,omitempty"`
|
||||||
Labels Labels `url:"labels,comma,omitempty" json:"labels,omitempty"`
|
Labels Labels `url:"labels,comma,omitempty" json:"labels,omitempty"`
|
||||||
IIDs []int `url:"iids[],omitempty" json:"iids,omitempty"`
|
NotLabels Labels `url:"not[labels],comma,omitempty" json:"not[labels],omitempty"`
|
||||||
Milestone *string `url:"milestone,omitempty" json:"milestone,omitempty"`
|
WithLabelDetails *bool `url:"with_labels_details,omitempty" json:"with_labels_details,omitempty"`
|
||||||
Scope *string `url:"scope,omitempty" json:"scope,omitempty"`
|
IIDs []int `url:"iids[],omitempty" json:"iids,omitempty"`
|
||||||
AuthorID *int `url:"author_id,omitempty" json:"author_id,omitempty"`
|
Milestone *string `url:"milestone,omitempty" json:"milestone,omitempty"`
|
||||||
AssigneeID *int `url:"assignee_id,omitempty" json:"assignee_id,omitempty"`
|
NotMilestone *string `url:"not[milestone],omitempty" json:"not[milestone],omitempty"`
|
||||||
MyReactionEmoji *string `url:"my_reaction_emoji,omitempty" json:"my_reaction_emoji,omitempty"`
|
Scope *string `url:"scope,omitempty" json:"scope,omitempty"`
|
||||||
OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"`
|
AuthorID *int `url:"author_id,omitempty" json:"author_id,omitempty"`
|
||||||
Sort *string `url:"sort,omitempty" json:"sort,omitempty"`
|
NotAuthorID []int `url:"not[author_id],omitempty" json:"not[author_id],omitempty"`
|
||||||
Search *string `url:"search,omitempty" json:"search,omitempty"`
|
AuthorUsername *string `url:"author_username,omitempty" json:"author_username,omitempty"`
|
||||||
In *string `url:"in,omitempty" json:"in,omitempty"`
|
AssigneeID *int `url:"assignee_id,omitempty" json:"assignee_id,omitempty"`
|
||||||
CreatedAfter *time.Time `url:"created_after,omitempty" json:"created_after,omitempty"`
|
NotAssigneeID []int `url:"not[assignee_id],omitempty" json:"not[assignee_id],omitempty"`
|
||||||
CreatedBefore *time.Time `url:"created_before,omitempty" json:"created_before,omitempty"`
|
AssigneeUsername *string `url:"assignee_username,omitempty" json:"assignee_username,omitempty"`
|
||||||
UpdatedAfter *time.Time `url:"updated_after,omitempty" json:"updated_after,omitempty"`
|
MyReactionEmoji *string `url:"my_reaction_emoji,omitempty" json:"my_reaction_emoji,omitempty"`
|
||||||
UpdatedBefore *time.Time `url:"updated_before,omitempty" json:"updated_before,omitempty"`
|
NotMyReactionEmoji []string `url:"not[my_reaction_emoji],omitempty" json:"not[my_reaction_emoji],omitempty"`
|
||||||
|
OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"`
|
||||||
|
Sort *string `url:"sort,omitempty" json:"sort,omitempty"`
|
||||||
|
Search *string `url:"search,omitempty" json:"search,omitempty"`
|
||||||
|
In *string `url:"in,omitempty" json:"in,omitempty"`
|
||||||
|
CreatedAfter *time.Time `url:"created_after,omitempty" json:"created_after,omitempty"`
|
||||||
|
CreatedBefore *time.Time `url:"created_before,omitempty" json:"created_before,omitempty"`
|
||||||
|
UpdatedAfter *time.Time `url:"updated_after,omitempty" json:"updated_after,omitempty"`
|
||||||
|
UpdatedBefore *time.Time `url:"updated_before,omitempty" json:"updated_before,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListGroupIssues gets a list of group issues. This function accepts
|
// ListGroupIssues gets a list of group issues. This function accepts
|
||||||
|
@ -220,24 +305,30 @@ func (s *IssuesService) ListGroupIssues(pid interface{}, opt *ListGroupIssuesOpt
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/api/issues.html#list-project-issues
|
// GitLab API docs: https://docs.gitlab.com/ce/api/issues.html#list-project-issues
|
||||||
type ListProjectIssuesOptions struct {
|
type ListProjectIssuesOptions struct {
|
||||||
ListOptions
|
ListOptions
|
||||||
IIDs []int `url:"iids[],omitempty" json:"iids,omitempty"`
|
IIDs []int `url:"iids[],omitempty" json:"iids,omitempty"`
|
||||||
State *string `url:"state,omitempty" json:"state,omitempty"`
|
State *string `url:"state,omitempty" json:"state,omitempty"`
|
||||||
Labels Labels `url:"labels,comma,omitempty" json:"labels,omitempty"`
|
Labels Labels `url:"labels,comma,omitempty" json:"labels,omitempty"`
|
||||||
WithLabelDetails *bool `url:"with_labels_details,omitempty" json:"with_labels_details,omitempty"`
|
NotLabels Labels `url:"not[labels],comma,omitempty" json:"not[labels],omitempty"`
|
||||||
Milestone *string `url:"milestone,omitempty" json:"milestone,omitempty"`
|
WithLabelDetails *bool `url:"with_labels_details,omitempty" json:"with_labels_details,omitempty"`
|
||||||
Scope *string `url:"scope,omitempty" json:"scope,omitempty"`
|
Milestone *string `url:"milestone,omitempty" json:"milestone,omitempty"`
|
||||||
AuthorID *int `url:"author_id,omitempty" json:"author_id,omitempty"`
|
NotMilestone []string `url:"not[milestone],omitempty" json:"not[milestone],omitempty"`
|
||||||
AssigneeID *int `url:"assignee_id,omitempty" json:"assignee_id,omitempty"`
|
Scope *string `url:"scope,omitempty" json:"scope,omitempty"`
|
||||||
MyReactionEmoji *string `url:"my_reaction_emoji,omitempty" json:"my_reaction_emoji,omitempty"`
|
AuthorID *int `url:"author_id,omitempty" json:"author_id,omitempty"`
|
||||||
OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"`
|
NotAuthorID []int `url:"not[author_id],omitempty" json:"not[author_id],omitempty"`
|
||||||
Sort *string `url:"sort,omitempty" json:"sort,omitempty"`
|
AssigneeID *int `url:"assignee_id,omitempty" json:"assignee_id,omitempty"`
|
||||||
Search *string `url:"search,omitempty" json:"search,omitempty"`
|
NotAssigneeID []int `url:"not[assignee_id],omitempty" json:"not[assignee_id],omitempty"`
|
||||||
In *string `url:"in,omitempty" json:"in,omitempty"`
|
AssigneeUsername *string `url:"assignee_username,omitempty" json:"assignee_username,omitempty"`
|
||||||
CreatedAfter *time.Time `url:"created_after,omitempty" json:"created_after,omitempty"`
|
MyReactionEmoji *string `url:"my_reaction_emoji,omitempty" json:"my_reaction_emoji,omitempty"`
|
||||||
CreatedBefore *time.Time `url:"created_before,omitempty" json:"created_before,omitempty"`
|
NotMyReactionEmoji []string `url:"not[my_reaction_emoji],omitempty" json:"not[my_reaction_emoji],omitempty"`
|
||||||
UpdatedAfter *time.Time `url:"updated_after,omitempty" json:"updated_after,omitempty"`
|
OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"`
|
||||||
UpdatedBefore *time.Time `url:"updated_before,omitempty" json:"updated_before,omitempty"`
|
Sort *string `url:"sort,omitempty" json:"sort,omitempty"`
|
||||||
Confidential *bool `url:"confidential,omitempty" json:"confidential,omitempty"`
|
Search *string `url:"search,omitempty" json:"search,omitempty"`
|
||||||
|
In *string `url:"in,omitempty" json:"in,omitempty"`
|
||||||
|
CreatedAfter *time.Time `url:"created_after,omitempty" json:"created_after,omitempty"`
|
||||||
|
CreatedBefore *time.Time `url:"created_before,omitempty" json:"created_before,omitempty"`
|
||||||
|
UpdatedAfter *time.Time `url:"updated_after,omitempty" json:"updated_after,omitempty"`
|
||||||
|
UpdatedBefore *time.Time `url:"updated_before,omitempty" json:"updated_before,omitempty"`
|
||||||
|
Confidential *bool `url:"confidential,omitempty" json:"confidential,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListProjectIssues gets a list of project issues. This function accepts
|
// ListProjectIssues gets a list of project issues. This function accepts
|
||||||
|
@ -341,6 +432,8 @@ type UpdateIssueOptions struct {
|
||||||
AssigneeIDs []int `url:"assignee_ids,omitempty" json:"assignee_ids,omitempty"`
|
AssigneeIDs []int `url:"assignee_ids,omitempty" json:"assignee_ids,omitempty"`
|
||||||
MilestoneID *int `url:"milestone_id,omitempty" json:"milestone_id,omitempty"`
|
MilestoneID *int `url:"milestone_id,omitempty" json:"milestone_id,omitempty"`
|
||||||
Labels *Labels `url:"labels,comma,omitempty" json:"labels,omitempty"`
|
Labels *Labels `url:"labels,comma,omitempty" json:"labels,omitempty"`
|
||||||
|
AddLabels *Labels `url:"add_labels,comma,omitempty" json:"add_labels,omitempty"`
|
||||||
|
RemoveLabels *Labels `url:"remove_labels,comma,omitempty" json:"remove_labels,omitempty"`
|
||||||
StateEvent *string `url:"state_event,omitempty" json:"state_event,omitempty"`
|
StateEvent *string `url:"state_event,omitempty" json:"state_event,omitempty"`
|
||||||
UpdatedAt *time.Time `url:"updated_at,omitempty" json:"updated_at,omitempty"`
|
UpdatedAt *time.Time `url:"updated_at,omitempty" json:"updated_at,omitempty"`
|
||||||
DueDate *ISOTime `url:"due_date,omitempty" json:"due_date,omitempty"`
|
DueDate *ISOTime `url:"due_date,omitempty" json:"due_date,omitempty"`
|
||||||
|
@ -585,3 +678,28 @@ func (s *IssuesService) ResetSpentTime(pid interface{}, issue int, options ...Re
|
||||||
func (s *IssuesService) GetTimeSpent(pid interface{}, issue int, options ...RequestOptionFunc) (*TimeStats, *Response, error) {
|
func (s *IssuesService) GetTimeSpent(pid interface{}, issue int, options ...RequestOptionFunc) (*TimeStats, *Response, error) {
|
||||||
return s.timeStats.getTimeSpent(pid, "issues", issue, options...)
|
return s.timeStats.getTimeSpent(pid, "issues", issue, options...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetParticipants gets a list of issue participants.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ce/api/issues.html#participants-on-issues
|
||||||
|
func (s *IssuesService) GetParticipants(pid interface{}, issue int, options ...RequestOptionFunc) ([]*BasicUser, *Response, error) {
|
||||||
|
project, err := parseID(pid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
u := fmt.Sprintf("projects/%s/issues/%d/participants", pathEscape(project), issue)
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest("GET", u, nil, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var bu []*BasicUser
|
||||||
|
resp, err := s.client.Do(req, &bu)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return bu, resp, err
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,186 @@
|
||||||
|
//
|
||||||
|
// Copyright 2017, Sander van Harmelen
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
//
|
||||||
|
|
||||||
|
package gitlab
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IssuesStatisticsService handles communication with the issues statistics
|
||||||
|
// related methods of the GitLab API.
|
||||||
|
//
|
||||||
|
// GitLab API docs: https://docs.gitlab.com/ee/api/issues_statistics.html
|
||||||
|
type IssuesStatisticsService struct {
|
||||||
|
client *Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// IssuesStatistics represents a GitLab issues statistic.
|
||||||
|
//
|
||||||
|
// GitLab API docs: https://docs.gitlab.com/ee/api/issues_statistics.html
|
||||||
|
type IssuesStatistics struct {
|
||||||
|
Statistics struct {
|
||||||
|
Counts struct {
|
||||||
|
All int `json:"all"`
|
||||||
|
Closed int `json:"closed"`
|
||||||
|
Opened int `json:"opened"`
|
||||||
|
} `json:"counts"`
|
||||||
|
} `json:"statistics"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n IssuesStatistics) String() string {
|
||||||
|
return Stringify(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetIssuesStatisticsOptions represents the available GetIssuesStatistics() options.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ee/api/issues_statistics.html#get-issues-statistics
|
||||||
|
type GetIssuesStatisticsOptions struct {
|
||||||
|
Labels *Labels `url:"labels,omitempty" json:"labels,omitempty"`
|
||||||
|
Milestone *Milestone `url:"milestone,omitempty" json:"milestone,omitempty"`
|
||||||
|
Scope *string `url:"scope,omitempty" json:"scope,omitempty"`
|
||||||
|
AuthorID *int `url:"author_id,omitempty" json:"author_id,omitempty"`
|
||||||
|
AuthorUsername *string `url:"author_username,omitempty" json:"author_username,omitempty"`
|
||||||
|
AssigneeID *int `url:"assignee_id,omitempty" json:"assignee_id,omitempty"`
|
||||||
|
AssigneeUsername []string `url:"assignee_username,omitempty" json:"assignee_username,omitempty"`
|
||||||
|
MyReactionEmoji *string `url:"my_reaction_emoji,omitempty" json:"my_reaction_emoji,omitempty"`
|
||||||
|
IIDs []int `url:"iids,omitempty" json:"iids,omitempty"`
|
||||||
|
Search *string `url:"search,omitempty" json:"search,omitempty"`
|
||||||
|
In *string `url:"in,omitempty" json:"in,omitempty"`
|
||||||
|
CreatedAfter *time.Time `url:"created_after,omitempty" json:"created_after,omitempty"`
|
||||||
|
CreatedBefore *time.Time `url:"created_before,omitempty" json:"created_before,omitempty"`
|
||||||
|
UpdatedAfter *time.Time `url:"updated_after,omitempty" json:"updated_after,omitempty"`
|
||||||
|
UpdatedBefore *time.Time `url:"updated_before,omitempty" json:"updated_before,omitempty"`
|
||||||
|
Confidential *bool `url:"confidential,omitempty" json:"confidential,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetIssuesStatistics gets issues statistics on all issues the authenticated
|
||||||
|
// user has access to.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ee/api/issues_statistics.html#get-issues-statistics
|
||||||
|
func (s *IssuesStatisticsService) GetIssuesStatistics(opt *GetIssuesStatisticsOptions, options ...RequestOptionFunc) (*IssuesStatistics, *Response, error) {
|
||||||
|
req, err := s.client.NewRequest("GET", "issues_statistics", opt, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
is := new(IssuesStatistics)
|
||||||
|
resp, err := s.client.Do(req, is)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return is, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetGroupIssuesStatisticsOptions represents the available GetGroupIssuesStatistics()
|
||||||
|
// options.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ee/api/issues_statistics.html#get-group-issues-statistics
|
||||||
|
type GetGroupIssuesStatisticsOptions struct {
|
||||||
|
Labels *Labels `url:"labels,omitempty" json:"labels,omitempty"`
|
||||||
|
IIDs []int `url:"iids,omitempty" json:"iids,omitempty"`
|
||||||
|
Milestone *Milestone `url:"milestone,omitempty" json:"milestone,omitempty"`
|
||||||
|
Scope *string `url:"scope,omitempty" json:"scope,omitempty"`
|
||||||
|
AuthorID *int `url:"author_id,omitempty" json:"author_id,omitempty"`
|
||||||
|
AuthorUsername *string `url:"author_username,omitempty" json:"author_username,omitempty"`
|
||||||
|
AssigneeID *int `url:"assignee_id,omitempty" json:"assignee_id,omitempty"`
|
||||||
|
AssigneeUsername []string `url:"assignee_username,omitempty" json:"assignee_username,omitempty"`
|
||||||
|
MyReactionEmoji *string `url:"my_reaction_emoji,omitempty" json:"my_reaction_emoji,omitempty"`
|
||||||
|
Search *string `url:"search,omitempty" json:"search,omitempty"`
|
||||||
|
CreatedAfter *time.Time `url:"created_after,omitempty" json:"created_after,omitempty"`
|
||||||
|
CreatedBefore *time.Time `url:"created_before,omitempty" json:"created_before,omitempty"`
|
||||||
|
UpdatedAfter *time.Time `url:"updated_after,omitempty" json:"updated_after,omitempty"`
|
||||||
|
UpdatedBefore *time.Time `url:"updated_before,omitempty" json:"updated_before,omitempty"`
|
||||||
|
Confidential *bool `url:"confidential,omitempty" json:"confidential,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetGroupIssuesStatistics gets issues count statistics for given group.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ee/api/issues_statistics.html#get-group-issues-statistics
|
||||||
|
func (s *IssuesStatisticsService) GetGroupIssuesStatistics(gid interface{}, opt *GetGroupIssuesStatisticsOptions, options ...RequestOptionFunc) (*IssuesStatistics, *Response, error) {
|
||||||
|
group, err := parseID(gid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
u := fmt.Sprintf("groups/%s/issues_statistics", pathEscape(group))
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest("GET", u, opt, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
is := new(IssuesStatistics)
|
||||||
|
resp, err := s.client.Do(req, is)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return is, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetProjectIssuesStatisticsOptions represents the available
|
||||||
|
// GetProjectIssuesStatistics() options.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ee/api/issues_statistics.html#get-project-issues-statistics
|
||||||
|
type GetProjectIssuesStatisticsOptions struct {
|
||||||
|
IIDs []int `url:"iids,omitempty" json:"iids,omitempty"`
|
||||||
|
Labels *Labels `url:"labels,omitempty" json:"labels,omitempty"`
|
||||||
|
Milestone *Milestone `url:"milestone,omitempty" json:"milestone,omitempty"`
|
||||||
|
Scope *string `url:"scope,omitempty" json:"scope,omitempty"`
|
||||||
|
AuthorID *int `url:"author_id,omitempty" json:"author_id,omitempty"`
|
||||||
|
AuthorUsername *string `url:"author_username,omitempty" json:"author_username,omitempty"`
|
||||||
|
AssigneeID *int `url:"assignee_id,omitempty" json:"assignee_id,omitempty"`
|
||||||
|
AssigneeUsername []string `url:"assignee_username,omitempty" json:"assignee_username,omitempty"`
|
||||||
|
MyReactionEmoji *string `url:"my_reaction_emoji,omitempty" json:"my_reaction_emoji,omitempty"`
|
||||||
|
Search *string `url:"search,omitempty" json:"search,omitempty"`
|
||||||
|
CreatedAfter *time.Time `url:"created_after,omitempty" json:"created_after,omitempty"`
|
||||||
|
CreatedBefore *time.Time `url:"created_before,omitempty" json:"created_before,omitempty"`
|
||||||
|
UpdatedAfter *time.Time `url:"updated_after,omitempty" json:"updated_after,omitempty"`
|
||||||
|
UpdatedBefore *time.Time `url:"updated_before,omitempty" json:"updated_before,omitempty"`
|
||||||
|
Confidential *bool `url:"confidential,omitempty" json:"confidential,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetProjectIssuesStatistics gets issues count statistics for given project.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ee/api/issues_statistics.html#get-project-issues-statistics
|
||||||
|
func (s *IssuesStatisticsService) GetProjectIssuesStatistics(pid interface{}, opt *GetProjectIssuesStatisticsOptions, options ...RequestOptionFunc) (*IssuesStatistics, *Response, error) {
|
||||||
|
project, err := parseID(pid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
u := fmt.Sprintf("projects/%s/issues_statistics", pathEscape(project))
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest("GET", u, opt, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
is := new(IssuesStatistics)
|
||||||
|
resp, err := s.client.Do(req, is)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return is, resp, err
|
||||||
|
}
|
|
@ -73,7 +73,11 @@ func (l Label) String() string {
|
||||||
// ListLabelsOptions represents the available ListLabels() options.
|
// ListLabelsOptions represents the available ListLabels() options.
|
||||||
//
|
//
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/api/labels.html#list-labels
|
// GitLab API docs: https://docs.gitlab.com/ce/api/labels.html#list-labels
|
||||||
type ListLabelsOptions ListOptions
|
type ListLabelsOptions struct {
|
||||||
|
ListOptions
|
||||||
|
WithCounts *bool `url:"with_counts,omitempty" json:"with_counts,omitempty"`
|
||||||
|
IncludeAncestorGroups *bool `url:"include_ancestor_groups,omitempty" json:"include_ancestor_groups,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
// ListLabels gets all labels for given project.
|
// ListLabels gets all labels for given project.
|
||||||
//
|
//
|
||||||
|
@ -99,6 +103,34 @@ func (s *LabelsService) ListLabels(pid interface{}, opt *ListLabelsOptions, opti
|
||||||
return l, resp, err
|
return l, resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetLabel get a single label for a given project.
|
||||||
|
//
|
||||||
|
// GitLab API docs: https://docs.gitlab.com/ce/api/labels.html#get-a-single-project-label
|
||||||
|
func (s *LabelsService) GetLabel(pid interface{}, labelID interface{}, options ...RequestOptionFunc) (*Label, *Response, error) {
|
||||||
|
project, err := parseID(pid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
label, err := parseID(labelID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
u := fmt.Sprintf("projects/%s/labels/%s", pathEscape(project), label)
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest("GET", u, nil, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var l *Label
|
||||||
|
resp, err := s.client.Do(req, &l)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return l, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
// CreateLabelOptions represents the available CreateLabel() options.
|
// CreateLabelOptions represents the available CreateLabel() options.
|
||||||
//
|
//
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/api/labels.html#create-a-new-label
|
// GitLab API docs: https://docs.gitlab.com/ce/api/labels.html#create-a-new-label
|
||||||
|
@ -248,3 +280,26 @@ func (s *LabelsService) UnsubscribeFromLabel(pid interface{}, labelID interface{
|
||||||
|
|
||||||
return s.client.Do(req, nil)
|
return s.client.Do(req, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PromoteLabel Promotes a project label to a group label.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ce/api/labels.html#promote-a-project-label-to-a-group-label
|
||||||
|
func (s *LabelsService) PromoteLabel(pid interface{}, labelID interface{}, options ...RequestOptionFunc) (*Response, error) {
|
||||||
|
project, err := parseID(pid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
label, err := parseID(labelID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
u := fmt.Sprintf("projects/%s/labels/%s/promote", pathEscape(project), label)
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest("PUT", u, nil, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.client.Do(req, nil)
|
||||||
|
}
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
|
|
||||||
package gitlab
|
package gitlab
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
// LicenseService handles communication with the license
|
// LicenseService handles communication with the license
|
||||||
// related methods of the GitLab API.
|
// related methods of the GitLab API.
|
||||||
//
|
//
|
||||||
|
@ -30,17 +32,30 @@ type LicenseService struct {
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ee/api/license.html
|
// https://docs.gitlab.com/ee/api/license.html
|
||||||
type License struct {
|
type License struct {
|
||||||
StartsAt *ISOTime `json:"starts_at"`
|
ID int `json:"id"`
|
||||||
ExpiresAt *ISOTime `json:"expires_at"`
|
Plan string `json:"plan"`
|
||||||
Licensee struct {
|
CreatedAt *time.Time `json:"created_at"`
|
||||||
|
StartsAt *ISOTime `json:"starts_at"`
|
||||||
|
ExpiresAt *ISOTime `json:"expires_at"`
|
||||||
|
HistoricalMax int `json:"historical_max"`
|
||||||
|
MaximumUserCount int `json:"maximum_user_count"`
|
||||||
|
Expired bool `json:"expired"`
|
||||||
|
Overage int `json:"overage"`
|
||||||
|
UserLimit int `json:"user_limit"`
|
||||||
|
ActiveUsers int `json:"active_users"`
|
||||||
|
Licensee struct {
|
||||||
Name string `json:"Name"`
|
Name string `json:"Name"`
|
||||||
Company string `json:"Company"`
|
Company string `json:"Company"`
|
||||||
Email string `json:"Email"`
|
Email string `json:"Email"`
|
||||||
} `json:"licensee"`
|
} `json:"licensee"`
|
||||||
UserLimit int `json:"user_limit"`
|
// Add on codes that may occur in legacy licenses that don't have a plan yet.
|
||||||
ActiveUsers int `json:"active_users"`
|
// https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/app/models/license.rb
|
||||||
AddOns struct {
|
AddOns struct {
|
||||||
GitLabFileLocks int `json:"GitLabFileLocks"`
|
GitLabAuditorUser int `json:"GitLab_Auditor_User"`
|
||||||
|
GitLabDeployBoard int `json:"GitLab_DeployBoard"`
|
||||||
|
GitLabFileLocks int `json:"GitLab_FileLocks"`
|
||||||
|
GitLabGeo int `json:"GitLab_Geo"`
|
||||||
|
GitLabServiceDesk int `json:"GitLab_ServiceDesk"`
|
||||||
} `json:"add_ons"`
|
} `json:"add_ons"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -240,7 +240,7 @@ type ListProjectMergeRequestsOptions struct {
|
||||||
Sort *string `url:"sort,omitempty" json:"sort,omitempty"`
|
Sort *string `url:"sort,omitempty" json:"sort,omitempty"`
|
||||||
Milestone *string `url:"milestone,omitempty" json:"milestone,omitempty"`
|
Milestone *string `url:"milestone,omitempty" json:"milestone,omitempty"`
|
||||||
View *string `url:"view,omitempty" json:"view,omitempty"`
|
View *string `url:"view,omitempty" json:"view,omitempty"`
|
||||||
Labels *Labels `url:"labels,omitempty" json:"labels,omitempty"`
|
Labels Labels `url:"labels,comma,omitempty" json:"labels,omitempty"`
|
||||||
CreatedAfter *time.Time `url:"created_after,omitempty" json:"created_after,omitempty"`
|
CreatedAfter *time.Time `url:"created_after,omitempty" json:"created_after,omitempty"`
|
||||||
CreatedBefore *time.Time `url:"created_before,omitempty" json:"created_before,omitempty"`
|
CreatedBefore *time.Time `url:"created_before,omitempty" json:"created_before,omitempty"`
|
||||||
UpdatedAfter *time.Time `url:"updated_after,omitempty" json:"updated_after,omitempty"`
|
UpdatedAfter *time.Time `url:"updated_after,omitempty" json:"updated_after,omitempty"`
|
||||||
|
@ -399,6 +399,31 @@ func (s *MergeRequestsService) GetMergeRequestChanges(pid interface{}, mergeRequ
|
||||||
return m, resp, err
|
return m, resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetMergeRequestParticipants gets a list of merge request participants.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ee/api/merge_requests.html#get-single-mr-participants
|
||||||
|
func (s *MergeRequestsService) GetMergeRequestParticipants(pid interface{}, mergeRequest int, options ...RequestOptionFunc) ([]*BasicUser, *Response, error) {
|
||||||
|
project, err := parseID(pid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
u := fmt.Sprintf("projects/%s/merge_requests/%d/participants", pathEscape(project), mergeRequest)
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest("GET", u, nil, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var ps []*BasicUser
|
||||||
|
resp, err := s.client.Do(req, &ps)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ps, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
// ListMergeRequestPipelines gets all pipelines for the provided merge request.
|
// ListMergeRequestPipelines gets all pipelines for the provided merge request.
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
|
@ -424,6 +449,31 @@ func (s *MergeRequestsService) ListMergeRequestPipelines(pid interface{}, mergeR
|
||||||
return p, resp, err
|
return p, resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateMergeRequestPipeline creates a new pipeline for a merge request.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ce/api/merge_requests.html#create-mr-pipeline
|
||||||
|
func (s *MergeRequestsService) CreateMergeRequestPipeline(pid interface{}, mergeRequest int, options ...RequestOptionFunc) (*PipelineInfo, *Response, error) {
|
||||||
|
project, err := parseID(pid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
u := fmt.Sprintf("projects/%s/merge_requests/%d/pipelines", pathEscape(project), mergeRequest)
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest("POST", u, nil, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
p := new(PipelineInfo)
|
||||||
|
resp, err := s.client.Do(req, p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return p, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
// GetIssuesClosedOnMergeOptions represents the available GetIssuesClosedOnMerge()
|
// GetIssuesClosedOnMergeOptions represents the available GetIssuesClosedOnMerge()
|
||||||
// options.
|
// options.
|
||||||
//
|
//
|
||||||
|
|
|
@ -41,6 +41,7 @@ type Milestone struct {
|
||||||
StartDate *ISOTime `json:"start_date"`
|
StartDate *ISOTime `json:"start_date"`
|
||||||
DueDate *ISOTime `json:"due_date"`
|
DueDate *ISOTime `json:"due_date"`
|
||||||
State string `json:"state"`
|
State string `json:"state"`
|
||||||
|
WebURL string `json:"web_url"`
|
||||||
UpdatedAt *time.Time `json:"updated_at"`
|
UpdatedAt *time.Time `json:"updated_at"`
|
||||||
CreatedAt *time.Time `json:"created_at"`
|
CreatedAt *time.Time `json:"created_at"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -230,6 +230,25 @@ func (s *PipelineSchedulesService) DeletePipelineSchedule(pid interface{}, sched
|
||||||
return s.client.Do(req, nil)
|
return s.client.Do(req, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RunPipelineSchedule triggers a new scheduled pipeline to run immediately.
|
||||||
|
//
|
||||||
|
// Gitlab API docs:
|
||||||
|
// https://docs.gitlab.com/ce/api/pipeline_schedules.html#run-a-scheduled-pipeline-immediately
|
||||||
|
func (s *PipelineSchedulesService) RunPipelineSchedule(pid interface{}, schedule int, options ...RequestOptionFunc) (*Response, error) {
|
||||||
|
project, err := parseID(pid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
u := fmt.Sprintf("projects/%s/pipeline_schedules/%d/play", pathEscape(project), schedule)
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest("POST", u, nil, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.client.Do(req, nil)
|
||||||
|
}
|
||||||
|
|
||||||
// CreatePipelineScheduleVariableOptions represents the available
|
// CreatePipelineScheduleVariableOptions represents the available
|
||||||
// CreatePipelineScheduleVariable() options.
|
// CreatePipelineScheduleVariable() options.
|
||||||
//
|
//
|
||||||
|
|
|
@ -109,7 +109,7 @@ type ListProjectPipelinesOptions struct {
|
||||||
Name *string `url:"name,omitempty" json:"name,omitempty"`
|
Name *string `url:"name,omitempty" json:"name,omitempty"`
|
||||||
Username *string `url:"username,omitempty" json:"username,omitempty"`
|
Username *string `url:"username,omitempty" json:"username,omitempty"`
|
||||||
UpdatedAfter *time.Time `url:"updated_after,omitempty" json:"updated_after,omitempty"`
|
UpdatedAfter *time.Time `url:"updated_after,omitempty" json:"updated_after,omitempty"`
|
||||||
UpdatedBefore *time.Time `url:"update_before,omitempty" json:"updated_before,omitempty"`
|
UpdatedBefore *time.Time `url:"updated_before,omitempty" json:"updated_before,omitempty"`
|
||||||
OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"`
|
OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"`
|
||||||
Sort *string `url:"sort,omitempty" json:"sort,omitempty"`
|
Sort *string `url:"sort,omitempty" json:"sort,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,145 @@
|
||||||
|
//
|
||||||
|
// Copyright 2017, Sander van Harmelen
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
//
|
||||||
|
|
||||||
|
package gitlab
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ProjectMirrorService handles communication with the project mirror
|
||||||
|
// related methods of the GitLab API.
|
||||||
|
//
|
||||||
|
// GitLAb API docs: https://docs.gitlab.com/ce/api/remote_mirrors.html
|
||||||
|
type ProjectMirrorService struct {
|
||||||
|
client *Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProjectMirror represents a project mirror configuration.
|
||||||
|
//
|
||||||
|
// GitLAb API docs: https://docs.gitlab.com/ce/api/remote_mirrors.html
|
||||||
|
type ProjectMirror struct {
|
||||||
|
Enabled bool `json:"enabled"`
|
||||||
|
ID int `json:"id"`
|
||||||
|
LastError string `json:"last_error"`
|
||||||
|
LastSuccessfulUpdateAt *time.Time `json:"last_successful_update_at"`
|
||||||
|
LastUpdateAt *time.Time `json:"last_update_at"`
|
||||||
|
LastUpdateStartedAt *time.Time `json:"last_update_started_at"`
|
||||||
|
OnlyProtectedBranches bool `json:"only_protected_branches"`
|
||||||
|
KeepDivergentRefs bool `json:"keep_divergent_refs"`
|
||||||
|
UpdateStatus string `json:"update_status"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListProjectMirror gets a list of mirrors configured on the project.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ce/api/remote_mirrors.html#list-a-projects-remote-mirrors
|
||||||
|
func (s *ProjectMirrorService) ListProjectMirror(pid interface{}, options ...RequestOptionFunc) ([]*ProjectMirror, *Response, error) {
|
||||||
|
project, err := parseID(pid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
u := fmt.Sprintf("projects/%s/remote_mirrors", pathEscape(project))
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest("GET", u, nil, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var pm []*ProjectMirror
|
||||||
|
resp, err := s.client.Do(req, &pm)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return pm, resp, err
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddProjectMirrorOptions contains the properties requires to create
|
||||||
|
// a new project mirror.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ce/api/remote_mirrors.html#create-a-remote-mirror
|
||||||
|
type AddProjectMirrorOptions struct {
|
||||||
|
URL *string `url:"url,omitempty" json:"url,omitempty"`
|
||||||
|
Enabled *bool `url:"enabled,omitempty" json:"enabled,omitempty"`
|
||||||
|
OnlyProtectedBranches *bool `url:"only_protected_branches,omitempty" json:"only_protected_branches,omitempty"`
|
||||||
|
KeepDivergentRefs *bool `url:"keep_divergent_refs,omitempty" json:"keep_divergent_refs,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddProjectMirror creates a new mirror on the project.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ce/api/remote_mirrors.html#create-a-remote-mirror
|
||||||
|
func (s *ProjectMirrorService) AddProjectMirror(pid interface{}, opt *AddProjectMirrorOptions, options ...RequestOptionFunc) (*ProjectMirror, *Response, error) {
|
||||||
|
project, err := parseID(pid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
u := fmt.Sprintf("projects/%s/remote_mirrors", pathEscape(project))
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest("POST", u, opt, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
pm := new(ProjectMirror)
|
||||||
|
resp, err := s.client.Do(req, pm)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return pm, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// EditProjectMirrorOptions contains the properties requires to edit
|
||||||
|
// an existing project mirror.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ce/api/remote_mirrors.html#update-a-remote-mirrors-attributes
|
||||||
|
type EditProjectMirrorOptions struct {
|
||||||
|
Enabled *bool `url:"enabled,omitempty" json:"enabled,omitempty"`
|
||||||
|
OnlyProtectedBranches *bool `url:"only_protected_branches,omitempty" json:"only_protected_branches,omitempty"`
|
||||||
|
KeepDivergentRefs *bool `url:"keep_divergent_refs,omitempty" json:"keep_divergent_refs,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// EditProjectMirror updates a project team member to a specified access level..
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ce/api/remote_mirrors.html#update-a-remote-mirrors-attributes
|
||||||
|
func (s *ProjectMirrorService) EditProjectMirror(pid interface{}, mirror int, opt *EditProjectMirrorOptions, options ...RequestOptionFunc) (*ProjectMirror, *Response, error) {
|
||||||
|
project, err := parseID(pid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
u := fmt.Sprintf("projects/%s/remote_mirrors/%d", pathEscape(project), mirror)
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest("PUT", u, opt, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
pm := new(ProjectMirror)
|
||||||
|
resp, err := s.client.Do(req, pm)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return pm, resp, err
|
||||||
|
}
|
|
@ -91,7 +91,7 @@ type CreateProjectSnippetOptions struct {
|
||||||
Title *string `url:"title,omitempty" json:"title,omitempty"`
|
Title *string `url:"title,omitempty" json:"title,omitempty"`
|
||||||
FileName *string `url:"file_name,omitempty" json:"file_name,omitempty"`
|
FileName *string `url:"file_name,omitempty" json:"file_name,omitempty"`
|
||||||
Description *string `url:"description,omitempty" json:"description,omitempty"`
|
Description *string `url:"description,omitempty" json:"description,omitempty"`
|
||||||
Code *string `url:"code,omitempty" json:"code,omitempty"`
|
Content *string `url:"content,omitempty" json:"content,omitempty"`
|
||||||
Visibility *VisibilityValue `url:"visibility,omitempty" json:"visibility,omitempty"`
|
Visibility *VisibilityValue `url:"visibility,omitempty" json:"visibility,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,7 +129,7 @@ type UpdateProjectSnippetOptions struct {
|
||||||
Title *string `url:"title,omitempty" json:"title,omitempty"`
|
Title *string `url:"title,omitempty" json:"title,omitempty"`
|
||||||
FileName *string `url:"file_name,omitempty" json:"file_name,omitempty"`
|
FileName *string `url:"file_name,omitempty" json:"file_name,omitempty"`
|
||||||
Description *string `url:"description,omitempty" json:"description,omitempty"`
|
Description *string `url:"description,omitempty" json:"description,omitempty"`
|
||||||
Code *string `url:"code,omitempty" json:"code,omitempty"`
|
Content *string `url:"content,omitempty" json:"content,omitempty"`
|
||||||
Visibility *VisibilityValue `url:"visibility,omitempty" json:"visibility,omitempty"`
|
Visibility *VisibilityValue `url:"visibility,omitempty" json:"visibility,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,57 +38,68 @@ type ProjectsService struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html
|
// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html
|
||||||
type Project struct {
|
type Project struct {
|
||||||
ID int `json:"id"`
|
ID int `json:"id"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
DefaultBranch string `json:"default_branch"`
|
DefaultBranch string `json:"default_branch"`
|
||||||
Public bool `json:"public"`
|
Public bool `json:"public"`
|
||||||
Visibility VisibilityValue `json:"visibility"`
|
Visibility VisibilityValue `json:"visibility"`
|
||||||
SSHURLToRepo string `json:"ssh_url_to_repo"`
|
SSHURLToRepo string `json:"ssh_url_to_repo"`
|
||||||
HTTPURLToRepo string `json:"http_url_to_repo"`
|
HTTPURLToRepo string `json:"http_url_to_repo"`
|
||||||
WebURL string `json:"web_url"`
|
WebURL string `json:"web_url"`
|
||||||
ReadmeURL string `json:"readme_url"`
|
ReadmeURL string `json:"readme_url"`
|
||||||
TagList []string `json:"tag_list"`
|
TagList []string `json:"tag_list"`
|
||||||
Owner *User `json:"owner"`
|
Owner *User `json:"owner"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
NameWithNamespace string `json:"name_with_namespace"`
|
NameWithNamespace string `json:"name_with_namespace"`
|
||||||
Path string `json:"path"`
|
Path string `json:"path"`
|
||||||
PathWithNamespace string `json:"path_with_namespace"`
|
PathWithNamespace string `json:"path_with_namespace"`
|
||||||
IssuesEnabled bool `json:"issues_enabled"`
|
IssuesEnabled bool `json:"issues_enabled"`
|
||||||
OpenIssuesCount int `json:"open_issues_count"`
|
OpenIssuesCount int `json:"open_issues_count"`
|
||||||
MergeRequestsEnabled bool `json:"merge_requests_enabled"`
|
MergeRequestsEnabled bool `json:"merge_requests_enabled"`
|
||||||
ApprovalsBeforeMerge int `json:"approvals_before_merge"`
|
ApprovalsBeforeMerge int `json:"approvals_before_merge"`
|
||||||
JobsEnabled bool `json:"jobs_enabled"`
|
JobsEnabled bool `json:"jobs_enabled"`
|
||||||
WikiEnabled bool `json:"wiki_enabled"`
|
WikiEnabled bool `json:"wiki_enabled"`
|
||||||
SnippetsEnabled bool `json:"snippets_enabled"`
|
SnippetsEnabled bool `json:"snippets_enabled"`
|
||||||
ResolveOutdatedDiffDiscussions bool `json:"resolve_outdated_diff_discussions"`
|
ResolveOutdatedDiffDiscussions bool `json:"resolve_outdated_diff_discussions"`
|
||||||
ContainerRegistryEnabled bool `json:"container_registry_enabled"`
|
ContainerRegistryEnabled bool `json:"container_registry_enabled"`
|
||||||
CreatedAt *time.Time `json:"created_at,omitempty"`
|
CreatedAt *time.Time `json:"created_at,omitempty"`
|
||||||
LastActivityAt *time.Time `json:"last_activity_at,omitempty"`
|
LastActivityAt *time.Time `json:"last_activity_at,omitempty"`
|
||||||
CreatorID int `json:"creator_id"`
|
CreatorID int `json:"creator_id"`
|
||||||
Namespace *ProjectNamespace `json:"namespace"`
|
Namespace *ProjectNamespace `json:"namespace"`
|
||||||
ImportStatus string `json:"import_status"`
|
ImportStatus string `json:"import_status"`
|
||||||
ImportError string `json:"import_error"`
|
ImportError string `json:"import_error"`
|
||||||
Permissions *Permissions `json:"permissions"`
|
Permissions *Permissions `json:"permissions"`
|
||||||
MarkedForDeletionAt *ISOTime `json:"marked_for_deletion_at"`
|
MarkedForDeletionAt *ISOTime `json:"marked_for_deletion_at"`
|
||||||
Archived bool `json:"archived"`
|
Archived bool `json:"archived"`
|
||||||
AvatarURL string `json:"avatar_url"`
|
AvatarURL string `json:"avatar_url"`
|
||||||
SharedRunnersEnabled bool `json:"shared_runners_enabled"`
|
SharedRunnersEnabled bool `json:"shared_runners_enabled"`
|
||||||
ForksCount int `json:"forks_count"`
|
ForksCount int `json:"forks_count"`
|
||||||
StarCount int `json:"star_count"`
|
StarCount int `json:"star_count"`
|
||||||
RunnersToken string `json:"runners_token"`
|
RunnersToken string `json:"runners_token"`
|
||||||
PublicBuilds bool `json:"public_builds"`
|
PublicBuilds bool `json:"public_builds"`
|
||||||
OnlyAllowMergeIfPipelineSucceeds bool `json:"only_allow_merge_if_pipeline_succeeds"`
|
OnlyAllowMergeIfPipelineSucceeds bool `json:"only_allow_merge_if_pipeline_succeeds"`
|
||||||
OnlyAllowMergeIfAllDiscussionsAreResolved bool `json:"only_allow_merge_if_all_discussions_are_resolved"`
|
OnlyAllowMergeIfAllDiscussionsAreResolved bool `json:"only_allow_merge_if_all_discussions_are_resolved"`
|
||||||
RemoveSourceBranchAfterMerge bool `json:"remove_source_branch_after_merge"`
|
RemoveSourceBranchAfterMerge bool `json:"remove_source_branch_after_merge"`
|
||||||
LFSEnabled bool `json:"lfs_enabled"`
|
LFSEnabled bool `json:"lfs_enabled"`
|
||||||
RequestAccessEnabled bool `json:"request_access_enabled"`
|
RequestAccessEnabled bool `json:"request_access_enabled"`
|
||||||
MergeMethod MergeMethodValue `json:"merge_method"`
|
MergeMethod MergeMethodValue `json:"merge_method"`
|
||||||
ForkedFromProject *ForkParent `json:"forked_from_project"`
|
ForkedFromProject *ForkParent `json:"forked_from_project"`
|
||||||
Mirror bool `json:"mirror"`
|
Mirror bool `json:"mirror"`
|
||||||
MirrorUserID int `json:"mirror_user_id"`
|
MirrorUserID int `json:"mirror_user_id"`
|
||||||
MirrorTriggerBuilds bool `json:"mirror_trigger_builds"`
|
MirrorTriggerBuilds bool `json:"mirror_trigger_builds"`
|
||||||
OnlyMirrorProtectedBranches bool `json:"only_mirror_protected_branches"`
|
OnlyMirrorProtectedBranches bool `json:"only_mirror_protected_branches"`
|
||||||
MirrorOverwritesDivergedBranches bool `json:"mirror_overwrites_diverged_branches"`
|
MirrorOverwritesDivergedBranches bool `json:"mirror_overwrites_diverged_branches"`
|
||||||
|
ServiceDeskEnabled bool `json:"service_desk_enabled"`
|
||||||
|
ServiceDeskAddress string `json:"service_desk_address"`
|
||||||
|
IssuesAccessLevel AccessControlValue `json:"issues_access_level"`
|
||||||
|
RepositoryAccessLevel AccessControlValue `json:"repository_access_level"`
|
||||||
|
MergeRequestsAccessLevel AccessControlValue `json:"merge_requests_access_level"`
|
||||||
|
ForkingAccessLevel AccessControlValue `json:"forking_access_level"`
|
||||||
|
WikiAccessLevel AccessControlValue `json:"wiki_access_level"`
|
||||||
|
BuildsAccessLevel AccessControlValue `json:"builds_access_level"`
|
||||||
|
SnippetsAccessLevel AccessControlValue `json:"snippets_access_level"`
|
||||||
|
PagesAccessLevel AccessControlValue `json:"pages_access_level"`
|
||||||
|
AutocloseReferencedIssues bool `json:"autoclose_referenced_issues"`
|
||||||
SharedWithGroups []struct {
|
SharedWithGroups []struct {
|
||||||
GroupID int `json:"group_id"`
|
GroupID int `json:"group_id"`
|
||||||
GroupName string `json:"group_name"`
|
GroupName string `json:"group_name"`
|
||||||
|
@ -213,20 +224,27 @@ func (s ProjectApprovalRule) String() string {
|
||||||
type ListProjectsOptions struct {
|
type ListProjectsOptions struct {
|
||||||
ListOptions
|
ListOptions
|
||||||
Archived *bool `url:"archived,omitempty" json:"archived,omitempty"`
|
Archived *bool `url:"archived,omitempty" json:"archived,omitempty"`
|
||||||
|
Visibility *VisibilityValue `url:"visibility,omitempty" json:"visibility,omitempty"`
|
||||||
OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"`
|
OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"`
|
||||||
Sort *string `url:"sort,omitempty" json:"sort,omitempty"`
|
Sort *string `url:"sort,omitempty" json:"sort,omitempty"`
|
||||||
Search *string `url:"search,omitempty" json:"search,omitempty"`
|
Search *string `url:"search,omitempty" json:"search,omitempty"`
|
||||||
|
SearchNamespaces *bool `url:"search_namespaces,omitempty" json:"search_namespaces,omitempty"`
|
||||||
Simple *bool `url:"simple,omitempty" json:"simple,omitempty"`
|
Simple *bool `url:"simple,omitempty" json:"simple,omitempty"`
|
||||||
Owned *bool `url:"owned,omitempty" json:"owned,omitempty"`
|
Owned *bool `url:"owned,omitempty" json:"owned,omitempty"`
|
||||||
Membership *bool `url:"membership,omitempty" json:"membership,omitempty"`
|
Membership *bool `url:"membership,omitempty" json:"membership,omitempty"`
|
||||||
Starred *bool `url:"starred,omitempty" json:"starred,omitempty"`
|
Starred *bool `url:"starred,omitempty" json:"starred,omitempty"`
|
||||||
Statistics *bool `url:"statistics,omitempty" json:"statistics,omitempty"`
|
Statistics *bool `url:"statistics,omitempty" json:"statistics,omitempty"`
|
||||||
Visibility *VisibilityValue `url:"visibility,omitempty" json:"visibility,omitempty"`
|
WithCustomAttributes *bool `url:"with_custom_attributes,omitempty" json:"with_custom_attributes,omitempty"`
|
||||||
WithIssuesEnabled *bool `url:"with_issues_enabled,omitempty" json:"with_issues_enabled,omitempty"`
|
WithIssuesEnabled *bool `url:"with_issues_enabled,omitempty" json:"with_issues_enabled,omitempty"`
|
||||||
WithMergeRequestsEnabled *bool `url:"with_merge_requests_enabled,omitempty" json:"with_merge_requests_enabled,omitempty"`
|
WithMergeRequestsEnabled *bool `url:"with_merge_requests_enabled,omitempty" json:"with_merge_requests_enabled,omitempty"`
|
||||||
MinAccessLevel *AccessLevelValue `url:"min_access_level,omitempty" json:"min_access_level,omitempty"`
|
|
||||||
WithCustomAttributes *bool `url:"with_custom_attributes,omitempty" json:"with_custom_attributes,omitempty"`
|
|
||||||
WithProgrammingLanguage *string `url:"with_programming_language,omitempty" json:"with_programming_language,omitempty"`
|
WithProgrammingLanguage *string `url:"with_programming_language,omitempty" json:"with_programming_language,omitempty"`
|
||||||
|
WikiChecksumFailed *bool `url:"wiki_checksum_failed,omitempty" json:"wiki_checksum_failed,omitempty"`
|
||||||
|
RepositoryChecksumFailed *bool `url:"repository_checksum_failed,omitempty" json:"repository_checksum_failed,omitempty"`
|
||||||
|
MinAccessLevel *AccessLevelValue `url:"min_access_level,omitempty" json:"min_access_level,omitempty"`
|
||||||
|
IDAfter *int `url:"id_after,omitempty" json:"id_after,omitempty"`
|
||||||
|
IDBefore *int `url:"id_before,omitempty" json:"id_before,omitempty"`
|
||||||
|
LastActivityAfter *time.Time `url:"last_activity_after,omitempty" json:"last_activity_after,omitempty"`
|
||||||
|
LastActivityBefore *time.Time `url:"last_activity_before,omitempty" json:"last_activity_before,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListProjects gets a list of projects accessible by the authenticated user.
|
// ListProjects gets a list of projects accessible by the authenticated user.
|
||||||
|
@ -444,39 +462,60 @@ func (s *ProjectsService) GetProjectEvents(pid interface{}, opt *GetProjectEvent
|
||||||
//
|
//
|
||||||
// GitLab API docs: https://docs.gitlab.com/ee/api/projects.html#create-project
|
// GitLab API docs: https://docs.gitlab.com/ee/api/projects.html#create-project
|
||||||
type CreateProjectOptions struct {
|
type CreateProjectOptions struct {
|
||||||
Name *string `url:"name,omitempty" json:"name,omitempty"`
|
Name *string `url:"name,omitempty" json:"name,omitempty"`
|
||||||
Path *string `url:"path,omitempty" json:"path,omitempty"`
|
Path *string `url:"path,omitempty" json:"path,omitempty"`
|
||||||
DefaultBranch *string `url:"default_branch,omitempty" json:"default_branch,omitempty"`
|
NamespaceID *int `url:"namespace_id,omitempty" json:"namespace_id,omitempty"`
|
||||||
NamespaceID *int `url:"namespace_id,omitempty" json:"namespace_id,omitempty"`
|
DefaultBranch *string `url:"default_branch,omitempty" json:"default_branch,omitempty"`
|
||||||
Description *string `url:"description,omitempty" json:"description,omitempty"`
|
Description *string `url:"description,omitempty" json:"description,omitempty"`
|
||||||
IssuesEnabled *bool `url:"issues_enabled,omitempty" json:"issues_enabled,omitempty"`
|
IssuesAccessLevel *AccessControlValue `url:"issues_access_level,omitempty" json:"issues_access_level,omitempty"`
|
||||||
MergeRequestsEnabled *bool `url:"merge_requests_enabled,omitempty" json:"merge_requests_enabled,omitempty"`
|
RepositoryAccessLevel *AccessControlValue `url:"repository_access_level,omitempty" json:"repository_access_level,omitempty"`
|
||||||
JobsEnabled *bool `url:"jobs_enabled,omitempty" json:"jobs_enabled,omitempty"`
|
MergeRequestsAccessLevel *AccessControlValue `url:"merge_requests_access_level,omitempty" json:"merge_requests_access_level,omitempty"`
|
||||||
WikiEnabled *bool `url:"wiki_enabled,omitempty" json:"wiki_enabled,omitempty"`
|
ForkingAccessLevel *AccessControlValue `url:"forking_access_level,omitempty" json:"forking_access_level,omitempty"`
|
||||||
SnippetsEnabled *bool `url:"snippets_enabled,omitempty" json:"snippets_enabled,omitempty"`
|
BuildsAccessLevel *AccessControlValue `url:"builds_access_level,omitempty" json:"builds_access_level,omitempty"`
|
||||||
ResolveOutdatedDiffDiscussions *bool `url:"resolve_outdated_diff_discussions,omitempty" json:"resolve_outdated_diff_discussions,omitempty"`
|
WikiAccessLevel *AccessControlValue `url:"wiki_access_level,omitempty" json:"wiki_access_level,omitempty"`
|
||||||
ContainerRegistryEnabled *bool `url:"container_registry_enabled,omitempty" json:"container_registry_enabled,omitempty"`
|
SnippetsAccessLevel *AccessControlValue `url:"snippets_access_level,omitempty" json:"snippets_access_level,omitempty"`
|
||||||
SharedRunnersEnabled *bool `url:"shared_runners_enabled,omitempty" json:"shared_runners_enabled,omitempty"`
|
PagesAccessLevel *AccessControlValue `url:"pages_access_level,omitempty" json:"pages_access_level,omitempty"`
|
||||||
Visibility *VisibilityValue `url:"visibility,omitempty" json:"visibility,omitempty"`
|
EmailsDisabled *bool `url:"emails_disabled,omitempty" json:"emails_disabled,omitempty"`
|
||||||
ImportURL *string `url:"import_url,omitempty" json:"import_url,omitempty"`
|
ResolveOutdatedDiffDiscussions *bool `url:"resolve_outdated_diff_discussions,omitempty" json:"resolve_outdated_diff_discussions,omitempty"`
|
||||||
PublicBuilds *bool `url:"public_builds,omitempty" json:"public_builds,omitempty"`
|
ContainerRegistryEnabled *bool `url:"container_registry_enabled,omitempty" json:"container_registry_enabled,omitempty"`
|
||||||
OnlyAllowMergeIfPipelineSucceeds *bool `url:"only_allow_merge_if_pipeline_succeeds,omitempty" json:"only_allow_merge_if_pipeline_succeeds,omitempty"`
|
SharedRunnersEnabled *bool `url:"shared_runners_enabled,omitempty" json:"shared_runners_enabled,omitempty"`
|
||||||
OnlyAllowMergeIfAllDiscussionsAreResolved *bool `url:"only_allow_merge_if_all_discussions_are_resolved,omitempty" json:"only_allow_merge_if_all_discussions_are_resolved,omitempty"`
|
Visibility *VisibilityValue `url:"visibility,omitempty" json:"visibility,omitempty"`
|
||||||
MergeMethod *MergeMethodValue `url:"merge_method,omitempty" json:"merge_method,omitempty"`
|
ImportURL *string `url:"import_url,omitempty" json:"import_url,omitempty"`
|
||||||
RemoveSourceBranchAfterMerge *bool `url:"remove_source_branch_after_merge,omitempty" json:"remove_source_branch_after_merge,omitempty"`
|
PublicBuilds *bool `url:"public_builds,omitempty" json:"public_builds,omitempty"`
|
||||||
LFSEnabled *bool `url:"lfs_enabled,omitempty" json:"lfs_enabled,omitempty"`
|
OnlyAllowMergeIfPipelineSucceeds *bool `url:"only_allow_merge_if_pipeline_succeeds,omitempty" json:"only_allow_merge_if_pipeline_succeeds,omitempty"`
|
||||||
RequestAccessEnabled *bool `url:"request_access_enabled,omitempty" json:"request_access_enabled,omitempty"`
|
OnlyAllowMergeIfAllDiscussionsAreResolved *bool `url:"only_allow_merge_if_all_discussions_are_resolved,omitempty" json:"only_allow_merge_if_all_discussions_are_resolved,omitempty"`
|
||||||
TagList *[]string `url:"tag_list,omitempty" json:"tag_list,omitempty"`
|
MergeMethod *MergeMethodValue `url:"merge_method,omitempty" json:"merge_method,omitempty"`
|
||||||
PrintingMergeRequestLinkEnabled *bool `url:"printing_merge_request_link_enabled,omitempty" json:"printing_merge_request_link_enabled,omitempty"`
|
RemoveSourceBranchAfterMerge *bool `url:"remove_source_branch_after_merge,omitempty" json:"remove_source_branch_after_merge,omitempty"`
|
||||||
BuildCoverageRegex *string `url:"build_coverage_regex,omitempty" json:"build_coverage_regex,omitempty"`
|
LFSEnabled *bool `url:"lfs_enabled,omitempty" json:"lfs_enabled,omitempty"`
|
||||||
CIConfigPath *string `url:"ci_config_path,omitempty" json:"ci_config_path,omitempty"`
|
RequestAccessEnabled *bool `url:"request_access_enabled,omitempty" json:"request_access_enabled,omitempty"`
|
||||||
ApprovalsBeforeMerge *int `url:"approvals_before_merge,omitempty" json:"approvals_before_merge,omitempty"`
|
TagList *[]string `url:"tag_list,omitempty" json:"tag_list,omitempty"`
|
||||||
Mirror *bool `url:"mirror,omitempty" json:"mirror,omitempty"`
|
PrintingMergeRequestLinkEnabled *bool `url:"printing_merge_request_link_enabled,omitempty" json:"printing_merge_request_link_enabled,omitempty"`
|
||||||
MirrorTriggerBuilds *bool `url:"mirror_trigger_builds,omitempty" json:"mirror_trigger_builds,omitempty"`
|
BuildGitStrategy *string `url:"build_git_strategy,omitempty" json:"build_git_strategy,omitempty"`
|
||||||
InitializeWithReadme *bool `url:"initialize_with_readme,omitempty" json:"initialize_with_readme,omitempty"`
|
BuildTimeout *int `url:"build_timeout,omitempty" json:"build_timeout,omitempty"`
|
||||||
TemplateName *string `url:"template_name,omitempty" json:"template_name,omitempty"`
|
AutoCancelPendingPipelines *string `url:"auto_cancel_pending_pipelines,omitempty" json:"auto_cancel_pending_pipelines,omitempty"`
|
||||||
UseCustomTemplate *bool `url:"use_custom_template,omitempty" json:"use_custom_template,omitempty"`
|
BuildCoverageRegex *string `url:"build_coverage_regex,omitempty" json:"build_coverage_regex,omitempty"`
|
||||||
GroupWithProjectTemplatesID *int `url:"group_with_project_templates_id,omitempty" json:"group_with_project_templates_id,omitempty"`
|
CIConfigPath *string `url:"ci_config_path,omitempty" json:"ci_config_path,omitempty"`
|
||||||
|
AutoDevopsEnabled *bool `url:"auto_devops_enabled,omitempty" json:"auto_devops_enabled,omitempty"`
|
||||||
|
AutoDevopsDeployStrategy *string `url:"auto_devops_deploy_strategy,omitempty" json:"auto_devops_deploy_strategy,omitempty"`
|
||||||
|
ApprovalsBeforeMerge *int `url:"approvals_before_merge,omitempty" json:"approvals_before_merge,omitempty"`
|
||||||
|
ExternalAuthorizationClassificationLabel *string `url:"external_authorization_classification_label,omitempty" json:"external_authorization_classification_label,omitempty"`
|
||||||
|
Mirror *bool `url:"mirror,omitempty" json:"mirror,omitempty"`
|
||||||
|
MirrorTriggerBuilds *bool `url:"mirror_trigger_builds,omitempty" json:"mirror_trigger_builds,omitempty"`
|
||||||
|
InitializeWithReadme *bool `url:"initialize_with_readme,omitempty" json:"initialize_with_readme,omitempty"`
|
||||||
|
TemplateName *string `url:"template_name,omitempty" json:"template_name,omitempty"`
|
||||||
|
TemplateProjectID *int `url:"template_project_id,omitempty" json:"template_project_id,omitempty"`
|
||||||
|
UseCustomTemplate *bool `url:"use_custom_template,omitempty" json:"use_custom_template,omitempty"`
|
||||||
|
GroupWithProjectTemplatesID *int `url:"group_with_project_templates_id,omitempty" json:"group_with_project_templates_id,omitempty"`
|
||||||
|
PackagesEnabled *bool `url:"packages_enabled,omitempty" json:"packages_enabled,omitempty"`
|
||||||
|
ServiceDeskEnabled *bool `url:"service_desk_enabled,omitempty" json:"service_desk_enabled,omitempty"`
|
||||||
|
AutocloseReferencedIssues *bool `url:"autoclose_referenced_issues,omitempty" json:"autoclose_referenced_issues,omitempty"`
|
||||||
|
|
||||||
|
// Deprecated members
|
||||||
|
IssuesEnabled *bool `url:"issues_enabled,omitempty" json:"issues_enabled,omitempty"`
|
||||||
|
MergeRequestsEnabled *bool `url:"merge_requests_enabled,omitempty" json:"merge_requests_enabled,omitempty"`
|
||||||
|
JobsEnabled *bool `url:"jobs_enabled,omitempty" json:"jobs_enabled,omitempty"`
|
||||||
|
WikiEnabled *bool `url:"wiki_enabled,omitempty" json:"wiki_enabled,omitempty"`
|
||||||
|
SnippetsEnabled *bool `url:"snippets_enabled,omitempty" json:"snippets_enabled,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateProject creates a new project owned by the authenticated user.
|
// CreateProject creates a new project owned by the authenticated user.
|
||||||
|
@ -530,39 +569,57 @@ func (s *ProjectsService) CreateProjectForUser(user int, opt *CreateProjectForUs
|
||||||
//
|
//
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#edit-project
|
// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#edit-project
|
||||||
type EditProjectOptions struct {
|
type EditProjectOptions struct {
|
||||||
Name *string `url:"name,omitempty" json:"name,omitempty"`
|
Name *string `url:"name,omitempty" json:"name,omitempty"`
|
||||||
Path *string `url:"path,omitempty" json:"path,omitempty"`
|
Path *string `url:"path,omitempty" json:"path,omitempty"`
|
||||||
DefaultBranch *string `url:"default_branch,omitempty" json:"default_branch,omitempty"`
|
DefaultBranch *string `url:"default_branch,omitempty" json:"default_branch,omitempty"`
|
||||||
Description *string `url:"description,omitempty" json:"description,omitempty"`
|
Description *string `url:"description,omitempty" json:"description,omitempty"`
|
||||||
IssuesEnabled *bool `url:"issues_enabled,omitempty" json:"issues_enabled,omitempty"`
|
IssuesAccessLevel *AccessControlValue `url:"issues_access_level,omitempty" json:"issues_access_level,omitempty"`
|
||||||
MergeRequestsEnabled *bool `url:"merge_requests_enabled,omitempty" json:"merge_requests_enabled,omitempty"`
|
RepositoryAccessLevel *AccessControlValue `url:"repository_access_level,omitempty" json:"repository_access_level,omitempty"`
|
||||||
JobsEnabled *bool `url:"jobs_enabled,omitempty" json:"jobs_enabled,omitempty"`
|
MergeRequestsAccessLevel *AccessControlValue `url:"merge_requests_access_level,omitempty" json:"merge_requests_access_level,omitempty"`
|
||||||
WikiEnabled *bool `url:"wiki_enabled,omitempty" json:"wiki_enabled,omitempty"`
|
ForkingAccessLevel *AccessControlValue `url:"forking_access_level,omitempty" json:"forking_access_level,omitempty"`
|
||||||
SnippetsEnabled *bool `url:"snippets_enabled,omitempty" json:"snippets_enabled,omitempty"`
|
BuildsAccessLevel *AccessControlValue `url:"builds_access_level,omitempty" json:"builds_access_level,omitempty"`
|
||||||
ResolveOutdatedDiffDiscussions *bool `url:"resolve_outdated_diff_discussions,omitempty" json:"resolve_outdated_diff_discussions,omitempty"`
|
WikiAccessLevel *AccessControlValue `url:"wiki_access_level,omitempty" json:"wiki_access_level,omitempty"`
|
||||||
ContainerRegistryEnabled *bool `url:"container_registry_enabled,omitempty" json:"container_registry_enabled,omitempty"`
|
SnippetsAccessLevel *AccessControlValue `url:"snippets_access_level,omitempty" json:"snippets_access_level,omitempty"`
|
||||||
SharedRunnersEnabled *bool `url:"shared_runners_enabled,omitempty" json:"shared_runners_enabled,omitempty"`
|
PagesAccessLevel *AccessControlValue `url:"pages_access_level,omitempty" json:"pages_access_level,omitempty"`
|
||||||
Visibility *VisibilityValue `url:"visibility,omitempty" json:"visibility,omitempty"`
|
EmailsDisabled *bool `url:"emails_disabled,omitempty" json:"emails_disabled,omitempty"`
|
||||||
ImportURL *string `url:"import_url,omitempty" json:"import_url,omitempty"`
|
ResolveOutdatedDiffDiscussions *bool `url:"resolve_outdated_diff_discussions,omitempty" json:"resolve_outdated_diff_discussions,omitempty"`
|
||||||
PublicBuilds *bool `url:"public_builds,omitempty" json:"public_builds,omitempty"`
|
ContainerRegistryEnabled *bool `url:"container_registry_enabled,omitempty" json:"container_registry_enabled,omitempty"`
|
||||||
OnlyAllowMergeIfPipelineSucceeds *bool `url:"only_allow_merge_if_pipeline_succeeds,omitempty" json:"only_allow_merge_if_pipeline_succeeds,omitempty"`
|
SharedRunnersEnabled *bool `url:"shared_runners_enabled,omitempty" json:"shared_runners_enabled,omitempty"`
|
||||||
OnlyAllowMergeIfAllDiscussionsAreResolved *bool `url:"only_allow_merge_if_all_discussions_are_resolved,omitempty" json:"only_allow_merge_if_all_discussions_are_resolved,omitempty"`
|
Visibility *VisibilityValue `url:"visibility,omitempty" json:"visibility,omitempty"`
|
||||||
MergeMethod *MergeMethodValue `url:"merge_method,omitempty" json:"merge_method,omitempty"`
|
ImportURL *string `url:"import_url,omitempty" json:"import_url,omitempty"`
|
||||||
RemoveSourceBranchAfterMerge *bool `url:"remove_source_branch_after_merge,omitempty" json:"remove_source_branch_after_merge,omitempty"`
|
PublicBuilds *bool `url:"public_builds,omitempty" json:"public_builds,omitempty"`
|
||||||
LFSEnabled *bool `url:"lfs_enabled,omitempty" json:"lfs_enabled,omitempty"`
|
OnlyAllowMergeIfPipelineSucceeds *bool `url:"only_allow_merge_if_pipeline_succeeds,omitempty" json:"only_allow_merge_if_pipeline_succeeds,omitempty"`
|
||||||
RequestAccessEnabled *bool `url:"request_access_enabled,omitempty" json:"request_access_enabled,omitempty"`
|
OnlyAllowMergeIfAllDiscussionsAreResolved *bool `url:"only_allow_merge_if_all_discussions_are_resolved,omitempty" json:"only_allow_merge_if_all_discussions_are_resolved,omitempty"`
|
||||||
TagList *[]string `url:"tag_list,omitempty" json:"tag_list,omitempty"`
|
MergeMethod *MergeMethodValue `url:"merge_method,omitempty" json:"merge_method,omitempty"`
|
||||||
BuildCoverageRegex *string `url:"build_coverage_regex,omitempty" json:"build_coverage_regex,omitempty"`
|
RemoveSourceBranchAfterMerge *bool `url:"remove_source_branch_after_merge,omitempty" json:"remove_source_branch_after_merge,omitempty"`
|
||||||
CIConfigPath *string `url:"ci_config_path,omitempty" json:"ci_config_path,omitempty"`
|
LFSEnabled *bool `url:"lfs_enabled,omitempty" json:"lfs_enabled,omitempty"`
|
||||||
CIDefaultGitDepth *int `url:"ci_default_git_depth,omitempty" json:"ci_default_git_depth,omitempty"`
|
RequestAccessEnabled *bool `url:"request_access_enabled,omitempty" json:"request_access_enabled,omitempty"`
|
||||||
ApprovalsBeforeMerge *int `url:"approvals_before_merge,omitempty" json:"approvals_before_merge,omitempty"`
|
TagList *[]string `url:"tag_list,omitempty" json:"tag_list,omitempty"`
|
||||||
ExternalAuthorizationClassificationLabel *string `url:"external_authorization_classification_label,omitempty" json:"external_authorization_classification_label,omitempty"`
|
BuildGitStrategy *string `url:"build_git_strategy,omitempty" json:"build_git_strategy,omitempty"`
|
||||||
Mirror *bool `url:"mirror,omitempty" json:"mirror,omitempty"`
|
BuildTimeout *int `url:"build_timeout,omitempty" json:"build_timeout,omitempty"`
|
||||||
MirrorTriggerBuilds *bool `url:"mirror_trigger_builds,omitempty" json:"mirror_trigger_builds,omitempty"`
|
AutoCancelPendingPipelines *string `url:"auto_cancel_pending_pipelines,omitempty" json:"auto_cancel_pending_pipelines,omitempty"`
|
||||||
MirrorUserID *int `url:"mirror_user_id,omitempty" json:"mirror_user_id,omitempty"`
|
BuildCoverageRegex *string `url:"build_coverage_regex,omitempty" json:"build_coverage_regex,omitempty"`
|
||||||
OnlyMirrorProtectedBranches *bool `url:"only_mirror_protected_branches,omitempty" json:"only_mirror_protected_branches,omitempty"`
|
CIConfigPath *string `url:"ci_config_path,omitempty" json:"ci_config_path,omitempty"`
|
||||||
MirrorOverwritesDivergedBranches *bool `url:"mirror_overwrites_diverged_branches,omitempty" json:"mirror_overwrites_diverged_branches,omitempty"`
|
CIDefaultGitDepth *int `url:"ci_default_git_depth,omitempty" json:"ci_default_git_depth,omitempty"`
|
||||||
PackagesEnabled *bool `url:"packages_enabled,omitempty" json:"packages_enabled,omitempty"`
|
AutoDevopsEnabled *bool `url:"auto_devops_enabled,omitempty" json:"auto_devops_enabled,omitempty"`
|
||||||
|
AutoDevopsDeployStrategy *string `url:"auto_devops_deploy_strategy,omitempty" json:"auto_devops_deploy_strategy,omitempty"`
|
||||||
|
ApprovalsBeforeMerge *int `url:"approvals_before_merge,omitempty" json:"approvals_before_merge,omitempty"`
|
||||||
|
ExternalAuthorizationClassificationLabel *string `url:"external_authorization_classification_label,omitempty" json:"external_authorization_classification_label,omitempty"`
|
||||||
|
Mirror *bool `url:"mirror,omitempty" json:"mirror,omitempty"`
|
||||||
|
MirrorUserID *int `url:"mirror_user_id,omitempty" json:"mirror_user_id,omitempty"`
|
||||||
|
MirrorTriggerBuilds *bool `url:"mirror_trigger_builds,omitempty" json:"mirror_trigger_builds,omitempty"`
|
||||||
|
OnlyMirrorProtectedBranches *bool `url:"only_mirror_protected_branches,omitempty" json:"only_mirror_protected_branches,omitempty"`
|
||||||
|
MirrorOverwritesDivergedBranches *bool `url:"mirror_overwrites_diverged_branches,omitempty" json:"mirror_overwrites_diverged_branches,omitempty"`
|
||||||
|
PackagesEnabled *bool `url:"packages_enabled,omitempty" json:"packages_enabled,omitempty"`
|
||||||
|
ServiceDeskEnabled *bool `url:"service_desk_enabled,omitempty" json:"service_desk_enabled,omitempty"`
|
||||||
|
AutocloseReferencedIssues *bool `url:"autoclose_referenced_issues,omitempty" json:"autoclose_referenced_issues,omitempty"`
|
||||||
|
|
||||||
|
// Deprecated members
|
||||||
|
IssuesEnabled *bool `url:"issues_enabled,omitempty" json:"issues_enabled,omitempty"`
|
||||||
|
MergeRequestsEnabled *bool `url:"merge_requests_enabled,omitempty" json:"merge_requests_enabled,omitempty"`
|
||||||
|
JobsEnabled *bool `url:"jobs_enabled,omitempty" json:"jobs_enabled,omitempty"`
|
||||||
|
WikiEnabled *bool `url:"wiki_enabled,omitempty" json:"wiki_enabled,omitempty"`
|
||||||
|
SnippetsEnabled *bool `url:"snippets_enabled,omitempty" json:"snippets_enabled,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// EditProject updates an existing project.
|
// EditProject updates an existing project.
|
||||||
|
@ -816,6 +873,7 @@ type ProjectHook struct {
|
||||||
ConfidentialNoteEvents bool `json:"confidential_note_events"`
|
ConfidentialNoteEvents bool `json:"confidential_note_events"`
|
||||||
ProjectID int `json:"project_id"`
|
ProjectID int `json:"project_id"`
|
||||||
PushEvents bool `json:"push_events"`
|
PushEvents bool `json:"push_events"`
|
||||||
|
PushEventsBranchFilter string `json:"push_events_branch_filter"`
|
||||||
IssuesEvents bool `json:"issues_events"`
|
IssuesEvents bool `json:"issues_events"`
|
||||||
ConfidentialIssuesEvents bool `json:"confidential_issues_events"`
|
ConfidentialIssuesEvents bool `json:"confidential_issues_events"`
|
||||||
MergeRequestsEvents bool `json:"merge_requests_events"`
|
MergeRequestsEvents bool `json:"merge_requests_events"`
|
||||||
|
@ -891,6 +949,7 @@ type AddProjectHookOptions struct {
|
||||||
URL *string `url:"url,omitempty" json:"url,omitempty"`
|
URL *string `url:"url,omitempty" json:"url,omitempty"`
|
||||||
ConfidentialNoteEvents *bool `url:"confidential_note_events,omitempty" json:"confidential_note_events,omitempty"`
|
ConfidentialNoteEvents *bool `url:"confidential_note_events,omitempty" json:"confidential_note_events,omitempty"`
|
||||||
PushEvents *bool `url:"push_events,omitempty" json:"push_events,omitempty"`
|
PushEvents *bool `url:"push_events,omitempty" json:"push_events,omitempty"`
|
||||||
|
PushEventsBranchFilter *string `url:"push_events_branch_filter,omitempty" json:"push_events_branch_filter,omitempty"`
|
||||||
IssuesEvents *bool `url:"issues_events,omitempty" json:"issues_events,omitempty"`
|
IssuesEvents *bool `url:"issues_events,omitempty" json:"issues_events,omitempty"`
|
||||||
ConfidentialIssuesEvents *bool `url:"confidential_issues_events,omitempty" json:"confidential_issues_events,omitempty"`
|
ConfidentialIssuesEvents *bool `url:"confidential_issues_events,omitempty" json:"confidential_issues_events,omitempty"`
|
||||||
MergeRequestsEvents *bool `url:"merge_requests_events,omitempty" json:"merge_requests_events,omitempty"`
|
MergeRequestsEvents *bool `url:"merge_requests_events,omitempty" json:"merge_requests_events,omitempty"`
|
||||||
|
@ -936,6 +995,7 @@ type EditProjectHookOptions struct {
|
||||||
URL *string `url:"url,omitempty" json:"url,omitempty"`
|
URL *string `url:"url,omitempty" json:"url,omitempty"`
|
||||||
ConfidentialNoteEvents *bool `url:"confidential_note_events,omitempty" json:"confidential_note_events,omitempty"`
|
ConfidentialNoteEvents *bool `url:"confidential_note_events,omitempty" json:"confidential_note_events,omitempty"`
|
||||||
PushEvents *bool `url:"push_events,omitempty" json:"push_events,omitempty"`
|
PushEvents *bool `url:"push_events,omitempty" json:"push_events,omitempty"`
|
||||||
|
PushEventsBranchFilter *string `url:"push_events_branch_filter,omitempty" json:"push_events_branch_filter,omitempty"`
|
||||||
IssuesEvents *bool `url:"issues_events,omitempty" json:"issues_events,omitempty"`
|
IssuesEvents *bool `url:"issues_events,omitempty" json:"issues_events,omitempty"`
|
||||||
ConfidentialIssuesEvents *bool `url:"confidential_issues_events,omitempty" json:"confidential_issues_events,omitempty"`
|
ConfidentialIssuesEvents *bool `url:"confidential_issues_events,omitempty" json:"confidential_issues_events,omitempty"`
|
||||||
MergeRequestsEvents *bool `url:"merge_requests_events,omitempty" json:"merge_requests_events,omitempty"`
|
MergeRequestsEvents *bool `url:"merge_requests_events,omitempty" json:"merge_requests_events,omitempty"`
|
||||||
|
|
|
@ -37,6 +37,8 @@ type ProtectedBranchesService struct {
|
||||||
// https://docs.gitlab.com/ce/api/protected_branches.html#protected-branches-api
|
// https://docs.gitlab.com/ce/api/protected_branches.html#protected-branches-api
|
||||||
type BranchAccessDescription struct {
|
type BranchAccessDescription struct {
|
||||||
AccessLevel AccessLevelValue `json:"access_level"`
|
AccessLevel AccessLevelValue `json:"access_level"`
|
||||||
|
UserID int `json:"user_id"`
|
||||||
|
GroupID int `json:"group_id"`
|
||||||
AccessLevelDescription string `json:"access_level_description"`
|
AccessLevelDescription string `json:"access_level_description"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,6 +51,7 @@ type ProtectedBranch struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
PushAccessLevels []*BranchAccessDescription `json:"push_access_levels"`
|
PushAccessLevels []*BranchAccessDescription `json:"push_access_levels"`
|
||||||
MergeAccessLevels []*BranchAccessDescription `json:"merge_access_levels"`
|
MergeAccessLevels []*BranchAccessDescription `json:"merge_access_levels"`
|
||||||
|
UnprotectAccessLevels []*BranchAccessDescription `json:"unprotect_access_levels"`
|
||||||
CodeOwnerApprovalRequired bool `json:"code_owner_approval_required"`
|
CodeOwnerApprovalRequired bool `json:"code_owner_approval_required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,10 +118,24 @@ func (s *ProtectedBranchesService) GetProtectedBranch(pid interface{}, branch st
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/protected_branches.html#protect-repository-branches
|
// https://docs.gitlab.com/ce/api/protected_branches.html#protect-repository-branches
|
||||||
type ProtectRepositoryBranchesOptions struct {
|
type ProtectRepositoryBranchesOptions struct {
|
||||||
Name *string `url:"name,omitempty" json:"name,omitempty"`
|
Name *string `url:"name,omitempty" json:"name,omitempty"`
|
||||||
PushAccessLevel *AccessLevelValue `url:"push_access_level,omitempty" json:"push_access_level,omitempty"`
|
PushAccessLevel *AccessLevelValue `url:"push_access_level,omitempty" json:"push_access_level,omitempty"`
|
||||||
MergeAccessLevel *AccessLevelValue `url:"merge_access_level,omitempty" json:"merge_access_level,omitempty"`
|
MergeAccessLevel *AccessLevelValue `url:"merge_access_level,omitempty" json:"merge_access_level,omitempty"`
|
||||||
CodeOwnerApprovalRequired *bool `url:"code_owner_approval_required,omitempty" json:"code_owner_approval_required,omitempty"`
|
UnprotectAccessLevel *AccessLevelValue `url:"unprotect_access_level,omitempty" json:"unprotect_access_level,omitempty"`
|
||||||
|
AllowedToPush []*ProtectBranchPermissionOptions `url:"allowed_to_push,omitempty" json:"allowed_to_push,omitempty"`
|
||||||
|
AllowedToMerge []*ProtectBranchPermissionOptions `url:"allowed_to_merge,omitempty" json:"allowed_to_merge,omitempty"`
|
||||||
|
AllowedToUnprotect []*ProtectBranchPermissionOptions `url:"allowed_to_unprotect,omitempty" json:"allowed_to_unprotect,omitempty"`
|
||||||
|
CodeOwnerApprovalRequired *bool `url:"code_owner_approval_required,omitempty" json:"code_owner_approval_required,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProtectBranchPermissionOptions represents a branch permission option.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ce/api/protected_branches.html#protect-repository-branches
|
||||||
|
type ProtectBranchPermissionOptions struct {
|
||||||
|
UserID *int `url:"user_id,omitempty" json:"user_id,omitempty"`
|
||||||
|
GroupID *int `url:"group_id,omitempty" json:"group_id,omitempty"`
|
||||||
|
AccessLevel *AccessLevelValue `url:"access_level,omitempty" json:"access_level,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProtectRepositoryBranches protects a single repository branch or several
|
// ProtectRepositoryBranches protects a single repository branch or several
|
||||||
|
|
|
@ -21,6 +21,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RepositoryFilesService handles communication with the repository files
|
// RepositoryFilesService handles communication with the repository files
|
||||||
|
@ -35,15 +36,16 @@ type RepositoryFilesService struct {
|
||||||
//
|
//
|
||||||
// GitLab API docs: https://docs.gitlab.com/ce/api/repository_files.html
|
// GitLab API docs: https://docs.gitlab.com/ce/api/repository_files.html
|
||||||
type File struct {
|
type File struct {
|
||||||
FileName string `json:"file_name"`
|
FileName string `json:"file_name"`
|
||||||
FilePath string `json:"file_path"`
|
FilePath string `json:"file_path"`
|
||||||
Size int `json:"size"`
|
Size int `json:"size"`
|
||||||
Encoding string `json:"encoding"`
|
Encoding string `json:"encoding"`
|
||||||
Content string `json:"content"`
|
Content string `json:"content"`
|
||||||
Ref string `json:"ref"`
|
Ref string `json:"ref"`
|
||||||
BlobID string `json:"blob_id"`
|
BlobID string `json:"blob_id"`
|
||||||
CommitID string `json:"commit_id"`
|
CommitID string `json:"commit_id"`
|
||||||
SHA256 string `json:"content_sha256"`
|
SHA256 string `json:"content_sha256"`
|
||||||
|
LastCommitID string `json:"last_commit_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r File) String() string {
|
func (r File) String() string {
|
||||||
|
@ -123,13 +125,14 @@ func (s *RepositoryFilesService) GetFileMetaData(pid interface{}, fileName strin
|
||||||
}
|
}
|
||||||
|
|
||||||
f := &File{
|
f := &File{
|
||||||
BlobID: resp.Header.Get("X-Gitlab-Blob-Id"),
|
BlobID: resp.Header.Get("X-Gitlab-Blob-Id"),
|
||||||
CommitID: resp.Header.Get("X-Gitlab-Last-Commit-Id"),
|
CommitID: resp.Header.Get("X-Gitlab-Last-Commit-Id"),
|
||||||
Encoding: resp.Header.Get("X-Gitlab-Encoding"),
|
Encoding: resp.Header.Get("X-Gitlab-Encoding"),
|
||||||
FileName: resp.Header.Get("X-Gitlab-File-Name"),
|
FileName: resp.Header.Get("X-Gitlab-File-Name"),
|
||||||
FilePath: resp.Header.Get("X-Gitlab-File-Path"),
|
FilePath: resp.Header.Get("X-Gitlab-File-Path"),
|
||||||
Ref: resp.Header.Get("X-Gitlab-Ref"),
|
Ref: resp.Header.Get("X-Gitlab-Ref"),
|
||||||
SHA256: resp.Header.Get("X-Gitlab-Content-Sha256"),
|
SHA256: resp.Header.Get("X-Gitlab-Content-Sha256"),
|
||||||
|
LastCommitID: resp.Header.Get("X-Gitlab-Last-Commit-Id"),
|
||||||
}
|
}
|
||||||
|
|
||||||
if sizeString := resp.Header.Get("X-Gitlab-Size"); sizeString != "" {
|
if sizeString := resp.Header.Get("X-Gitlab-Size"); sizeString != "" {
|
||||||
|
@ -142,6 +145,66 @@ func (s *RepositoryFilesService) GetFileMetaData(pid interface{}, fileName strin
|
||||||
return f, resp, err
|
return f, resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FileBlameRange represents one item of blame information.
|
||||||
|
//
|
||||||
|
// GitLab API docs: https://docs.gitlab.com/ce/api/repository_files.html
|
||||||
|
type FileBlameRange struct {
|
||||||
|
Commit struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
ParentIDs []string `json:"parent_ids"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
AuthoredDate *time.Time `json:"authored_date"`
|
||||||
|
AuthorName string `json:"author_name"`
|
||||||
|
AuthorEmail string `json:"author_email"`
|
||||||
|
CommittedDate *time.Time `json:"committed_date"`
|
||||||
|
CommitterName string `json:"committer_name"`
|
||||||
|
CommitterEmail string `json:"committer_email"`
|
||||||
|
} `json:"commit"`
|
||||||
|
Lines []string `json:"lines"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b FileBlameRange) String() string {
|
||||||
|
return Stringify(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFileBlameOptions represents the available GetFileBlame() options.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ce/api/repository_files.html#get-file-blame-from-repository
|
||||||
|
type GetFileBlameOptions struct {
|
||||||
|
Ref *string `url:"ref,omitempty" json:"ref,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFileBlame allows you to receive blame information. Each blame range
|
||||||
|
// contains lines and corresponding commit info.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ce/api/repository_files.html#get-file-blame-from-repository
|
||||||
|
func (s *RepositoryFilesService) GetFileBlame(pid interface{}, file string, opt *GetFileBlameOptions, options ...RequestOptionFunc) ([]*FileBlameRange, *Response, error) {
|
||||||
|
project, err := parseID(pid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
u := fmt.Sprintf(
|
||||||
|
"projects/%s/repository/files/%s/blame",
|
||||||
|
pathEscape(project),
|
||||||
|
url.PathEscape(file),
|
||||||
|
)
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest("GET", u, opt, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var br []*FileBlameRange
|
||||||
|
resp, err := s.client.Do(req, &br)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return br, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
// GetRawFileOptions represents the available GetRawFile() options.
|
// GetRawFileOptions represents the available GetRawFile() options.
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
|
@ -282,9 +345,11 @@ func (s *RepositoryFilesService) UpdateFile(pid interface{}, fileName string, op
|
||||||
// https://docs.gitlab.com/ce/api/repository_files.html#delete-existing-file-in-repository
|
// https://docs.gitlab.com/ce/api/repository_files.html#delete-existing-file-in-repository
|
||||||
type DeleteFileOptions struct {
|
type DeleteFileOptions struct {
|
||||||
Branch *string `url:"branch,omitempty" json:"branch,omitempty"`
|
Branch *string `url:"branch,omitempty" json:"branch,omitempty"`
|
||||||
|
StartBranch *string `url:"start_branch,omitempty" json:"start_branch,omitempty"`
|
||||||
AuthorEmail *string `url:"author_email,omitempty" json:"author_email,omitempty"`
|
AuthorEmail *string `url:"author_email,omitempty" json:"author_email,omitempty"`
|
||||||
AuthorName *string `url:"author_name,omitempty" json:"author_name,omitempty"`
|
AuthorName *string `url:"author_name,omitempty" json:"author_name,omitempty"`
|
||||||
CommitMessage *string `url:"commit_message,omitempty" json:"commit_message,omitempty"`
|
CommitMessage *string `url:"commit_message,omitempty" json:"commit_message,omitempty"`
|
||||||
|
LastCommitID *string `url:"last_commit_id,omitempty" json:"last_commit_id,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteFile deletes an existing file in a repository
|
// DeleteFile deletes an existing file in a repository
|
||||||
|
|
|
@ -336,6 +336,44 @@ func (s *RunnersService) DisableProjectRunner(pid interface{}, runner int, optio
|
||||||
return s.client.Do(req, nil)
|
return s.client.Do(req, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ListGroupsRunnersOptions represents the available ListGroupsRunners() options.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ee/api/runners.html#list-groups-runners
|
||||||
|
type ListGroupsRunnersOptions struct {
|
||||||
|
ListOptions
|
||||||
|
Type *string `url:"type,omitempty" json:"type,omitempty"`
|
||||||
|
Status *string `url:"status,omitempty" json:"status,omitempty"`
|
||||||
|
TagList []string `url:"tag_list,comma,omitempty" json:"tag_list,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListGroupsRunners lists all runners (specific and shared) available in the
|
||||||
|
// group as well it’s ancestor groups. Shared runners are listed if at least one
|
||||||
|
// shared runner is defined.
|
||||||
|
//
|
||||||
|
// GitLab API docs:
|
||||||
|
// https://docs.gitlab.com/ee/api/runners.html#list-groups-runners
|
||||||
|
func (s *RunnersService) ListGroupsRunners(gid interface{}, opt *ListGroupsRunnersOptions, options ...RequestOptionFunc) ([]*Runner, *Response, error) {
|
||||||
|
group, err := parseID(gid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
u := fmt.Sprintf("groups/%s/runners", pathEscape(group))
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest("GET", u, opt, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var rs []*Runner
|
||||||
|
resp, err := s.client.Do(req, &rs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return rs, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
// RegisterNewRunnerOptions represents the available RegisterNewRunner()
|
// RegisterNewRunnerOptions represents the available RegisterNewRunner()
|
||||||
// options.
|
// options.
|
||||||
//
|
//
|
||||||
|
|
|
@ -54,6 +54,30 @@ type Service struct {
|
||||||
WikiPageEvents bool `json:"wiki_page_events"`
|
WikiPageEvents bool `json:"wiki_page_events"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ListServices gets a list of all active services.
|
||||||
|
//
|
||||||
|
// GitLab API docs: https://docs.gitlab.com/ce/api/services.html#list-all-active-services
|
||||||
|
func (s *ServicesService) ListServices(pid interface{}, options ...RequestOptionFunc) ([]*Service, *Response, error) {
|
||||||
|
project, err := parseID(pid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
u := fmt.Sprintf("projects/%s/services", pathEscape(project))
|
||||||
|
|
||||||
|
req, err := s.client.NewRequest("GET", u, nil, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var svcs []*Service
|
||||||
|
resp, err := s.client.Do(req, &svcs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return svcs, resp, err
|
||||||
|
}
|
||||||
|
|
||||||
// DroneCIService represents Drone CI service settings.
|
// DroneCIService represents Drone CI service settings.
|
||||||
//
|
//
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
|
@ -251,7 +275,7 @@ type GithubService struct {
|
||||||
// https://docs.gitlab.com/ce/api/services.html#github-premium
|
// https://docs.gitlab.com/ce/api/services.html#github-premium
|
||||||
type GithubServiceProperties struct {
|
type GithubServiceProperties struct {
|
||||||
RepositoryURL string `json:"repository_url,omitempty"`
|
RepositoryURL string `json:"repository_url,omitempty"`
|
||||||
StaticContext string `json:"static_context,omitempty"`
|
StaticContext bool `json:"static_context,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetGithubService gets Github service settings for a project.
|
// GetGithubService gets Github service settings for a project.
|
||||||
|
@ -665,7 +689,17 @@ type MicrosoftTeamsService struct {
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/services.html#microsoft-teams
|
// https://docs.gitlab.com/ce/api/services.html#microsoft-teams
|
||||||
type MicrosoftTeamsServiceProperties struct {
|
type MicrosoftTeamsServiceProperties struct {
|
||||||
WebHook string `json:"webhook"`
|
WebHook string `json:"webhook"`
|
||||||
|
NotifyOnlyBrokenPipelines BoolValue `json:"notify_only_broken_pipelines"`
|
||||||
|
BranchesToBeNotified string `json:"branches_to_be_notified"`
|
||||||
|
IssuesEvents BoolValue `json:"issues_events"`
|
||||||
|
ConfidentialIssuesEvents BoolValue `json:"confidential_issues_events"`
|
||||||
|
MergeRequestsEvents BoolValue `json:"merge_requests_events"`
|
||||||
|
TagPushEvents BoolValue `json:"tag_push_events"`
|
||||||
|
NoteEvents BoolValue `json:"note_events"`
|
||||||
|
ConfidentialNoteEvents BoolValue `json:"confidential_note_events"`
|
||||||
|
PipelineEvents BoolValue `json:"pipeline_events"`
|
||||||
|
WikiPageEvents BoolValue `json:"wiki_page_events"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetMicrosoftTeamsService gets MicrosoftTeams service settings for a project.
|
// GetMicrosoftTeamsService gets MicrosoftTeams service settings for a project.
|
||||||
|
@ -699,7 +733,18 @@ func (s *ServicesService) GetMicrosoftTeamsService(pid interface{}, options ...R
|
||||||
// GitLab API docs:
|
// GitLab API docs:
|
||||||
// https://docs.gitlab.com/ce/api/services.html#create-edit-microsoft-teams-service
|
// https://docs.gitlab.com/ce/api/services.html#create-edit-microsoft-teams-service
|
||||||
type SetMicrosoftTeamsServiceOptions struct {
|
type SetMicrosoftTeamsServiceOptions struct {
|
||||||
WebHook *string `url:"webhook,omitempty" json:"webhook,omitempty"`
|
WebHook *string `url:"webhook,omitempty" json:"webhook,omitempty"`
|
||||||
|
NotifyOnlyBrokenPipelines *bool `url:"notify_only_broken_pipelines" json:"notify_only_broken_pipelines"`
|
||||||
|
BranchesToBeNotified *string `url:"branches_to_be_notified,omitempty" json:"branches_to_be_notified,omitempty"`
|
||||||
|
PushEvents *bool `url:"push_events,omitempty" json:"push_events,omitempty"`
|
||||||
|
IssuesEvents *bool `url:"issues_events,omitempty" json:"issues_events,omitempty"`
|
||||||
|
ConfidentialIssuesEvents *bool `url:"confidential_issues_events,omitempty" json:"confidential_issues_events,omitempty"`
|
||||||
|
MergeRequestsEvents *bool `url:"merge_requests_events,omitempty" json:"merge_requests_events,omitempty"`
|
||||||
|
TagPushEvents *bool `url:"tag_push_events,omitempty" json:"tag_push_events,omitempty"`
|
||||||
|
NoteEvents *bool `url:"note_events,omitempty" json:"note_events,omitempty"`
|
||||||
|
ConfidentialNoteEvents *bool `url:"confidential_note_events,omitempty" json:"confidential_note_events,omitempty"`
|
||||||
|
PipelineEvents *bool `url:"pipeline_events,omitempty" json:"pipeline_events,omitempty"`
|
||||||
|
WikiPageEvents *bool `url:"wiki_page_events,omitempty" json:"wiki_page_events,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetMicrosoftTeamsService sets Microsoft Teams service for a project
|
// SetMicrosoftTeamsService sets Microsoft Teams service for a project
|
||||||
|
@ -756,6 +801,7 @@ type PipelinesEmailProperties struct {
|
||||||
Recipients string `json:"recipients,omitempty"`
|
Recipients string `json:"recipients,omitempty"`
|
||||||
NotifyOnlyBrokenPipelines BoolValue `json:"notify_only_broken_pipelines,omitempty"`
|
NotifyOnlyBrokenPipelines BoolValue `json:"notify_only_broken_pipelines,omitempty"`
|
||||||
NotifyOnlyDefaultBranch BoolValue `json:"notify_only_default_branch,omitempty"`
|
NotifyOnlyDefaultBranch BoolValue `json:"notify_only_default_branch,omitempty"`
|
||||||
|
BranchesToBeNotified string `json:"branches_to_be_notified,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPipelinesEmailService gets Pipelines Email service settings for a project.
|
// GetPipelinesEmailService gets Pipelines Email service settings for a project.
|
||||||
|
|
|
@ -58,6 +58,7 @@ func (t Tag) String() string {
|
||||||
type ListTagsOptions struct {
|
type ListTagsOptions struct {
|
||||||
ListOptions
|
ListOptions
|
||||||
OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"`
|
OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"`
|
||||||
|
Search *string `url:"search,omitempty" json:"search,omitempty"`
|
||||||
Sort *string `url:"sort,omitempty" json:"sort,omitempty"`
|
Sort *string `url:"sort,omitempty" json:"sort,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,430 @@
|
||||||
|
package gitlab
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AccessControlValue represents an access control value within GitLab,
|
||||||
|
// used for managing access to certain project features.
|
||||||
|
//
|
||||||
|
// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html
|
||||||
|
type AccessControlValue string
|
||||||
|
|
||||||
|
// List of available access control values.
|
||||||
|
//
|
||||||
|
// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html
|
||||||
|
const (
|
||||||
|
DisabledAccessControl AccessControlValue = "disabled"
|
||||||
|
EnabledAccessControl AccessControlValue = "enabled"
|
||||||
|
PrivateAccessControl AccessControlValue = "private"
|
||||||
|
PublicAccessControl AccessControlValue = "public"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AccessControl is a helper routine that allocates a new AccessControlValue
|
||||||
|
// to store v and returns a pointer to it.
|
||||||
|
func AccessControl(v AccessControlValue) *AccessControlValue {
|
||||||
|
p := new(AccessControlValue)
|
||||||
|
*p = v
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// AccessLevelValue represents a permission level within GitLab.
|
||||||
|
//
|
||||||
|
// GitLab API docs: https://docs.gitlab.com/ce/permissions/permissions.html
|
||||||
|
type AccessLevelValue int
|
||||||
|
|
||||||
|
// List of available access levels
|
||||||
|
//
|
||||||
|
// GitLab API docs: https://docs.gitlab.com/ce/permissions/permissions.html
|
||||||
|
const (
|
||||||
|
NoPermissions AccessLevelValue = 0
|
||||||
|
GuestPermissions AccessLevelValue = 10
|
||||||
|
ReporterPermissions AccessLevelValue = 20
|
||||||
|
DeveloperPermissions AccessLevelValue = 30
|
||||||
|
MaintainerPermissions AccessLevelValue = 40
|
||||||
|
OwnerPermissions AccessLevelValue = 50
|
||||||
|
|
||||||
|
// These are deprecated and should be removed in a future version
|
||||||
|
MasterPermissions AccessLevelValue = 40
|
||||||
|
OwnerPermission AccessLevelValue = 50
|
||||||
|
)
|
||||||
|
|
||||||
|
// AccessLevel is a helper routine that allocates a new AccessLevelValue
|
||||||
|
// to store v and returns a pointer to it.
|
||||||
|
func AccessLevel(v AccessLevelValue) *AccessLevelValue {
|
||||||
|
p := new(AccessLevelValue)
|
||||||
|
*p = v
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuildStateValue represents a GitLab build state.
|
||||||
|
type BuildStateValue string
|
||||||
|
|
||||||
|
// These constants represent all valid build states.
|
||||||
|
const (
|
||||||
|
Pending BuildStateValue = "pending"
|
||||||
|
Created BuildStateValue = "created"
|
||||||
|
Running BuildStateValue = "running"
|
||||||
|
Success BuildStateValue = "success"
|
||||||
|
Failed BuildStateValue = "failed"
|
||||||
|
Canceled BuildStateValue = "canceled"
|
||||||
|
Skipped BuildStateValue = "skipped"
|
||||||
|
Manual BuildStateValue = "manual"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BuildState is a helper routine that allocates a new BuildStateValue
|
||||||
|
// to store v and returns a pointer to it.
|
||||||
|
func BuildState(v BuildStateValue) *BuildStateValue {
|
||||||
|
p := new(BuildStateValue)
|
||||||
|
*p = v
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeploymentStatusValue represents a Gitlab deployment status.
|
||||||
|
type DeploymentStatusValue string
|
||||||
|
|
||||||
|
// These constants represent all valid deployment statuses.
|
||||||
|
const (
|
||||||
|
DeploymentStatusCreated DeploymentStatusValue = "created"
|
||||||
|
DeploymentStatusRunning DeploymentStatusValue = "running"
|
||||||
|
DeploymentStatusSuccess DeploymentStatusValue = "success"
|
||||||
|
DeploymentStatusFailed DeploymentStatusValue = "failed"
|
||||||
|
DeploymentStatusCanceled DeploymentStatusValue = "canceled"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DeploymentStatus is a helper routine that allocates a new
|
||||||
|
// DeploymentStatusValue to store v and returns a pointer to it.
|
||||||
|
func DeploymentStatus(v DeploymentStatusValue) *DeploymentStatusValue {
|
||||||
|
p := new(DeploymentStatusValue)
|
||||||
|
*p = v
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// ISOTime represents an ISO 8601 formatted date
|
||||||
|
type ISOTime time.Time
|
||||||
|
|
||||||
|
// ISO 8601 date format
|
||||||
|
const iso8601 = "2006-01-02"
|
||||||
|
|
||||||
|
// MarshalJSON implements the json.Marshaler interface
|
||||||
|
func (t ISOTime) MarshalJSON() ([]byte, error) {
|
||||||
|
if y := time.Time(t).Year(); y < 0 || y >= 10000 {
|
||||||
|
// ISO 8901 uses 4 digits for the years
|
||||||
|
return nil, errors.New("json: ISOTime year outside of range [0,9999]")
|
||||||
|
}
|
||||||
|
|
||||||
|
b := make([]byte, 0, len(iso8601)+2)
|
||||||
|
b = append(b, '"')
|
||||||
|
b = time.Time(t).AppendFormat(b, iso8601)
|
||||||
|
b = append(b, '"')
|
||||||
|
|
||||||
|
return b, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON implements the json.Unmarshaler interface
|
||||||
|
func (t *ISOTime) UnmarshalJSON(data []byte) error {
|
||||||
|
// Ignore null, like in the main JSON package
|
||||||
|
if string(data) == "null" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
isotime, err := time.Parse(`"`+iso8601+`"`, string(data))
|
||||||
|
*t = ISOTime(isotime)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeValues implements the query.Encoder interface
|
||||||
|
func (t *ISOTime) EncodeValues(key string, v *url.Values) error {
|
||||||
|
if t == nil || (time.Time(*t)).IsZero() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
v.Add(key, t.String())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// String implements the Stringer interface
|
||||||
|
func (t ISOTime) String() string {
|
||||||
|
return time.Time(t).Format(iso8601)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotificationLevelValue represents a notification level.
|
||||||
|
type NotificationLevelValue int
|
||||||
|
|
||||||
|
// String implements the fmt.Stringer interface.
|
||||||
|
func (l NotificationLevelValue) String() string {
|
||||||
|
return notificationLevelNames[l]
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON implements the json.Marshaler interface.
|
||||||
|
func (l NotificationLevelValue) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(l.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON implements the json.Unmarshaler interface.
|
||||||
|
func (l *NotificationLevelValue) UnmarshalJSON(data []byte) error {
|
||||||
|
var raw interface{}
|
||||||
|
if err := json.Unmarshal(data, &raw); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch raw := raw.(type) {
|
||||||
|
case float64:
|
||||||
|
*l = NotificationLevelValue(raw)
|
||||||
|
case string:
|
||||||
|
*l = notificationLevelTypes[raw]
|
||||||
|
case nil:
|
||||||
|
// No action needed.
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("json: cannot unmarshal %T into Go value of type %T", raw, *l)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// List of valid notification levels.
|
||||||
|
const (
|
||||||
|
DisabledNotificationLevel NotificationLevelValue = iota
|
||||||
|
ParticipatingNotificationLevel
|
||||||
|
WatchNotificationLevel
|
||||||
|
GlobalNotificationLevel
|
||||||
|
MentionNotificationLevel
|
||||||
|
CustomNotificationLevel
|
||||||
|
)
|
||||||
|
|
||||||
|
var notificationLevelNames = [...]string{
|
||||||
|
"disabled",
|
||||||
|
"participating",
|
||||||
|
"watch",
|
||||||
|
"global",
|
||||||
|
"mention",
|
||||||
|
"custom",
|
||||||
|
}
|
||||||
|
|
||||||
|
var notificationLevelTypes = map[string]NotificationLevelValue{
|
||||||
|
"disabled": DisabledNotificationLevel,
|
||||||
|
"participating": ParticipatingNotificationLevel,
|
||||||
|
"watch": WatchNotificationLevel,
|
||||||
|
"global": GlobalNotificationLevel,
|
||||||
|
"mention": MentionNotificationLevel,
|
||||||
|
"custom": CustomNotificationLevel,
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotificationLevel is a helper routine that allocates a new NotificationLevelValue
|
||||||
|
// to store v and returns a pointer to it.
|
||||||
|
func NotificationLevel(v NotificationLevelValue) *NotificationLevelValue {
|
||||||
|
p := new(NotificationLevelValue)
|
||||||
|
*p = v
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// VisibilityValue represents a visibility level within GitLab.
|
||||||
|
//
|
||||||
|
// GitLab API docs: https://docs.gitlab.com/ce/api/
|
||||||
|
type VisibilityValue string
|
||||||
|
|
||||||
|
// List of available visibility levels.
|
||||||
|
//
|
||||||
|
// GitLab API docs: https://docs.gitlab.com/ce/api/
|
||||||
|
const (
|
||||||
|
PrivateVisibility VisibilityValue = "private"
|
||||||
|
InternalVisibility VisibilityValue = "internal"
|
||||||
|
PublicVisibility VisibilityValue = "public"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Visibility is a helper routine that allocates a new VisibilityValue
|
||||||
|
// to store v and returns a pointer to it.
|
||||||
|
func Visibility(v VisibilityValue) *VisibilityValue {
|
||||||
|
p := new(VisibilityValue)
|
||||||
|
*p = v
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProjectCreationLevelValue represents a project creation level within GitLab.
|
||||||
|
//
|
||||||
|
// GitLab API docs: https://docs.gitlab.com/ce/api/
|
||||||
|
type ProjectCreationLevelValue string
|
||||||
|
|
||||||
|
// List of available project creation levels.
|
||||||
|
//
|
||||||
|
// GitLab API docs: https://docs.gitlab.com/ce/api/
|
||||||
|
const (
|
||||||
|
NoOneProjectCreation ProjectCreationLevelValue = "noone"
|
||||||
|
MaintainerProjectCreation ProjectCreationLevelValue = "maintainer"
|
||||||
|
DeveloperProjectCreation ProjectCreationLevelValue = "developer"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ProjectCreationLevel is a helper routine that allocates a new ProjectCreationLevelValue
|
||||||
|
// to store v and returns a pointer to it.
|
||||||
|
func ProjectCreationLevel(v ProjectCreationLevelValue) *ProjectCreationLevelValue {
|
||||||
|
p := new(ProjectCreationLevelValue)
|
||||||
|
*p = v
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubGroupCreationLevelValue represents a sub group creation level within GitLab.
|
||||||
|
//
|
||||||
|
// GitLab API docs: https://docs.gitlab.com/ce/api/
|
||||||
|
type SubGroupCreationLevelValue string
|
||||||
|
|
||||||
|
// List of available sub group creation levels.
|
||||||
|
//
|
||||||
|
// GitLab API docs: https://docs.gitlab.com/ce/api/
|
||||||
|
const (
|
||||||
|
OwnerSubGroupCreationLevelValue SubGroupCreationLevelValue = "owner"
|
||||||
|
MaintainerSubGroupCreationLevelValue SubGroupCreationLevelValue = "maintainer"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SubGroupCreationLevel is a helper routine that allocates a new SubGroupCreationLevelValue
|
||||||
|
// to store v and returns a pointer to it.
|
||||||
|
func SubGroupCreationLevel(v SubGroupCreationLevelValue) *SubGroupCreationLevelValue {
|
||||||
|
p := new(SubGroupCreationLevelValue)
|
||||||
|
*p = v
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// VariableTypeValue represents a variable type within GitLab.
|
||||||
|
//
|
||||||
|
// GitLab API docs: https://docs.gitlab.com/ce/api/
|
||||||
|
type VariableTypeValue string
|
||||||
|
|
||||||
|
// List of available variable types.
|
||||||
|
//
|
||||||
|
// GitLab API docs: https://docs.gitlab.com/ce/api/
|
||||||
|
const (
|
||||||
|
EnvVariableType VariableTypeValue = "env_var"
|
||||||
|
FileVariableType VariableTypeValue = "file"
|
||||||
|
)
|
||||||
|
|
||||||
|
// VariableType is a helper routine that allocates a new VariableTypeValue
|
||||||
|
// to store v and returns a pointer to it.
|
||||||
|
func VariableType(v VariableTypeValue) *VariableTypeValue {
|
||||||
|
p := new(VariableTypeValue)
|
||||||
|
*p = v
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// MergeMethodValue represents a project merge type within GitLab.
|
||||||
|
//
|
||||||
|
// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#project-merge-method
|
||||||
|
type MergeMethodValue string
|
||||||
|
|
||||||
|
// List of available merge type
|
||||||
|
//
|
||||||
|
// GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#project-merge-method
|
||||||
|
const (
|
||||||
|
NoFastForwardMerge MergeMethodValue = "merge"
|
||||||
|
FastForwardMerge MergeMethodValue = "ff"
|
||||||
|
RebaseMerge MergeMethodValue = "rebase_merge"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MergeMethod is a helper routine that allocates a new MergeMethod
|
||||||
|
// to sotre v and returns a pointer to it.
|
||||||
|
func MergeMethod(v MergeMethodValue) *MergeMethodValue {
|
||||||
|
p := new(MergeMethodValue)
|
||||||
|
*p = v
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// EventTypeValue represents actions type for contribution events
|
||||||
|
type EventTypeValue string
|
||||||
|
|
||||||
|
// List of available action type
|
||||||
|
//
|
||||||
|
// GitLab API docs: https://docs.gitlab.com/ce/api/events.html#action-types
|
||||||
|
const (
|
||||||
|
CreatedEventType EventTypeValue = "created"
|
||||||
|
UpdatedEventType EventTypeValue = "updated"
|
||||||
|
ClosedEventType EventTypeValue = "closed"
|
||||||
|
ReopenedEventType EventTypeValue = "reopened"
|
||||||
|
PushedEventType EventTypeValue = "pushed"
|
||||||
|
CommentedEventType EventTypeValue = "commented"
|
||||||
|
MergedEventType EventTypeValue = "merged"
|
||||||
|
JoinedEventType EventTypeValue = "joined"
|
||||||
|
LeftEventType EventTypeValue = "left"
|
||||||
|
DestroyedEventType EventTypeValue = "destroyed"
|
||||||
|
ExpiredEventType EventTypeValue = "expired"
|
||||||
|
)
|
||||||
|
|
||||||
|
// EventTargetTypeValue represents actions type value for contribution events
|
||||||
|
type EventTargetTypeValue string
|
||||||
|
|
||||||
|
// List of available action type
|
||||||
|
//
|
||||||
|
// GitLab API docs: https://docs.gitlab.com/ce/api/events.html#target-types
|
||||||
|
const (
|
||||||
|
IssueEventTargetType EventTargetTypeValue = "issue"
|
||||||
|
MilestoneEventTargetType EventTargetTypeValue = "milestone"
|
||||||
|
MergeRequestEventTargetType EventTargetTypeValue = "merge_request"
|
||||||
|
NoteEventTargetType EventTargetTypeValue = "note"
|
||||||
|
ProjectEventTargetType EventTargetTypeValue = "project"
|
||||||
|
SnippetEventTargetType EventTargetTypeValue = "snippet"
|
||||||
|
UserEventTargetType EventTargetTypeValue = "user"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Bool is a helper routine that allocates a new bool value
|
||||||
|
// to store v and returns a pointer to it.
|
||||||
|
func Bool(v bool) *bool {
|
||||||
|
p := new(bool)
|
||||||
|
*p = v
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int is a helper routine that allocates a new int32 value
|
||||||
|
// to store v and returns a pointer to it, but unlike Int32
|
||||||
|
// its argument value is an int.
|
||||||
|
func Int(v int) *int {
|
||||||
|
p := new(int)
|
||||||
|
*p = v
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// String is a helper routine that allocates a new string value
|
||||||
|
// to store v and returns a pointer to it.
|
||||||
|
func String(v string) *string {
|
||||||
|
p := new(string)
|
||||||
|
*p = v
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// Time is a helper routine that allocates a new time.Time value
|
||||||
|
// to store v and returns a pointer to it.
|
||||||
|
func Time(v time.Time) *time.Time {
|
||||||
|
p := new(time.Time)
|
||||||
|
*p = v
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// BoolValue is a boolean value with advanced json unmarshaling features.
|
||||||
|
type BoolValue bool
|
||||||
|
|
||||||
|
// UnmarshalJSON allows 1, 0, "true", and "false" to be considered as boolean values
|
||||||
|
// Needed for:
|
||||||
|
// https://gitlab.com/gitlab-org/gitlab-ce/issues/50122
|
||||||
|
// https://gitlab.com/gitlab-org/gitlab/-/issues/233941
|
||||||
|
// https://github.com/gitlabhq/terraform-provider-gitlab/issues/348
|
||||||
|
func (t *BoolValue) UnmarshalJSON(b []byte) error {
|
||||||
|
switch string(b) {
|
||||||
|
case `"1"`:
|
||||||
|
*t = true
|
||||||
|
return nil
|
||||||
|
case `"0"`:
|
||||||
|
*t = false
|
||||||
|
return nil
|
||||||
|
case `"true"`:
|
||||||
|
*t = true
|
||||||
|
return nil
|
||||||
|
case `"false"`:
|
||||||
|
*t = false
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
var v bool
|
||||||
|
err := json.Unmarshal(b, &v)
|
||||||
|
*t = BoolValue(v)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
|
@ -112,6 +112,7 @@ type ListUsersOptions struct {
|
||||||
CreatedAfter *time.Time `url:"created_after,omitempty" json:"created_after,omitempty"`
|
CreatedAfter *time.Time `url:"created_after,omitempty" json:"created_after,omitempty"`
|
||||||
OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"`
|
OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"`
|
||||||
Sort *string `url:"sort,omitempty" json:"sort,omitempty"`
|
Sort *string `url:"sort,omitempty" json:"sort,omitempty"`
|
||||||
|
External *bool `url:"external,omitempty" json:"external,omitempty"`
|
||||||
WithCustomAttributes *bool `url:"with_custom_attributes,omitempty" json:"with_custom_attributes,omitempty"`
|
WithCustomAttributes *bool `url:"with_custom_attributes,omitempty" json:"with_custom_attributes,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -829,6 +830,7 @@ type UserActivity struct {
|
||||||
// GitLap API docs:
|
// GitLap API docs:
|
||||||
// https://docs.gitlab.com/ce/api/users.html#get-user-activities-admin-only
|
// https://docs.gitlab.com/ce/api/users.html#get-user-activities-admin-only
|
||||||
type GetUserActivitiesOptions struct {
|
type GetUserActivitiesOptions struct {
|
||||||
|
ListOptions
|
||||||
From *ISOTime `url:"from,omitempty" json:"from,omitempty"`
|
From *ISOTime `url:"from,omitempty" json:"from,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -53,10 +53,9 @@ func Every(interval time.Duration) Limit {
|
||||||
//
|
//
|
||||||
// The methods AllowN, ReserveN, and WaitN consume n tokens.
|
// The methods AllowN, ReserveN, and WaitN consume n tokens.
|
||||||
type Limiter struct {
|
type Limiter struct {
|
||||||
limit Limit
|
|
||||||
burst int
|
|
||||||
|
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
|
limit Limit
|
||||||
|
burst int
|
||||||
tokens float64
|
tokens float64
|
||||||
// last is the last time the limiter's tokens field was updated
|
// last is the last time the limiter's tokens field was updated
|
||||||
last time.Time
|
last time.Time
|
||||||
|
@ -76,6 +75,8 @@ func (lim *Limiter) Limit() Limit {
|
||||||
// Burst values allow more events to happen at once.
|
// Burst values allow more events to happen at once.
|
||||||
// A zero Burst allows no events, unless limit == Inf.
|
// A zero Burst allows no events, unless limit == Inf.
|
||||||
func (lim *Limiter) Burst() int {
|
func (lim *Limiter) Burst() int {
|
||||||
|
lim.mu.Lock()
|
||||||
|
defer lim.mu.Unlock()
|
||||||
return lim.burst
|
return lim.burst
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -229,7 +230,7 @@ func (lim *Limiter) WaitN(ctx context.Context, n int) (err error) {
|
||||||
lim.mu.Unlock()
|
lim.mu.Unlock()
|
||||||
|
|
||||||
if n > burst && limit != Inf {
|
if n > burst && limit != Inf {
|
||||||
return fmt.Errorf("rate: Wait(n=%d) exceeds limiter's burst %d", n, lim.burst)
|
return fmt.Errorf("rate: Wait(n=%d) exceeds limiter's burst %d", n, burst)
|
||||||
}
|
}
|
||||||
// Check if ctx is already cancelled
|
// Check if ctx is already cancelled
|
||||||
select {
|
select {
|
||||||
|
@ -359,6 +360,7 @@ func (lim *Limiter) reserveN(now time.Time, n int, maxFutureReserve time.Duratio
|
||||||
|
|
||||||
// advance calculates and returns an updated state for lim resulting from the passage of time.
|
// advance calculates and returns an updated state for lim resulting from the passage of time.
|
||||||
// lim is not changed.
|
// lim is not changed.
|
||||||
|
// advance requires that lim.mu is held.
|
||||||
func (lim *Limiter) advance(now time.Time) (newNow time.Time, newLast time.Time, newTokens float64) {
|
func (lim *Limiter) advance(now time.Time) (newNow time.Time, newLast time.Time, newTokens float64) {
|
||||||
last := lim.last
|
last := lim.last
|
||||||
if now.Before(last) {
|
if now.Before(last) {
|
||||||
|
|
|
@ -198,8 +198,12 @@ func TypeExpr(fset *token.FileSet, f *ast.File, pkg *types.Package, typ types.Ty
|
||||||
X: ast.NewIdent(pkgName),
|
X: ast.NewIdent(pkgName),
|
||||||
Sel: ast.NewIdent(t.Obj().Name()),
|
Sel: ast.NewIdent(t.Obj().Name()),
|
||||||
}
|
}
|
||||||
|
case *types.Struct:
|
||||||
|
return ast.NewIdent(t.String())
|
||||||
|
case *types.Interface:
|
||||||
|
return ast.NewIdent(t.String())
|
||||||
default:
|
default:
|
||||||
return nil // TODO: anonymous structs, but who does that
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -693,8 +693,8 @@ func candidateImportName(pkg *pkg) string {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAllCandidates gets all of the packages starting with prefix that can be
|
// GetAllCandidates calls wrapped for each package whose name starts with
|
||||||
// imported by filename, sorted by import path.
|
// searchPrefix, and can be imported from filename with the package name filePkg.
|
||||||
func GetAllCandidates(ctx context.Context, wrapped func(ImportFix), searchPrefix, filename, filePkg string, env *ProcessEnv) error {
|
func GetAllCandidates(ctx context.Context, wrapped func(ImportFix), searchPrefix, filename, filePkg string, env *ProcessEnv) error {
|
||||||
callback := &scanCallback{
|
callback := &scanCallback{
|
||||||
rootFound: func(gopathwalk.Root) bool {
|
rootFound: func(gopathwalk.Root) bool {
|
||||||
|
@ -728,6 +728,35 @@ func GetAllCandidates(ctx context.Context, wrapped func(ImportFix), searchPrefix
|
||||||
return getCandidatePkgs(ctx, callback, filename, filePkg, env)
|
return getCandidatePkgs(ctx, callback, filename, filePkg, env)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetImportPaths calls wrapped for each package whose import path starts with
|
||||||
|
// searchPrefix, and can be imported from filename with the package name filePkg.
|
||||||
|
func GetImportPaths(ctx context.Context, wrapped func(ImportFix), searchPrefix, filename, filePkg string, env *ProcessEnv) error {
|
||||||
|
callback := &scanCallback{
|
||||||
|
rootFound: func(gopathwalk.Root) bool {
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
dirFound: func(pkg *pkg) bool {
|
||||||
|
if !canUse(filename, pkg.dir) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return strings.HasPrefix(pkg.importPathShort, searchPrefix)
|
||||||
|
},
|
||||||
|
packageNameLoaded: func(pkg *pkg) bool {
|
||||||
|
wrapped(ImportFix{
|
||||||
|
StmtInfo: ImportInfo{
|
||||||
|
ImportPath: pkg.importPathShort,
|
||||||
|
Name: candidateImportName(pkg),
|
||||||
|
},
|
||||||
|
IdentName: pkg.packageName,
|
||||||
|
FixType: AddImport,
|
||||||
|
Relevance: pkg.relevance,
|
||||||
|
})
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return getCandidatePkgs(ctx, callback, filename, filePkg, env)
|
||||||
|
}
|
||||||
|
|
||||||
// A PackageExport is a package and its exports.
|
// A PackageExport is a package and its exports.
|
||||||
type PackageExport struct {
|
type PackageExport struct {
|
||||||
Fix *ImportFix
|
Fix *ImportFix
|
||||||
|
|
|
@ -420,7 +420,7 @@ github.com/gorilla/securecookie
|
||||||
github.com/gorilla/sessions
|
github.com/gorilla/sessions
|
||||||
# github.com/hashicorp/go-cleanhttp v0.5.1
|
# github.com/hashicorp/go-cleanhttp v0.5.1
|
||||||
github.com/hashicorp/go-cleanhttp
|
github.com/hashicorp/go-cleanhttp
|
||||||
# github.com/hashicorp/go-retryablehttp v0.6.6
|
# github.com/hashicorp/go-retryablehttp v0.6.7
|
||||||
## explicit
|
## explicit
|
||||||
github.com/hashicorp/go-retryablehttp
|
github.com/hashicorp/go-retryablehttp
|
||||||
# github.com/hashicorp/go-version v0.0.0-00010101000000-000000000000 => github.com/6543/go-version v1.2.3
|
# github.com/hashicorp/go-version v0.0.0-00010101000000-000000000000 => github.com/6543/go-version v1.2.3
|
||||||
|
@ -728,7 +728,7 @@ github.com/unknwon/paginater
|
||||||
github.com/urfave/cli
|
github.com/urfave/cli
|
||||||
# github.com/willf/bitset v1.1.10
|
# github.com/willf/bitset v1.1.10
|
||||||
github.com/willf/bitset
|
github.com/willf/bitset
|
||||||
# github.com/xanzy/go-gitlab v0.31.0
|
# github.com/xanzy/go-gitlab v0.37.0
|
||||||
## explicit
|
## explicit
|
||||||
github.com/xanzy/go-gitlab
|
github.com/xanzy/go-gitlab
|
||||||
# github.com/xanzy/ssh-agent v0.2.1
|
# github.com/xanzy/ssh-agent v0.2.1
|
||||||
|
@ -846,10 +846,10 @@ golang.org/x/text/transform
|
||||||
golang.org/x/text/unicode/bidi
|
golang.org/x/text/unicode/bidi
|
||||||
golang.org/x/text/unicode/norm
|
golang.org/x/text/unicode/norm
|
||||||
golang.org/x/text/width
|
golang.org/x/text/width
|
||||||
# golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1
|
# golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e
|
||||||
## explicit
|
## explicit
|
||||||
golang.org/x/time/rate
|
golang.org/x/time/rate
|
||||||
# golang.org/x/tools v0.0.0-20200814230902-9882f1d1823d
|
# golang.org/x/tools v0.0.0-20200825202427-b303f430e36d
|
||||||
## explicit
|
## explicit
|
||||||
golang.org/x/tools/cover
|
golang.org/x/tools/cover
|
||||||
golang.org/x/tools/go/analysis
|
golang.org/x/tools/go/analysis
|
||||||
|
|
Loading…
Reference in New Issue