diff --git a/go.mod b/go.mod index 2e356b8..fea6e26 100644 --- a/go.mod +++ b/go.mod @@ -3,10 +3,11 @@ module github.com/bitrise-steplib/steps-certificate-and-profile-installer go 1.15 require ( - github.com/bitrise-io/go-steputils v0.0.0-20210507072936-92fde382fb33 - github.com/bitrise-io/go-utils v0.0.0-20210506064210-b22e2b7b3ad3 - github.com/bitrise-io/go-xcode v0.0.0-20210507081243-7cf47826c379 - github.com/hashicorp/go-version v1.3.0 - github.com/pkg/errors v0.9.1 - github.com/stretchr/testify v1.7.0 + github.com/bitrise-io/go-steputils v1.0.2 + github.com/bitrise-io/go-steputils/v2 v2.0.0-alpha.2 + github.com/bitrise-io/go-utils v1.0.2 + github.com/bitrise-io/go-utils/v2 v2.0.0-alpha.11 + github.com/bitrise-io/go-xcode v1.0.10 + github.com/bitrise-io/go-xcode/v2 v2.0.0-alpha.24 + github.com/stretchr/testify v1.7.1 ) diff --git a/go.sum b/go.sum index 583570d..845b90e 100644 --- a/go.sum +++ b/go.sum @@ -1,74 +1,92 @@ -github.com/bitrise-io/go-steputils v0.0.0-20210505101226-8536c460c5c1/go.mod h1:gF9UfXaXyxkKLAKgo+RpZFsRemnTv6wAPTljpJXoxI8= -github.com/bitrise-io/go-steputils v0.0.0-20210507072936-92fde382fb33 h1:4QpxkX2oNefww7k76ZUD4C7unfP4c8H0c0QP7SdfrTQ= -github.com/bitrise-io/go-steputils v0.0.0-20210507072936-92fde382fb33/go.mod h1:YCtb1VETn/rF9tCt9oInhd/cwbt1ETPm+dTlDIfyD+A= -github.com/bitrise-io/go-utils v0.0.0-20201019131314-6cc2aa4d248a/go.mod h1:tTEsKvbz1LbzuN/KpVFHXnLtcAPdEgIdM41s0lL407s= -github.com/bitrise-io/go-utils v0.0.0-20210323091856-00429d8e1e87/go.mod h1:tTEsKvbz1LbzuN/KpVFHXnLtcAPdEgIdM41s0lL407s= -github.com/bitrise-io/go-utils v0.0.0-20210505121718-07411d72e36e/go.mod h1:nhdaDQFvaMny1CugVV6KjK92/q97ENo0RuKSW5I4fbA= -github.com/bitrise-io/go-utils v0.0.0-20210506064210-b22e2b7b3ad3 h1:XVwIES1M3PdXr8gUIlG5LLUe6TC6Gfl1j/pB27ehbME= -github.com/bitrise-io/go-utils v0.0.0-20210506064210-b22e2b7b3ad3/go.mod h1:nhdaDQFvaMny1CugVV6KjK92/q97ENo0RuKSW5I4fbA= -github.com/bitrise-io/go-xcode v0.0.0-20210507081243-7cf47826c379 h1:HhaFXhGzk0oxU4gc+P+pHEbR9rhA4xhvEpAn93tRB8A= -github.com/bitrise-io/go-xcode v0.0.0-20210507081243-7cf47826c379/go.mod h1:Uf8cyzwr2RkrSMGonihVOOxMoQoR8UI7XeqHOGJTekI= -github.com/bitrise-io/pkcs12 v0.0.0-20210430063833-0da06eb56630 h1:V+xoYqGSkN8aUxCc806zDKjGGpBVUtV0Vytf5OsB3gc= -github.com/bitrise-io/pkcs12 v0.0.0-20210430063833-0da06eb56630/go.mod h1:UiXKNs0essbC14a2TvGlnUKo9isP9m4guPrp8KJHJpU= -github.com/bitrise-io/xcode-project v0.0.0-20201203153351-7ad13a1dd021/go.mod h1:R2iDrjJlNQtVwIdXT+F9bcx/YTNJPdd0tXfFRJsxHaM= +github.com/bitrise-io/go-plist v0.0.0-20210301100253-4b1a112ccd10 h1:/2OyBFI7GjYKexBPcfTPvKFz8Ks7qYzkkz2SQ8aiJgc= +github.com/bitrise-io/go-plist v0.0.0-20210301100253-4b1a112ccd10/go.mod h1:pARutiL3kEuRLV3JvswidvfCj+9Y3qMZtji2BDqLFsA= +github.com/bitrise-io/go-steputils v1.0.1/go.mod h1:YIUaQnIAyK4pCvQG0hYHVkSzKNT9uL2FWmkFNW4mfNI= +github.com/bitrise-io/go-steputils v1.0.2 h1:BEFG87r7uA/Yabk4SmuxP2yOgjjO+YGsDOYXtUH8IJ0= +github.com/bitrise-io/go-steputils v1.0.2/go.mod h1:YIUaQnIAyK4pCvQG0hYHVkSzKNT9uL2FWmkFNW4mfNI= +github.com/bitrise-io/go-steputils/v2 v2.0.0-alpha.2 h1:WfhgPqLyg+VPNb6istzlJqalk81kb9Wt9IcQIQTOsxE= +github.com/bitrise-io/go-steputils/v2 v2.0.0-alpha.2/go.mod h1:OC0mHpjD/bqmsHlhG+FWgTouBbcJvmyx896PDP3dRBs= +github.com/bitrise-io/go-utils v1.0.1/go.mod h1:ZY1DI+fEpZuFpO9szgDeICM4QbqoWVt0RSY3tRI1heY= +github.com/bitrise-io/go-utils v1.0.2 h1:w4Mz2IvrgDzrFJECuHdvsK1LHO30cdtuy9bBa7Lw2c0= +github.com/bitrise-io/go-utils v1.0.2/go.mod h1:ZY1DI+fEpZuFpO9szgDeICM4QbqoWVt0RSY3tRI1heY= +github.com/bitrise-io/go-utils/v2 v2.0.0-alpha.1/go.mod h1:sy+Ir1X8P3tAAx/qU/r+hqDjHDcrMjIzDEvId1wqNc4= +github.com/bitrise-io/go-utils/v2 v2.0.0-alpha.11 h1:IacLMHL7hhgVcqtx15Bysq738P8FRCp6ckGk1NvioWo= +github.com/bitrise-io/go-utils/v2 v2.0.0-alpha.11/go.mod h1:SJqGxzwjIAx2LVQxNGS4taN7X//eDPJLrFxJ1MpOuyA= +github.com/bitrise-io/go-xcode v1.0.9/go.mod h1:Y0Wu2dXm0MilJ/4D3+gPHaNMlUcP+1DjIPoLPykq7wY= +github.com/bitrise-io/go-xcode v1.0.10 h1:8Lfp+lKBXiNKAgKUEQ65gczBQrtSt9ediPXKsHMR4c0= +github.com/bitrise-io/go-xcode v1.0.10/go.mod h1:Y0Wu2dXm0MilJ/4D3+gPHaNMlUcP+1DjIPoLPykq7wY= +github.com/bitrise-io/go-xcode/v2 v2.0.0-alpha.24 h1:IoycjurbkHOTCB+cpQwrQs4XCfrk9Oa9hpMOvSPvxsU= +github.com/bitrise-io/go-xcode/v2 v2.0.0-alpha.24/go.mod h1:8WBcRgrVXY8tzR7NcjE4fw6WguOIfB3YcC7ZTcQYUEY= +github.com/bitrise-io/pkcs12 v0.0.0-20211108084543-e52728e011c8 h1:kmvU8AxrNTxXsVPKepBHD8W+eCVmeaKyTkRuUJB2K38= +github.com/bitrise-io/pkcs12 v0.0.0-20211108084543-e52728e011c8/go.mod h1:UiXKNs0essbC14a2TvGlnUKo9isP9m4guPrp8KJHJpU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa h1:RDBNVkRviHZtvDvId8XSGPu3rmpmSe+wKRcEWNgsfWU= github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/go-version v1.3.0 h1:McDWVJIU/y+u1BRV06dPaLfLCaT7fUTJLp5r04x7iNw= +github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/golang-jwt/jwt/v4 v4.4.1 h1:pC5DB52sCeK48Wlb9oPcdhnjkz1TKt1D/P7WKJ0kUcQ= +github.com/golang-jwt/jwt/v4 v4.4.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= +github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI= +github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= +github.com/hashicorp/go-retryablehttp v0.7.0/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= +github.com/hashicorp/go-retryablehttp v0.7.1 h1:sUiuQAnLlbvmExtFQs72iFW/HXeUn8Z1aJLQ4LJJbTQ= +github.com/hashicorp/go-retryablehttp v0.7.1/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= github.com/hashicorp/go-version v1.3.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.5.0 h1:O293SZ2Eg+AAYijkVK3jR786Am1bhDEh2GHT0tIVE5E= +github.com/hashicorp/go-version v1.5.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.3.0 h1:NGXK3lHquSN08v5vWalVI/L8XU9hdzE/G6xsrze47As= github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200219091948-cb0a6d8edb6c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +golang.org/x/crypto v0.0.0-20211202192323-5770296d904e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220712014510-0a85c31ab51e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200220224806-8a925fa4c0df/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -howett.net/plist v0.0.0-20201203080718-1454fab16a06 h1:QDxUo/w2COstK1wIBYpzQlHX/NqaQTcf9jyz347nI58= -howett.net/plist v0.0.0-20201203080718-1454fab16a06/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0= +gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA= +gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM= +howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g= diff --git a/main.go b/main.go index 049b48a..c9612ea 100644 --- a/main.go +++ b/main.go @@ -2,24 +2,21 @@ package main import ( "fmt" - "io" - "net/http" - "net/url" "os" - "path" "strings" - "time" "unicode/utf8" "github.com/bitrise-io/go-steputils/input" - "github.com/bitrise-io/go-utils/command" + "github.com/bitrise-io/go-steputils/v2/stepconf" "github.com/bitrise-io/go-utils/log" - "github.com/bitrise-io/go-utils/pathutil" + "github.com/bitrise-io/go-utils/retry" + "github.com/bitrise-io/go-utils/v2/command" + "github.com/bitrise-io/go-utils/v2/env" "github.com/bitrise-io/go-xcode/certificateutil" - "github.com/bitrise-io/go-xcode/plistutil" - "github.com/bitrise-io/go-xcode/profileutil" - version "github.com/hashicorp/go-version" - "github.com/pkg/errors" + "github.com/bitrise-io/go-xcode/v2/autocodesign/certdownloader" + "github.com/bitrise-io/go-xcode/v2/autocodesign/codesignasset" + "github.com/bitrise-io/go-xcode/v2/autocodesign/keychain" + "github.com/bitrise-io/go-xcode/v2/autocodesign/profiledownloader" ) // Config ... @@ -132,72 +129,6 @@ func (c Config) validate() error { return nil } -func downloadFile(destionationPath, URL string) error { - url, err := url.Parse(URL) - if err != nil { - return err - } - - scheme := url.Scheme - - tmpDstFilePath := "" - if scheme != "file" { - tmpDir, err := pathutil.NormalizedOSTempDirPath("download") - if err != nil { - return err - } - - tmpDst := path.Join(tmpDir, "tmp_file") - tmpDstFile, err := os.Create(tmpDst) - if err != nil { - return err - } - defer func() { - if err := tmpDstFile.Close(); err != nil { - log.Errorf("Failed to close file (%s), error: %s", tmpDst, err) - } - }() - - success := false - var response *http.Response - for i := 0; i < 3 && !success; i++ { - if i > 0 { - fmt.Println("-> Retrying...") - time.Sleep(3 * time.Second) - } - - response, err = http.Get(URL) - if err != nil { - log.Errorf(err.Error()) - } else { - success = true - } - - if response != nil { - defer func() { - if err := response.Body.Close(); err != nil { - log.Errorf("Failed to close response body, error: %s", err) - } - }() - } - } - if !success { - return err - } - - _, err = io.Copy(tmpDstFile, response.Body) - if err != nil { - return err - } - - tmpDstFilePath = tmpDstFile.Name() - } else { - tmpDstFilePath = strings.Replace(URL, scheme+"://", "", -1) - } - - return command.CopyFile(tmpDstFilePath, destionationPath) -} - func strip(str string) string { str = strings.TrimSpace(str) return strings.Trim(str, "\"") @@ -248,69 +179,11 @@ func printCertificateInfo(info certificateutil.CertificateInfoModel) { log.Donef(info.CommonName) log.Printf("serial: %s", info.Serial) log.Printf("team: %s (%s)", info.TeamName, info.TeamID) - log.Printf("expire: %s", info.EndDate) - - if err := info.CheckValidity(); err != nil { - log.Errorf("[X] %s", err) - } -} - -func collectCapabilities(profileType profileutil.ProfileType, entitlements plistutil.PlistData) map[string]interface{} { - capabilities := map[string]interface{}{} - for key, value := range entitlements { - found := profileutil.KnownProfileCapabilitiesMap[profileType][key] - if found { - capabilities[key] = value - } - } - return capabilities -} - -func printProfileInfo(profileType profileutil.ProfileType, info profileutil.ProvisioningProfileInfoModel, installedCertificates []certificateutil.CertificateInfoModel) { - log.Donef("%s (%s)", info.Name, info.UUID) - log.Printf("exportType: %s", string(info.ExportType)) - log.Printf("team: %s (%s)", info.TeamName, info.TeamID) - log.Printf("bundleID: %s", info.BundleID) - - capabilities := collectCapabilities(profileType, info.Entitlements) - if len(capabilities) > 0 { - log.Printf("capabilities:") - for key, value := range capabilities { - log.Printf("- %s: %v", key, value) - } - } - - log.Printf("certificates:") - for _, certificateInfo := range info.DeveloperCertificates { - log.Printf("- %s", certificateInfo.CommonName) - log.Printf(" serial: %s", certificateInfo.Serial) - log.Printf(" teamID: %s", certificateInfo.TeamID) - } - - if len(info.ProvisionedDevices) > 0 { - log.Printf("devices:") - for _, deviceID := range info.ProvisionedDevices { - log.Printf("- %s", deviceID) - } - } - - log.Printf("expire: %s", info.ExpirationDate) - - if !info.HasInstalledCertificate(installedCertificates) { - log.Errorf("[X] none of the profile's certificates are installed") - } + log.Printf("expiry: %s", info.EndDate) if err := info.CheckValidity(); err != nil { log.Errorf("[X] %s", err) } - - if info.IsXcodeManaged() { - log.Warnf("[!] xcode managed profile") - } -} - -func commandError(printableCmd string, cmdOut string, cmdErr error) error { - return errors.Wrapf(cmdErr, "%s failed, out: %s", printableCmd, cmdOut) } func failF(format string, v ...interface{}) { @@ -332,7 +205,7 @@ func main() { fmt.Println() // Collect Certificates - certificateURLPassphraseMap := map[string]string{} + var certificateURLPassphraseMap []certdownloader.CertificateAndPassphrase if configs.CertificateURL != "" { certificateURLs := splitAndTrimSpace(configs.CertificateURL, "|") @@ -343,7 +216,7 @@ func main() { if len(certificateURLs) != len(certificatePassphrases) { failF( "Certificate URL count: (%d), is not equal to Certificate passphrase count: (%d).\n"+ - "This could be because one of your passphrases contains a pipe character (\"|\") " + + "This could be because one of your passphrases contains a pipe character (\"|\") "+ "which is not supported, as it is used as the delimiter in the step input.", len(certificateURLs), len(certificatePassphrases), @@ -354,13 +227,19 @@ func main() { certificateURL := certificateURLs[i] certificatePassphrase := certificatePassphrases[i] - certificateURLPassphraseMap[certificateURL] = certificatePassphrase + certificateURLPassphraseMap = append(certificateURLPassphraseMap, certdownloader.CertificateAndPassphrase{ + URL: certificateURL, + Passphrase: certificatePassphrase, + }) } } if configs.DefaultCertificateURL != "" && configs.InstallDefaults == "yes" { log.Printf("Default Certificate given") - certificateURLPassphraseMap[configs.DefaultCertificateURL] = configs.DefaultCertificatePassphrase + certificateURLPassphraseMap = append(certificateURLPassphraseMap, certdownloader.CertificateAndPassphrase{ + URL: configs.DefaultCertificateURL, + Passphrase: configs.DefaultCertificatePassphrase, + }) } certificateCount := len(certificateURLPassphraseMap) @@ -385,189 +264,60 @@ func main() { log.Warnf("No Provisioning Profile provided") } - // Init - homeDir := os.Getenv("HOME") - provisioningProfileDir := path.Join(homeDir, "Library/MobileDevice/Provisioning Profiles") - if exist, err := pathutil.IsPathExists(provisioningProfileDir); err != nil { - failF("Failed to check path (%s), err: %s", provisioningProfileDir, err) - } else if !exist { - if err := os.MkdirAll(provisioningProfileDir, 0777); err != nil { - failF("Failed to create path (%s), err: %s", provisioningProfileDir, err) - } - } - - tempDir, err := pathutil.NormalizedOSTempDirPath("bitrise-cert-tmp") + keychainWriter, err := keychain.New(configs.KeychainPath, stepconf.Secret(configs.KeychainPassword), command.NewFactory(env.NewRepository())) if err != nil { - failF("Failed to create tmp directory, err: %s", err) + failE(fmt.Errorf("Failed to open Keychain: %w", err)) } - if exist, err := pathutil.IsPathExists(configs.KeychainPath); err != nil { - failF("Failed to check path (%s), err: %s", configs.KeychainPath, err) - } else if !exist { - fmt.Println() - log.Warnf("Keychain (%s) does not exist", configs.KeychainPath) + httpClient := retry.NewHTTPClient().StandardClient() + certDownloader := certdownloader.NewDownloader(certificateURLPassphraseMap, httpClient) + profileDownloader := profiledownloader.New(provisioningProfileURLs, httpClient) + assetInstaller := codesignasset.NewWriter(*keychainWriter) - keychainPth := fmt.Sprintf("%s-db", configs.KeychainPath) - - log.Printf(" Checking (%s)", keychainPth) - - if exist, err := pathutil.IsPathExists(keychainPth); err != nil { - failF("Failed to check path (%s), err: %s", keychainPth, err) - } else if !exist { - log.Infof("Creating keychain: %s", configs.KeychainPath) - - cmd := command.New("security", "-v", "create-keychain", "-p", configs.KeychainPassword, configs.KeychainPath) - if out, err := cmd.RunAndReturnTrimmedCombinedOutput(); err != nil { - failE(commandError(cmd.PrintableCommandArgs(), out, err)) - } - } - } else { - log.Printf("Keychain already exists, using it: %s", configs.KeychainPath) - } - - // - // Download certificate fmt.Println() - log.Infof("Downloading & installing Certificate(s)") - fmt.Println() - - certificatePassphraseMap := map[string]string{} - idx := 0 - for certURL, pass := range certificateURLPassphraseMap { - log.Printf("Downloading certificate: %d/%d", idx+1, certificateCount) - - certPath := path.Join(tempDir, fmt.Sprintf("Certificate-%d.p12", idx)) - if err := downloadFile(certPath, certURL); err != nil { - failF("Download failed, err: %s", err) - } - certificatePassphraseMap[certPath] = pass + log.Infof("Downloading Certificate(s)...") - idx++ + certificates, err := certDownloader.GetCertificates() + if err != nil { + failE(fmt.Errorf("Download failed: %w", err)) } - // - // Install certificate - log.Printf("Installing downloaded certificates") + log.Printf("%d Certificate(s) downloaded.", len(certificates)) + fmt.Println() + log.Infof("Installing downloaded Certificates") - installedCertificates := []certificateutil.CertificateInfoModel{} + for i, cert := range certificates { + log.Printf("%d/%d Certificate:", i+1, len(certificates)) + printCertificateInfo(cert) - for cert, pass := range certificatePassphraseMap { - certInfos, err := certificateutil.CertificatesFromPKCS12File(cert, pass) - if err != nil { - failF("Failed to parse certificate, error: %s", err) + if err := assetInstaller.InstallCertificate(cert); err != nil { + failE(fmt.Errorf("Failed to install certificate: %w", err)) } - installedCertificates = append(installedCertificates, certInfos...) - for _, certInfo := range certInfos { - printCertificateInfo(certInfo) - } fmt.Println() - - // Unlock keychain (if locked) - cmd := command.New("security", "unlock-keychain", "-p", configs.KeychainPassword, configs.KeychainPath) - if out, err := cmd.RunAndReturnTrimmedCombinedOutput(); err != nil { - failE(commandError(cmd.PrintableCommandArgs(), out, err)) - } - - // Import items into a keychain. - cmd = command.New("security", "import", cert, "-k", configs.KeychainPath, "-P", pass, "-A") - if out, err := cmd.RunAndReturnTrimmedCombinedOutput(); err != nil { - failE(commandError(cmd.PrintableCommandArgs(), out, err)) - } - } - - // This is new behavior in Sierra, [openradar](https://openradar.appspot.com/28524119) - // You need to use "security set-key-partition-list -S apple-tool:,apple: -k keychainPass keychainName" after importing the item and before attempting to use it via codesign. - cmd := command.New("sw_vers", "-productVersion") - out, err := cmd.RunAndReturnTrimmedCombinedOutput() - if err != nil { - failE(commandError(cmd.PrintableCommandArgs(), out, err)) - } - - osVersion, err := version.NewVersion(out) - if err != nil { - failF("Failed to parse os version (%s), error: %s", out, err) } - sierraVersionStr := "10.12.0" - sierraVersion, err := version.NewVersion(sierraVersionStr) - if err != nil { - failF("Failed to parse os version (%s), error: %s", sierraVersionStr, err) - } - - if !osVersion.LessThan(sierraVersion) { - cmd := command.New("security", "set-key-partition-list", "-S", "apple-tool:,apple:", "-k", configs.KeychainPassword, configs.KeychainPath) - if out, err := cmd.RunAndReturnTrimmedCombinedOutput(); err != nil { - failE(commandError(cmd.PrintableCommandArgs(), out, err)) - } - } - // --- - - // Set keychain settings: Lock keychain when the system sleeps, Lock keychain after timeout interval, Timeout in seconds - cmd = command.New("security", "-v", "set-keychain-settings", "-lut", "72000", configs.KeychainPath) - if out, err := cmd.RunAndReturnTrimmedCombinedOutput(); err != nil { - failE(commandError(cmd.PrintableCommandArgs(), out, err)) - } + fmt.Println() + log.Infof("Downloading Provisioning Profile(s)...") - // List keychains - cmd = command.New("security", "list-keychains") - listKeychainsOut, err := cmd.RunAndReturnTrimmedCombinedOutput() + profiles, err := profileDownloader.GetProfiles() if err != nil { - failE(commandError(cmd.PrintableCommandArgs(), listKeychainsOut, err)) - } - - keychainList := splitAndStrip(listKeychainsOut, "\n") - keychainList = appendWithoutDuplicatesAndKeepOrder(keychainList, configs.KeychainPath) - - // Set keychain search path - args := append([]string{"-v", "list-keychains", "-s"}, keychainList...) - cmd = command.New("security", args...) - if out, err := cmd.RunAndReturnTrimmedCombinedOutput(); err != nil { - failE(commandError(cmd.PrintableCommandArgs(), out, err)) + failE(fmt.Errorf("Download failed: %w", err)) } - // Set the default keychain - cmd = command.New("security", "-v", "default-keychain", "-s", configs.KeychainPath) - if out, err := cmd.RunAndReturnTrimmedCombinedOutput(); err != nil { - failE(commandError(cmd.PrintableCommandArgs(), out, err)) - } + log.Printf("%d Provisoning Profile(s) downloaded.", len(profiles)) - // - // Install provisioning profiles fmt.Println() - log.Infof("Downloading & installing Provisioning Profile(s)") + log.Infof("Installing Provisioning Profile(s)") - for idx, profileURL := range provisioningProfileURLs { + for i, profile := range profiles { + log.Printf("%d/%d Provisioning Profile:", i+1, len(profiles)) + log.Printf("%s", profile.Info.String(certificates...)) fmt.Println() - log.Printf("Downloading provisioning profile: %d/%d", idx+1, profileCount) - - provisioningProfileExt := "provisionprofile" - profileType := profileutil.ProfileTypeMacOs - if !strings.Contains(profileURL, "."+provisioningProfileExt) { - provisioningProfileExt = "mobileprovision" - profileType = profileutil.ProfileTypeIos - } - profileTmpPth := path.Join(tempDir, fmt.Sprintf("profile-%d.%s", idx, provisioningProfileExt)) - if err := downloadFile(profileTmpPth, profileURL); err != nil { - failF("Download failed, err: %s", err) + if err := assetInstaller.InstallProfile(profile.Profile); err != nil { + failE(fmt.Errorf("Failed to install Provisioning Profile: %w", err)) } - - profile, err := profileutil.NewProvisioningProfileInfoFromFile(profileTmpPth) - if err != nil { - failF("Failed to parse profile, error: %s", err) - } - - profilePth := path.Join(provisioningProfileDir, profile.UUID+"."+provisioningProfileExt) - - log.Printf("Moving it to: %s", profilePth) - - if err := command.CopyFile(profileTmpPth, profilePth); err != nil { - failF("Failed to copy profile from: %s to: %s", profileTmpPth, profilePth) - } - - fmt.Println() - printProfileInfo(profileType, profile, installedCertificates) } } diff --git a/step.yml b/step.yml index b221db0..9bdeba8 100644 --- a/step.yml +++ b/step.yml @@ -34,12 +34,9 @@ description: |- website: https://github.com/bitrise-steplib/steps-certificate-and-profile-installer source_code_url: https://github.com/bitrise-steplib/steps-certificate-and-profile-installer support_url: https://github.com/bitrise-steplib/steps-certificate-and-profile-installer/issues -host_os_tags: -- osx-10.10 project_type_tags: - ios - macos -- xamarin - cordova - ionic - react-native @@ -49,12 +46,6 @@ type_tags: is_requires_admin_user: false is_always_run: false is_skippable: false -deps: - brew: - - name: go - apt_get: - - name: golang - bin_name: go toolkit: go: package_name: github.com/bitrise-steplib/steps-certificate-and-profile-installer diff --git a/vendor/github.com/bitrise-io/go-steputils/input/fileprovider.go b/vendor/github.com/bitrise-io/go-steputils/input/fileprovider.go index b8528e6..fd42c05 100644 --- a/vendor/github.com/bitrise-io/go-steputils/input/fileprovider.go +++ b/vendor/github.com/bitrise-io/go-steputils/input/fileprovider.go @@ -16,6 +16,8 @@ const ( // FileDownloader .. type FileDownloader interface { Get(destination, source string) error + GetRemoteContents(source string) ([]byte, error) + ReadLocalFile(path string) ([]byte, error) } // FileProvider supports retrieving the local path to a file either provided @@ -34,23 +36,25 @@ func NewFileProvider(filedownloader FileDownloader) FileProvider { // LocalPath ... func (fileProvider FileProvider) LocalPath(path string) (string, error) { + if strings.HasPrefix(path, fileSchema) { // Local file + return fileProvider.trimmedFilePath(path) + } - var localPath string - if strings.HasPrefix(path, fileSchema) { - trimmedPath, err := fileProvider.trimmedFilePath(path) - if err != nil { - return "", err - } - localPath = trimmedPath - } else { - downloadedPath, err := fileProvider.downloadFile(path) + return fileProvider.downloadFileToLocalPath(path) +} + +// Contents returns the contents of remote or local URL +func (fileProvider FileProvider) Contents(srcPath string) ([]byte, error) { + if strings.HasPrefix(srcPath, fileSchema) { // Local file + trimmedPath, err := fileProvider.trimmedFilePath(srcPath) if err != nil { - return "", err + return nil, err } - localPath = downloadedPath + + return fileProvider.filedownloader.ReadLocalFile(trimmedPath) } - return localPath, nil + return fileProvider.filedownloader.GetRemoteContents(srcPath) } // Removes file:// from the begining of the path @@ -59,7 +63,7 @@ func (fileProvider FileProvider) trimmedFilePath(path string) (string, error) { return pathutil.AbsPath(pth) } -func (fileProvider FileProvider) downloadFile(url string) (string, error) { +func (fileProvider FileProvider) downloadFileToLocalPath(url string) (string, error) { tmpDir, err := pathutil.NormalizedOSTempDirPath("FileProviderprovider") if err != nil { return "", err diff --git a/vendor/github.com/bitrise-io/go-steputils/v2/stepconf/errors.go b/vendor/github.com/bitrise-io/go-steputils/v2/stepconf/errors.go new file mode 100644 index 0000000..52b6d0b --- /dev/null +++ b/vendor/github.com/bitrise-io/go-steputils/v2/stepconf/errors.go @@ -0,0 +1,26 @@ +package stepconf + +import ( + "errors" + "strings" +) + +// ErrNotStructPtr indicates a type is not a pointer to a struct. +var ErrNotStructPtr = errors.New("must be a pointer to a struct") + +// ParseError occurs when a struct field cannot be set. +type ParseError struct { + Field string + Value string + Err error +} + +// Error implements builtin errors.Error. +func (e *ParseError) Error() string { + segments := []string{e.Field} + if e.Value != "" { + segments = append(segments, e.Value) + } + segments = append(segments, e.Err.Error()) + return strings.Join(segments, ": ") +} diff --git a/vendor/github.com/bitrise-io/go-steputils/v2/stepconf/secret.go b/vendor/github.com/bitrise-io/go-steputils/v2/stepconf/secret.go new file mode 100644 index 0000000..f9140ce --- /dev/null +++ b/vendor/github.com/bitrise-io/go-steputils/v2/stepconf/secret.go @@ -0,0 +1,15 @@ +package stepconf + +// Secret variables are not shown in the printed output. +type Secret string + +const secret = "*****" + +// String implements fmt.Stringer.String. +// When a Secret is printed, it's masking the underlying string with asterisks. +func (s Secret) String() string { + if s == "" { + return "" + } + return secret +} diff --git a/vendor/github.com/bitrise-io/go-steputils/v2/stepconf/stepconf.go b/vendor/github.com/bitrise-io/go-steputils/v2/stepconf/stepconf.go new file mode 100644 index 0000000..1f0acf6 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-steputils/v2/stepconf/stepconf.go @@ -0,0 +1,450 @@ +package stepconf + +import ( + "errors" + "fmt" + "os" + "reflect" + "regexp" + "strconv" + "strings" + + "github.com/bitrise-io/go-utils/parseutil" + "github.com/bitrise-io/go-utils/v2/env" +) + +const ( + rangeMinimumGroupName = "min" + rangeMaximumGroupName = "max" + rangeMinBracketGroupName = "minbr" + rangeMaxBracketGroupName = "maxbr" + rangeRegex = `range(?P<` + rangeMinBracketGroupName + `>\[|\])(?P<` + rangeMinimumGroupName + `>.*?)\.\.(?P<` + rangeMaximumGroupName + `>.*?)(?P<` + rangeMaxBracketGroupName + `>\[|\])` + multilineConstraintName = "multiline" +) + +// parse populates a struct with the retrieved values from environment variables +// described by struct tags and applies the defined validations. +func parse(conf interface{}, envRepository env.Repository) error { + c := reflect.ValueOf(conf) + if c.Kind() != reflect.Ptr { + return ErrNotStructPtr + } + c = c.Elem() + if c.Kind() != reflect.Struct { + return ErrNotStructPtr + } + t := c.Type() + + var errs []*ParseError + for i := 0; i < c.NumField(); i++ { + tag, ok := t.Field(i).Tag.Lookup("env") + if !ok { + continue + } + key, constraint := parseTag(tag) + value := envRepository.Get(key) + + if err := setField(c.Field(i), value, constraint); err != nil { + errs = append(errs, &ParseError{t.Field(i).Name, value, err}) + } + } + if len(errs) > 0 { + errorString := "failed to parse config:" + for _, err := range errs { + errorString += fmt.Sprintf("\n- %s", err) + } + + errorString += fmt.Sprintf("\n\n%s", toString(conf)) + return errors.New(errorString) + } + + return nil +} + +// parseTag splits a struct field's env tag into its name and option. +func parseTag(tag string) (string, string) { + if idx := strings.Index(tag, ","); idx != -1 { + return tag[:idx], tag[idx+1:] + } + return tag, "" +} + +func setField(field reflect.Value, value, constraint string) error { + if err := validateConstraint(value, constraint); err != nil { + return err + } + + if value == "" { + return nil + } + + if field.Kind() == reflect.Ptr { + // If field is a pointer type, then set its value to be a pointer to a new zero value, matching field underlying type. + var dePtrdType = field.Type().Elem() // get the type field can point to + var newPtrType = reflect.New(dePtrdType) // create new ptr address for type with non-nil zero value + field.Set(newPtrType) // assign value to pointer + field = field.Elem() + } + + switch field.Kind() { + case reflect.String: + field.SetString(value) + case reflect.Bool: + b, err := parseutil.ParseBool(value) + if err != nil { + return errors.New("can't convert to bool") + } + field.SetBool(b) + case reflect.Int: + n, err := strconv.ParseInt(value, 10, 32) + if err != nil { + return errors.New("can't convert to int") + } + field.SetInt(n) + case reflect.Float64: + value = strings.TrimSpace(value) + f, err := strconv.ParseFloat(value, 64) + if err != nil { + return errors.New("can't convert to float") + } + field.SetFloat(f) + case reflect.Slice: + if constraint == multilineConstraintName { + field.Set(reflect.ValueOf(strings.Split(value, "\n"))) + } else { + field.Set(reflect.ValueOf(strings.Split(value, "|"))) + } + default: + return fmt.Errorf("type is not supported (%s)", field.Kind()) + } + return nil +} + +func validateConstraint(value, constraint string) error { + switch constraint { + case "": + break + case "required": + if value == "" { + return errors.New("required variable is not present") + } + case "file", "dir": + if err := checkPath(value, constraint == "dir"); err != nil { + return err + } + // TODO: use FindStringSubmatch to distinguish no match and match for empty string. + case regexp.MustCompile(`^opt\[.*]$`).FindString(constraint): + if !contains(value, constraint) { + // TODO: print only the value options, not the whole string. + return fmt.Errorf("value is not in value options (%s)", constraint) + } + case regexp.MustCompile(rangeRegex).FindString(constraint): + if err := validateRangeFields(value, constraint); err != nil { + return err + } + case multilineConstraintName: + break + default: + return fmt.Errorf("invalid constraint (%s)", constraint) + } + return nil +} + +// validateRangeFields validates if the given range is proper. Ranges are optional, empty values are valid. +func validateRangeFields(valueStr, constraint string) error { + if valueStr == "" { + return nil + } + constraintMin, constraintMax, constraintMinBr, constraintMaxBr, err := getRangeValues(constraint) + if err != nil { + return err + } + min, err := parseValueStr(constraintMin) + if err != nil { + return fmt.Errorf("failed to parse min value %s: %s", constraintMin, err) + } + max, err := parseValueStr(constraintMax) + if err != nil { + return fmt.Errorf("failed to parse max value %s: %s", constraintMax, err) + } + value, err := parseValueStr(valueStr) + if err != nil { + return fmt.Errorf("failed to parse value %s: %s", valueStr, err) + } + isMinInclusiveBool, err := isMinInclusive(constraintMinBr) + if err != nil { + return err + } + isMaxInclusiveBool, err := isMaxInclusive(constraintMaxBr) + if err != nil { + return err + } + + if err := validateRangeFieldValues(min, max, isMinInclusiveBool, isMaxInclusiveBool, value); err != nil { + return err + } + if err := validateRangeFieldTypes(min, max, value); err != nil { + return err + } + + return nil +} + +func isMinInclusive(bracket string) (bool, error) { + switch bracket { + case "[": + return true, nil + case "]": + return false, nil + default: + return false, fmt.Errorf("invalid string found for bracket: %s", bracket) + } +} + +func isMaxInclusive(bracket string) (bool, error) { + switch bracket { + case "[": + return false, nil + case "]": + return true, nil + default: + return false, fmt.Errorf("invalid string found for bracket: %s", bracket) + } +} + +func validateRangeFieldValues(min interface{}, max interface{}, minInclusive bool, maxInclusive bool, value interface{}) error { + if value == nil { + return fmt.Errorf("value is not present") + } + var err error + var valueFloat float64 + if valueFloat, err = getFloatValue(value); err != nil { + return err + } + + var minErr error + var minFloat float64 + if min != nil { + if minFloat, err = getFloatValue(min); err != nil { + return err + } + minErr = validateRangeMinFieldValue(minFloat, valueFloat, minInclusive) + } + + var maxErr error + var maxFloat float64 + if max != nil { + var err error + if maxFloat, err = getFloatValue(max); err != nil { + return err + } + maxErr = validateRangeMaxFieldValue(maxFloat, valueFloat, maxInclusive) + } + + if min != nil && max != nil { + if minFloat > maxFloat { + return fmt.Errorf("constraint logic is wrong, minimum value %f is bigger than maximum %f", minFloat, maxFloat) + } + if minFloat == maxFloat { + return fmt.Errorf("minimum value %f is equal to maximum %f, for this case use optional value", minFloat, maxFloat) + } + } + + if min == nil { + return maxErr + } else if max == nil { + return minErr + } + if minErr != nil || maxErr != nil { + return fmt.Errorf("value %f is out of range %f-%f", value, minFloat, maxFloat) + } + return nil +} + +func validateRangeFieldTypes(min interface{}, max interface{}, value interface{}) error { + if value == nil { + return fmt.Errorf("value cannot be nil") + } + var minType string + var maxType string + var valueType string + var err error + + if valueType, err = getTypeOf(value); err != nil { + return err + } + if min != nil { + if minType, err = getTypeOf(min); err != nil { + return err + } + } + if max != nil { + if maxType, err = getTypeOf(max); err != nil { + return err + } + } + + if maxType != "" && minType != "" && !hasSameContent(minType, maxType, valueType) { + return fmt.Errorf("invalid constraint and value combination, minimum is %s, maximum is %s, value is %s, but they should be the same", minType, maxType, valueType) + } + + if maxType != "" && !hasSameContent(maxType, valueType) { + return fmt.Errorf("invalid constraint and value combination, maximum is %s, value is %s, but they should be the same", maxType, valueType) + } + + if minType != "" && !hasSameContent(minType, valueType) { + return fmt.Errorf("invalid constraint and value combination, minimum is %s, value is %s, but they should be the same", minType, valueType) + } + return nil +} + +func hasSameContent(strs ...string) bool { + length := len(strs) + if length == 1 { + return true + } + firstItem := strs[0] + for i := 1; i < length; i++ { + if strings.Compare(firstItem, strs[i]) != 0 { + return false + } + } + return true +} + +func getTypeOf(v interface{}) (string, error) { + switch v.(type) { + case int64: + return "int64", nil + case float64: + return "float64", nil + default: + return "unknown", fmt.Errorf("could not find type for %v", v) + } +} + +func parseValueStr(value string) (interface{}, error) { + var err error + var parsedInt int64 + var parsedFloat float64 + if parsedInt, err = strconv.ParseInt(value, 10, 64); err != nil { + // Could be float + if parsedFloat, err = strconv.ParseFloat(value, 64); err != nil { + // It is invalid. + return nil, fmt.Errorf("value %s is could not be parsed", value) + } + return parsedFloat, nil + } + return parsedInt, nil +} + +func getFloatValue(value interface{}) (float64, error) { + switch i := value.(type) { + case int64: + return float64(i), nil + case float64: + return i, nil + case string: + var parsedValue interface{} + var err error + if parsedValue, err = parseValueStr(i); err != nil { + return 0, err + } + return getFloatValue(parsedValue) + default: + return 0, fmt.Errorf("not supported type %T", value) + } +} + +func validateRangeMinFieldValue(min float64, value float64, inclusive bool) error { + if inclusive && min > value { + return fmt.Errorf("value %f is out of range, less than minimum %f", value, min) + } else if !inclusive && min >= value { + return fmt.Errorf("value %f is out of range, greater or equal than maximum %f", value, min) + } + return nil +} + +func validateRangeMaxFieldValue(max float64, value float64, inclusive bool) error { + if inclusive && max < value { + return fmt.Errorf("value %f is out of range, greater than maximum %f", value, max) + + } else if !inclusive && max <= value { + return fmt.Errorf("value %f is out of range, greater or equal than maximum %f", value, max) + } + return nil +} + +// getRangeValues reads up the given range constraint and returns the values, or an error if the constraint is malformed or could not be parsed. +func getRangeValues(value string) (min string, max string, minBracket string, maxBracket string, err error) { + regex := regexp.MustCompile(rangeRegex) + groups := regex.FindStringSubmatch(value) + if len(groups) < 1 { + return "", "", "", "", fmt.Errorf("value in value options is malformed (%s)", value) + } + + groupMap := getRegexGroupMap(groups, regex) + minStr := groupMap[rangeMinimumGroupName] + maxStr := groupMap[rangeMaximumGroupName] + minBr := groupMap[rangeMinBracketGroupName] + maxBr := groupMap[rangeMaxBracketGroupName] + if minStr == "" && maxStr == "" { + return "", "", "", "", fmt.Errorf("constraint contains no limits") + } + return minStr, maxStr, minBr, maxBr, nil +} + +func getRegexGroupMap(groups []string, regex *regexp.Regexp) map[string]string { + result := make(map[string]string) + for i, value := range regex.SubexpNames() { + if i != 0 && value != "" { + result[value] = groups[i] + } + } + return result +} + +func checkPath(path string, dir bool) error { + file, err := os.Stat(path) + if err != nil { + // TODO: check case when file exist but os.Stat fails. + return os.ErrNotExist + } + if dir && !file.IsDir() { + return errors.New("not a directory") + } + return nil +} + +// contains reports whether s is within the value options, where value options +// are parsed from opt, which format's is opt[item1,item2,item3]. If an option +// contains commas, it should be single quoted (eg. opt[item1,'item2,item3']). +func contains(s, opt string) bool { + opt = strings.TrimSuffix(strings.TrimPrefix(opt, "opt["), "]") + var valueOpts []string + if strings.Contains(opt, "'") { + // The single quotes separate the options with comma and without comma + // Eg. "a,b,'c,d',e" will results "a,b," "c,d" and ",e" strings. + for _, s := range strings.Split(opt, "'") { + switch { + case s == "," || s == "": + case !strings.HasPrefix(s, ",") && !strings.HasSuffix(s, ","): + // If a string doesn't starts nor ends with a comma it means it's an option which + // contains comma, so we just append it to valueOpts as it is. Eg. "c,d" from above. + valueOpts = append(valueOpts, s) + default: + // If a string starts or ends with comma it means that it contains options without comma. + // So we split the string at commas to get the options. Eg. "a,b," and ",e" from above. + valueOpts = append(valueOpts, strings.Split(strings.Trim(s, ","), ",")...) + } + } + } else { + valueOpts = strings.Split(opt, ",") + } + for _, valOpt := range valueOpts { + if valOpt == s { + return true + } + } + return false +} diff --git a/vendor/github.com/bitrise-io/go-steputils/v2/stepconf/stepinput.go b/vendor/github.com/bitrise-io/go-steputils/v2/stepconf/stepinput.go new file mode 100644 index 0000000..abd4727 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-steputils/v2/stepconf/stepinput.go @@ -0,0 +1,24 @@ +package stepconf + +import "github.com/bitrise-io/go-utils/v2/env" + +// InputParser ... +type InputParser interface { + Parse(input interface{}) error +} + +type inputParser struct { + envRepository env.Repository +} + +// NewInputParser ... +func NewInputParser(envRepository env.Repository) InputParser { + return inputParser{ + envRepository: envRepository, + } +} + +// Parse ... +func (p inputParser) Parse(input interface{}) error { + return parse(input, p.envRepository) +} diff --git a/vendor/github.com/bitrise-io/go-steputils/v2/stepconf/strings.go b/vendor/github.com/bitrise-io/go-steputils/v2/stepconf/strings.go new file mode 100644 index 0000000..325d5fb --- /dev/null +++ b/vendor/github.com/bitrise-io/go-steputils/v2/stepconf/strings.go @@ -0,0 +1,57 @@ +package stepconf + +import ( + "fmt" + "reflect" + "strings" + + "github.com/bitrise-io/go-utils/colorstring" +) + +// Print the name of the struct with Title case in blue color with followed by a newline, +// then print all fields formatted as '- field name: field value` separated by newline. +func Print(config interface{}) { + fmt.Print(toString(config)) +} + +func valueString(v reflect.Value) string { + if v.Kind() != reflect.Ptr { + if v.Kind() == reflect.String && v.Len() == 0 { + return fmt.Sprintf("") + } + return fmt.Sprintf("%v", v.Interface()) + } + + if !v.IsNil() { + return fmt.Sprintf("%v", v.Elem().Interface()) + } + + return "" +} + +// returns the name of the struct with Title case in blue color followed by a newline, +// then print all fields formatted as '- field name: field value` separated by newline. +func toString(config interface{}) string { + v := reflect.ValueOf(config) + t := reflect.TypeOf(config) + + if v.Kind() == reflect.Ptr { + v = v.Elem() + } + + if t.Kind() == reflect.Ptr { + t = t.Elem() + } + + str := fmt.Sprint(colorstring.Bluef("%s:\n", strings.Title(t.Name()))) + for i := 0; i < t.NumField(); i++ { + field := t.Field(i) + var key, _ = parseTag(field.Tag.Get("env")) + if key == "" { + key = field.Name + } + str += fmt.Sprintf("- %s: %s\n", key, valueString(v.Field(i))) + } + + return str +} diff --git a/vendor/github.com/bitrise-io/go-utils/errorutil/errorutil.go b/vendor/github.com/bitrise-io/go-utils/errorutil/errorutil.go new file mode 100644 index 0000000..d7d8955 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-utils/errorutil/errorutil.go @@ -0,0 +1,34 @@ +// Package errorutil ... +package errorutil + +import ( + "errors" + "os/exec" + "regexp" +) + +func exitCode(err error) int { + var exitError *exec.ExitError + if errors.As(err, &exitError) { + return exitError.ProcessState.ExitCode() + } + return -1 +} + +// IsExitStatusError ... +func IsExitStatusError(err error) bool { + return exitCode(err) != -1 +} + +// IsExitStatusErrorStr ... +func IsExitStatusErrorStr(errString string) bool { + // https://golang.org/src/os/exec_posix.go?s=2421:2459#L87 + // example exit status error string: exit status 1 + var rex = regexp.MustCompile(`^exit status \d{1,3}$`) + return rex.MatchString(errString) +} + +// CmdExitCodeFromError ... +func CmdExitCodeFromError(err error) (int, error) { + return exitCode(err), err +} diff --git a/vendor/github.com/bitrise-io/go-utils/filedownloader/filedownloader.go b/vendor/github.com/bitrise-io/go-utils/filedownloader/filedownloader.go new file mode 100644 index 0000000..30a3905 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-utils/filedownloader/filedownloader.go @@ -0,0 +1,120 @@ +package filedownloader + +import ( + "bytes" + "context" + "fmt" + "io" + "net/http" + "os" + + "github.com/bitrise-io/go-utils/log" +) + +// HTTPClient ... +type HTTPClient interface { + Do(req *http.Request) (*http.Response, error) +} + +// FileDownloader ... +type FileDownloader struct { + client HTTPClient + context context.Context +} + +// New ... +func New(client HTTPClient) FileDownloader { + return FileDownloader{ + client: client, + } +} + +// NewWithContext ... +func NewWithContext(context context.Context, client HTTPClient) FileDownloader { + return FileDownloader{ + client: client, + context: context, + } +} + +// GetWithFallback downloads a file from a given source. Provided destination should be a file that does not exist. +// You can specify fallback sources which will be used in order if downloading fails from either source. +func (downloader FileDownloader) GetWithFallback(destination, source string, fallbackSources ...string) error { + sources := append([]string{source}, fallbackSources...) + for _, source := range sources { + err := downloader.Get(destination, source) + if err != nil { + log.Errorf("Could not download file from: %s", err) + } else { + log.Infof("URL used to download file: %s", source) + return nil + } + } + + return fmt.Errorf("None of the sources returned 200 OK status") +} + +// Get downloads a file from a given source. Provided destination should be a file that does not exist. +func (downloader FileDownloader) Get(destination, source string) error { + f, err := os.Create(destination) + if err != nil { + return err + } + + defer func() { + if err := f.Close(); err != nil { + log.Errorf("Failed to close file, error: %s", err) + } + }() + + return download(downloader.context, downloader.client, source, f) +} + +// GetRemoteContents fetches a remote URL contents +func (downloader FileDownloader) GetRemoteContents(URL string) ([]byte, error) { + var buffer bytes.Buffer + if err := download(downloader.context, downloader.client, URL, &buffer); err != nil { + return nil, err + } + + return buffer.Bytes(), nil +} + +// ReadLocalFile returns a local file contents +func (downloader FileDownloader) ReadLocalFile(path string) ([]byte, error) { + return os.ReadFile(path) +} + +func download(context context.Context, client HTTPClient, source string, destination io.Writer) error { + req, err := http.NewRequest(http.MethodGet, source, nil) + if err != nil { + return fmt.Errorf("failed to create request: %s", err) + } + + if context != nil { + req = req.WithContext(context) + } + + resp, err := client.Do(req) + if err != nil { + return err + } + + defer func() { + if resp.Body != nil { + if err := resp.Body.Close(); err != nil { + log.Errorf("Failed to close body, error: %s", err) + } + } + }() + + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("unable to download file from: %s. Status code: %d", source, resp.StatusCode) + } + + if _, err = io.Copy(destination, resp.Body); err != nil { + return err + } + + return nil +} diff --git a/vendor/github.com/bitrise-io/go-utils/httputil/httputil.go b/vendor/github.com/bitrise-io/go-utils/httputil/httputil.go new file mode 100644 index 0000000..a4bb718 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-utils/httputil/httputil.go @@ -0,0 +1,46 @@ +package httputil + +import ( + "net/http" + "net/http/httputil" + + "github.com/bitrise-io/go-utils/log" +) + +// PrintRequest ... +func PrintRequest(request *http.Request) error { + if request == nil { + return nil + } + + dump, err := httputil.DumpRequest(request, true) + if err != nil { + return err + } + + log.Debugf("%s", dump) + + return nil +} + +// PrintResponse ... +func PrintResponse(response *http.Response) error { + if response == nil { + return nil + } + + dump, err := httputil.DumpResponse(response, true) + if err != nil { + return err + } + log.Debugf("%s", dump) + + return nil +} + +// IsUserFixable returns true if statusCode is a value +// that is deemed retryable, i.e. something that could +// be fixed by the user. +func IsUserFixable(statusCode int) bool { + return statusCode == 400 || statusCode == 401 +} diff --git a/vendor/github.com/bitrise-io/go-utils/parseutil/parseutil.go b/vendor/github.com/bitrise-io/go-utils/parseutil/parseutil.go new file mode 100644 index 0000000..08cec36 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-utils/parseutil/parseutil.go @@ -0,0 +1,95 @@ +package parseutil + +import ( + "errors" + "fmt" + "strconv" + "strings" + + "github.com/bitrise-io/go-utils/pointers" +) + +// ParseBool ... +func ParseBool(userInputStr string) (bool, error) { + if userInputStr == "" { + return false, errors.New("No string to parse") + } + userInputStr = strings.TrimSpace(userInputStr) + + lowercased := strings.ToLower(userInputStr) + if lowercased == "yes" || lowercased == "y" { + return true, nil + } + if lowercased == "no" || lowercased == "n" { + return false, nil + } + return strconv.ParseBool(lowercased) +} + +// CastToString ... +func CastToString(value interface{}) string { + casted, ok := value.(string) + + if !ok { + castedStr := fmt.Sprintf("%v", value) + casted = castedStr + } + + return casted +} + +// CastToStringPtr ... +func CastToStringPtr(value interface{}) *string { + castedValue := CastToString(value) + return pointers.NewStringPtr(castedValue) +} + +// CastToBool ... +func CastToBool(value interface{}) (bool, bool) { + casted, ok := value.(bool) + + if !ok { + castedStr := CastToString(value) + + castedBool, err := ParseBool(castedStr) + if err != nil { + return false, false + } + + casted = castedBool + } + + return casted, true +} + +// CastToBoolPtr ... +func CastToBoolPtr(value interface{}) (*bool, bool) { + castedValue, ok := CastToBool(value) + if !ok { + return nil, false + } + return pointers.NewBoolPtr(castedValue), true +} + +// CastToMapStringInterface ... +func CastToMapStringInterface(value interface{}) (map[string]interface{}, bool) { + castedValue, ok := value.(map[interface{}]interface{}) + desiredMap := map[string]interface{}{} + for key, value := range castedValue { + keyStr, ok := key.(string) + if !ok { + return map[string]interface{}{}, false + } + desiredMap[keyStr] = value + } + return desiredMap, ok +} + +// CastToMapStringInterfacePtr ... +func CastToMapStringInterfacePtr(value interface{}) (*map[string]interface{}, bool) { + casted, ok := CastToMapStringInterface(value) + if !ok { + return nil, false + } + return pointers.NewMapStringInterfacePtr(casted), true +} diff --git a/vendor/github.com/bitrise-io/go-utils/pathutil/path_filter.go b/vendor/github.com/bitrise-io/go-utils/pathutil/path_filter.go new file mode 100644 index 0000000..db0665d --- /dev/null +++ b/vendor/github.com/bitrise-io/go-utils/pathutil/path_filter.go @@ -0,0 +1,164 @@ +package pathutil + +import ( + "errors" + "io/ioutil" + "os" + "path/filepath" + "regexp" + "strings" +) + +// ListEntries filters contents of a directory using the provided filters +func ListEntries(dir string, filters ...FilterFunc) ([]string, error) { + absDir, err := filepath.Abs(dir) + if err != nil { + return []string{}, err + } + + entries, err := ioutil.ReadDir(absDir) + if err != nil { + return []string{}, err + } + + var paths []string + for _, entry := range entries { + pth := filepath.Join(absDir, entry.Name()) + paths = append(paths, pth) + } + + return FilterPaths(paths, filters...) +} + +// FilterPaths ... +func FilterPaths(fileList []string, filters ...FilterFunc) ([]string, error) { + var filtered []string + + for _, pth := range fileList { + allowed := true + for _, filter := range filters { + if allows, err := filter(pth); err != nil { + return []string{}, err + } else if !allows { + allowed = false + break + } + } + if allowed { + filtered = append(filtered, pth) + } + } + + return filtered, nil +} + +// FilterFunc ... +type FilterFunc func(string) (bool, error) + +// BaseFilter ... +func BaseFilter(base string, allowed bool) FilterFunc { + return func(pth string) (bool, error) { + b := filepath.Base(pth) + return allowed == strings.EqualFold(base, b), nil + } +} + +// ExtensionFilter ... +func ExtensionFilter(ext string, allowed bool) FilterFunc { + return func(pth string) (bool, error) { + e := filepath.Ext(pth) + return allowed == strings.EqualFold(ext, e), nil + } +} + +// RegexpFilter ... +func RegexpFilter(pattern string, allowed bool) FilterFunc { + return func(pth string) (bool, error) { + re := regexp.MustCompile(pattern) + found := re.FindString(pth) != "" + return allowed == found, nil + } +} + +// ComponentFilter ... +func ComponentFilter(component string, allowed bool) FilterFunc { + return func(pth string) (bool, error) { + found := false + pathComponents := strings.Split(pth, string(filepath.Separator)) + for _, c := range pathComponents { + if c == component { + found = true + } + } + return allowed == found, nil + } +} + +// ComponentWithExtensionFilter ... +func ComponentWithExtensionFilter(ext string, allowed bool) FilterFunc { + return func(pth string) (bool, error) { + found := false + pathComponents := strings.Split(pth, string(filepath.Separator)) + for _, c := range pathComponents { + e := filepath.Ext(c) + if e == ext { + found = true + } + } + return allowed == found, nil + } +} + +// IsDirectoryFilter ... +func IsDirectoryFilter(allowed bool) FilterFunc { + return func(pth string) (bool, error) { + fileInf, err := os.Lstat(pth) + if err != nil { + return false, err + } + if fileInf == nil { + return false, errors.New("no file info available") + } + return allowed == fileInf.IsDir(), nil + } +} + +// InDirectoryFilter ... +func InDirectoryFilter(dir string, allowed bool) FilterFunc { + return func(pth string) (bool, error) { + in := filepath.Dir(pth) == dir + return allowed == in, nil + } +} + +// DirectoryContainsFileFilter returns a FilterFunc that checks if a directory contains a file +func DirectoryContainsFileFilter(fileName string) FilterFunc { + return func(pth string) (bool, error) { + isDir, err := IsDirectoryFilter(true)(pth) + if err != nil { + return false, err + } + if !isDir { + return false, nil + } + + absPath := filepath.Join(pth, fileName) + if _, err := os.Lstat(absPath); err != nil { + if !os.IsNotExist(err) { + return false, err + } + return false, nil + } + return true, nil + } +} + +// FileContainsFilter ... +func FileContainsFilter(pth, str string) (bool, error) { + bytes, err := ioutil.ReadFile(pth) + if err != nil { + return false, err + } + + return strings.Contains(string(bytes), str), nil +} diff --git a/vendor/github.com/bitrise-io/go-utils/pathutil/pathutil.go b/vendor/github.com/bitrise-io/go-utils/pathutil/pathutil.go index a88ff5f..947c97c 100644 --- a/vendor/github.com/bitrise-io/go-utils/pathutil/pathutil.go +++ b/vendor/github.com/bitrise-io/go-utils/pathutil/pathutil.go @@ -10,47 +10,34 @@ import ( "strings" ) -// RevokableChangeDir ... -func RevokableChangeDir(dir string) (func() error, error) { - origDir, err := CurrentWorkingDirectoryAbsolutePath() - if err != nil { - return nil, err - } - - revokeFn := func() error { - return os.Chdir(origDir) +// NormalizedOSTempDirPath ... +// Creates a temp dir, and returns its path. +// If tmpDirNamePrefix is provided it'll be used +// as the tmp dir's name prefix. +// Normalized: it's guaranteed that the path won't end with '/'. +func NormalizedOSTempDirPath(tmpDirNamePrefix string) (retPth string, err error) { + retPth, err = ioutil.TempDir("", tmpDirNamePrefix) + if strings.HasSuffix(retPth, "/") { + retPth = retPth[:len(retPth)-1] } - - return revokeFn, os.Chdir(dir) + return } -// ChangeDirForFunction ... -func ChangeDirForFunction(dir string, fn func()) error { - revokeFn, err := RevokableChangeDir(dir) - if err != nil { - return err - } - - fn() - - return revokeFn() +// CurrentWorkingDirectoryAbsolutePath ... +func CurrentWorkingDirectoryAbsolutePath() (string, error) { + return filepath.Abs("./") } -// IsRelativePath ... -func IsRelativePath(pth string) bool { - if strings.HasPrefix(pth, "./") { - return true - } - - if strings.HasPrefix(pth, "/") { - return false - } - - if strings.HasPrefix(pth, "$") { - return false +// UserHomeDir ... +func UserHomeDir() string { + if runtime.GOOS == "windows" { + home := os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH") + if home == "" { + home = os.Getenv("USERPROFILE") + } + return home } - - return true + return os.Getenv("HOME") } // EnsureDirExist ... @@ -76,12 +63,6 @@ func genericIsPathExists(pth string) (os.FileInfo, bool, error) { return fileInf, false, err } -// IsPathExists ... -func IsPathExists(pth string) (bool, error) { - _, isExists, err := genericIsPathExists(pth) - return isExists, err -} - // PathCheckAndInfos ... // Returns: // 1. file info or nil @@ -106,6 +87,32 @@ func IsDirExists(pth string) (bool, error) { return fileInf.IsDir(), nil } +// IsPathExists ... +func IsPathExists(pth string) (bool, error) { + _, isExists, err := genericIsPathExists(pth) + return isExists, err +} + +// +// Path modifier functions + +// PathModifier ... +type PathModifier interface { + AbsPath(pth string) (string, error) +} + +type defaultPathModifier struct{} + +// NewPathModifier ... +func NewPathModifier() PathModifier { + return defaultPathModifier{} +} + +// AbsPath ... +func (defaultPathModifier) AbsPath(pth string) (string, error) { + return AbsPath(pth) +} + // AbsPath expands ENV vars and the ~ character // then call Go's Abs func AbsPath(pth string) (string, error) { @@ -150,37 +157,65 @@ func ExpandTilde(pth string) (string, error) { return pth, nil } -// CurrentWorkingDirectoryAbsolutePath ... -func CurrentWorkingDirectoryAbsolutePath() (string, error) { - return filepath.Abs("./") -} +// IsRelativePath ... +func IsRelativePath(pth string) bool { + if strings.HasPrefix(pth, "./") { + return true + } -// UserHomeDir ... -func UserHomeDir() string { - if runtime.GOOS == "windows" { - home := os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH") - if home == "" { - home = os.Getenv("USERPROFILE") - } - return home + if strings.HasPrefix(pth, "/") { + return false } - return os.Getenv("HOME") -} -// NormalizedOSTempDirPath ... -// Creates a temp dir, and returns its path. -// If tmpDirNamePrefix is provided it'll be used -// as the tmp dir's name prefix. -// Normalized: it's guaranteed that the path won't end with '/'. -func NormalizedOSTempDirPath(tmpDirNamePrefix string) (retPth string, err error) { - retPth, err = ioutil.TempDir("", tmpDirNamePrefix) - if strings.HasSuffix(retPth, "/") { - retPth = retPth[:len(retPth)-1] + if strings.HasPrefix(pth, "$") { + return false } - return + + return true } // GetFileName returns the name of the file from a given path or the name of the directory if it is a directory func GetFileName(path string) string { return strings.TrimSuffix(filepath.Base(path), filepath.Ext(path)) } + +// EscapeGlobPath escapes a partial path, determined at runtime, used as a parameter for filepath.Glob +func EscapeGlobPath(path string) string { + var escaped string + for _, ch := range path { + if ch == '[' || ch == ']' || ch == '-' || ch == '*' || ch == '?' || ch == '\\' { + escaped += "\\" + } + escaped += string(ch) + } + return escaped +} + +// +// Change dir functions + +// RevokableChangeDir ... +func RevokableChangeDir(dir string) (func() error, error) { + origDir, err := CurrentWorkingDirectoryAbsolutePath() + if err != nil { + return nil, err + } + + revokeFn := func() error { + return os.Chdir(origDir) + } + + return revokeFn, os.Chdir(dir) +} + +// ChangeDirForFunction ... +func ChangeDirForFunction(dir string, fn func()) error { + revokeFn, err := RevokableChangeDir(dir) + if err != nil { + return err + } + + fn() + + return revokeFn() +} diff --git a/vendor/github.com/bitrise-io/go-utils/pathutil/sortable_path.go b/vendor/github.com/bitrise-io/go-utils/pathutil/sortable_path.go new file mode 100644 index 0000000..97f63c3 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-utils/pathutil/sortable_path.go @@ -0,0 +1,119 @@ +package pathutil + +import ( + "os" + "path/filepath" + "sort" + "strings" +) + +// ListPathInDirSortedByComponents ... +func ListPathInDirSortedByComponents(searchDir string, relPath bool) ([]string, error) { + searchDir, err := filepath.Abs(searchDir) + if err != nil { + return []string{}, err + } + + var fileList []string + + if err := filepath.Walk(searchDir, func(path string, _ os.FileInfo, walkErr error) error { + if walkErr != nil { + return walkErr + } + + if relPath { + rel, err := filepath.Rel(searchDir, path) + if err != nil { + return err + } + path = rel + } + + fileList = append(fileList, path) + + return nil + }); err != nil { + return []string{}, err + } + return SortPathsByComponents(fileList) +} + +// SortablePath ... +type SortablePath struct { + Pth string + AbsPth string + Components []string +} + +// NewSortablePath ... +func NewSortablePath(pth string) (SortablePath, error) { + absPth, err := AbsPath(pth) + if err != nil { + return SortablePath{}, err + } + + components := strings.Split(absPth, string(os.PathSeparator)) + fixedComponents := []string{} + for _, comp := range components { + if comp != "" { + fixedComponents = append(fixedComponents, comp) + } + } + + return SortablePath{ + Pth: pth, + AbsPth: absPth, + Components: fixedComponents, + }, nil +} + +// BySortablePathComponents .. +type BySortablePathComponents []SortablePath + +func (s BySortablePathComponents) Len() int { + return len(s) +} +func (s BySortablePathComponents) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} +func (s BySortablePathComponents) Less(i, j int) bool { + path1 := s[i] + path2 := s[j] + + d1 := len(path1.Components) + d2 := len(path2.Components) + + if d1 < d2 { + return true + } else if d1 > d2 { + return false + } + + // if same component size, + // do alphabetic sort based on the last component + base1 := filepath.Base(path1.AbsPth) + base2 := filepath.Base(path2.AbsPth) + + return base1 < base2 +} + +// SortPathsByComponents ... +func SortPathsByComponents(paths []string) ([]string, error) { + sortableFiles := []SortablePath{} + for _, pth := range paths { + sortable, err := NewSortablePath(pth) + if err != nil { + return []string{}, err + } + sortableFiles = append(sortableFiles, sortable) + } + + sort.Sort(BySortablePathComponents(sortableFiles)) + + sortedFiles := []string{} + for _, pth := range sortableFiles { + sortedFiles = append(sortedFiles, pth.Pth) + } + + return sortedFiles, nil +} diff --git a/vendor/github.com/bitrise-io/go-utils/pointers/pointers.go b/vendor/github.com/bitrise-io/go-utils/pointers/pointers.go new file mode 100644 index 0000000..e26647d --- /dev/null +++ b/vendor/github.com/bitrise-io/go-utils/pointers/pointers.go @@ -0,0 +1,98 @@ +package pointers + +import "time" + +// NewBoolPtr ... +func NewBoolPtr(val bool) *bool { + ptrValue := new(bool) + *ptrValue = val + return ptrValue +} + +// NewStringPtr ... +func NewStringPtr(val string) *string { + ptrValue := new(string) + *ptrValue = val + return ptrValue +} + +// NewTimePtr ... +func NewTimePtr(val time.Time) *time.Time { + ptrValue := new(time.Time) + *ptrValue = val + return ptrValue +} + +// NewIntPtr ... +func NewIntPtr(val int) *int { + ptrValue := new(int) + *ptrValue = val + return ptrValue +} + +// NewInt64Ptr ... +func NewInt64Ptr(val int64) *int64 { + ptrValue := new(int64) + *ptrValue = val + return ptrValue +} + +// NewMapStringInterfacePtr ... +func NewMapStringInterfacePtr(val map[string]interface{}) *map[string]interface{} { + ptrValue := new(map[string]interface{}) + *ptrValue = map[string]interface{}{} + for key, value := range val { + (*ptrValue)[key] = value + } + return ptrValue +} + +// ------------------------------------------------------ +// --- Safe Getters + +// Bool ... +func Bool(val *bool) bool { + return BoolWithDefault(val, false) +} + +// BoolWithDefault ... +func BoolWithDefault(val *bool, defaultValue bool) bool { + if val == nil { + return defaultValue + } + return *val +} + +// String ... +func String(val *string) string { + return StringWithDefault(val, "") +} + +// StringWithDefault ... +func StringWithDefault(val *string, defaultValue string) string { + if val == nil { + return defaultValue + } + return *val +} + +// TimeWithDefault ... +func TimeWithDefault(val *time.Time, defaultValue time.Time) time.Time { + if val == nil { + return defaultValue + } + return *val +} + +// Int ... +func Int(val *int) int { + return IntWithDefault(val, 0) +} + +// IntWithDefault ... +func IntWithDefault(val *int, defaultValue int) int { + if val == nil { + return defaultValue + } + return *val +} diff --git a/vendor/github.com/bitrise-io/go-utils/retry/retry.go b/vendor/github.com/bitrise-io/go-utils/retry/retry.go new file mode 100644 index 0000000..facb89f --- /dev/null +++ b/vendor/github.com/bitrise-io/go-utils/retry/retry.go @@ -0,0 +1,78 @@ +package retry + +import ( + "fmt" + "time" +) + +// Action ... +type Action func(attempt uint) error + +// AbortableAction ... +type AbortableAction func(attempt uint) (error, bool) + +// Model ... +type Model struct { + retry uint + waitTime time.Duration +} + +// Times ... +func Times(retry uint) *Model { + Model := Model{} + return Model.Times(retry) +} + +// Times ... +func (Model *Model) Times(retry uint) *Model { + Model.retry = retry + return Model +} + +// Wait ... +func Wait(waitTime time.Duration) *Model { + Model := Model{} + return Model.Wait(waitTime) +} + +// Wait ... +func (Model *Model) Wait(waitTime time.Duration) *Model { + Model.waitTime = waitTime + return Model +} + +// Try continues executing the supplied action while this action parameter returns an error and the configured +// number of times has not been reached. Otherwise, it stops and returns the last received error. +func (Model Model) Try(action Action) error { + return Model.TryWithAbort(func(attempt uint) (error, bool) { + return action(attempt), false + }) +} + +// TryWithAbort continues executing the supplied action while this action parameter returns an error, a false bool +// value and the configured number of times has not been reached. Returning a true value from the action aborts the +// retry loop. +// +// Good for retrying actions which can return a mix of retryable and non-retryable failures. +func (Model Model) TryWithAbort(action AbortableAction) error { + if action == nil { + return fmt.Errorf("no action specified") + } + + var err error + var shouldAbort bool + + for attempt := uint(0); (0 == attempt || nil != err) && attempt <= Model.retry; attempt++ { + if attempt > 0 && Model.waitTime > 0 { + time.Sleep(Model.waitTime) + } + + err, shouldAbort = action(attempt) + + if shouldAbort { + break + } + } + + return err +} diff --git a/vendor/github.com/bitrise-io/go-utils/retry/retryhttp.go b/vendor/github.com/bitrise-io/go-utils/retry/retryhttp.go new file mode 100644 index 0000000..235a7fc --- /dev/null +++ b/vendor/github.com/bitrise-io/go-utils/retry/retryhttp.go @@ -0,0 +1,23 @@ +package retry + +import ( + "github.com/bitrise-io/go-utils/log" + retryablehttp "github.com/hashicorp/go-retryablehttp" +) + +// HTTPLogAdaptor adapts the retryablehttp.Logger interface to the go-utils logger. +type HTTPLogAdaptor struct{} + +// Printf implements the retryablehttp.Logger interface +func (*HTTPLogAdaptor) Printf(fmtStr string, vars ...interface{}) { + log.Debugf(fmtStr, vars...) +} + +// NewHTTPClient returns a retryable HTTP client +func NewHTTPClient() *retryablehttp.Client { + client := retryablehttp.NewClient() + client.Logger = &HTTPLogAdaptor{} + client.ErrorHandler = retryablehttp.PassthroughErrorHandler + + return client +} diff --git a/vendor/github.com/bitrise-io/go-utils/sliceutil/sliceutil.go b/vendor/github.com/bitrise-io/go-utils/sliceutil/sliceutil.go new file mode 100644 index 0000000..94b1a2c --- /dev/null +++ b/vendor/github.com/bitrise-io/go-utils/sliceutil/sliceutil.go @@ -0,0 +1,46 @@ +package sliceutil + +import "strings" + +// UniqueStringSlice - returns a cleaned up list, +// where every item is unique. +// Does NOT guarantee any ordering, the result can +// be in any order! +func UniqueStringSlice(strs []string) []string { + lookupMap := map[string]interface{}{} + for _, aStr := range strs { + lookupMap[aStr] = 1 + } + uniqueStrs := []string{} + for k := range lookupMap { + uniqueStrs = append(uniqueStrs, k) + } + return uniqueStrs +} + +// IndexOfStringInSlice ... +func IndexOfStringInSlice(searchFor string, searchIn []string) int { + for idx, anItm := range searchIn { + if anItm == searchFor { + return idx + } + } + return -1 +} + +// IsStringInSlice ... +func IsStringInSlice(searchFor string, searchIn []string) bool { + return IndexOfStringInSlice(searchFor, searchIn) >= 0 +} + +// CleanWhitespace removes leading and trailing white space from each element of the input slice. +// Elements that end up as empty strings are excluded from the result depending on the value of the omitEmpty flag. +func CleanWhitespace(list []string, omitEmpty bool) (items []string) { + for _, e := range list { + e = strings.TrimSpace(e) + if !omitEmpty || len(e) > 0 { + items = append(items, e) + } + } + return +} diff --git a/vendor/github.com/bitrise-io/go-utils/v2/LICENSE b/vendor/github.com/bitrise-io/go-utils/v2/LICENSE new file mode 100644 index 0000000..a6a5c39 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-utils/v2/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 Bitrise + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/vendor/github.com/bitrise-io/go-utils/v2/command/command.go b/vendor/github.com/bitrise-io/go-utils/v2/command/command.go new file mode 100644 index 0000000..eedc628 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-utils/v2/command/command.go @@ -0,0 +1,120 @@ +package command + +import ( + "io" + "os/exec" + "strconv" + "strings" + + "github.com/bitrise-io/go-utils/v2/env" +) + +// Opts ... +type Opts struct { + Stdout io.Writer + Stderr io.Writer + Stdin io.Reader + Env []string + Dir string +} + +// Factory ... +type Factory interface { + Create(name string, args []string, opts *Opts) Command +} + +type factory struct { + envRepository env.Repository +} + +// NewFactory ... +func NewFactory(envRepository env.Repository) Factory { + return factory{envRepository: envRepository} +} + +// Create ... +func (f factory) Create(name string, args []string, opts *Opts) Command { + cmd := exec.Command(name, args...) + if opts != nil { + cmd.Stdout = opts.Stdout + cmd.Stderr = opts.Stderr + cmd.Stdin = opts.Stdin + + // If Env is nil, the new process uses the current process's + // environment. + // If we pass env vars we want to append them to the + // current process's environment. + cmd.Env = append(f.envRepository.List(), opts.Env...) + cmd.Dir = opts.Dir + } + return command{cmd} +} + +// Command ... +type Command interface { + PrintableCommandArgs() string + Run() error + RunAndReturnExitCode() (int, error) + RunAndReturnTrimmedOutput() (string, error) + RunAndReturnTrimmedCombinedOutput() (string, error) + Start() error + Wait() error +} + +type command struct { + cmd *exec.Cmd +} + +// PrintableCommandArgs ... +func (c command) PrintableCommandArgs() string { + return printableCommandArgs(false, c.cmd.Args) +} + +// Run ... +func (c command) Run() error { + return c.cmd.Run() +} + +// RunAndReturnExitCode ... +func (c command) RunAndReturnExitCode() (int, error) { + err := c.cmd.Run() + exitCode := c.cmd.ProcessState.ExitCode() + return exitCode, err +} + +// RunAndReturnTrimmedOutput ... +func (c command) RunAndReturnTrimmedOutput() (string, error) { + outBytes, err := c.cmd.Output() + outStr := string(outBytes) + return strings.TrimSpace(outStr), err +} + +// RunAndReturnTrimmedCombinedOutput ... +func (c command) RunAndReturnTrimmedCombinedOutput() (string, error) { + outBytes, err := c.cmd.CombinedOutput() + outStr := string(outBytes) + return strings.TrimSpace(outStr), err +} + +// Start ... +func (c command) Start() error { + return c.cmd.Start() +} + +// Wait ... +func (c command) Wait() error { + return c.cmd.Wait() +} + +func printableCommandArgs(isQuoteFirst bool, fullCommandArgs []string) string { + var cmdArgsDecorated []string + for idx, anArg := range fullCommandArgs { + quotedArg := strconv.Quote(anArg) + if idx == 0 && !isQuoteFirst { + quotedArg = anArg + } + cmdArgsDecorated = append(cmdArgsDecorated, quotedArg) + } + + return strings.Join(cmdArgsDecorated, " ") +} diff --git a/vendor/github.com/bitrise-io/go-utils/v2/env/env.go b/vendor/github.com/bitrise-io/go-utils/v2/env/env.go new file mode 100644 index 0000000..85cf1ae --- /dev/null +++ b/vendor/github.com/bitrise-io/go-utils/v2/env/env.go @@ -0,0 +1,58 @@ +package env + +import ( + "os" + "os/exec" +) + +// CommandLocator ... +type CommandLocator interface { + LookPath(file string) (string, error) +} + +type commandLocator struct{} + +// NewCommandLocator ... +func NewCommandLocator() CommandLocator { + return commandLocator{} +} + +// LookPath ... +func (l commandLocator) LookPath(file string) (string, error) { + return exec.LookPath(file) +} + +// Repository ... +type Repository interface { + List() []string + Unset(key string) error + Get(key string) string + Set(key, value string) error +} + +// NewRepository ... +func NewRepository() Repository { + return repository{} +} + +type repository struct{} + +// Get ... +func (d repository) Get(key string) string { + return os.Getenv(key) +} + +// Set ... +func (d repository) Set(key, value string) error { + return os.Setenv(key, value) +} + +// Unset ... +func (d repository) Unset(key string) error { + return os.Unsetenv(key) +} + +// List ... +func (d repository) List() []string { + return os.Environ() +} diff --git a/vendor/github.com/bitrise-io/go-xcode/LICENSE b/vendor/github.com/bitrise-io/go-xcode/LICENSE new file mode 100644 index 0000000..cdfcf1f --- /dev/null +++ b/vendor/github.com/bitrise-io/go-xcode/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Bitrise + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/bitrise-io/go-xcode/certificateutil/test_util.go b/vendor/github.com/bitrise-io/go-xcode/certificateutil/test_util.go index e5e54d6..1123373 100644 --- a/vendor/github.com/bitrise-io/go-xcode/certificateutil/test_util.go +++ b/vendor/github.com/bitrise-io/go-xcode/certificateutil/test_util.go @@ -28,14 +28,13 @@ func GenerateTestCertificate(serial int64, teamID, teamName, commonName string, KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, } - // generate private key - privatekey, err := rsa.GenerateKey(rand.Reader, 2048) + CAprivatekey, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { return nil, nil, err } // Self-signed certificate, parent is the template - CAcertData, err := x509.CreateCertificate(rand.Reader, CAtemplate, CAtemplate, &privatekey.PublicKey, privatekey) + CAcertData, err := x509.CreateCertificate(rand.Reader, CAtemplate, CAtemplate, &CAprivatekey.PublicKey, CAprivatekey) if err != nil { return nil, nil, err } @@ -60,13 +59,12 @@ func GenerateTestCertificate(serial int64, teamID, teamName, commonName string, KeyUsage: x509.KeyUsageDigitalSignature, } - // generate private key - privatekey, err = rsa.GenerateKey(rand.Reader, 2048) + privatekey, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { return nil, nil, err } - certData, err := x509.CreateCertificate(rand.Reader, template, CAcert, &privatekey.PublicKey, privatekey) + certData, err := x509.CreateCertificate(rand.Reader, template, CAcert, &privatekey.PublicKey, CAprivatekey) if err != nil { return nil, nil, err } diff --git a/vendor/github.com/bitrise-io/go-xcode/devportalservice/devportalservice.go b/vendor/github.com/bitrise-io/go-xcode/devportalservice/devportalservice.go new file mode 100644 index 0000000..a4702b5 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-xcode/devportalservice/devportalservice.go @@ -0,0 +1,275 @@ +package devportalservice + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "os" + "regexp" + "strings" + "text/template" + "time" + + "github.com/bitrise-io/go-utils/fileutil" + "github.com/bitrise-io/go-utils/log" +) + +type httpClient interface { + Do(req *http.Request) (*http.Response, error) +} + +// AppleDeveloperConnectionProvider ... +type AppleDeveloperConnectionProvider interface { + GetAppleDeveloperConnection() (*AppleDeveloperConnection, error) +} + +// BitriseClient implements AppleDeveloperConnectionProvider through the Bitrise.io API. +type BitriseClient struct { + httpClient httpClient + buildURL, buildAPIToken string + + readBytesFromFile func(pth string) ([]byte, error) +} + +// NewBitriseClient creates a new instance of BitriseClient. +func NewBitriseClient(client httpClient, buildURL, buildAPIToken string) *BitriseClient { + return &BitriseClient{ + httpClient: client, + buildURL: buildURL, + buildAPIToken: buildAPIToken, + readBytesFromFile: fileutil.ReadBytesFromFile, + } +} + +const appleDeveloperConnectionPath = "apple_developer_portal_data.json" + +func privateKeyWithHeader(privateKey string) string { + if strings.HasPrefix(privateKey, "-----BEGIN PRIVATE KEY----") { + return privateKey + } + + return fmt.Sprint( + "-----BEGIN PRIVATE KEY-----\n", + privateKey, + "\n-----END PRIVATE KEY-----", + ) +} + +// GetAppleDeveloperConnection fetches the Bitrise.io Apple Developer connection. +func (c *BitriseClient) GetAppleDeveloperConnection() (*AppleDeveloperConnection, error) { + var rawCreds []byte + var err error + + if strings.HasPrefix(c.buildURL, "file://") { + rawCreds, err = c.readBytesFromFile(strings.TrimPrefix(c.buildURL, "file://")) + } else { + rawCreds, err = c.download() + } + if err != nil { + return nil, fmt.Errorf("failed to fetch authentication credentials: %v", err) + } + + type data struct { + *AppleIDConnection + *APIKeyConnection + TestDevices []TestDevice `json:"test_devices"` + } + var d data + if err := json.Unmarshal([]byte(rawCreds), &d); err != nil { + return nil, fmt.Errorf("failed to unmarshal authentication credentials from response (%s): %s", rawCreds, err) + } + + if d.APIKeyConnection != nil { + if d.APIKeyConnection.IssuerID == "" { + return nil, fmt.Errorf("invalid authentication credentials, empty issuer_id in response (%s)", rawCreds) + } + if d.APIKeyConnection.KeyID == "" { + return nil, fmt.Errorf("invalid authentication credentials, empty key_id in response (%s)", rawCreds) + } + if d.APIKeyConnection.PrivateKey == "" { + return nil, fmt.Errorf("invalid authentication credentials, empty private_key in response (%s)", rawCreds) + } + + d.APIKeyConnection.PrivateKey = privateKeyWithHeader(d.APIKeyConnection.PrivateKey) + } + + testDevices, duplicatedDevices := validateTestDevice(d.TestDevices) + + return &AppleDeveloperConnection{ + AppleIDConnection: d.AppleIDConnection, + APIKeyConnection: d.APIKeyConnection, + TestDevices: testDevices, + DuplicatedTestDevices: duplicatedDevices, + }, nil +} + +func (c *BitriseClient) download() ([]byte, error) { + url := fmt.Sprintf("%s/%s", c.buildURL, appleDeveloperConnectionPath) + req, err := http.NewRequest(http.MethodGet, url, nil) + if err != nil { + return nil, fmt.Errorf("failed to create request for URL (%s): %s", url, err) + } + req.Header.Add("BUILD_API_TOKEN", c.buildAPIToken) + + resp, err := c.httpClient.Do(req) + if err != nil { + // On error, any Response can be ignored + return nil, fmt.Errorf("failed to perform request: %s", err) + } + + // The client must close the response body when finished with it + defer func() { + if cerr := resp.Body.Close(); cerr != nil { + log.Warnf("Failed to close response body: %s", cerr) + } + }() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("failed to read response body: %s", err) + } + + if resp.StatusCode != http.StatusOK { + return nil, NetworkError{Status: resp.StatusCode} + } + + return body, nil +} + +type cookie struct { + Name string `json:"name"` + Path string `json:"path"` + Value string `json:"value"` + Domain string `json:"domain"` + Secure bool `json:"secure"` + Expires string `json:"expires,omitempty"` + MaxAge int `json:"max_age,omitempty"` + Httponly bool `json:"httponly"` + ForDomain *bool `json:"for_domain,omitempty"` +} + +// AppleIDConnection represents a Bitrise.io Apple ID-based Apple Developer connection. +type AppleIDConnection struct { + AppleID string `json:"apple_id"` + Password string `json:"password"` + AppSpecificPassword string `json:"app_specific_password"` + SessionExpiryDate *time.Time `json:"connection_expiry_date"` + SessionCookies map[string][]cookie `json:"session_cookies"` +} + +// APIKeyConnection represents a Bitrise.io API key-based Apple Developer connection. +type APIKeyConnection struct { + KeyID string `json:"key_id"` + IssuerID string `json:"issuer_id"` + PrivateKey string `json:"private_key"` +} + +// TestDevice ... +type TestDevice struct { + ID int `json:"id"` + UserID int `json:"user_id"` + // DeviceID is the Apple device UDID + DeviceID string `json:"device_identifier"` + Title string `json:"title"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + DeviceType string `json:"device_type"` +} + +// IsEqualUDID compares two UDIDs (stored in the DeviceID field of TestDevice) +func IsEqualUDID(UDID string, otherUDID string) bool { + return normalizeDeviceUDID(UDID) == normalizeDeviceUDID(otherUDID) +} + +// AppleDeveloperConnection represents a Bitrise.io Apple Developer connection. +// https://devcenter.bitrise.io/getting-started/configuring-bitrise-steps-that-require-apple-developer-account-data/ +type AppleDeveloperConnection struct { + AppleIDConnection *AppleIDConnection + APIKeyConnection *APIKeyConnection + TestDevices, DuplicatedTestDevices []TestDevice +} + +// FastlaneLoginSession returns the Apple ID login session in a ruby/object:HTTP::Cookie format. +// The session can be used as a value for FASTLANE_SESSION environment variable: https://docs.fastlane.tools/best-practices/continuous-integration/#two-step-or-two-factor-auth. +func (c *AppleIDConnection) FastlaneLoginSession() (string, error) { + var rubyCookies []string + for _, cookie := range c.SessionCookies["https://idmsa.apple.com"] { + if rubyCookies == nil { + rubyCookies = append(rubyCookies, "---"+"\n") + } + + if cookie.ForDomain == nil { + b := true + cookie.ForDomain = &b + } + + tmpl, err := template.New("").Parse(`- !ruby/object:HTTP::Cookie + name: {{.Name}} + value: {{.Value}} + domain: {{.Domain}} + for_domain: {{.ForDomain}} + path: "{{.Path}}" +`) + if err != nil { + return "", fmt.Errorf("failed to parse template: %s", err) + } + + var b bytes.Buffer + err = tmpl.Execute(&b, cookie) + if err != nil { + return "", fmt.Errorf("failed to execute template on cookie: %s: %s", cookie.Name, err) + } + + rubyCookies = append(rubyCookies, b.String()+"\n") + } + return strings.Join(rubyCookies, ""), nil +} + +func validDeviceUDID(udid string) string { + r := regexp.MustCompile("[^a-zA-Z0-9-]") + return r.ReplaceAllLiteralString(udid, "") +} + +func normalizeDeviceUDID(udid string) string { + return strings.ToLower(strings.ReplaceAll(validDeviceUDID(udid), "-", "")) +} + +// validateTestDevice filters out duplicated devices +// it does not change UDID casing or remove '-' separator, only to filter out whitespace or unsupported characters +func validateTestDevice(deviceList []TestDevice) (validDevices, duplicatedDevices []TestDevice) { + bitriseDevices := make(map[string]bool) + for _, device := range deviceList { + normalizedID := normalizeDeviceUDID(device.DeviceID) + if _, ok := bitriseDevices[normalizedID]; ok { + duplicatedDevices = append(duplicatedDevices, device) + + continue + } + + bitriseDevices[normalizedID] = true + device.DeviceID = validDeviceUDID(device.DeviceID) + validDevices = append(validDevices, device) + } + + return validDevices, duplicatedDevices +} + +// WritePrivateKeyToFile writes the contents of the private key to a temporary file and returns its path +func (c *APIKeyConnection) WritePrivateKeyToFile() (string, error) { + privatekeyFile, err := os.CreateTemp("", fmt.Sprintf("AuthKey_%s_*.p8", c.KeyID)) + if err != nil { + return "", fmt.Errorf("failed to create private key file: %s", err) + } + + if _, err := privatekeyFile.Write([]byte(c.PrivateKey)); err != nil { + return "", fmt.Errorf("failed to write private key: %s", err) + } + + if err := privatekeyFile.Close(); err != nil { + return "", fmt.Errorf("failed to close private key file: %s", err) + } + + return privatekeyFile.Name(), nil +} diff --git a/vendor/github.com/bitrise-io/go-xcode/devportalservice/devportalservice_testdata.go b/vendor/github.com/bitrise-io/go-xcode/devportalservice/devportalservice_testdata.go new file mode 100644 index 0000000..f397dba --- /dev/null +++ b/vendor/github.com/bitrise-io/go-xcode/devportalservice/devportalservice_testdata.go @@ -0,0 +1,222 @@ +package devportalservice + +import ( + "time" +) + +func toTime(str string) *time.Time { + if str == "" { + return nil + } + t, err := time.Parse(time.RFC3339, str) + if err != nil { + panic(err) + } + return &t +} + +const testDevicesResponseBody = `{ + "test_devices":[ + { + "id":24, + "user_id":4, + "device_identifier":"asdf12345ad9b298cb9a9f28555c49573d8bc322", + "title":"iPhone 6", + "created_at":"2015-03-13T16:16:13.665Z", + "updated_at":"2015-03-13T16:16:13.665Z", + "device_type":"ios" + }, + { + "id":28, + "user_id":4, + "device_identifier":"asdf12341e73b76df6e99d0d713133c3e078418f", + "title":"iPad mini 2 (Wi-Fi)", + "created_at":"2015-03-19T13:25:43.487Z", + "updated_at":"2015-03-19T13:25:43.487Z", + "device_type":"ios" + } + ] +} +` + +var testDevices = []TestDevice{ + { + ID: 24, + UserID: 4, + DeviceID: "asdf12345ad9b298cb9a9f28555c49573d8bc322", + Title: "iPhone 6", + CreatedAt: *toTime("2015-03-13T16:16:13.665Z"), + UpdatedAt: *toTime("2015-03-13T16:16:13.665Z"), + DeviceType: "ios", + }, + { + ID: 28, + UserID: 4, + DeviceID: "asdf12341e73b76df6e99d0d713133c3e078418f", + Title: "iPad mini 2 (Wi-Fi)", + CreatedAt: *toTime("2015-03-19T13:25:43.487Z"), + UpdatedAt: *toTime("2015-03-19T13:25:43.487Z"), + DeviceType: "ios", + }, +} + +var testConnectionOnlyDevices = AppleDeveloperConnection{ + AppleIDConnection: nil, + APIKeyConnection: nil, + TestDevices: testDevices, +} + +const testAppleIDConnectionResponseBody = `{ + "apple_id": "example@example.io", + "password": "highSecurityPassword", + "connection_expiry_date": "2019-04-06T12:04:59.000Z", + "session_cookies": { + "https://idmsa.apple.com": [ + { + "name": "DES58b0eba556d80ed2b98707e15ffafd344", + "path": "/", + "value": "HSARMTKNSRVTWFlaFrGQTmfmFBwJuiX/aaaaaaaaa+A7FbJa4V8MmWijnJknnX06ME0KrI9V8vFg==SRVT", + "domain": "idmsa.apple.com", + "secure": true, + "expires": "2019-04-06T12:04:59Z", + "max_age": 2592000, + "httponly": true + }, + { + "name": "myacinfo", + "path": "/", + "value": "DAWTKNV26a0a6db3ae43acd203d0d03e8bc45000cd4bdc668e90953f22ca3b36eaab0e18634660a10cf28cc65d8ddf633c017de09477dfb18c8a3d6961f96cbbf064be616e80cee62d3d7f39a485bf826377c5b5dbbfc4a97dcdb462052db73a3a1d9b4a325d5bdd496190b3088878cecce17e4d6db9230e0575cfbe7a8754d1de0c937080ef84569b6e4a75237c2ec01cf07db060a11d92e7220707dd00a2a565ee9e06074d8efa6a1b7f83db3e1b2acdafb5fc0708443e77e6d71e168ae2a83b848122264b2da5cadfd9e451f9fe3f6eebc71904d4bc36acc528cc2a844d4f2eb527649a69523756ec9955457f704c28a3b6b9f97d6df900bd60044d5bc50408260f096954f03c53c16ac40a796dc439b859f882a50390b1c7517a9f4479fb1ce9ba2db241d6b8f2eb127c46ef96e0ccccccccc", + "domain": "apple.com", + "secure": true, + "httponly": true + } + ] + } +}` + +const testFastlaneSession = `--- +- !ruby/object:HTTP::Cookie + name: DES58b0eba556d80ed2b98707e15ffafd344 + value: HSARMTKNSRVTWFlaFrGQTmfmFBwJuiX/aaaaaaaaa+A7FbJa4V8MmWijnJknnX06ME0KrI9V8vFg==SRVT + domain: idmsa.apple.com + for_domain: true + path: "/" + +- !ruby/object:HTTP::Cookie + name: myacinfo + value: DAWTKNV26a0a6db3ae43acd203d0d03e8bc45000cd4bdc668e90953f22ca3b36eaab0e18634660a10cf28cc65d8ddf633c017de09477dfb18c8a3d6961f96cbbf064be616e80cee62d3d7f39a485bf826377c5b5dbbfc4a97dcdb462052db73a3a1d9b4a325d5bdd496190b3088878cecce17e4d6db9230e0575cfbe7a8754d1de0c937080ef84569b6e4a75237c2ec01cf07db060a11d92e7220707dd00a2a565ee9e06074d8efa6a1b7f83db3e1b2acdafb5fc0708443e77e6d71e168ae2a83b848122264b2da5cadfd9e451f9fe3f6eebc71904d4bc36acc528cc2a844d4f2eb527649a69523756ec9955457f704c28a3b6b9f97d6df900bd60044d5bc50408260f096954f03c53c16ac40a796dc439b859f882a50390b1c7517a9f4479fb1ce9ba2db241d6b8f2eb127c46ef96e0ccccccccc + domain: apple.com + for_domain: true + path: "/" + +` + +var testAppleIDConnection = AppleIDConnection{ + AppleID: "example@example.io", + Password: "highSecurityPassword", + SessionExpiryDate: toTime("2019-04-06T12:04:59.000Z"), + SessionCookies: map[string][]cookie{ + "https://idmsa.apple.com": { + { + Name: "DES58b0eba556d80ed2b98707e15ffafd344", + Path: "/", + Value: "HSARMTKNSRVTWFlaFrGQTmfmFBwJuiX/aaaaaaaaa+A7FbJa4V8MmWijnJknnX06ME0KrI9V8vFg==SRVT", + Domain: "idmsa.apple.com", + Secure: true, + Expires: "2019-04-06T12:04:59Z", + MaxAge: 2592000, + Httponly: true, + }, + { + Name: "myacinfo", + Path: "/", + Value: "DAWTKNV26a0a6db3ae43acd203d0d03e8bc45000cd4bdc668e90953f22ca3b36eaab0e18634660a10cf28cc65d8ddf633c017de09477dfb18c8a3d6961f96cbbf064be616e80cee62d3d7f39a485bf826377c5b5dbbfc4a97dcdb462052db73a3a1d9b4a325d5bdd496190b3088878cecce17e4d6db9230e0575cfbe7a8754d1de0c937080ef84569b6e4a75237c2ec01cf07db060a11d92e7220707dd00a2a565ee9e06074d8efa6a1b7f83db3e1b2acdafb5fc0708443e77e6d71e168ae2a83b848122264b2da5cadfd9e451f9fe3f6eebc71904d4bc36acc528cc2a844d4f2eb527649a69523756ec9955457f704c28a3b6b9f97d6df900bd60044d5bc50408260f096954f03c53c16ac40a796dc439b859f882a50390b1c7517a9f4479fb1ce9ba2db241d6b8f2eb127c46ef96e0ccccccccc", + Domain: "apple.com", + Secure: true, + Httponly: true, + }, + }, + }, +} + +var testConnectionWithAppleIDConnection = AppleDeveloperConnection{ + AppleIDConnection: &testAppleIDConnection, + APIKeyConnection: nil, + TestDevices: nil, +} + +const testAPIKeyConnectionResponseBody = `{ + "key_id": "ASDF4H9LNQ", + "issuer_id": "asdf1234-7325-47e3-e053-5b8c7c11a4d1", + "private_key": "-----BEGIN PRIVATE KEY-----\nASdf1234MBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQg9O4G/HVLgSqc2i7x\nasDF12346UNzKCEwOfQ1ixC0G9agCgYIKoZIzj0DAQehRANCAARcJQItGFcefLRc\naSDf1234ka9BMpRjjr3NWyCWl817HCdXXckuc22RjnKxRnYMBBDv8zPDX0k9TbST\nacgZ04Gg\n-----END PRIVATE KEY-----" +}` + +var testAPIKeyConnection = APIKeyConnection{ + KeyID: "ASDF4H9LNQ", + IssuerID: "asdf1234-7325-47e3-e053-5b8c7c11a4d1", + PrivateKey: "-----BEGIN PRIVATE KEY-----\nASdf1234MBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQg9O4G/HVLgSqc2i7x\nasDF12346UNzKCEwOfQ1ixC0G9agCgYIKoZIzj0DAQehRANCAARcJQItGFcefLRc\naSDf1234ka9BMpRjjr3NWyCWl817HCdXXckuc22RjnKxRnYMBBDv8zPDX0k9TbST\nacgZ04Gg\n-----END PRIVATE KEY-----", +} + +var testConnectionWithAPIKeyConnection = AppleDeveloperConnection{ + AppleIDConnection: nil, + APIKeyConnection: &testAPIKeyConnection, + TestDevices: nil, +} + +const testAppleIDAndAPIKeyConnectionResponseBody = `{ + "apple_id": "example@example.io", + "password": "highSecurityPassword", + "connection_expiry_date": "2019-04-06T12:04:59.000Z", + "session_cookies": { + "https://idmsa.apple.com": [ + { + "name": "DES58b0eba556d80ed2b98707e15ffafd344", + "path": "/", + "value": "HSARMTKNSRVTWFlaFrGQTmfmFBwJuiX/aaaaaaaaa+A7FbJa4V8MmWijnJknnX06ME0KrI9V8vFg==SRVT", + "domain": "idmsa.apple.com", + "secure": true, + "expires": "2019-04-06T12:04:59Z", + "max_age": 2592000, + "httponly": true + }, + { + "name": "myacinfo", + "path": "/", + "value": "DAWTKNV26a0a6db3ae43acd203d0d03e8bc45000cd4bdc668e90953f22ca3b36eaab0e18634660a10cf28cc65d8ddf633c017de09477dfb18c8a3d6961f96cbbf064be616e80cee62d3d7f39a485bf826377c5b5dbbfc4a97dcdb462052db73a3a1d9b4a325d5bdd496190b3088878cecce17e4d6db9230e0575cfbe7a8754d1de0c937080ef84569b6e4a75237c2ec01cf07db060a11d92e7220707dd00a2a565ee9e06074d8efa6a1b7f83db3e1b2acdafb5fc0708443e77e6d71e168ae2a83b848122264b2da5cadfd9e451f9fe3f6eebc71904d4bc36acc528cc2a844d4f2eb527649a69523756ec9955457f704c28a3b6b9f97d6df900bd60044d5bc50408260f096954f03c53c16ac40a796dc439b859f882a50390b1c7517a9f4479fb1ce9ba2db241d6b8f2eb127c46ef96e0ccccccccc", + "domain": "apple.com", + "secure": true, + "httponly": true + } + ] + }, + "key_id": "ASDF4H9LNQ", + "issuer_id": "asdf1234-7325-47e3-e053-5b8c7c11a4d1", + "private_key": "-----BEGIN PRIVATE KEY-----\nASdf1234MBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQg9O4G/HVLgSqc2i7x\nasDF12346UNzKCEwOfQ1ixC0G9agCgYIKoZIzj0DAQehRANCAARcJQItGFcefLRc\naSDf1234ka9BMpRjjr3NWyCWl817HCdXXckuc22RjnKxRnYMBBDv8zPDX0k9TbST\nacgZ04Gg\n-----END PRIVATE KEY-----", + "test_devices":[ + { + "id":24, + "user_id":4, + "device_identifier":"asdf12345ad9b298cb9a9f28555c49573d8bc322", + "title":"iPhone 6", + "created_at":"2015-03-13T16:16:13.665Z", + "updated_at":"2015-03-13T16:16:13.665Z", + "device_type":"ios" + }, + { + "id":28, + "user_id":4, + "device_identifier":"asdf12341e73b76df6e99d0d713133c3e078418f", + "title":"iPad mini 2 (Wi-Fi)", + "created_at":"2015-03-19T13:25:43.487Z", + "updated_at":"2015-03-19T13:25:43.487Z", + "device_type":"ios" + } + ] +} +` + +var testConnectionWithAppleIDAndAPIKeyConnection = AppleDeveloperConnection{ + AppleIDConnection: &testAppleIDConnection, + APIKeyConnection: &testAPIKeyConnection, + TestDevices: testDevices, +} diff --git a/vendor/github.com/bitrise-io/go-xcode/devportalservice/errors.go b/vendor/github.com/bitrise-io/go-xcode/devportalservice/errors.go new file mode 100644 index 0000000..c6243a7 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-xcode/devportalservice/errors.go @@ -0,0 +1,12 @@ +package devportalservice + +import "fmt" + +// NetworkError represents a networking issue. +type NetworkError struct { + Status int +} + +func (e NetworkError) Error() string { + return fmt.Sprintf("network request failed with status %d", e.Status) +} diff --git a/vendor/github.com/bitrise-io/go-xcode/exportoptions/appstore_options.go b/vendor/github.com/bitrise-io/go-xcode/exportoptions/appstore_options.go index 66ddf73..3ed5816 100644 --- a/vendor/github.com/bitrise-io/go-xcode/exportoptions/appstore_options.go +++ b/vendor/github.com/bitrise-io/go-xcode/exportoptions/appstore_options.go @@ -19,13 +19,16 @@ type AppStoreOptionsModel struct { // for app-store exports UploadBitcode bool UploadSymbols bool + // Should Xcode manage the app's build number when uploading to App Store Connect? Defaults to YES. + ManageAppVersion bool } // NewAppStoreOptions ... func NewAppStoreOptions() AppStoreOptionsModel { return AppStoreOptionsModel{ - UploadBitcode: UploadBitcodeDefault, - UploadSymbols: UploadSymbolsDefault, + UploadBitcode: UploadBitcodeDefault, + UploadSymbols: UploadSymbolsDefault, + ManageAppVersion: manageAppVersionDefault, } } @@ -42,6 +45,9 @@ func (options AppStoreOptionsModel) Hash() map[string]interface{} { if options.UploadSymbols != UploadSymbolsDefault { hash[UploadSymbolsKey] = options.UploadSymbols } + if options.ManageAppVersion != manageAppVersionDefault { + hash[manageAppVersionKey] = options.ManageAppVersion + } if options.ICloudContainerEnvironment != "" { hash[ICloudContainerEnvironmentKey] = options.ICloudContainerEnvironment } diff --git a/vendor/github.com/bitrise-io/go-xcode/exportoptions/properties.go b/vendor/github.com/bitrise-io/go-xcode/exportoptions/properties.go index aca83eb..bb2e94c 100644 --- a/vendor/github.com/bitrise-io/go-xcode/exportoptions/properties.go +++ b/vendor/github.com/bitrise-io/go-xcode/exportoptions/properties.go @@ -149,6 +149,11 @@ const UploadSymbolsKey = "uploadSymbols" // UploadSymbolsDefault ... const UploadSymbolsDefault = true +const ( + manageAppVersionKey = "manageAppVersionAndBuildNumber" + manageAppVersionDefault = true +) + // ProvisioningProfilesKey ... const ProvisioningProfilesKey = "provisioningProfiles" diff --git a/vendor/github.com/bitrise-io/go-xcode/models/models.go b/vendor/github.com/bitrise-io/go-xcode/models/models.go deleted file mode 100644 index 42ea372..0000000 --- a/vendor/github.com/bitrise-io/go-xcode/models/models.go +++ /dev/null @@ -1,8 +0,0 @@ -package models - -// XcodebuildVersionModel ... -type XcodebuildVersionModel struct { - Version string - BuildVersion string - MajorVersion int64 -} diff --git a/vendor/github.com/bitrise-io/go-xcode/profileutil/capabilities.go b/vendor/github.com/bitrise-io/go-xcode/profileutil/capabilities.go index 7bc709b..3e5b850 100644 --- a/vendor/github.com/bitrise-io/go-xcode/profileutil/capabilities.go +++ b/vendor/github.com/bitrise-io/go-xcode/profileutil/capabilities.go @@ -1,6 +1,7 @@ package profileutil import ( + "github.com/bitrise-io/go-utils/log" "github.com/bitrise-io/go-xcode/plistutil" ) @@ -19,6 +20,8 @@ func MatchTargetAndProfileEntitlements(targetEntitlements plistutil.PlistData, p } } + log.Debugf("Found %v entitlements from %v target", len(missingEntitlements), len(targetEntitlements)) + return missingEntitlements } diff --git a/vendor/github.com/bitrise-io/go-xcode/profileutil/info_model.go b/vendor/github.com/bitrise-io/go-xcode/profileutil/info_model.go index 2b1ed57..22c4758 100644 --- a/vendor/github.com/bitrise-io/go-xcode/profileutil/info_model.go +++ b/vendor/github.com/bitrise-io/go-xcode/profileutil/info_model.go @@ -33,6 +33,19 @@ type ProvisioningProfileInfoModel struct { Type ProfileType } +func collectCapabilitesPrintableInfo(entitlements plistutil.PlistData) map[string]interface{} { + capabilities := map[string]interface{}{} + + for key, value := range entitlements { + if KnownProfileCapabilitiesMap[ProfileTypeIos][key] || + KnownProfileCapabilitiesMap[ProfileTypeMacOs][key] { + capabilities[key] = value + } + } + + return capabilities +} + // PrintableProvisioningProfileInfo ... func (info ProvisioningProfileInfoModel) String(installedCertificates ...certificateutil.CertificateInfoModel) string { printable := map[string]interface{}{} @@ -40,8 +53,11 @@ func (info ProvisioningProfileInfoModel) String(installedCertificates ...certifi printable["export_type"] = string(info.ExportType) printable["team"] = fmt.Sprintf("%s (%s)", info.TeamName, info.TeamID) printable["bundle_id"] = info.BundleID - printable["expire"] = info.ExpirationDate.String() + printable["expiry"] = info.ExpirationDate.String() printable["is_xcode_managed"] = info.IsXcodeManaged() + + printable["capabilities"] = collectCapabilitesPrintableInfo(info.Entitlements) + if info.ProvisionedDevices != nil { printable["devices"] = info.ProvisionedDevices } @@ -60,6 +76,7 @@ func (info ProvisioningProfileInfoModel) String(installedCertificates ...certifi if installedCertificates != nil && !info.HasInstalledCertificate(installedCertificates) { errors = append(errors, "none of the profile's certificates are installed") } + if err := info.CheckValidity(); err != nil { errors = append(errors, err.Error()) } @@ -127,12 +144,23 @@ func NewProvisioningProfileInfo(provisioningProfile pkcs7.PKCS7) (ProvisioningPr return ProvisioningProfileInfoModel{}, err } - platform, _ := data.GetStringArray("Platform") - profileType := ProfileTypeMacOs - if len(platform) != 0 { - if strings.ToLower(platform[0]) == string(ProfileTypeIos) { - profileType = ProfileTypeIos - } + platforms, _ := data.GetStringArray("Platform") + if len(platforms) == 0 { + return ProvisioningProfileInfoModel{}, fmt.Errorf("missing Platform array in profile") + } + + platform := strings.ToLower(platforms[0]) + var profileType ProfileType + + switch platform { + case string(ProfileTypeIos): + profileType = ProfileTypeIos + case string(ProfileTypeMacOs): + profileType = ProfileTypeMacOs + case string(ProfileTypeTvOs): + profileType = ProfileTypeTvOs + default: + return ProvisioningProfileInfoModel{}, fmt.Errorf("unknown platform type: %s", platform) } profile := PlistData(data) diff --git a/vendor/github.com/bitrise-io/go-xcode/profileutil/util.go b/vendor/github.com/bitrise-io/go-xcode/profileutil/util.go index e39de14..015d478 100644 --- a/vendor/github.com/bitrise-io/go-xcode/profileutil/util.go +++ b/vendor/github.com/bitrise-io/go-xcode/profileutil/util.go @@ -5,7 +5,6 @@ import ( "github.com/bitrise-io/go-utils/fileutil" "github.com/bitrise-io/go-utils/pathutil" - "github.com/bitrise-io/go-xcode/utility" "github.com/fullsailor/pkcs7" ) @@ -16,7 +15,10 @@ type ProfileType string const ProfileTypeIos ProfileType = "ios" // ProfileTypeMacOs ... -const ProfileTypeMacOs ProfileType = "macOs" +const ProfileTypeMacOs ProfileType = "osx" + +// ProfileTypeTvOs ... +const ProfileTypeTvOs ProfileType = "tvos" // ProvProfileSystemDirPath ... const ProvProfileSystemDirPath = "~/Library/MobileDevice/Provisioning Profiles" @@ -47,7 +49,7 @@ func InstalledProvisioningProfiles(profileType ProfileType) ([]*pkcs7.PKCS7, err return nil, err } - pattern := filepath.Join(utility.EscapeGlobPath(absProvProfileDirPath), "*"+ext) + pattern := filepath.Join(pathutil.EscapeGlobPath(absProvProfileDirPath), "*"+ext) pths, err := filepath.Glob(pattern) if err != nil { return nil, err diff --git a/vendor/github.com/bitrise-io/go-xcode/utility/glob.go b/vendor/github.com/bitrise-io/go-xcode/utility/glob.go deleted file mode 100644 index 1333c6f..0000000 --- a/vendor/github.com/bitrise-io/go-xcode/utility/glob.go +++ /dev/null @@ -1,13 +0,0 @@ -package utility - -// EscapeGlobPath escapes a partial path, determined at runtime, used as a parameter for filepath.Glob -func EscapeGlobPath(path string) string { - var escaped string - for _, ch := range path { - if ch == '[' || ch == ']' || ch == '-' || ch == '*' || ch == '?' || ch == '\\' { - escaped += "\\" - } - escaped += string(ch) - } - return escaped -} diff --git a/vendor/github.com/bitrise-io/go-xcode/utility/path.go b/vendor/github.com/bitrise-io/go-xcode/utility/path.go deleted file mode 100644 index 45830d3..0000000 --- a/vendor/github.com/bitrise-io/go-xcode/utility/path.go +++ /dev/null @@ -1,103 +0,0 @@ -package utility - -import ( - "fmt" - "io/ioutil" - "path/filepath" - "strings" - - "github.com/bitrise-io/go-utils/pathutil" -) - -// FilterFunc ... -type FilterFunc func(pth string) (bool, error) - -// FilterPaths ... -func FilterPaths(paths []string, filters ...FilterFunc) ([]string, error) { - filtered := []string{} - - for _, pth := range paths { - allowed := true - for _, filter := range filters { - if allows, err := filter(pth); err != nil { - return []string{}, err - } else if !allows { - allowed = false - break - } - } - if allowed { - filtered = append(filtered, pth) - } - } - - return filtered, nil -} - -// ListEntries ... -func ListEntries(dir string, filters ...FilterFunc) ([]string, error) { - absDir, err := filepath.Abs(dir) - if err != nil { - return []string{}, err - } - - entries, err := ioutil.ReadDir(absDir) - if err != nil { - return []string{}, err - } - - paths := []string{} - for _, entry := range entries { - pth := filepath.Join(absDir, entry.Name()) - paths = append(paths, pth) - } - - return FilterPaths(paths, filters...) -} - -// ExtensionFilter ... -func ExtensionFilter(ext string, allowed bool) FilterFunc { - return func(pth string) (bool, error) { - e := filepath.Ext(pth) - return (allowed == strings.EqualFold(ext, e)), nil - } -} - -// BaseFilter ... -func BaseFilter(base string, allowed bool) FilterFunc { - return func(pth string) (bool, error) { - b := filepath.Base(pth) - return (allowed == strings.EqualFold(base, b)), nil - } -} - -// FindFileInAppDir ... -func FindFileInAppDir(appDir, fileName string) (string, error) { - filePth := filepath.Join(appDir, fileName) - if exist, err := pathutil.IsPathExists(filePth); err != nil { - return "", err - } else if exist { - return filePth, nil - } - // --- - - // It's somewhere else - let's find it! - apps, err := ListEntries(appDir, ExtensionFilter(".app", true)) - if err != nil { - return "", err - } - - for _, app := range apps { - pths, err := ListEntries(app, BaseFilter(fileName, true)) - if err != nil { - return "", err - } - - if len(pths) > 0 { - return pths[0], nil - } - } - // --- - - return "", fmt.Errorf("failed to find %s", fileName) -} diff --git a/vendor/github.com/bitrise-io/go-xcode/utility/utility.go b/vendor/github.com/bitrise-io/go-xcode/utility/utility.go deleted file mode 100644 index 5d605c5..0000000 --- a/vendor/github.com/bitrise-io/go-xcode/utility/utility.go +++ /dev/null @@ -1,51 +0,0 @@ -package utility - -import ( - "fmt" - "strconv" - "strings" - - "github.com/bitrise-io/go-utils/command" - "github.com/bitrise-io/go-xcode/models" -) - -func getXcodeVersionFromXcodebuildOutput(outStr string) (models.XcodebuildVersionModel, error) { - split := strings.Split(outStr, "\n") - if len(split) == 0 { - return models.XcodebuildVersionModel{}, fmt.Errorf("failed to parse xcodebuild version output (%s)", outStr) - } - - xcodebuildVersion := split[0] - buildVersion := split[1] - - split = strings.Split(xcodebuildVersion, " ") - if len(split) != 2 { - return models.XcodebuildVersionModel{}, fmt.Errorf("failed to parse xcodebuild version output (%s)", outStr) - } - - version := split[1] - - split = strings.Split(version, ".") - majorVersionStr := split[0] - - majorVersion, err := strconv.ParseInt(majorVersionStr, 10, 32) - if err != nil { - return models.XcodebuildVersionModel{}, fmt.Errorf("failed to parse xcodebuild version output (%s), error: %s", outStr, err) - } - - return models.XcodebuildVersionModel{ - Version: xcodebuildVersion, - BuildVersion: buildVersion, - MajorVersion: majorVersion, - }, nil -} - -// GetXcodeVersion ... -func GetXcodeVersion() (models.XcodebuildVersionModel, error) { - cmd := command.New("xcodebuild", "-version") - outStr, err := cmd.RunAndReturnTrimmedCombinedOutput() - if err != nil { - return models.XcodebuildVersionModel{}, fmt.Errorf("xcodebuild -version failed, err: %s, details: %s", err, outStr) - } - return getXcodeVersionFromXcodebuildOutput(outStr) -} diff --git a/vendor/github.com/bitrise-io/go-xcode/v2/LICENSE b/vendor/github.com/bitrise-io/go-xcode/v2/LICENSE new file mode 100644 index 0000000..cdfcf1f --- /dev/null +++ b/vendor/github.com/bitrise-io/go-xcode/v2/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Bitrise + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/autocodesign.go b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/autocodesign.go new file mode 100644 index 0000000..89776ce --- /dev/null +++ b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/autocodesign.go @@ -0,0 +1,240 @@ +// Package autocodesign is a framework for automatic code signing. +// +// Contains common types, interfaces and logic needed for codesigning. +// Parsing an Xcode project or archive and applying settings is not part of the package, for modularity. +package autocodesign + +import ( + "errors" + "fmt" + "math/big" + + "github.com/bitrise-io/go-utils/log" + "github.com/bitrise-io/go-xcode/certificateutil" + "github.com/bitrise-io/go-xcode/devportalservice" + "github.com/bitrise-io/go-xcode/profileutil" + "github.com/bitrise-io/go-xcode/v2/autocodesign/devportalclient/appstoreconnect" + "github.com/bitrise-io/go-xcode/xcodeproject/serialized" +) + +// Profile represents a provisioning profiles +type Profile interface { + ID() string + Attributes() appstoreconnect.ProfileAttributes + CertificateIDs() ([]string, error) + DeviceIDs() ([]string, error) + BundleID() (appstoreconnect.BundleID, error) + Entitlements() (Entitlements, error) +} + +// AppCodesignAssets is the result of ensuring codesigning assets +type AppCodesignAssets struct { + ArchivableTargetProfilesByBundleID map[string]Profile + UITestTargetProfilesByBundleID map[string]Profile + Certificate certificateutil.CertificateInfoModel +} + +// Platform ... +type Platform string + +// Const +const ( + IOS Platform = "iOS" + TVOS Platform = "tvOS" + MacOS Platform = "macOS" +) + +// DistributionType ... +type DistributionType string + +// DistributionTypes ... +var ( + Development DistributionType = "development" + AppStore DistributionType = "app-store" + AdHoc DistributionType = "ad-hoc" + Enterprise DistributionType = "enterprise" +) + +// Entitlement ... +type Entitlement serialized.Object + +// Entitlements is all the entitlements that are contained in a target or profile +type Entitlements serialized.Object + +// Certificate is certificate present on Apple App Store Connect API, could match a local certificate +type Certificate struct { + CertificateInfo certificateutil.CertificateInfoModel + ID string +} + +// DevPortalClient abstract away the Apple Developer Portal API +type DevPortalClient interface { + QueryCertificateBySerial(serial big.Int) (Certificate, error) + QueryAllIOSCertificates() (map[appstoreconnect.CertificateType][]Certificate, error) + + ListDevices(UDID string, platform appstoreconnect.DevicePlatform) ([]appstoreconnect.Device, error) + RegisterDevice(testDevice devportalservice.TestDevice) (*appstoreconnect.Device, error) + + FindProfile(name string, profileType appstoreconnect.ProfileType) (Profile, error) + DeleteProfile(id string) error + CreateProfile(name string, profileType appstoreconnect.ProfileType, bundleID appstoreconnect.BundleID, certificateIDs []string, deviceIDs []string) (Profile, error) + + FindBundleID(bundleIDIdentifier string) (*appstoreconnect.BundleID, error) + CheckBundleIDEntitlements(bundleID appstoreconnect.BundleID, appEntitlements Entitlements) error + SyncBundleID(bundleID appstoreconnect.BundleID, appEntitlements Entitlements) error + CreateBundleID(bundleIDIdentifier, appIDName string) (*appstoreconnect.BundleID, error) +} + +// AssetWriter ... +type AssetWriter interface { + Write(codesignAssetsByDistributionType map[DistributionType]AppCodesignAssets) error + InstallCertificate(certificate certificateutil.CertificateInfoModel) error + InstallProfile(profile Profile) error +} + +// LocalCodeSignAssetManager ... +type LocalCodeSignAssetManager interface { + FindCodesignAssets(appLayout AppLayout, distrType DistributionType, certsByType map[appstoreconnect.CertificateType][]Certificate, deviceIDs []string, minProfileDaysValid int) (*AppCodesignAssets, *AppLayout, error) +} + +// AppLayout contains codesigning related settings that are needed to ensure codesigning files +type AppLayout struct { + Platform Platform + EntitlementsByArchivableTargetBundleID map[string]Entitlements + UITestTargetBundleIDs []string +} + +// CertificateProvider returns codesigning certificates (with private key) +type CertificateProvider interface { + GetCertificates() ([]certificateutil.CertificateInfoModel, error) +} + +// LocalCertificates is a map from the certificate type (development, distribution) to an array of installed certs +type LocalCertificates map[appstoreconnect.CertificateType][]certificateutil.CertificateInfoModel + +// LocalProfile ... +type LocalProfile struct { + Profile Profile + Info profileutil.ProvisioningProfileInfoModel +} + +// ProfileProvider returns provisioning profiles +type ProfileProvider interface { + IsAvailable() bool + GetProfiles() ([]LocalProfile, error) +} + +// CodesignAssetsOpts are codesigning related parameters that are not specified by the project (or archive) +type CodesignAssetsOpts struct { + DistributionType DistributionType + TypeToLocalCertificates LocalCertificates + BitriseTestDevices []devportalservice.TestDevice + MinProfileValidityDays int + FallbackToLocalAssetsOnAPIFailure bool + VerboseLog bool +} + +// CodesignAssetManager ... +type CodesignAssetManager interface { + EnsureCodesignAssets(appLayout AppLayout, opts CodesignAssetsOpts) (map[DistributionType]AppCodesignAssets, error) +} + +type codesignAssetManager struct { + devPortalClient DevPortalClient + assetWriter AssetWriter + localCodeSignAssetManager LocalCodeSignAssetManager +} + +// NewCodesignAssetManager ... +func NewCodesignAssetManager(devPortalClient DevPortalClient, assetWriter AssetWriter, localCodeSignAssetManager LocalCodeSignAssetManager) CodesignAssetManager { + return codesignAssetManager{ + devPortalClient: devPortalClient, + assetWriter: assetWriter, + localCodeSignAssetManager: localCodeSignAssetManager, + } +} + +// EnsureCodesignAssets is the main entry point of the codesigning logic +func (m codesignAssetManager) EnsureCodesignAssets(appLayout AppLayout, opts CodesignAssetsOpts) (map[DistributionType]AppCodesignAssets, error) { + signUITestTargets := len(appLayout.UITestTargetBundleIDs) > 0 + certsByType, distrTypes, err := selectCertificatesAndDistributionTypes( + m.devPortalClient, + opts.TypeToLocalCertificates, + opts.DistributionType, + signUITestTargets, + opts.VerboseLog, + ) + if err != nil { + return nil, err + } + + var devPortalDeviceIDs []string + var devPortalDeviceUDIDs []string + if DistributionTypeRequiresDeviceList(distrTypes) { + devPortalDevices, err := EnsureTestDevices(m.devPortalClient, opts.BitriseTestDevices, appLayout.Platform) + if err != nil { + return nil, fmt.Errorf("failed to ensure test devices: %w", err) + } + + for _, devPortalDevice := range devPortalDevices { + devPortalDeviceIDs = append(devPortalDeviceIDs, devPortalDevice.ID) + devPortalDeviceUDIDs = append(devPortalDeviceUDIDs, devPortalDevice.Attributes.UDID) + } + } + + codesignAssetsByDistributionType := map[DistributionType]AppCodesignAssets{} + + for _, distrType := range distrTypes { + localCodesignAssets, missingAppLayout, err := m.localCodeSignAssetManager.FindCodesignAssets(appLayout, distrType, certsByType, devPortalDeviceUDIDs, opts.MinProfileValidityDays) + if err != nil { + return nil, fmt.Errorf("failed to collect local code signing assets: %w", err) + } + + printExistingCodesignAssets(localCodesignAssets, distrType) + if localCodesignAssets != nil { + // Did not check if selected certificate is installed yet + fmt.Println() + log.Infof("Installing certificate") + log.Printf("certificate: %s", localCodesignAssets.Certificate.CommonName) + if err := m.assetWriter.InstallCertificate(localCodesignAssets.Certificate); err != nil { + return nil, fmt.Errorf("failed to install certificate: %w", err) + } + } + + finalAssets := localCodesignAssets + if missingAppLayout != nil { + printMissingCodeSignAssets(missingAppLayout) + + // Ensure Profiles + newCodesignAssets, err := ensureProfiles(m.devPortalClient, distrType, certsByType, *missingAppLayout, devPortalDeviceIDs, opts.MinProfileValidityDays) + if err != nil { + switch { + case errors.As(err, &ErrAppClipAppID{}): + log.Warnf("Can't create Application Identifier for App Clip targets.") + log.Warnf("Please generate the Application Identifier manually on Apple Developer Portal, after that the Step will continue working.") + case errors.As(err, &ErrAppClipAppIDWithAppleSigning{}): + log.Warnf("Can't manage Application Identifier for App Clip target with 'Sign In With Apple' capability.") + log.Warnf("Please configure Capabilities on Apple Developer Portal for App Clip target manually, after that the Step will continue working.") + } + + return nil, fmt.Errorf("failed to ensure profiles: %w", err) + } + + // Install new certificates and profiles + fmt.Println() + log.Infof("Installing certificates and profiles") + if err := m.assetWriter.Write(map[DistributionType]AppCodesignAssets{distrType: *newCodesignAssets}); err != nil { + return nil, fmt.Errorf("failed to install codesigning files: %w", err) + } + + // Merge local and recently generated code signing assets + finalAssets = mergeCodeSignAssets(localCodesignAssets, newCodesignAssets) + } + + if finalAssets != nil { + codesignAssetsByDistributionType[distrType] = *finalAssets + } + } + + return codesignAssetsByDistributionType, nil +} diff --git a/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/certdownloader/certdownloader.go b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/certdownloader/certdownloader.go new file mode 100644 index 0000000..fe5a988 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/certdownloader/certdownloader.go @@ -0,0 +1,86 @@ +// Package certdownloader implements a autocodesign.CertificateProvider which fetches Bitrise hosted Xcode codesigning certificates. +package certdownloader + +import ( + "context" + "fmt" + "net/http" + "time" + + "github.com/bitrise-io/go-steputils/input" + "github.com/bitrise-io/go-utils/filedownloader" + "github.com/bitrise-io/go-utils/log" + "github.com/bitrise-io/go-xcode/certificateutil" + "github.com/bitrise-io/go-xcode/v2/autocodesign" +) + +// CertificateAndPassphrase contains a p12 file URL and passphrase +type CertificateAndPassphrase struct { + URL, Passphrase string +} + +type downloader struct { + certs []CertificateAndPassphrase + client *http.Client +} + +// NewDownloader ... +func NewDownloader(certs []CertificateAndPassphrase, client *http.Client) autocodesign.CertificateProvider { + return downloader{ + certs: certs, + client: client, + } +} + +// GetCertificates ... +func (d downloader) GetCertificates() ([]certificateutil.CertificateInfoModel, error) { + var certInfos []certificateutil.CertificateInfoModel + + for i, p12 := range d.certs { + log.Debugf("Downloading p12 file number %d from %s", i, p12.URL) + + certInfo, err := downloadAndParsePKCS12(d.client, p12.URL, p12.Passphrase) + if err != nil { + return nil, err + } + + log.Debugf("Codesign identities included:\n%s", certsToString(certInfo)) + certInfos = append(certInfos, certInfo...) + } + + return certInfos, nil +} + +// downloadAndParsePKCS12 downloads a pkcs12 format file and parses certificates and matching private keys. +func downloadAndParsePKCS12(httpClient *http.Client, certificateURL, passphrase string) ([]certificateutil.CertificateInfoModel, error) { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) + defer cancel() + + downloader := filedownloader.NewWithContext(ctx, httpClient) + fileProvider := input.NewFileProvider(downloader) + + contents, err := fileProvider.Contents(certificateURL) + if err != nil { + return nil, err + } else if contents == nil { + return nil, fmt.Errorf("certificate (%s) is empty", certificateURL) + } + + infos, err := certificateutil.CertificatesFromPKCS12Content(contents, passphrase) + if err != nil { + return nil, fmt.Errorf("failed to parse certificate (%s), err: %s", certificateURL, err) + } + + return infos, nil +} + +func certsToString(certs []certificateutil.CertificateInfoModel) (s string) { + for i, cert := range certs { + s += "- " + s += cert.String() + if i < len(certs)-1 { + s += "\n" + } + } + return +} diff --git a/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/certificates.go b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/certificates.go new file mode 100644 index 0000000..b3d0402 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/certificates.go @@ -0,0 +1,196 @@ +package autocodesign + +import ( + "fmt" + "strings" + + "github.com/bitrise-io/go-utils/log" + "github.com/bitrise-io/go-xcode/certificateutil" + "github.com/bitrise-io/go-xcode/v2/autocodesign/devportalclient/appstoreconnect" +) + +func selectCertificatesAndDistributionTypes(certificateSource DevPortalClient, typeToLocalCerts LocalCertificates, distribution DistributionType, signUITestTargets bool, verboseLog bool) (map[appstoreconnect.CertificateType][]Certificate, []DistributionType, error) { + certType, ok := CertificateTypeByDistribution[distribution] + if !ok { + panic(fmt.Sprintf("no valid certificate provided for distribution type: %s", distribution)) + } + + distrTypes := []DistributionType{distribution} + requiredCertTypes := map[appstoreconnect.CertificateType]bool{certType: true} + if distribution != Development { + distrTypes = append(distrTypes, Development) + + if signUITestTargets { + log.Warnf("UITest target requires development code signing in addition to the specified %s code signing", distribution) + requiredCertTypes[appstoreconnect.IOSDevelopment] = true + } else { + requiredCertTypes[appstoreconnect.IOSDevelopment] = false + } + } + + certsByType, err := getValidCertificates(typeToLocalCerts, certificateSource, requiredCertTypes, verboseLog) + if err != nil { + if missingCertErr, ok := err.(missingCertificateError); ok { + return nil, nil, &DetailedError{ + ErrorMessage: "", + Title: fmt.Sprintf("No valid %s type certificates uploaded", missingCertErr.Type), + Description: fmt.Sprintf("Maybe you forgot to provide a(n) %s type certificate.", missingCertErr.Type), + Recommendation: fmt.Sprintf("Upload a %s type certificate (.p12) on the Code Signing tab of the Workflow Editor.", missingCertErr.Type), + } + } + return nil, nil, fmt.Errorf("failed to get valid certificates: %s", err) + } + + if len(certsByType) == 1 && distribution != Development { + // remove development distribution if there is no development certificate uploaded + distrTypes = []DistributionType{distribution} + } + log.Printf("ensuring codesigning files for distribution types: %s", distrTypes) + + return certsByType, distrTypes, nil +} + +func getValidCertificates(typeToLocalCerts LocalCertificates, client DevPortalClient, requiredCertificateTypes map[appstoreconnect.CertificateType]bool, isDebugLog bool) (map[appstoreconnect.CertificateType][]Certificate, error) { + log.Debugf("Certificates required for Development: %t; Distribution: %t", requiredCertificateTypes[appstoreconnect.IOSDevelopment], requiredCertificateTypes[appstoreconnect.IOSDistribution]) + + for certificateType, required := range requiredCertificateTypes { + if required && len(typeToLocalCerts[certificateType]) == 0 { + return map[appstoreconnect.CertificateType][]Certificate{}, missingCertificateError{certificateType} + } + } + + // only for debugging + if isDebugLog { + if err := logAllAPICertificates(client); err != nil { + log.Debugf("Failed to log all Developer Portal certificates: %s", err) + } + } + + validAPICertificates := map[appstoreconnect.CertificateType][]Certificate{} + for certificateType, validLocalCertificates := range typeToLocalCerts { + matchingCertificates := matchLocalToAPICertificates(client, validLocalCertificates) + + if len(matchingCertificates) > 0 { + log.Debugf("Certificates type %s has matches on Developer Portal:", certificateType) + for _, cert := range matchingCertificates { + log.Debugf("- %s", cert.CertificateInfo) + } + } + + if requiredCertificateTypes[certificateType] && len(matchingCertificates) == 0 { + return nil, fmt.Errorf("not found any of the following %s certificates on Developer Portal:\n%s", certificateType, certsToString(validLocalCertificates)) + } + + if len(matchingCertificates) > 0 { + validAPICertificates[certificateType] = matchingCertificates + } + } + + return validAPICertificates, nil +} + +// GetValidLocalCertificates returns validated and deduplicated local certificates +func GetValidLocalCertificates(certificates []certificateutil.CertificateInfoModel) (LocalCertificates, error) { + preFilteredCerts := certificateutil.FilterValidCertificateInfos(certificates) + + if len(preFilteredCerts.InvalidCertificates) != 0 { + log.Warnf("Ignoring expired or not yet valid certificates: %s", preFilteredCerts.InvalidCertificates) + } + if len(preFilteredCerts.DuplicatedCertificates) != 0 { + log.Warnf("Ignoring duplicated certificates with the same name: %s", preFilteredCerts.DuplicatedCertificates) + } + + log.Debugf("Valid and deduplicated certificates:\n%s", certsToString(preFilteredCerts.ValidCertificates)) + + localCertificates := LocalCertificates{} + for _, certType := range []appstoreconnect.CertificateType{appstoreconnect.IOSDevelopment, appstoreconnect.IOSDistribution} { + localCertificates[certType] = filterCertificates(preFilteredCerts.ValidCertificates, certType) + } + + log.Debugf("Valid and deduplicated certificates:\n%s", certsToString(preFilteredCerts.ValidCertificates)) + + return localCertificates, nil +} + +// matchLocalToAPICertificates ... +func matchLocalToAPICertificates(client DevPortalClient, localCertificates []certificateutil.CertificateInfoModel) []Certificate { + var matchingCertificates []Certificate + + for _, localCert := range localCertificates { + cert, err := client.QueryCertificateBySerial(*localCert.Certificate.SerialNumber) + if err != nil { + log.Warnf("Certificate (%s) not found on Developer Portal: %s", localCert, err) + continue + } + cert.CertificateInfo = localCert + + log.Debugf("Certificate (%s) found with ID: %s", localCert, cert.ID) + + matchingCertificates = append(matchingCertificates, cert) + } + + return matchingCertificates +} + +// logAllAPICertificates ... +func logAllAPICertificates(client DevPortalClient) error { + certificates, err := client.QueryAllIOSCertificates() + if err != nil { + return fmt.Errorf("failed to query certificates on Developer Portal: %s", err) + } + + for certType, certs := range certificates { + log.Debugf("Developer Portal %s certificates:", certType) + for _, cert := range certs { + log.Debugf("- %s", cert.CertificateInfo) + } + } + + return nil +} + +// filterCertificates returns the certificates matching to the given common name, developer team ID, and distribution type. +func filterCertificates(certificates []certificateutil.CertificateInfoModel, certificateType appstoreconnect.CertificateType) []certificateutil.CertificateInfoModel { + // filter by distribution type + var filteredCertificates []certificateutil.CertificateInfoModel + for _, certificate := range certificates { + if certificateType == appstoreconnect.IOSDistribution && isDistributionCertificate(certificate) { + filteredCertificates = append(filteredCertificates, certificate) + } else if certificateType == appstoreconnect.IOSDevelopment && !isDistributionCertificate(certificate) { + filteredCertificates = append(filteredCertificates, certificate) + } + } + + log.Debugf("Valid certificates with type %s:\n%s", certificateType, certsToString(filteredCertificates)) + + if len(filteredCertificates) == 0 { + return nil + } + + log.Debugf("Valid certificates with type %s:\n%s", certificateType, certsToString(filteredCertificates)) + + if len(filteredCertificates) == 0 { + return nil + } + + log.Debugf("Valid certificates with type %s\n%s ", certificateType, certsToString(filteredCertificates)) + + return filteredCertificates +} + +func isDistributionCertificate(cert certificateutil.CertificateInfoModel) bool { + // Apple certificate types: https://help.apple.com/xcode/mac/current/#/dev80c6204ec) + return strings.HasPrefix(strings.ToLower(cert.CommonName), strings.ToLower("iPhone Distribution")) || + strings.HasPrefix(strings.ToLower(cert.CommonName), strings.ToLower("Apple Distribution")) +} + +func certsToString(certs []certificateutil.CertificateInfoModel) (s string) { + for i, cert := range certs { + s += "- " + s += cert.String() + if i < len(certs)-1 { + s += "\n" + } + } + return +} diff --git a/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/codesignasset/writer.go b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/codesignasset/writer.go new file mode 100644 index 0000000..9a08582 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/codesignasset/writer.go @@ -0,0 +1,109 @@ +// Package codesignasset implements a autocodesign.AssetWriter which writes certificates, profiles to the keychain and filesystem. +package codesignasset + +import ( + "fmt" + "io/ioutil" + "os" + "path" + + "github.com/bitrise-io/go-utils/log" + "github.com/bitrise-io/go-utils/pathutil" + "github.com/bitrise-io/go-xcode/certificateutil" + "github.com/bitrise-io/go-xcode/v2/autocodesign" + "github.com/bitrise-io/go-xcode/v2/autocodesign/devportalclient/appstoreconnect" + "github.com/bitrise-io/go-xcode/v2/autocodesign/keychain" +) + +const ( + // ProfileIOSExtension is the iOS provisioning profile extension + ProfileIOSExtension = ".mobileprovision" + // ProfileMacExtension is the macOS provisioning profile extension + ProfileMacExtension = ".provisionprofile" +) + +// Writer ... +type Writer struct { + keychain keychain.Keychain +} + +// NewWriter ... +func NewWriter(keychain keychain.Keychain) Writer { + return Writer{ + keychain: keychain, + } +} + +// Write ... +func (w Writer) Write(codesignAssetsByDistributionType map[autocodesign.DistributionType]autocodesign.AppCodesignAssets) error { + i := 0 + for _, codesignAssets := range codesignAssetsByDistributionType { + log.Printf("certificate: %s", codesignAssets.Certificate.CommonName) + + if err := w.keychain.InstallCertificate(codesignAssets.Certificate, ""); err != nil { + return fmt.Errorf("failed to install certificate: %s", err) + } + + log.Printf("profiles:") + for _, profile := range codesignAssets.ArchivableTargetProfilesByBundleID { + log.Printf("- %s", profile.Attributes().Name) + + if err := w.InstallProfile(profile); err != nil { + return fmt.Errorf("failed to write profile to file: %s", err) + } + } + + for _, profile := range codesignAssets.UITestTargetProfilesByBundleID { + log.Printf("- %s", profile.Attributes().Name) + + if err := w.InstallProfile(profile); err != nil { + return fmt.Errorf("failed to write profile to file: %s", err) + } + } + + if i < len(codesignAssetsByDistributionType)-1 { + fmt.Println() + } + i++ + } + + return nil +} + +// InstallCertificate installs the certificate to the Keychain +func (w Writer) InstallCertificate(certificate certificateutil.CertificateInfoModel) error { + // Empty passphrase provided, as already parsed certificate + private key + return w.keychain.InstallCertificate(certificate, "") +} + +// InstallProfile writes the provided profile under the `$HOME/Library/MobileDevice/Provisioning Profiles` directory. +// Xcode uses profiles located in that directory. +// The file extension depends on the profile's platform `IOS` => `.mobileprovision`, `MAC_OS` => `.provisionprofile` +func (w Writer) InstallProfile(profile autocodesign.Profile) error { + homeDir := os.Getenv("HOME") + profilesDir := path.Join(homeDir, "Library/MobileDevice/Provisioning Profiles") + if exists, err := pathutil.IsDirExists(profilesDir); err != nil { + return fmt.Errorf("failed to check directory (%s) for provisioning profiles: %s", profilesDir, err) + } else if !exists { + if err := os.MkdirAll(profilesDir, 0600); err != nil { + return fmt.Errorf("failed to generate directory (%s) for provisioning profiles: %s", profilesDir, err) + } + } + + var ext string + switch profile.Attributes().Platform { + case appstoreconnect.IOS: + ext = ProfileIOSExtension + case appstoreconnect.MacOS: + ext = ProfileMacExtension + default: + return fmt.Errorf("failed to write profile to file, unsupported platform: (%s). Supported platforms: %s, %s", profile.Attributes().Platform, appstoreconnect.IOS, appstoreconnect.MacOS) + } + + name := path.Join(profilesDir, profile.Attributes().UUID+ext) + if err := ioutil.WriteFile(name, profile.Attributes().ProfileContent, 0600); err != nil { + return fmt.Errorf("failed to write profile to file: %s", err) + } + + return nil +} diff --git a/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/devices.go b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/devices.go new file mode 100644 index 0000000..6995a7b --- /dev/null +++ b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/devices.go @@ -0,0 +1,117 @@ +package autocodesign + +import ( + "errors" + "fmt" + + "github.com/bitrise-io/go-utils/log" + "github.com/bitrise-io/go-xcode/devportalservice" + "github.com/bitrise-io/go-xcode/v2/autocodesign/devportalclient/appstoreconnect" +) + +// EnsureTestDevices fetches devices from Apple, and register missing devices. +// Leave testDevices empty, to skip device registration. +func EnsureTestDevices(deviceClient DevPortalClient, testDevices []devportalservice.TestDevice, platform Platform) ([]appstoreconnect.Device, error) { + fmt.Println() + log.Infof("Fetching Apple Developer Portal devices") + + // IOS device platform includes: APPLE_WATCH, IPAD, IPHONE, IPOD and APPLE_TV device classes. + devPortalDevices, err := deviceClient.ListDevices("", appstoreconnect.IOSDevice) + if err != nil { + return nil, fmt.Errorf("failed to fetch devices: %s", err) + } + + log.Printf("%d devices are registered on the Apple Developer Portal", len(devPortalDevices)) + for _, devPortalDevice := range devPortalDevices { + log.Debugf("- %s, %s, UDID (%s), ID (%s)", devPortalDevice.Attributes.Name, devPortalDevice.Attributes.DeviceClass, devPortalDevice.Attributes.UDID, devPortalDevice.ID) + } + + if len(testDevices) != 0 { + fmt.Println() + log.Infof("Checking if %d Bitrise test device(s) are registered on Developer Portal", len(testDevices)) + for _, d := range testDevices { + log.Debugf("- %s, %s, UDID (%s), added at %s", d.Title, d.DeviceType, d.DeviceID, d.UpdatedAt) + } + + newDevPortalDevices, err := registerMissingTestDevices(deviceClient, testDevices, devPortalDevices) + if err != nil { + return nil, fmt.Errorf("failed to register Bitrise Test device on Apple Developer Portal: %s", err) + } + devPortalDevices = append(devPortalDevices, newDevPortalDevices...) + } + + devPortalDevices = filterDevPortalDevices(devPortalDevices, platform) + return devPortalDevices, nil +} + +func registerMissingTestDevices(client DevPortalClient, testDevices []devportalservice.TestDevice, devPortalDevices []appstoreconnect.Device) ([]appstoreconnect.Device, error) { + if client == nil { + return []appstoreconnect.Device{}, fmt.Errorf("the App Store Connect API client not provided") + } + + var newDevPortalDevices []appstoreconnect.Device + + for _, testDevice := range testDevices { + log.Printf("checking if the device (%s) is registered", testDevice.DeviceID) + + devPortalDevice := findDevPortalDevice(testDevice, devPortalDevices) + if devPortalDevice != nil { + log.Printf("device already registered") + continue + } + + log.Printf("registering device") + newDevPortalDevice, err := client.RegisterDevice(testDevice) + if err != nil { + var registrationError appstoreconnect.DeviceRegistrationError + if errors.As(err, ®istrationError) { + log.Warnf("Failed to register device (can be caused by invalid UDID or trying to register a Mac device): %s", registrationError.Reason) + + continue + } + + return nil, err + } + + if newDevPortalDevice != nil { + newDevPortalDevices = append(newDevPortalDevices, *newDevPortalDevice) + } + } + + return newDevPortalDevices, nil +} + +func findDevPortalDevice(testDevice devportalservice.TestDevice, devPortalDevices []appstoreconnect.Device) *appstoreconnect.Device { + for _, devPortalDevice := range devPortalDevices { + if devportalservice.IsEqualUDID(devPortalDevice.Attributes.UDID, testDevice.DeviceID) { + return &devPortalDevice + } + } + return nil +} + +func filterDevPortalDevices(devPortalDevices []appstoreconnect.Device, platform Platform) []appstoreconnect.Device { + var filteredDevices []appstoreconnect.Device + + for _, devPortalDevice := range devPortalDevices { + deviceClass := devPortalDevice.Attributes.DeviceClass + + switch platform { + case IOS: + isIosOrWatchosDevice := deviceClass == appstoreconnect.AppleWatch || + deviceClass == appstoreconnect.Ipad || + deviceClass == appstoreconnect.Iphone || + deviceClass == appstoreconnect.Ipod + + if isIosOrWatchosDevice { + filteredDevices = append(filteredDevices, devPortalDevice) + } + case TVOS: + if deviceClass == appstoreconnect.AppleTV { + filteredDevices = append(filteredDevices, devPortalDevice) + } + } + } + + return filteredDevices +} diff --git a/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/devportalclient/appstoreconnect/appstoreconnect.go b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/devportalclient/appstoreconnect/appstoreconnect.go new file mode 100644 index 0000000..a17e50b --- /dev/null +++ b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/devportalclient/appstoreconnect/appstoreconnect.go @@ -0,0 +1,282 @@ +// Package appstoreconnect implements a client for the App Store Connect API. +// +// It contains type definitions, authentication and API calls, without business logic built on those API calls. +package appstoreconnect + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/url" + "reflect" + "time" + + "github.com/bitrise-io/go-utils/httputil" + "github.com/bitrise-io/go-utils/log" + "github.com/bitrise-io/go-utils/retry" + "github.com/golang-jwt/jwt/v4" + "github.com/google/go-querystring/query" + "github.com/hashicorp/go-retryablehttp" +) + +const ( + baseURL = "https://api.appstoreconnect.apple.com/" + apiVersion = "v1" +) + +var ( + // A given token can be reused for up to 20 minutes: + // https://developer.apple.com/documentation/appstoreconnectapi/generating_tokens_for_api_requests + // + // Using 19 minutes to make sure time inaccuracies at token validation does not cause issues. + jwtDuration = 19 * time.Minute + jwtReserveTime = 2 * time.Minute +) + +// HTTPClient ... +type HTTPClient interface { + Do(req *http.Request) (*http.Response, error) +} + +type service struct { + client *Client +} + +// Client communicate with the Apple API +type Client struct { + EnableDebugLogs bool + + keyID string + issuerID string + privateKeyContent []byte + + token *jwt.Token + signedToken string + + client HTTPClient + BaseURL *url.URL + + common service // Reuse a single struct instead of allocating one for each service on the heap. + Provisioning *ProvisioningService +} + +// NewRetryableHTTPClient create a new http client with retry settings. +func NewRetryableHTTPClient() *http.Client { + client := retry.NewHTTPClient() + client.CheckRetry = func(ctx context.Context, resp *http.Response, err error) (bool, error) { + if resp != nil && resp.StatusCode == http.StatusUnauthorized { + log.Debugf("Received HTTP 401 (Unauthorized), retrying request...") + return true, nil + } + + shouldRetry, err := retryablehttp.DefaultRetryPolicy(ctx, resp, err) + if shouldRetry && resp != nil { + log.Debugf("Retry network error: %d", resp.StatusCode) + } + + return shouldRetry, err + } + return client.StandardClient() +} + +// NewClient creates a new client +func NewClient(httpClient HTTPClient, keyID, issuerID string, privateKey []byte) *Client { + baseURL, err := url.Parse(baseURL) + if err != nil { + panic("invalid api base url: " + err.Error()) + } + + c := &Client{ + keyID: keyID, + issuerID: issuerID, + privateKeyContent: privateKey, + + client: httpClient, + BaseURL: baseURL, + } + c.common.client = c + c.Provisioning = (*ProvisioningService)(&c.common) + + return c +} + +// ensureSignedToken makes sure that the JWT auth token is not expired +// and return a signed key +func (c *Client) ensureSignedToken() (string, error) { + if c.token != nil { + claim, ok := c.token.Claims.(claims) + if !ok { + return "", fmt.Errorf("failed to cast claim for token") + } + expiration := time.Unix(int64(claim.Expiration), 0) + + // A given token can be reused for up to 20 minutes: + // https://developer.apple.com/documentation/appstoreconnectapi/generating_tokens_for_api_requests + // + // The step generates a new token 2 minutes before the expiry. + if time.Until(expiration) > jwtReserveTime { + return c.signedToken, nil + } + + log.Debugf("JWT token expired, regenerating") + } else { + log.Debugf("Generating JWT token") + } + + c.token = createToken(c.keyID, c.issuerID) + var err error + if c.signedToken, err = signToken(c.token, c.privateKeyContent); err != nil { + return "", err + } + return c.signedToken, nil +} + +// NewRequest creates a new http.Request +func (c *Client) NewRequest(method, endpoint string, body interface{}) (*http.Request, error) { + endpoint = apiVersion + "/" + endpoint + u, err := c.BaseURL.Parse(endpoint) + if err != nil { + return nil, fmt.Errorf("parsing endpoint failed: %v", err) + } + + var buf io.ReadWriter + if body != nil { + buf = new(bytes.Buffer) + enc := json.NewEncoder(buf) + enc.SetEscapeHTML(false) + if err := enc.Encode(body); err != nil { + return nil, fmt.Errorf("encoding body failed: %v", err) + } + } + + req, err := http.NewRequest(method, u.String(), buf) + if err != nil { + return nil, fmt.Errorf("preparing request failed: %v", err) + } + + if body != nil { + req.Header.Set("Content-Type", "application/json") + } + + if _, ok := c.client.(*http.Client); ok { + signedToken, err := c.ensureSignedToken() + if err != nil { + return nil, fmt.Errorf("ensuring JWT token failed: %v", err) + } + req.Header.Set("Authorization", "Bearer "+signedToken) + } + + return req, nil +} + +func checkResponse(r *http.Response) error { + if r.StatusCode >= 200 && r.StatusCode <= 299 { + return nil + } + + errorResponse := &ErrorResponse{Response: r} + data, err := ioutil.ReadAll(r.Body) + if err == nil && data != nil { + if err := json.Unmarshal(data, errorResponse); err != nil { + log.Errorf("Failed to unmarshal response (%s): %s", string(data), err) + } + } + return errorResponse +} + +// Debugf ... +func (c *Client) Debugf(format string, v ...interface{}) { + if c.EnableDebugLogs { + log.Debugf(format, v...) + } +} + +// Do ... +func (c *Client) Do(req *http.Request, v interface{}) (*http.Response, error) { + c.Debugf("Request:") + if c.EnableDebugLogs { + if err := httputil.PrintRequest(req); err != nil { + c.Debugf("Failed to print request: %s", err) + } + } + + resp, err := c.client.Do(req) + + c.Debugf("Response:") + if c.EnableDebugLogs { + if err := httputil.PrintResponse(resp); err != nil { + c.Debugf("Failed to print response: %s", err) + } + } + + if err != nil { + return nil, err + } + defer func() { + if cerr := resp.Body.Close(); cerr != nil { + log.Warnf("Failed to close response body: %s", cerr) + } + }() + + if err := checkResponse(resp); err != nil { + return resp, err + } + + if v != nil { + decErr := json.NewDecoder(resp.Body).Decode(v) + if decErr == io.EOF { + decErr = nil // ignore EOF errors caused by empty response body + } + if decErr != nil { + err = decErr + } + } + + return resp, err +} + +// PagingOptions ... +type PagingOptions struct { + Limit int `url:"limit,omitempty"` + Cursor string `url:"cursor,omitempty"` + Next string `url:"-"` +} + +// UpdateCursor ... +func (opt *PagingOptions) UpdateCursor() error { + if opt != nil && opt.Next != "" { + u, err := url.Parse(opt.Next) + if err != nil { + return err + } + cursor := u.Query().Get("cursor") + opt.Cursor = cursor + } + return nil +} + +// addOptions adds the parameters in opt as URL query parameters to s. opt +// must be a struct whose fields may contain "url" tags. +func addOptions(s string, opt interface{}) (string, error) { + v := reflect.ValueOf(opt) + if v.Kind() == reflect.Ptr && v.IsNil() { + return s, nil + } + + u, err := url.Parse(s) + if err != nil { + return s, err + } + + qs, err := query.Values(opt) + if err != nil { + return s, err + } + + u.RawQuery = qs.Encode() + return u.String(), nil +} diff --git a/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/devportalclient/appstoreconnect/bundleids.go b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/devportalclient/appstoreconnect/bundleids.go new file mode 100644 index 0000000..b720924 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/devportalclient/appstoreconnect/bundleids.go @@ -0,0 +1,140 @@ +package appstoreconnect + +import ( + "net/http" + "strings" +) + +// BundleIDsEndpoint ... +const BundleIDsEndpoint = "bundleIds" + +// ListBundleIDsOptions ... +type ListBundleIDsOptions struct { + PagingOptions + FilterIdentifier string `url:"filter[identifier],omitempty"` + FilterName string `url:"filter[name],omitempty"` + FilterPlatform BundleIDPlatform `url:"filter[platform],omitempty"` + Include string `url:"include,omitempty"` +} + +// PagedDocumentLinks ... +type PagedDocumentLinks struct { + Next string `json:"next,omitempty"` +} + +// BundleIDAttributes ... +type BundleIDAttributes struct { + Identifier string `json:"identifier"` + Name string `json:"name"` + Platform string `json:"platform"` +} + +// Links ... +type Links struct { + Related string `json:"related"` + Self string `json:"next"` +} + +// RelationshipsLinks ... +type RelationshipsLinks struct { + Links Links `json:"links"` +} + +// BundleIDRelationships ... +type BundleIDRelationships struct { + Profiles RelationshipsLinks `json:"profiles"` + Capabilities RelationshipsLinks `json:"bundleIdCapabilities"` +} + +// BundleID ... +type BundleID struct { + Attributes BundleIDAttributes `json:"attributes"` + Relationships BundleIDRelationships `json:"relationships"` + + ID string `json:"id"` + Type string `json:"type"` +} + +// BundleIdsResponse ... +type BundleIdsResponse struct { + Data []BundleID `json:"data,omitempty"` + Links PagedDocumentLinks `json:"links,omitempty"` +} + +// ListBundleIDs ... +func (s ProvisioningService) ListBundleIDs(opt *ListBundleIDsOptions) (*BundleIdsResponse, error) { + if err := opt.UpdateCursor(); err != nil { + return nil, err + } + + u, err := addOptions(BundleIDsEndpoint, opt) + if err != nil { + return nil, err + } + + req, err := s.client.NewRequest(http.MethodGet, u, nil) + if err != nil { + return nil, err + } + + r := &BundleIdsResponse{} + if _, err := s.client.Do(req, r); err != nil { + return nil, err + } + + return r, err +} + +// BundleIDResponse ... +type BundleIDResponse struct { + Data BundleID `json:"data,omitempty"` +} + +// BundleIDCreateRequestDataAttributes ... +type BundleIDCreateRequestDataAttributes struct { + Identifier string `json:"identifier"` + Name string `json:"name"` + Platform BundleIDPlatform `json:"platform"` +} + +// BundleIDCreateRequestData ... +type BundleIDCreateRequestData struct { + Attributes BundleIDCreateRequestDataAttributes `json:"attributes"` + Type string `json:"type"` +} + +// BundleIDCreateRequest ... +type BundleIDCreateRequest struct { + Data BundleIDCreateRequestData `json:"data"` +} + +// CreateBundleID ... +func (s ProvisioningService) CreateBundleID(body BundleIDCreateRequest) (*BundleIDResponse, error) { + req, err := s.client.NewRequest(http.MethodPost, BundleIDsEndpoint, body) + if err != nil { + return nil, err + } + + r := &BundleIDResponse{} + if _, err := s.client.Do(req, r); err != nil { + return nil, err + } + + return r, nil +} + +// BundleID ... +func (s ProvisioningService) BundleID(relationshipLink string) (*BundleIDResponse, error) { + endpoint := strings.TrimPrefix(relationshipLink, baseURL+apiVersion) + req, err := s.client.NewRequest(http.MethodGet, endpoint, nil) + if err != nil { + return nil, err + } + + r := &BundleIDResponse{} + if _, err := s.client.Do(req, r); err != nil { + return nil, err + } + + return r, nil +} diff --git a/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/devportalclient/appstoreconnect/capabilities.go b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/devportalclient/appstoreconnect/capabilities.go new file mode 100644 index 0000000..0082d4f --- /dev/null +++ b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/devportalclient/appstoreconnect/capabilities.go @@ -0,0 +1,277 @@ +package appstoreconnect + +import ( + "net/http" + "strings" +) + +// BundleIDCapabilitiesEndpoint ... +const BundleIDCapabilitiesEndpoint = "bundleIdCapabilities" + +// CapabilityType ... +type CapabilityType string + +// CapabilityTypes ... +const ( + Ignored CapabilityType = "-ignored-" + ProfileAttachedEntitlement CapabilityType = "-profile-attached-" + ICloud CapabilityType = "ICLOUD" + InAppPurchase CapabilityType = "IN_APP_PURCHASE" + GameCenter CapabilityType = "GAME_CENTER" + PushNotifications CapabilityType = "PUSH_NOTIFICATIONS" + Wallet CapabilityType = "WALLET" + InterAppAudio CapabilityType = "INTER_APP_AUDIO" + Maps CapabilityType = "MAPS" + AssociatedDomains CapabilityType = "ASSOCIATED_DOMAINS" + PersonalVPN CapabilityType = "PERSONAL_VPN" + AppGroups CapabilityType = "APP_GROUPS" + Healthkit CapabilityType = "HEALTHKIT" + Homekit CapabilityType = "HOMEKIT" + WirelessAccessoryConfiguration CapabilityType = "WIRELESS_ACCESSORY_CONFIGURATION" + ApplePay CapabilityType = "APPLE_PAY" + DataProtection CapabilityType = "DATA_PROTECTION" + Sirikit CapabilityType = "SIRIKIT" + NetworkExtensions CapabilityType = "NETWORK_EXTENSIONS" + Multipath CapabilityType = "MULTIPATH" + HotSpot CapabilityType = "HOT_SPOT" + NFCTagReading CapabilityType = "NFC_TAG_READING" + Classkit CapabilityType = "CLASSKIT" + AutofillCredentialProvider CapabilityType = "AUTOFILL_CREDENTIAL_PROVIDER" + AccessWIFIInformation CapabilityType = "ACCESS_WIFI_INFORMATION" + NetworkCustomProtocol CapabilityType = "NETWORK_CUSTOM_PROTOCOL" + CoremediaHLSLowLatency CapabilityType = "COREMEDIA_HLS_LOW_LATENCY" + SystemExtensionInstall CapabilityType = "SYSTEM_EXTENSION_INSTALL" + UserManagement CapabilityType = "USER_MANAGEMENT" + SignInWithApple CapabilityType = "APPLE_ID_AUTH" + ParentApplicationIdentifiers CapabilityType = "ODIC_PARENT_BUNDLEID" + OnDemandInstallCapable CapabilityType = "ON_DEMAND_INSTALL_CAPABLE" +) + +// Entitlement keys ... +const ( + ParentApplicationIdentifierEntitlementKey = "com.apple.developer.parent-application-identifiers" + SignInWithAppleEntitlementKey = "com.apple.developer.applesignin" +) + +// ServiceTypeByKey ... +var ServiceTypeByKey = map[string]CapabilityType{ + "com.apple.security.application-groups": AppGroups, + "com.apple.developer.in-app-payments": ApplePay, + "com.apple.developer.associated-domains": AssociatedDomains, + "com.apple.developer.healthkit": Healthkit, + "com.apple.developer.homekit": Homekit, + "com.apple.developer.networking.HotspotConfiguration": HotSpot, + "com.apple.InAppPurchase": InAppPurchase, + "inter-app-audio": InterAppAudio, + "com.apple.developer.networking.multipath": Multipath, + "com.apple.developer.networking.networkextension": NetworkExtensions, + "com.apple.developer.nfc.readersession.formats": NFCTagReading, + "com.apple.developer.networking.vpn.api": PersonalVPN, + "aps-environment": PushNotifications, + "com.apple.developer.siri": Sirikit, + SignInWithAppleEntitlementKey: SignInWithApple, + "com.apple.developer.on-demand-install-capable": OnDemandInstallCapable, + "com.apple.developer.pass-type-identifiers": Wallet, + "com.apple.external-accessory.wireless-configuration": WirelessAccessoryConfiguration, + "com.apple.developer.default-data-protection": DataProtection, + "com.apple.developer.icloud-services": ICloud, + "com.apple.developer.authentication-services.autofill-credential-provider": AutofillCredentialProvider, + "com.apple.developer.networking.wifi-info": AccessWIFIInformation, + "com.apple.developer.ClassKit-environment": Classkit, + "com.apple.developer.coremedia.hls.low-latency": CoremediaHLSLowLatency, + // does not appear on developer portal + "com.apple.developer.icloud-container-identifiers": Ignored, + "com.apple.developer.ubiquity-container-identifiers": Ignored, + ParentApplicationIdentifierEntitlementKey: Ignored, + // These are entitlements not supported via the API and this step, + // profile needs to be manually generated on Apple Developer Portal. + "com.apple.developer.contacts.notes": ProfileAttachedEntitlement, + "com.apple.developer.carplay-audio": ProfileAttachedEntitlement, + "com.apple.developer.carplay-communication": ProfileAttachedEntitlement, + "com.apple.developer.carplay-charging": ProfileAttachedEntitlement, + "com.apple.developer.carplay-maps": ProfileAttachedEntitlement, + "com.apple.developer.carplay-parking": ProfileAttachedEntitlement, + "com.apple.developer.carplay-quick-ordering": ProfileAttachedEntitlement, + "com.apple.developer.exposure-notification": ProfileAttachedEntitlement, +} + +// CapabilitySettingAllowedInstances ... +type CapabilitySettingAllowedInstances string + +// AllowedInstances ... +const ( + Entry CapabilitySettingAllowedInstances = "ENTRY" + Single CapabilitySettingAllowedInstances = "SINGLE" + Multiple CapabilitySettingAllowedInstances = "MULTIPLE" +) + +// CapabilitySettingKey ... +type CapabilitySettingKey string + +// CapabilitySettingKeys +const ( + IcloudVersion CapabilitySettingKey = "ICLOUD_VERSION" + DataProtectionPermissionLevel CapabilitySettingKey = "DATA_PROTECTION_PERMISSION_LEVEL" + AppleIDAuthAppConsent CapabilitySettingKey = "APPLE_ID_AUTH_APP_CONSENT" + AppGroupIdentifiers CapabilitySettingKey = "APP_GROUP_IDENTIFIERS" +) + +// CapabilityOptionKey ... +type CapabilityOptionKey string + +// CapabilityOptionKeys ... +const ( + Xcode5 CapabilityOptionKey = "XCODE_5" + Xcode6 CapabilityOptionKey = "XCODE_6" + CompleteProtection CapabilityOptionKey = "COMPLETE_PROTECTION" + ProtectedUnlessOpen CapabilityOptionKey = "PROTECTED_UNLESS_OPEN" + ProtectedUntilFirstUserAuth CapabilityOptionKey = "PROTECTED_UNTIL_FIRST_USER_AUTH" +) + +// CapabilityOption ... +type CapabilityOption struct { + Description string `json:"description,omitempty"` + Enabled bool `json:"enabled,omitempty"` + EnabledByDefault bool `json:"enabledByDefault,omitempty"` + Key CapabilityOptionKey `json:"key,omitempty"` + Name string `json:"name,omitempty"` + SupportsWildcard bool `json:"supportsWildcard,omitempty"` +} + +// CapabilitySetting ... +type CapabilitySetting struct { + AllowedInstances CapabilitySettingAllowedInstances `json:"allowedInstances,omitempty"` + Description string `json:"description,omitempty"` + EnabledByDefault bool `json:"enabledByDefault,omitempty"` + Key CapabilitySettingKey `json:"key,omitempty"` + Name string `json:"name,omitempty"` + Options []CapabilityOption `json:"options,omitempty"` + Visible bool `json:"visible,omitempty"` + MinInstances int `json:"minInstances,omitempty"` +} + +// +// BundleIDCapabilityCreateRequest + +// BundleIDCapabilityCreateRequestDataAttributes ... +type BundleIDCapabilityCreateRequestDataAttributes struct { + CapabilityType CapabilityType `json:"capabilityType"` + Settings []CapabilitySetting `json:"settings"` +} + +// BundleIDCapabilityCreateRequestDataRelationships ... +type BundleIDCapabilityCreateRequestDataRelationships struct { + BundleID BundleIDCapabilityCreateRequestDataRelationshipsBundleID `json:"bundleId"` +} + +// BundleIDCapabilityCreateRequestDataRelationshipsBundleID ... +type BundleIDCapabilityCreateRequestDataRelationshipsBundleID struct { + Data BundleIDCapabilityCreateRequestDataRelationshipsBundleIDData `json:"data"` +} + +// BundleIDCapabilityCreateRequestDataRelationshipsBundleIDData ... +type BundleIDCapabilityCreateRequestDataRelationshipsBundleIDData struct { + ID string `json:"id"` + Type string `json:"type"` +} + +// BundleIDCapabilityCreateRequestData ... +type BundleIDCapabilityCreateRequestData struct { + Attributes BundleIDCapabilityCreateRequestDataAttributes `json:"attributes"` + Relationships BundleIDCapabilityCreateRequestDataRelationships `json:"relationships"` + Type string `json:"type"` +} + +// BundleIDCapabilityCreateRequest ... +type BundleIDCapabilityCreateRequest struct { + Data BundleIDCapabilityCreateRequestData `json:"data"` +} + +// +// BundleIDCapabilityUpdateRequest + +// BundleIDCapabilityUpdateRequestDataAttributes ... +type BundleIDCapabilityUpdateRequestDataAttributes struct { + CapabilityType CapabilityType `json:"capabilityType"` + Settings []CapabilitySetting `json:"settings"` +} + +// BundleIDCapabilityUpdateRequestData ... +type BundleIDCapabilityUpdateRequestData struct { + Attributes BundleIDCapabilityUpdateRequestDataAttributes `json:"attributes"` + ID string `json:"id"` + Type string `json:"type"` +} + +// BundleIDCapabilityUpdateRequest ... +type BundleIDCapabilityUpdateRequest struct { + Data BundleIDCapabilityUpdateRequestData `json:"data"` +} + +// BundleIDCapabilityAttributes ... +type BundleIDCapabilityAttributes struct { + CapabilityType CapabilityType `json:"capabilityType"` + Settings []CapabilitySetting `json:"settings"` +} + +// BundleIDCapability ... +type BundleIDCapability struct { + Attributes BundleIDCapabilityAttributes + ID string `json:"id"` + Type string `json:"type"` +} + +// BundleIDCapabilityResponse ... +type BundleIDCapabilityResponse struct { + Data BundleIDCapability `json:"data"` +} + +// BundleIDCapabilitiesResponse ... +type BundleIDCapabilitiesResponse struct { + Data []BundleIDCapability `json:"data"` +} + +// EnableCapability ... +func (s ProvisioningService) EnableCapability(body BundleIDCapabilityCreateRequest) (*BundleIDCapabilityResponse, error) { + req, err := s.client.NewRequest(http.MethodPost, BundleIDCapabilitiesEndpoint, body) + if err != nil { + return nil, err + } + + r := &BundleIDCapabilityResponse{} + if _, err := s.client.Do(req, r); err != nil { + return nil, err + } + + return r, nil +} + +// UpdateCapability ... +func (s ProvisioningService) UpdateCapability(id string, body BundleIDCapabilityUpdateRequest) (*BundleIDCapabilityResponse, error) { + req, err := s.client.NewRequest(http.MethodPatch, BundleIDCapabilitiesEndpoint+"/"+id, body) + if err != nil { + return nil, err + } + + r := &BundleIDCapabilityResponse{} + if _, err := s.client.Do(req, r); err != nil { + return nil, err + } + return r, nil +} + +// Capabilities ... +func (s ProvisioningService) Capabilities(relationshipLink string) (*BundleIDCapabilitiesResponse, error) { + endpoint := strings.TrimPrefix(relationshipLink, baseURL+apiVersion) + req, err := s.client.NewRequest(http.MethodGet, endpoint, nil) + if err != nil { + return nil, err + } + + r := &BundleIDCapabilitiesResponse{} + if _, err := s.client.Do(req, r); err != nil { + return nil, err + } + + return r, nil +} diff --git a/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/devportalclient/appstoreconnect/certificates.go b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/devportalclient/appstoreconnect/certificates.go new file mode 100644 index 0000000..e64ce5e --- /dev/null +++ b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/devportalclient/appstoreconnect/certificates.go @@ -0,0 +1,123 @@ +package appstoreconnect + +import ( + "fmt" + "net/http" + "strings" +) + +// CertificatesEndpoint ... +const CertificatesEndpoint = "certificates" + +// ListCertificatesOptions ... +type ListCertificatesOptions struct { + PagingOptions + FilterSerialNumber string `url:"filter[serialNumber],omitempty"` + FilterCertificateType CertificateType `url:"filter[certificateType],omitempty"` +} + +// CertificateType ... +type CertificateType string + +// CertificateTypes ... +const ( + Development CertificateType = "DEVELOPMENT" + Distribution CertificateType = "DISTRIBUTION" + IOSDevelopment CertificateType = "IOS_DEVELOPMENT" + IOSDistribution CertificateType = "IOS_DISTRIBUTION" + MacDistribution CertificateType = "MAC_APP_DISTRIBUTION" + MacInstallerDistribution CertificateType = "MAC_INSTALLER_DISTRIBUTION" + MacDevelopment CertificateType = "MAC_APP_DEVELOPMENT" + DeveloperIDKext CertificateType = "DEVELOPER_ID_KEXT" + DeveloperIDApplication CertificateType = "DEVELOPER_ID_APPLICATION" +) + +// CertificateAttributes ... +type CertificateAttributes struct { + CertificateContent []byte `json:"certificateContent"` + DisplayName string `json:"displayName"` + ExpirationDate string `json:"expirationDate"` + Name string `json:"name"` + Platform BundleIDPlatform `json:"platform"` + SerialNumber string `json:"serialNumber"` + CertificateType CertificateType `json:"certificateType"` +} + +// Certificate ... +type Certificate struct { + Attributes CertificateAttributes `json:"attributes"` + ID string `json:"id"` + Type string `json:"type"` +} + +// CertificatesResponse ... +type CertificatesResponse struct { + Data []Certificate `json:"data"` + Links PagedDocumentLinks `json:"links,omitempty"` +} + +// ListCertificates ... +func (s ProvisioningService) ListCertificates(opt *ListCertificatesOptions) (*CertificatesResponse, error) { + if err := opt.UpdateCursor(); err != nil { + return nil, err + } + + u, err := addOptions(CertificatesEndpoint, opt) + if err != nil { + return nil, err + } + + req, err := s.client.NewRequest(http.MethodGet, u, nil) + if err != nil { + return nil, err + } + + r := &CertificatesResponse{} + if _, err := s.client.Do(req, r); err != nil { + return nil, err + } + + return r, nil +} + +// FetchCertificate fetch the certificate entity from the +func (s ProvisioningService) FetchCertificate(serialNumber string) (Certificate, error) { + r, err := s.ListCertificates(&ListCertificatesOptions{ + FilterSerialNumber: serialNumber, + }) + if err != nil { + return Certificate{}, fmt.Errorf("failed to fetch certificate (%s): %s", serialNumber, err) + } + + if len(r.Data) == 0 { + return Certificate{}, fmt.Errorf("no certificate found with serial %s", serialNumber) + } else if len(r.Data) > 1 { + return Certificate{}, fmt.Errorf("multiple certificates found with serial %s: %s", serialNumber, r.Data) + } + return r.Data[0], nil +} + +// Certificates ... +func (s ProvisioningService) Certificates(relationshipLink string, opt *PagingOptions) (*CertificatesResponse, error) { + if err := opt.UpdateCursor(); err != nil { + return nil, err + } + + u, err := addOptions(relationshipLink, opt) + if err != nil { + return nil, err + } + + endpoint := strings.TrimPrefix(u, baseURL+apiVersion) + req, err := s.client.NewRequest(http.MethodGet, endpoint, nil) + if err != nil { + return nil, err + } + + r := &CertificatesResponse{} + if _, err := s.client.Do(req, r); err != nil { + return nil, err + } + + return r, nil +} diff --git a/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/devportalclient/appstoreconnect/devices.go b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/devportalclient/appstoreconnect/devices.go new file mode 100644 index 0000000..50f28cc --- /dev/null +++ b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/devportalclient/appstoreconnect/devices.go @@ -0,0 +1,160 @@ +package appstoreconnect + +import ( + "net/http" + "strings" +) + +// DevicesEndpoint ... +const DevicesEndpoint = "devices" + +// ListDevicesOptions ... +type ListDevicesOptions struct { + PagingOptions + FilterUDID string `url:"filter[udid],omitempty"` + FilterPlatform DevicePlatform `url:"filter[platform],omitempty"` + FilterStatus Status `url:"filter[status],omitempty"` +} + +// DeviceClass ... +type DeviceClass string + +// DeviceClasses ... +const ( + AppleWatch DeviceClass = "APPLE_WATCH" + Ipad DeviceClass = "IPAD" + Iphone DeviceClass = "IPHONE" + Ipod DeviceClass = "IPOD" + AppleTV DeviceClass = "APPLE_TV" + Mac DeviceClass = "MAC" +) + +// DevicePlatform ... +type DevicePlatform string + +// DevicePlatforms ... +const ( + IOSDevice DevicePlatform = "IOS" + MacOSDevice DevicePlatform = "MAC_OS" +) + +// Status ... +type Status string + +// Statuses ... +const ( + Enabled Status = "ENABLED" + Disabled Status = "DISABLED" +) + +// DeviceAttributes ... +type DeviceAttributes struct { + DeviceClass DeviceClass `json:"deviceClass"` + Model string `json:"model"` + Name string `json:"name"` + Platform BundleIDPlatform `json:"platform"` + Status Status `json:"status"` + UDID string `json:"udid"` + AddedDate string `json:"addedDate"` +} + +// Device ... +type Device struct { + Type string `json:"type"` + ID string `json:"id"` + Attributes DeviceAttributes `json:"attributes"` +} + +// DevicesResponse ... +type DevicesResponse struct { + Data []Device `json:"data"` + Links PagedDocumentLinks `json:"links,omitempty"` +} + +// DeviceResponse ... +type DeviceResponse struct { + Data Device `json:"data"` + Links PagedDocumentLinks `json:"links,omitempty"` +} + +// ListDevices ... +func (s ProvisioningService) ListDevices(opt *ListDevicesOptions) (*DevicesResponse, error) { + if err := opt.UpdateCursor(); err != nil { + return nil, err + } + + u, err := addOptions(DevicesEndpoint, opt) + if err != nil { + return nil, err + } + + req, err := s.client.NewRequest(http.MethodGet, u, nil) + if err != nil { + return nil, err + } + + r := &DevicesResponse{} + if _, err := s.client.Do(req, r); err != nil { + return nil, err + } + + return r, nil +} + +// DeviceCreateRequestDataAttributes ... +type DeviceCreateRequestDataAttributes struct { + Name string `json:"name"` + Platform BundleIDPlatform `json:"platform"` + UDID string `json:"udid"` +} + +// DeviceCreateRequestData ... +type DeviceCreateRequestData struct { + Attributes DeviceCreateRequestDataAttributes `json:"attributes"` + Type string `json:"type"` +} + +// DeviceCreateRequest ... +type DeviceCreateRequest struct { + Data DeviceCreateRequestData `json:"data"` +} + +// RegisterNewDevice ... +func (s ProvisioningService) RegisterNewDevice(body DeviceCreateRequest) (*DeviceResponse, error) { + req, err := s.client.NewRequest(http.MethodPost, DevicesEndpoint, body) + if err != nil { + return nil, err + } + + r := &DeviceResponse{} + if _, err := s.client.Do(req, r); err != nil { + return nil, err + } + + return r, nil +} + +// Devices ... +func (s ProvisioningService) Devices(relationshipLink string, opt *PagingOptions) (*DevicesResponse, error) { + if err := opt.UpdateCursor(); err != nil { + return nil, err + } + + u, err := addOptions(relationshipLink, opt) + if err != nil { + return nil, err + } + + endpoint := strings.TrimPrefix(u, baseURL+apiVersion) + req, err := s.client.NewRequest(http.MethodGet, endpoint, nil) + if err != nil { + return nil, err + } + + r := &DevicesResponse{} + if _, err := s.client.Do(req, r); err != nil { + return nil, err + } + + return r, nil +} diff --git a/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/devportalclient/appstoreconnect/error.go b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/devportalclient/appstoreconnect/error.go new file mode 100644 index 0000000..53f5d4b --- /dev/null +++ b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/devportalclient/appstoreconnect/error.go @@ -0,0 +1,48 @@ +package appstoreconnect + +import ( + "fmt" + "net/http" +) + +// ErrorResponseError ... +type ErrorResponseError struct { + Code string `json:"code,omitempty"` + Status string `json:"status,omitempty"` + ID string `json:"id,omitempty"` + Title string `json:"title,omitempty"` + Detail string `json:"detail,omitempty"` + Source interface{} `json:"source,omitempty"` +} + +// ErrorResponse ... +type ErrorResponse struct { + Response *http.Response + Errors []ErrorResponseError `json:"errors,omitempty"` +} + +// Error ... +func (r ErrorResponse) Error() string { + var m string + if r.Response.Request != nil { + m = fmt.Sprintf("%s %s: %d\n", r.Response.Request.Method, r.Response.Request.URL, r.Response.StatusCode) + } + + var s string + for _, err := range r.Errors { + m += s + fmt.Sprintf("- %s: %s: %s", err.Code, err.Title, err.Detail) + s = "\n" + } + + return m +} + +// DeviceRegistrationError ... +type DeviceRegistrationError struct { + Reason string +} + +// Error ... +func (e DeviceRegistrationError) Error() string { + return e.Reason +} diff --git a/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/devportalclient/appstoreconnect/jwt.go b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/devportalclient/appstoreconnect/jwt.go new file mode 100644 index 0000000..ce66577 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/devportalclient/appstoreconnect/jwt.go @@ -0,0 +1,60 @@ +package appstoreconnect + +import ( + "crypto/ecdsa" + "crypto/x509" + "encoding/pem" + "errors" + "fmt" + "time" + + "github.com/golang-jwt/jwt/v4" +) + +// signToken signs the JWT token with the given .p8 private key content +func signToken(token *jwt.Token, privateKeyContent []byte) (string, error) { + block, _ := pem.Decode(privateKeyContent) + if block == nil { + return "", errors.New("failed to parse private key as a PEM format") + } + key, err := x509.ParsePKCS8PrivateKey(block.Bytes) + if err != nil { + return "", fmt.Errorf("failed to sign JWT token, private key format is invalid: %v", err) + } + + privateKey, ok := key.(*ecdsa.PrivateKey) + if !ok { + return "", errors.New("not a private key") + } + + return token.SignedString(privateKey) +} + +// createToken creates a jwt.Token for the Apple API +func createToken(keyID string, issuerID string) *jwt.Token { + payload := claims{ + IssuerID: issuerID, + Expiration: time.Now().Add(jwtDuration).Unix(), + Audience: "appstoreconnect-v1", + } + + // registers headers: alg = ES256 and typ = JWT + token := jwt.NewWithClaims(jwt.SigningMethodES256, payload) + + header := token.Header + header["kid"] = keyID + + return token +} + +// claims represents the JWT payload for the Apple API +type claims struct { + IssuerID string `json:"iss"` + Expiration int64 `json:"exp"` + Audience string `json:"aud"` +} + +// Valid implements the jwt.Claims interface +func (c claims) Valid() error { + return nil +} diff --git a/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/devportalclient/appstoreconnect/profiles.go b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/devportalclient/appstoreconnect/profiles.go new file mode 100644 index 0000000..322d4e2 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/devportalclient/appstoreconnect/profiles.go @@ -0,0 +1,301 @@ +package appstoreconnect + +import ( + "net/http" + "strings" + + "github.com/bitrise-io/go-xcode/v2/autocodesign/devportalclient/time" + "github.com/bitrise-io/go-xcode/xcodeproject/serialized" +) + +// ProfilesEndpoint ... +const ProfilesEndpoint = "profiles" + +// ListProfilesOptions ... +type ListProfilesOptions struct { + PagingOptions + FilterProfileState ProfileState `url:"filter[profileState],omitempty"` + FilterProfileType ProfileType `url:"filter[profileType],omitempty"` + FilterName string `url:"filter[name],omitempty"` + Include string `url:"include,omitempty"` +} + +// BundleIDPlatform ... +type BundleIDPlatform string + +// BundleIDPlatforms ... +const ( + IOS BundleIDPlatform = "IOS" + MacOS BundleIDPlatform = "MAC_OS" + Universal BundleIDPlatform = "UNIVERSAL" +) + +// ProfileState ... +type ProfileState string + +// ProfileStates ... +const ( + Active ProfileState = "ACTIVE" + Invalid ProfileState = "INVALID" +) + +// ProfileType ... +type ProfileType string + +// ProfileTypes ... +const ( + IOSAppDevelopment ProfileType = "IOS_APP_DEVELOPMENT" + IOSAppStore ProfileType = "IOS_APP_STORE" + IOSAppAdHoc ProfileType = "IOS_APP_ADHOC" + IOSAppInHouse ProfileType = "IOS_APP_INHOUSE" + + MacAppDevelopment ProfileType = "MAC_APP_DEVELOPMENT" + MacAppStore ProfileType = "MAC_APP_STORE" + MacAppDirect ProfileType = "MAC_APP_DIRECT" + + TvOSAppDevelopment ProfileType = "TVOS_APP_DEVELOPMENT" + TvOSAppStore ProfileType = "TVOS_APP_STORE" + TvOSAppAdHoc ProfileType = "TVOS_APP_ADHOC" + TvOSAppInHouse ProfileType = "TVOS_APP_INHOUSE" +) + +// ReadableString returns the readable version of the ProjectType +// e.g: IOSAppDevelopment => development +func (t ProfileType) ReadableString() string { + switch t { + case IOSAppStore, MacAppStore, TvOSAppStore: + return "app store" + case IOSAppInHouse, TvOSAppInHouse: + return "enterprise" + case IOSAppAdHoc, TvOSAppAdHoc: + return "ad-hoc" + case IOSAppDevelopment, MacAppDevelopment, TvOSAppDevelopment: + return "development" + case MacAppDirect: + return "development ID" + } + return "" +} + +// ProfileAttributes ... +type ProfileAttributes struct { + Name string `json:"name"` + Platform BundleIDPlatform `json:"platform"` + ProfileContent []byte `json:"profileContent"` + UUID string `json:"uuid"` + CreatedDate string `json:"createdDate"` + ProfileState ProfileState `json:"profileState"` + ProfileType ProfileType `json:"profileType"` + ExpirationDate time.Time `json:"expirationDate"` +} + +// Profile ... +type Profile struct { + Attributes ProfileAttributes `json:"attributes"` + + Relationships struct { + BundleID struct { + Links struct { + Related string `json:"related"` + Self string `json:"self"` + } `json:"links"` + } `json:"bundleId"` + + Certificates struct { + Links struct { + Related string `json:"related"` + Self string `json:"self"` + } `json:"links"` + } `json:"certificates"` + + Devices struct { + Links struct { + Related string `json:"related"` + Self string `json:"self"` + } `json:"links"` + } `json:"devices"` + } `json:"relationships"` + + ID string `json:"id"` +} + +// ProfilesResponse ... +type ProfilesResponse struct { + Data []Profile `json:"data"` + Included []struct { + Type string `json:"type"` + ID string `json:"id"` + Attributes serialized.Object `json:"attributes"` + } `json:"included"` + Links PagedDocumentLinks `json:"links,omitempty"` +} + +// ListProfiles ... +func (s ProvisioningService) ListProfiles(opt *ListProfilesOptions) (*ProfilesResponse, error) { + if err := opt.UpdateCursor(); err != nil { + return nil, err + } + + u, err := addOptions(ProfilesEndpoint, opt) + if err != nil { + return nil, err + } + + req, err := s.client.NewRequest(http.MethodGet, u, nil) + if err != nil { + return nil, err + } + + r := &ProfilesResponse{} + if _, err := s.client.Do(req, r); err != nil { + return nil, err + } + + return r, nil +} + +// ProfileCreateRequestDataAttributes ... +type ProfileCreateRequestDataAttributes struct { + Name string `json:"name"` + ProfileType ProfileType `json:"profileType"` +} + +// ProfileCreateRequestDataRelationshipData ... +type ProfileCreateRequestDataRelationshipData struct { + ID string `json:"id"` + Type string `json:"type"` +} + +// ProfileCreateRequestDataRelationshipsBundleID ... +type ProfileCreateRequestDataRelationshipsBundleID struct { + Data ProfileCreateRequestDataRelationshipData `json:"data"` +} + +// ProfileCreateRequestDataRelationshipsCertificates ... +type ProfileCreateRequestDataRelationshipsCertificates struct { + Data []ProfileCreateRequestDataRelationshipData `json:"data"` +} + +// ProfileCreateRequestDataRelationshipsDevices ... +type ProfileCreateRequestDataRelationshipsDevices struct { + Data []ProfileCreateRequestDataRelationshipData `json:"data"` +} + +// ProfileCreateRequestDataRelationships ... +type ProfileCreateRequestDataRelationships struct { + BundleID ProfileCreateRequestDataRelationshipsBundleID `json:"bundleId"` + Certificates ProfileCreateRequestDataRelationshipsCertificates `json:"certificates"` + Devices ProfileCreateRequestDataRelationshipsDevices `json:"devices"` +} + +// ProfileCreateRequestData ... +type ProfileCreateRequestData struct { + Attributes ProfileCreateRequestDataAttributes `json:"attributes"` + Relationships ProfileCreateRequestDataRelationships `json:"relationships"` + Type string `json:"type"` +} + +// ProfileCreateRequest ... +type ProfileCreateRequest struct { + Data ProfileCreateRequestData `json:"data"` +} + +// NewProfileCreateRequest returns a ProfileCreateRequest structure +func NewProfileCreateRequest(profileType ProfileType, name, bundleIDID string, certificateIDs []string, deviceIDs []string) ProfileCreateRequest { + bundleIDData := ProfileCreateRequestDataRelationshipData{ + ID: bundleIDID, + Type: "bundleIds", + } + + relationships := ProfileCreateRequestDataRelationships{ + BundleID: ProfileCreateRequestDataRelationshipsBundleID{Data: bundleIDData}, + } + + var certData []ProfileCreateRequestDataRelationshipData + for _, id := range certificateIDs { + certData = append(certData, ProfileCreateRequestDataRelationshipData{ + ID: id, + Type: "certificates", + }) + } + relationships.Certificates = ProfileCreateRequestDataRelationshipsCertificates{Data: certData} + + var deviceData []ProfileCreateRequestDataRelationshipData + for _, id := range deviceIDs { + deviceData = append(deviceData, ProfileCreateRequestDataRelationshipData{ + ID: id, + Type: "devices", + }) + } + if len(deviceData) > 0 { + relationships.Devices = ProfileCreateRequestDataRelationshipsDevices{Data: deviceData} + } + + data := ProfileCreateRequestData{ + Attributes: ProfileCreateRequestDataAttributes{ + Name: name, + ProfileType: profileType, + }, + Relationships: relationships, + Type: "profiles", + } + + return ProfileCreateRequest{Data: data} +} + +// ProfileResponse ... +type ProfileResponse struct { + Data Profile `json:"data"` + Links PagedDocumentLinks `json:"links,omitempty"` +} + +// CreateProfile ... +func (s ProvisioningService) CreateProfile(body ProfileCreateRequest) (*ProfileResponse, error) { + req, err := s.client.NewRequest(http.MethodPost, ProfilesEndpoint, body) + if err != nil { + return nil, err + } + + r := &ProfileResponse{} + if _, err := s.client.Do(req, r); err != nil { + return nil, err + } + + return r, nil +} + +// DeleteProfile ... +func (s ProvisioningService) DeleteProfile(id string) error { + req, err := s.client.NewRequest(http.MethodDelete, ProfilesEndpoint+"/"+id, nil) + if err != nil { + return err + } + + _, err = s.client.Do(req, nil) + return err +} + +// Profiles fetches provisioning profiles pointed by a relationship URL. +func (s ProvisioningService) Profiles(relationshipLink string, opt *PagingOptions) (*ProfilesResponse, error) { + if err := opt.UpdateCursor(); err != nil { + return nil, err + } + + u, err := addOptions(relationshipLink, opt) + if err != nil { + return nil, err + } + + endpoint := strings.TrimPrefix(u, baseURL+apiVersion) + req, err := s.client.NewRequest(http.MethodGet, endpoint, nil) + if err != nil { + return nil, err + } + + r := &ProfilesResponse{} + if _, err := s.client.Do(req, r); err != nil { + return nil, err + } + + return r, nil +} diff --git a/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/devportalclient/appstoreconnect/provisioning.go b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/devportalclient/appstoreconnect/provisioning.go new file mode 100644 index 0000000..ddd0e85 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/devportalclient/appstoreconnect/provisioning.go @@ -0,0 +1,4 @@ +package appstoreconnect + +// ProvisioningService ... +type ProvisioningService service diff --git a/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/devportalclient/time/time.go b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/devportalclient/time/time.go new file mode 100644 index 0000000..7554641 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/devportalclient/time/time.go @@ -0,0 +1,61 @@ +package time + +import ( + "fmt" + "strings" + "time" +) + +// Time ... +type Time time.Time + +// UnmarshalJSON ... +func (t *Time) UnmarshalJSON(b []byte) error { + timeStr := strings.Trim(string(b), `"`) + var errors []error + + for _, timeFormat := range timeFormats() { + parsed, err := time.Parse(timeFormat, timeStr) + if err != nil { + errors = append(errors, err) + continue + } + + *t = Time(parsed) + return nil + } + + return fmt.Errorf("%s", errors) +} + +func timeFormats() []string { + formats := []string{time.RFC3339} + formats = append(formats, appleKeyAuthTimeFormats()...) + formats = append(formats, appleIDAuthTimeFormats()...) + + return formats +} + +func appleKeyAuthTimeFormats() []string { + // Apple is using an ISO 8601 time format (https://en.wikipedia.org/wiki/ISO_8601). In this format the offset from + // the UTC time can have the following equivalent and interchangeable formats: + // * [+/-]07:00 + // * [+/-]0700 + // * [+/-]07 + // (* also if there is no UTC offset then [+0000, +00:00, +00] are the same as adding a Z after the seconds) + // + // Go has built in support for ISO 8601 but only for the zero offset UTC and the [+/-]07:00 format under time.RFC3339. + // We still need to check for the other two. + return []string{ + "2006-01-02T15:04:05.000-0700", + "2006-01-02T15:04:05.000-07", + } +} + +func appleIDAuthTimeFormats() []string { + // Spaceship returns this time format when setting SPACESHIP_AVOID_XCODE_API=true. This is needed because Apple's + // API started to return an error for the old spaceship implementation. + return []string{ + "2006-01-02 15:04:05 UTC", + } +} diff --git a/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/entitlement.go b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/entitlement.go new file mode 100644 index 0000000..6a94f03 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/entitlement.go @@ -0,0 +1,252 @@ +package autocodesign + +import ( + "errors" + "fmt" + + "github.com/bitrise-io/go-utils/log" + "github.com/bitrise-io/go-utils/sliceutil" + "github.com/bitrise-io/go-xcode/v2/autocodesign/devportalclient/appstoreconnect" + "github.com/bitrise-io/go-xcode/xcodeproject/serialized" +) + +// ICloudIdentifiersEntitlementKey ... +const ICloudIdentifiersEntitlementKey = "com.apple.developer.icloud-container-identifiers" + +// DataProtections ... +var DataProtections = map[string]appstoreconnect.CapabilityOptionKey{ + "NSFileProtectionComplete": appstoreconnect.CompleteProtection, + "NSFileProtectionCompleteUnlessOpen": appstoreconnect.ProtectedUnlessOpen, + "NSFileProtectionCompleteUntilFirstUserAuthentication": appstoreconnect.ProtectedUntilFirstUserAuth, +} + +// Capability ... +func (e Entitlement) Capability() (*appstoreconnect.BundleIDCapability, error) { + if len(e) == 0 { + return nil, nil + } + + // List of capabilities that need to be configured manually on the Developer portal + capabilitiesWarn := map[appstoreconnect.CapabilityType]string{ + appstoreconnect.AppGroups: "App Groups", + appstoreconnect.ApplePay: "Apple Pay Payment Processing", + appstoreconnect.ICloud: "iCloud", + appstoreconnect.SignInWithApple: "Sign In with Apple", + } + + // List of capabilities that the API does not support and prevent autoprovisioning + capabilitiesError := map[appstoreconnect.CapabilityType]string{ + appstoreconnect.OnDemandInstallCapable: "On Demand Install Capable (App Clips)", + appstoreconnect.ParentApplicationIdentifiers: "Parent Bundle ID", + } + + entKey := serialized.Object(e).Keys()[0] + + capType, ok := appstoreconnect.ServiceTypeByKey[entKey] + if !ok { + return nil, errors.New("unknown entitlement key: " + entKey) + } + + if capType == appstoreconnect.Ignored { + return nil, nil + } + + capSetts := []appstoreconnect.CapabilitySetting{} + if capType == appstoreconnect.ICloud { + capSett := appstoreconnect.CapabilitySetting{ + Key: appstoreconnect.IcloudVersion, + Options: []appstoreconnect.CapabilityOption{ + { + Key: appstoreconnect.Xcode6, + }, + }, + } + capSetts = append(capSetts, capSett) + } else if capType == appstoreconnect.DataProtection { + entVal, err := serialized.Object(e).String(entKey) + if err != nil { + return nil, errors.New("no entitlements value for key: " + entKey) + } + + key, ok := DataProtections[entVal] + if !ok { + return nil, errors.New("no data protection level found for entitlement value: " + entVal) + } + + capSett := appstoreconnect.CapabilitySetting{ + Key: appstoreconnect.DataProtectionPermissionLevel, + Options: []appstoreconnect.CapabilityOption{ + { + Key: key, + }, + }, + } + capSetts = append(capSetts, capSett) + } else if capType == appstoreconnect.SignInWithApple { + capSett := appstoreconnect.CapabilitySetting{ + Key: appstoreconnect.AppleIDAuthAppConsent, + Options: []appstoreconnect.CapabilityOption{ + { + Key: "PRIMARY_APP_CONSENT", + }, + }, + } + capSetts = append(capSetts, capSett) + } + + if capName, contains := capabilitiesWarn[capType]; contains { + log.Warnf("This will enable the \"%s\" capability but details will have to be configured manually using the Apple Developer Portal", capName) + } + + if capName, contains := capabilitiesError[capType]; contains { + return nil, fmt.Errorf("step does not support creating an application identifier using the \"%s\" capability, please add your Application Identifier manually using the Apple Developer Portal", capName) + } + + return &appstoreconnect.BundleIDCapability{ + Attributes: appstoreconnect.BundleIDCapabilityAttributes{ + CapabilityType: capType, + Settings: capSetts, + }, + }, nil +} + +// IsProfileAttached returns an error if an entitlement does not match a Capability but needs to be addded to the profile +// as an additional entitlement, after submitting a request to Apple. +func (e Entitlement) IsProfileAttached() bool { + if len(e) == 0 { + return false + } + entKey := serialized.Object(e).Keys()[0] + + capType, ok := appstoreconnect.ServiceTypeByKey[entKey] + return ok && capType == appstoreconnect.ProfileAttachedEntitlement +} + +// AppearsOnDeveloperPortal reports whether the given (project) Entitlement needs to be registered on Apple Developer Portal or not. +// List of services, to be registered: https://developer.apple.com/documentation/appstoreconnectapi/capabilitytype. +func (e Entitlement) AppearsOnDeveloperPortal() bool { + if len(e) == 0 { + return false + } + entKey := serialized.Object(e).Keys()[0] + + capType, ok := appstoreconnect.ServiceTypeByKey[entKey] + return ok && capType != appstoreconnect.Ignored && capType != appstoreconnect.ProfileAttachedEntitlement +} + +// Equal ... +func (e Entitlement) Equal(cap appstoreconnect.BundleIDCapability, allEntitlements Entitlements) (bool, error) { + if len(e) == 0 { + return false, nil + } + + entKey := serialized.Object(e).Keys()[0] + + capType, ok := appstoreconnect.ServiceTypeByKey[entKey] + if !ok { + return false, errors.New("unknown entitlement key: " + entKey) + } + + if cap.Attributes.CapabilityType != capType { + return false, nil + } + + if capType == appstoreconnect.ICloud { + return iCloudEquals(allEntitlements, cap) + } else if capType == appstoreconnect.DataProtection { + entVal, err := serialized.Object(e).String(entKey) + if err != nil { + return false, err + } + return dataProtectionEquals(entVal, cap) + } + + return true, nil +} + +func (e Entitlements) iCloudServices() (iCloudDocuments, iCloudKit, keyValueStorage bool, err error) { + v, err := serialized.Object(e).String("com.apple.developer.ubiquity-kvstore-identifier") + if err != nil && !serialized.IsKeyNotFoundError(err) { + return false, false, false, err + } + keyValueStorage = v != "" + + iCloudServices, err := serialized.Object(e).StringSlice("com.apple.developer.icloud-services") + if err != nil && !serialized.IsKeyNotFoundError(err) { + return false, false, false, err + } + + if len(iCloudServices) > 0 { + iCloudDocuments = sliceutil.IsStringInSlice("CloudDocuments", iCloudServices) + iCloudKit = sliceutil.IsStringInSlice("CloudKit", iCloudServices) + } + return +} + +// ICloudContainers returns the list of iCloud containers +func (e Entitlements) ICloudContainers() ([]string, error) { + usesDocuments, usesCloudKit, _, err := e.iCloudServices() + if err != nil && !serialized.IsKeyNotFoundError(err) { + return nil, err + } + + if !usesCloudKit && !usesDocuments { + return nil, nil + } + + containers, err := serialized.Object(e).StringSlice(ICloudIdentifiersEntitlementKey) + if err != nil && !serialized.IsKeyNotFoundError(err) { + return nil, err + } + return containers, nil +} + +func iCloudEquals(ent Entitlements, cap appstoreconnect.BundleIDCapability) (bool, error) { + documents, cloudKit, kvStorage, err := ent.iCloudServices() + if err != nil { + return false, err + } + + if len(cap.Attributes.Settings) != 1 { + return false, nil + } + + capSett := cap.Attributes.Settings[0] + if capSett.Key != appstoreconnect.IcloudVersion { + return false, nil + } + if len(capSett.Options) != 1 { + return false, nil + } + + capSettOpt := capSett.Options[0] + if (documents || cloudKit || kvStorage) && capSettOpt.Key != appstoreconnect.Xcode6 { + return false, nil + } + return true, nil +} + +func dataProtectionEquals(entVal string, cap appstoreconnect.BundleIDCapability) (bool, error) { + key, ok := DataProtections[entVal] + if !ok { + return false, errors.New("no data protection level found for entitlement value: " + entVal) + } + + if len(cap.Attributes.Settings) != 1 { + return false, nil + } + + capSett := cap.Attributes.Settings[0] + if capSett.Key != appstoreconnect.DataProtectionPermissionLevel { + return false, nil + } + if len(capSett.Options) != 1 { + return false, nil + } + + capSettOpt := capSett.Options[0] + if capSettOpt.Key != key { + return false, nil + } + return true, nil +} diff --git a/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/errors.go b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/errors.go new file mode 100644 index 0000000..5af3ef1 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/errors.go @@ -0,0 +1,92 @@ +package autocodesign + +import ( + "fmt" + + "github.com/bitrise-io/go-xcode/v2/autocodesign/devportalclient/appstoreconnect" +) + +// DetailedError ... +type DetailedError struct { + ErrorMessage string + Title string + Description string + Recommendation string +} + +func (e DetailedError) Error() string { + message := "" + if e.ErrorMessage != "" { + message += e.ErrorMessage + "\n" + } + message += "\n" + if e.Title != "" { + message += e.Title + "\n" + } + if e.Description != "" { + message += e.Description + "\n" + } + if e.Recommendation != "" { + message += "\n" + message += e.Recommendation + "\n" + } + + return message +} + +// missingCertificateError ... +type missingCertificateError struct { + Type appstoreconnect.CertificateType +} + +func (e missingCertificateError) Error() string { + return fmt.Sprintf("no valid %s type certificates uploaded\n ", e.Type) +} + +// NonmatchingProfileError is returned when a profile/bundle ID does not match project requirements +// It is not a fatal error, as the profile can be regenerated +type NonmatchingProfileError struct { + Reason string +} + +func (e NonmatchingProfileError) Error() string { + return fmt.Sprintf("provisioning profile does not match requirements: %s", e.Reason) +} + +// ProfilesInconsistentError is returned when a profile is deleted by an other actor +type ProfilesInconsistentError struct { + wrapErr error +} + +// NewProfilesInconsistentError ... +func NewProfilesInconsistentError(wrapErr error) ProfilesInconsistentError { + return ProfilesInconsistentError{ + wrapErr: wrapErr, + } +} + +func (e ProfilesInconsistentError) Error() string { + return fmt.Sprintf("provisioning profiles were concurrently changed on Developer Portal, %s", e.wrapErr) +} + +func (e ProfilesInconsistentError) Unwrap() error { + return e.wrapErr +} + +// ErrAppClipAppID ... +type ErrAppClipAppID struct { +} + +// Error ... +func (ErrAppClipAppID) Error() string { + return "can't create Application Identifier for App Clip target" +} + +// ErrAppClipAppIDWithAppleSigning ... +type ErrAppClipAppIDWithAppleSigning struct { +} + +// Error ... +func (ErrAppClipAppIDWithAppleSigning) Error() string { + return "can't manage Application Identifier for App Clip target with 'Sign In With Apple' capability" +} diff --git a/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/keychain/keychain.go b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/keychain/keychain.go new file mode 100644 index 0000000..63cd8ce --- /dev/null +++ b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/keychain/keychain.go @@ -0,0 +1,229 @@ +// Package keychain contains methods to manage and install certificates to the MacOS keychain. +package keychain + +import ( + "fmt" + "path/filepath" + "strings" + + "github.com/bitrise-io/go-steputils/v2/stepconf" + "github.com/bitrise-io/go-utils/errorutil" + "github.com/bitrise-io/go-utils/fileutil" + "github.com/bitrise-io/go-utils/pathutil" + "github.com/bitrise-io/go-utils/v2/command" + "github.com/bitrise-io/go-xcode/certificateutil" + "github.com/hashicorp/go-version" +) + +// Keychain describes a macOS Keychain +type Keychain struct { + path string + password stepconf.Secret + + factory command.Factory +} + +// New ... +func New(pth string, pass stepconf.Secret, factory command.Factory) (*Keychain, error) { + if exist, err := pathutil.IsPathExists(pth); err != nil { + return nil, err + } else if exist { + return &Keychain{ + path: pth, + password: stepconf.Secret(pass), + factory: factory, + }, nil + } + + p := pth + "-db" + if exist, err := pathutil.IsPathExists(p); err != nil { + return nil, err + } else if exist { + return &Keychain{ + path: p, + password: pass, + factory: factory, + }, nil + } + + return createKeychain(pth, pass, factory) +} + +// InstallCertificate ... +func (k Keychain) InstallCertificate(cert certificateutil.CertificateInfoModel, pass stepconf.Secret) error { + b, err := cert.EncodeToP12("bitrise") + if err != nil { + return err + } + + tmpDir, err := pathutil.NormalizedOSTempDirPath("keychain") + if err != nil { + return err + } + pth := filepath.Join(tmpDir, "Certificate.p12") + if err := fileutil.WriteBytesToFile(pth, b); err != nil { + return err + } + + if err := k.unlock(); err != nil { + return err + } + + if err := k.importCertificate(pth, "bitrise"); err != nil { + return err + } + + if needed, err := k.isKeyPartitionListNeeded(); err != nil { + return err + } else if needed { + if err := k.setKeyPartitionList(); err != nil { + return err + } + } + + if err := k.setLockSettings(); err != nil { + return err + } + + if err := k.addToSearchPath(); err != nil { + return err + } + + return k.setAsDefault() +} + +func runSecurityCmd(factory command.Factory, args ...interface{}) error { + var printableArgs []string + var cmdArgs []string + for _, arg := range args { + v, ok := arg.(stepconf.Secret) + if ok { + printableArgs = append(printableArgs, v.String()) + cmdArgs = append(cmdArgs, string(v)) + } else if v, ok := arg.(string); ok { + printableArgs = append(printableArgs, v) + cmdArgs = append(cmdArgs, v) + } else if v, ok := arg.([]string); ok { + printableArgs = append(printableArgs, v...) + cmdArgs = append(cmdArgs, v...) + } else { + return fmt.Errorf("unknown arg provided: %T, string, []string, and stepconf.Secret are acceptable", arg) + } + } + + out, err := factory.Create("security", cmdArgs, nil).RunAndReturnTrimmedCombinedOutput() + if err != nil { + if errorutil.IsExitStatusError(err) { + return fmt.Errorf("%s failed: %s", strings.Join(append([]string{"security"}, printableArgs...), " "), out) + } + return fmt.Errorf("%s failed: %s", strings.Join(append([]string{"security"}, printableArgs...), " "), err) + } + return nil +} + +// listKeychains returns the paths of available keychains +func (k Keychain) listKeychains() ([]string, error) { + cmd := k.factory.Create("security", []string{"list-keychain"}, nil) + out, err := cmd.RunAndReturnTrimmedCombinedOutput() + if err != nil { + if errorutil.IsExitStatusError(err) { + return nil, fmt.Errorf("%s failed: %s", cmd.PrintableCommandArgs(), out) + } + return nil, fmt.Errorf("%s failed: %s", cmd.PrintableCommandArgs(), err) + } + + var keychains []string + for _, path := range strings.Split(out, "\n") { + trimmed := strings.TrimSpace(path) + trimmed = strings.Trim(trimmed, `"`) + keychains = append(keychains, trimmed) + } + + return keychains, nil +} + +// createKeychain creates a new keychain file at +// path, protected by password. Returns an error +// if the keychain could not be created, otherwise +// a Keychain object representing the created +// keychain is returned. +func createKeychain(path string, password stepconf.Secret, factory command.Factory) (*Keychain, error) { + err := runSecurityCmd(factory, "-v", "create-keychain", "-p", password, path) + if err != nil { + return nil, err + } + + return &Keychain{ + path: path, + password: password, + factory: factory, + }, nil +} + +// importCertificate adds the certificate at path, protected by +// passphrase to the k keychain. +func (k Keychain) importCertificate(path string, passphrase stepconf.Secret) error { + return runSecurityCmd(k.factory, "import", path, "-k", k.path, "-P", passphrase, "-A") +} + +// setKeyPartitionList sets the partition list +// for the keychain to allow access for tools. +func (k Keychain) setKeyPartitionList() error { + return runSecurityCmd(k.factory, "set-key-partition-list", "-S", "apple-tool:,apple:", "-k", k.password, k.path) +} + +// setLockSettings sets keychain autolocking. +func (k Keychain) setLockSettings() error { + return runSecurityCmd(k.factory, "-v", "set-keychain-settings", "-lut", "72000", k.path) +} + +// addToSearchPath registers the keychain +// in the systemwide search path +func (k Keychain) addToSearchPath() error { + keychains, err := k.listKeychains() + if err != nil { + return fmt.Errorf("get keychain list: %s", err) + } + + return runSecurityCmd(k.factory, "-v", "list-keychains", "-s", keychains) +} + +// setAsDefault sets the keychain as the +// default keychain for the system. +func (k Keychain) setAsDefault() error { + return runSecurityCmd(k.factory, "-v", "default-keychain", "-s", k.path) +} + +// unlock unlocks the keychain +func (k Keychain) unlock() error { + return runSecurityCmd(k.factory, "-v", "unlock-keychain", "-p", k.password, k.path) +} + +// isKeyPartitionListNeeded determines whether +// key partition lists are used by the system. +func (k Keychain) isKeyPartitionListNeeded() (bool, error) { + cmd := k.factory.Create("sw_vers", []string{"-productVersion"}, nil) + out, err := cmd.RunAndReturnTrimmedCombinedOutput() + if err != nil { + if errorutil.IsExitStatusError(err) { + return false, fmt.Errorf("%s failed: %s", cmd.PrintableCommandArgs(), out) + } + return false, fmt.Errorf("%s failed: %s", cmd.PrintableCommandArgs(), err) + } + + const versionSierra = "10.12.0" + sierra, err := version.NewVersion(versionSierra) + if err != nil { + return false, fmt.Errorf("invalid version (%s): %s", versionSierra, err) + } + + current, err := version.NewVersion(out) + if err != nil { + return false, fmt.Errorf("invalid version (%s): %s", current, err) + } + if current.GreaterThanOrEqual(sierra) { + return true, nil + } + + return false, nil +} diff --git a/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/localcodesignasset/localcodesignasset.go b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/localcodesignasset/localcodesignasset.go new file mode 100644 index 0000000..6c0587c --- /dev/null +++ b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/localcodesignasset/localcodesignasset.go @@ -0,0 +1,132 @@ +package localcodesignasset + +import ( + "fmt" + + "github.com/bitrise-io/go-xcode/v2/autocodesign" + "github.com/bitrise-io/go-xcode/v2/autocodesign/devportalclient/appstoreconnect" +) + +// Manager ... +type Manager struct { + profileProvider ProvisioningProfileProvider + profileConverter ProvisioningProfileConverter +} + +// NewManager ... +func NewManager(provisioningProfileProvider ProvisioningProfileProvider, provisioningProfileConverter ProvisioningProfileConverter) Manager { + return Manager{ + profileProvider: provisioningProfileProvider, + profileConverter: provisioningProfileConverter, + } +} + +// FindCodesignAssets ... +func (m Manager) FindCodesignAssets(appLayout autocodesign.AppLayout, distrType autocodesign.DistributionType, certsByType map[appstoreconnect.CertificateType][]autocodesign.Certificate, deviceIDs []string, minProfileDaysValid int) (*autocodesign.AppCodesignAssets, *autocodesign.AppLayout, error) { + profiles, err := m.profileProvider.ListProvisioningProfiles() + if err != nil { + return nil, nil, err + } + + certSerials := certificateSerials(certsByType, distrType) + + var asset *autocodesign.AppCodesignAssets + for bundleID, entitlements := range appLayout.EntitlementsByArchivableTargetBundleID { + profileInfo := findProfile(profiles, appLayout.Platform, distrType, bundleID, entitlements, minProfileDaysValid, certSerials, deviceIDs) + if profileInfo == nil { + continue + } + + profile, err := m.profileConverter.ProfileInfoToProfile(*profileInfo) + if err != nil { + return nil, nil, err + } + + if asset == nil { + asset = &autocodesign.AppCodesignAssets{ + ArchivableTargetProfilesByBundleID: map[string]autocodesign.Profile{ + bundleID: profile, + }, + } + } else { + profileByArchivableTargetBundleID := asset.ArchivableTargetProfilesByBundleID + if profileByArchivableTargetBundleID == nil { + profileByArchivableTargetBundleID = map[string]autocodesign.Profile{} + } + + profileByArchivableTargetBundleID[bundleID] = profile + asset.ArchivableTargetProfilesByBundleID = profileByArchivableTargetBundleID + } + + delete(appLayout.EntitlementsByArchivableTargetBundleID, bundleID) + } + + if distrType == autocodesign.Development { + bundleIDs := map[string]bool{} + for _, bundleID := range appLayout.UITestTargetBundleIDs { + bundleIDs[bundleID] = true // profile missing? + } + + for bundleID := range bundleIDs { + wildcardBundleID, err := autocodesign.CreateWildcardBundleID(bundleID) + if err != nil { + return nil, nil, fmt.Errorf("could not create wildcard bundle id: %s", err) + } + + // Capabilities are not supported for UITest targets. + profileInfo := findProfile(profiles, appLayout.Platform, distrType, wildcardBundleID, nil, minProfileDaysValid, certSerials, deviceIDs) + if profileInfo == nil { + continue + } + + profile, err := m.profileConverter.ProfileInfoToProfile(*profileInfo) + if err != nil { + return nil, nil, err + } + + if asset == nil { + asset = &autocodesign.AppCodesignAssets{ + UITestTargetProfilesByBundleID: map[string]autocodesign.Profile{ + bundleID: profile, + }, + } + } else { + profileByUITestTargetBundleID := asset.UITestTargetProfilesByBundleID + if profileByUITestTargetBundleID == nil { + profileByUITestTargetBundleID = map[string]autocodesign.Profile{} + } + + profileByUITestTargetBundleID[bundleID] = profile + asset.UITestTargetProfilesByBundleID = profileByUITestTargetBundleID + } + + bundleIDs[bundleID] = false + } + + var uiTestTargetBundleIDs []string + for bundleID, missing := range bundleIDs { + if missing { + uiTestTargetBundleIDs = append(uiTestTargetBundleIDs, bundleID) + } + } + + appLayout.UITestTargetBundleIDs = uiTestTargetBundleIDs + } + + if asset != nil { + // We will always have a certificate at this point because if we do not have any then we also could not have + // found a profile as all of them requires at least one certificate. + certificate, err := autocodesign.SelectCertificate(certsByType, distrType) + if err != nil { + return nil, nil, err + } + + asset.Certificate = certificate.CertificateInfo + } + + if len(appLayout.EntitlementsByArchivableTargetBundleID) == 0 && len(appLayout.UITestTargetBundleIDs) == 0 { + return asset, nil, nil + } + + return asset, &appLayout, nil +} diff --git a/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/localcodesignasset/profile.go b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/localcodesignasset/profile.go new file mode 100644 index 0000000..c685dd8 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/localcodesignasset/profile.go @@ -0,0 +1,81 @@ +package localcodesignasset + +import ( + "github.com/bitrise-io/go-xcode/profileutil" + "github.com/bitrise-io/go-xcode/v2/autocodesign" + "github.com/bitrise-io/go-xcode/v2/autocodesign/devportalclient/appstoreconnect" + "github.com/bitrise-io/go-xcode/v2/autocodesign/devportalclient/time" +) + +// Profile ... +type Profile struct { + attributes appstoreconnect.ProfileAttributes + id string + bundleID string + deviceIDs []string + certificateIDs []string +} + +// NewProfile wraps a local profile in the autocodesign.Profile interface +func NewProfile(info profileutil.ProvisioningProfileInfoModel, content []byte) autocodesign.Profile { + return Profile{ + attributes: appstoreconnect.ProfileAttributes{ + Name: info.Name, + UUID: info.UUID, + ProfileContent: content, + Platform: getBundleIDPlatform(info.Type), + ExpirationDate: time.Time(info.ExpirationDate), + }, + id: "", // only in case of Developer Portal Profiles + bundleID: info.BundleID, + certificateIDs: nil, // only in case of Developer Portal Profiles + deviceIDs: nil, // only in case of Developer Portal Profiles + } +} + +// ID ... +func (p Profile) ID() string { + return p.id +} + +// Attributes ... +func (p Profile) Attributes() appstoreconnect.ProfileAttributes { + return p.attributes +} + +// CertificateIDs ... +func (p Profile) CertificateIDs() ([]string, error) { + return p.certificateIDs, nil +} + +// DeviceIDs ... +func (p Profile) DeviceIDs() ([]string, error) { + return p.deviceIDs, nil +} + +// BundleID ... +func (p Profile) BundleID() (appstoreconnect.BundleID, error) { + return appstoreconnect.BundleID{ + ID: p.id, + Attributes: appstoreconnect.BundleIDAttributes{ + Identifier: p.bundleID, + Name: p.attributes.Name, + }, + }, nil +} + +// Entitlements ... +func (p Profile) Entitlements() (autocodesign.Entitlements, error) { + return autocodesign.ParseRawProfileEntitlements(p.attributes.ProfileContent) +} + +func getBundleIDPlatform(profileType profileutil.ProfileType) appstoreconnect.BundleIDPlatform { + switch profileType { + case profileutil.ProfileTypeIos, profileutil.ProfileTypeTvOs: + return appstoreconnect.IOS + case profileutil.ProfileTypeMacOs: + return appstoreconnect.MacOS + } + + return "" +} diff --git a/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/localcodesignasset/profileconverter.go b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/localcodesignasset/profileconverter.go new file mode 100644 index 0000000..30a1b4f --- /dev/null +++ b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/localcodesignasset/profileconverter.go @@ -0,0 +1,35 @@ +package localcodesignasset + +import ( + "io/ioutil" + + "github.com/bitrise-io/go-xcode/profileutil" + "github.com/bitrise-io/go-xcode/v2/autocodesign" +) + +// ProvisioningProfileConverter ... +type ProvisioningProfileConverter interface { + ProfileInfoToProfile(info profileutil.ProvisioningProfileInfoModel) (autocodesign.Profile, error) +} + +type provisioningProfileConverter struct { +} + +// NewProvisioningProfileConverter ... +func NewProvisioningProfileConverter() ProvisioningProfileConverter { + return provisioningProfileConverter{} +} + +// ProfileInfoToProfile ... +func (c provisioningProfileConverter) ProfileInfoToProfile(info profileutil.ProvisioningProfileInfoModel) (autocodesign.Profile, error) { + _, pth, err := profileutil.FindProvisioningProfile(info.UUID) + if err != nil { + return nil, err + } + content, err := ioutil.ReadFile(pth) + if err != nil { + return nil, err + } + + return NewProfile(info, content), nil +} diff --git a/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/localcodesignasset/profilelookup.go b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/localcodesignasset/profilelookup.go new file mode 100644 index 0000000..9c54c11 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/localcodesignasset/profilelookup.go @@ -0,0 +1,138 @@ +package localcodesignasset + +import ( + "reflect" + "strings" + "time" + + "github.com/bitrise-io/go-utils/sliceutil" + "github.com/bitrise-io/go-xcode/profileutil" + "github.com/bitrise-io/go-xcode/v2/autocodesign" +) + +func findProfile(localProfiles []profileutil.ProvisioningProfileInfoModel, platform autocodesign.Platform, distributionType autocodesign.DistributionType, bundleID string, entitlements autocodesign.Entitlements, minProfileDaysValid int, certSerials []string, deviceIDs []string) *profileutil.ProvisioningProfileInfoModel { + for _, profile := range localProfiles { + if isProfileMatching(profile, platform, distributionType, bundleID, entitlements, minProfileDaysValid, certSerials, deviceIDs) { + return &profile + } + } + + return nil +} + +func isProfileMatching(profile profileutil.ProvisioningProfileInfoModel, platform autocodesign.Platform, distributionType autocodesign.DistributionType, bundleID string, entitlements autocodesign.Entitlements, minProfileDaysValid int, certSerials []string, deviceIDs []string) bool { + if !isActive(profile, minProfileDaysValid) { + return false + } + + if !hasMatchingDistributionType(profile, distributionType) { + return false + } + + if !hasMatchingBundleID(profile, bundleID) { + return false + } + + if !hasMatchingPlatform(profile, platform) { + return false + } + + if !hasMatchingLocalCertificates(profile, certSerials) { + return false + } + + if !containsAllAppEntitlements(profile, entitlements) { + return false + } + + if !provisionsDevices(profile, deviceIDs) { + return false + } + + // Drop Xcode-managed profiles + // as Bitrise-managed automatic code signing enforces manually managed code signing on the given project. + if profile.IsXcodeManaged() { + return false + } + + return true +} + +func hasMatchingBundleID(profile profileutil.ProvisioningProfileInfoModel, bundleID string) bool { + return profile.BundleID == bundleID +} + +func hasMatchingLocalCertificates(profile profileutil.ProvisioningProfileInfoModel, localCertificateSerials []string) bool { + var profileCertificateSerials []string + for _, certificate := range profile.DeveloperCertificates { + profileCertificateSerials = append(profileCertificateSerials, certificate.Serial) + } + + for _, serial := range localCertificateSerials { + if !sliceutil.IsStringInSlice(serial, profileCertificateSerials) { + return false + } + } + + return true +} + +func containsAllAppEntitlements(profile profileutil.ProvisioningProfileInfoModel, appEntitlements autocodesign.Entitlements) bool { + profileEntitlements := autocodesign.Entitlements(profile.Entitlements) + hasMissingEntitlement := false + + for key, value := range appEntitlements { + profileEntitlementValue := profileEntitlements[key] + + // The project entitlement values can have variables coming from build settings which will be resolved later + // during the archive action. It is not the best but this is also the logic used at other places. An example of + // what we could be comparing: + // $(AppIdentifierPrefix)${BASE_BUNDLE_ID}.ios == 72SA8V3WYL.io.bitrise.samples.fruta.los + if key == autocodesign.ICloudIdentifiersEntitlementKey { + missingContainers, err := autocodesign.FindMissingContainers(appEntitlements, profileEntitlements) + if err != nil || len(missingContainers) > 0 { + return false + } + } else if !reflect.DeepEqual(profileEntitlementValue, value) { + return false + } + } + + return !hasMissingEntitlement +} + +func hasMatchingDistributionType(profile profileutil.ProvisioningProfileInfoModel, distributionType autocodesign.DistributionType) bool { + return autocodesign.DistributionType(profile.ExportType) == distributionType +} + +func isActive(profile profileutil.ProvisioningProfileInfoModel, minProfileDaysValid int) bool { + expiration := time.Now() + if minProfileDaysValid > 0 { + expiration = expiration.AddDate(0, 0, minProfileDaysValid) + } + + return expiration.Before(profile.ExpirationDate) +} + +func hasMatchingPlatform(profile profileutil.ProvisioningProfileInfoModel, platform autocodesign.Platform) bool { + return strings.ToLower(string(platform)) == string(profile.Type) +} + +func provisionsDevices(profile profileutil.ProvisioningProfileInfoModel, deviceIDs []string) bool { + if profile.ProvisionsAllDevices || len(deviceIDs) == 0 { + return true + } + + if len(profile.ProvisionedDevices) == 0 { + return false + } + + for _, deviceID := range deviceIDs { + if contains(profile.ProvisionedDevices, deviceID) { + continue + } + return false + } + + return true +} diff --git a/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/localcodesignasset/profileprovider.go b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/localcodesignasset/profileprovider.go new file mode 100644 index 0000000..fcdc5f9 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/localcodesignasset/profileprovider.go @@ -0,0 +1,20 @@ +package localcodesignasset + +import "github.com/bitrise-io/go-xcode/profileutil" + +// ProvisioningProfileProvider can list profile infos. +type ProvisioningProfileProvider interface { + ListProvisioningProfiles() ([]profileutil.ProvisioningProfileInfoModel, error) +} + +type provisioningProfileProvider struct{} + +// NewProvisioningProfileProvider ... +func NewProvisioningProfileProvider() ProvisioningProfileProvider { + return provisioningProfileProvider{} +} + +// ListProvisioningProfiles ... +func (p provisioningProfileProvider) ListProvisioningProfiles() ([]profileutil.ProvisioningProfileInfoModel, error) { + return profileutil.InstalledProvisioningProfileInfos(profileutil.ProfileTypeIos) +} diff --git a/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/localcodesignasset/utils.go b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/localcodesignasset/utils.go new file mode 100644 index 0000000..cb8e495 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/localcodesignasset/utils.go @@ -0,0 +1,27 @@ +package localcodesignasset + +import ( + "github.com/bitrise-io/go-xcode/v2/autocodesign" + "github.com/bitrise-io/go-xcode/v2/autocodesign/devportalclient/appstoreconnect" +) + +func certificateSerials(certsByType map[appstoreconnect.CertificateType][]autocodesign.Certificate, distrType autocodesign.DistributionType) []string { + certType := autocodesign.CertificateTypeByDistribution[distrType] + certs := certsByType[certType] + + var serials []string + for _, cert := range certs { + serials = append(serials, cert.CertificateInfo.Serial) + } + + return serials +} + +func contains(array []string, element string) bool { + for _, item := range array { + if item == element { + return true + } + } + return false +} diff --git a/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/mock_AssetWriter.go b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/mock_AssetWriter.go new file mode 100644 index 0000000..d95af49 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/mock_AssetWriter.go @@ -0,0 +1,55 @@ +// Code generated by mockery v2.10.0. DO NOT EDIT. + +package autocodesign + +import ( + certificateutil "github.com/bitrise-io/go-xcode/certificateutil" + mock "github.com/stretchr/testify/mock" +) + +// MockAssetWriter is an autogenerated mock type for the AssetWriter type +type MockAssetWriter struct { + mock.Mock +} + +// InstallCertificate provides a mock function with given fields: certificate +func (_m *MockAssetWriter) InstallCertificate(certificate certificateutil.CertificateInfoModel) error { + ret := _m.Called(certificate) + + var r0 error + if rf, ok := ret.Get(0).(func(certificateutil.CertificateInfoModel) error); ok { + r0 = rf(certificate) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// InstallProfile provides a mock function with given fields: profile +func (_m *MockAssetWriter) InstallProfile(profile Profile) error { + ret := _m.Called(profile) + + var r0 error + if rf, ok := ret.Get(0).(func(Profile) error); ok { + r0 = rf(profile) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Write provides a mock function with given fields: codesignAssetsByDistributionType +func (_m *MockAssetWriter) Write(codesignAssetsByDistributionType map[DistributionType]AppCodesignAssets) error { + ret := _m.Called(codesignAssetsByDistributionType) + + var r0 error + if rf, ok := ret.Get(0).(func(map[DistributionType]AppCodesignAssets) error); ok { + r0 = rf(codesignAssetsByDistributionType) + } else { + r0 = ret.Error(0) + } + + return r0 +} diff --git a/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/mock_CertificateProvider.go b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/mock_CertificateProvider.go new file mode 100644 index 0000000..3f67bf7 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/mock_CertificateProvider.go @@ -0,0 +1,38 @@ +// Code generated by mockery 2.9.4. DO NOT EDIT. + +package autocodesign + +import ( + certificateutil "github.com/bitrise-io/go-xcode/certificateutil" + mock "github.com/stretchr/testify/mock" +) + +// MockCertificateProvider is an autogenerated mock type for the CertificateProvider type +type MockCertificateProvider struct { + mock.Mock +} + +// GetCertificates provides a mock function with given fields: +func (_m *MockCertificateProvider) GetCertificates() ([]certificateutil.CertificateInfoModel, error) { + ret := _m.Called() + + var r0 []certificateutil.CertificateInfoModel + if rf, ok := ret.Get(0).(func() []certificateutil.CertificateInfoModel); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0, ok = ret.Get(0).([]certificateutil.CertificateInfoModel) + if !ok { + } + } + } + + var r1 error + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} diff --git a/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/mock_DevPortalClient.go b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/mock_DevPortalClient.go new file mode 100644 index 0000000..7aabbe2 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/mock_DevPortalClient.go @@ -0,0 +1,258 @@ +// Code generated by mockery 2.9.4. DO NOT EDIT. + +package autocodesign + +import ( + big "math/big" + + appstoreconnect "github.com/bitrise-io/go-xcode/v2/autocodesign/devportalclient/appstoreconnect" + + devportalservice "github.com/bitrise-io/go-xcode/devportalservice" + + mock "github.com/stretchr/testify/mock" +) + +// MockDevPortalClient is an autogenerated mock type for the DevPortalClient type +type MockDevPortalClient struct { + mock.Mock +} + +// CheckBundleIDEntitlements provides a mock function with given fields: bundleID, appEntitlements +func (_m *MockDevPortalClient) CheckBundleIDEntitlements(bundleID appstoreconnect.BundleID, appEntitlements Entitlements) error { + ret := _m.Called(bundleID, appEntitlements) + + var r0 error + if rf, ok := ret.Get(0).(func(appstoreconnect.BundleID, Entitlements) error); ok { + r0 = rf(bundleID, appEntitlements) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// CreateBundleID provides a mock function with given fields: bundleIDIdentifier, appIDName +func (_m *MockDevPortalClient) CreateBundleID(bundleIDIdentifier string, appIDName string) (*appstoreconnect.BundleID, error) { + ret := _m.Called(bundleIDIdentifier, appIDName) + + var r0 *appstoreconnect.BundleID + if rf, ok := ret.Get(0).(func(string, string) *appstoreconnect.BundleID); ok { + r0 = rf(bundleIDIdentifier, appIDName) + } else { + if ret.Get(0) != nil { + r0, ok = ret.Get(0).(*appstoreconnect.BundleID) + if !ok { + } + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(string, string) error); ok { + r1 = rf(bundleIDIdentifier, appIDName) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// CreateProfile provides a mock function with given fields: name, profileType, bundleID, certificateIDs, deviceIDs +func (_m *MockDevPortalClient) CreateProfile(name string, profileType appstoreconnect.ProfileType, bundleID appstoreconnect.BundleID, certificateIDs []string, deviceIDs []string) (Profile, error) { + ret := _m.Called(name, profileType, bundleID, certificateIDs, deviceIDs) + + var r0 Profile + if rf, ok := ret.Get(0).(func(string, appstoreconnect.ProfileType, appstoreconnect.BundleID, []string, []string) Profile); ok { + r0 = rf(name, profileType, bundleID, certificateIDs, deviceIDs) + } else { + if ret.Get(0) != nil { + r0, ok = ret.Get(0).(Profile) + if !ok { + } + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(string, appstoreconnect.ProfileType, appstoreconnect.BundleID, []string, []string) error); ok { + r1 = rf(name, profileType, bundleID, certificateIDs, deviceIDs) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// DeleteProfile provides a mock function with given fields: id +func (_m *MockDevPortalClient) DeleteProfile(id string) error { + ret := _m.Called(id) + + var r0 error + if rf, ok := ret.Get(0).(func(string) error); ok { + r0 = rf(id) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// FindBundleID provides a mock function with given fields: bundleIDIdentifier +func (_m *MockDevPortalClient) FindBundleID(bundleIDIdentifier string) (*appstoreconnect.BundleID, error) { + ret := _m.Called(bundleIDIdentifier) + + var r0 *appstoreconnect.BundleID + if rf, ok := ret.Get(0).(func(string) *appstoreconnect.BundleID); ok { + r0 = rf(bundleIDIdentifier) + } else { + if ret.Get(0) != nil { + r0, ok = ret.Get(0).(*appstoreconnect.BundleID) + if !ok { + } + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(string) error); ok { + r1 = rf(bundleIDIdentifier) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// FindProfile provides a mock function with given fields: name, profileType +func (_m *MockDevPortalClient) FindProfile(name string, profileType appstoreconnect.ProfileType) (Profile, error) { + ret := _m.Called(name, profileType) + + var r0 Profile + if rf, ok := ret.Get(0).(func(string, appstoreconnect.ProfileType) Profile); ok { + r0 = rf(name, profileType) + } else { + if ret.Get(0) != nil { + r0, ok = ret.Get(0).(Profile) + if !ok { + } + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(string, appstoreconnect.ProfileType) error); ok { + r1 = rf(name, profileType) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ListDevices provides a mock function with given fields: UDID, platform +func (_m *MockDevPortalClient) ListDevices(UDID string, platform appstoreconnect.DevicePlatform) ([]appstoreconnect.Device, error) { + ret := _m.Called(UDID, platform) + + var r0 []appstoreconnect.Device + if rf, ok := ret.Get(0).(func(string, appstoreconnect.DevicePlatform) []appstoreconnect.Device); ok { + r0 = rf(UDID, platform) + } else { + if ret.Get(0) != nil { + r0, ok = ret.Get(0).([]appstoreconnect.Device) + if !ok { + } + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(string, appstoreconnect.DevicePlatform) error); ok { + r1 = rf(UDID, platform) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// QueryAllIOSCertificates provides a mock function with given fields: +func (_m *MockDevPortalClient) QueryAllIOSCertificates() (map[appstoreconnect.CertificateType][]Certificate, error) { + ret := _m.Called() + + var r0 map[appstoreconnect.CertificateType][]Certificate + if rf, ok := ret.Get(0).(func() map[appstoreconnect.CertificateType][]Certificate); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0, ok = ret.Get(0).(map[appstoreconnect.CertificateType][]Certificate) + if !ok { + } + } + } + + var r1 error + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// QueryCertificateBySerial provides a mock function with given fields: serial +func (_m *MockDevPortalClient) QueryCertificateBySerial(serial big.Int) (Certificate, error) { + ret := _m.Called(serial) + + var r0 Certificate + if rf, ok := ret.Get(0).(func(big.Int) Certificate); ok { + r0 = rf(serial) + } else { + r0, ok = ret.Get(0).(Certificate) + if !ok { + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(big.Int) error); ok { + r1 = rf(serial) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// RegisterDevice provides a mock function with given fields: testDevice +func (_m *MockDevPortalClient) RegisterDevice(testDevice devportalservice.TestDevice) (*appstoreconnect.Device, error) { + ret := _m.Called(testDevice) + + var r0 *appstoreconnect.Device + if rf, ok := ret.Get(0).(func(devportalservice.TestDevice) *appstoreconnect.Device); ok { + r0 = rf(testDevice) + } else { + if ret.Get(0) != nil { + r0, ok = ret.Get(0).(*appstoreconnect.Device) + if !ok { + } + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(devportalservice.TestDevice) error); ok { + r1 = rf(testDevice) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// SyncBundleID provides a mock function with given fields: bundleID, appEntitlements +func (_m *MockDevPortalClient) SyncBundleID(bundleID appstoreconnect.BundleID, appEntitlements Entitlements) error { + ret := _m.Called(bundleID, appEntitlements) + + var r0 error + if rf, ok := ret.Get(0).(func(appstoreconnect.BundleID, Entitlements) error); ok { + r0 = rf(bundleID, appEntitlements) + } else { + r0 = ret.Error(0) + } + + return r0 +} diff --git a/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/mock_LocalCodeSignAssetManager.go b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/mock_LocalCodeSignAssetManager.go new file mode 100644 index 0000000..804faf7 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/mock_LocalCodeSignAssetManager.go @@ -0,0 +1,49 @@ +// Code generated by mockery 2.9.4. DO NOT EDIT. + +package autocodesign + +import ( + appstoreconnect "github.com/bitrise-io/go-xcode/v2/autocodesign/devportalclient/appstoreconnect" + mock "github.com/stretchr/testify/mock" +) + +// MockLocalCodeSignAssetManager is an autogenerated mock type for the LocalCodeSignAssetManager type +type MockLocalCodeSignAssetManager struct { + mock.Mock +} + +// FindCodesignAssets provides a mock function with given fields: appLayout, distrType, certsByType, deviceIDs, minProfileDaysValid +func (_m *MockLocalCodeSignAssetManager) FindCodesignAssets(appLayout AppLayout, distrType DistributionType, certsByType map[appstoreconnect.CertificateType][]Certificate, deviceIDs []string, minProfileDaysValid int) (*AppCodesignAssets, *AppLayout, error) { + ret := _m.Called(appLayout, distrType, certsByType, deviceIDs, minProfileDaysValid) + + var r0 *AppCodesignAssets + if rf, ok := ret.Get(0).(func(AppLayout, DistributionType, map[appstoreconnect.CertificateType][]Certificate, []string, int) *AppCodesignAssets); ok { + r0 = rf(appLayout, distrType, certsByType, deviceIDs, minProfileDaysValid) + } else { + if ret.Get(0) != nil { + r0, ok = ret.Get(0).(*AppCodesignAssets) + if !ok { + } + } + } + + var r1 *AppLayout + if rf, ok := ret.Get(1).(func(AppLayout, DistributionType, map[appstoreconnect.CertificateType][]Certificate, []string, int) *AppLayout); ok { + r1 = rf(appLayout, distrType, certsByType, deviceIDs, minProfileDaysValid) + } else { + if ret.Get(1) != nil { + r1, ok = ret.Get(1).(*AppLayout) + if !ok { + } + } + } + + var r2 error + if rf, ok := ret.Get(2).(func(AppLayout, DistributionType, map[appstoreconnect.CertificateType][]Certificate, []string, int) error); ok { + r2 = rf(appLayout, distrType, certsByType, deviceIDs, minProfileDaysValid) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} diff --git a/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/mock_Profile.go b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/mock_Profile.go new file mode 100644 index 0000000..f67a4fd --- /dev/null +++ b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/mock_Profile.go @@ -0,0 +1,143 @@ +// Code generated by mockery 2.9.4. DO NOT EDIT. + +package autocodesign + +import ( + appstoreconnect "github.com/bitrise-io/go-xcode/v2/autocodesign/devportalclient/appstoreconnect" + mock "github.com/stretchr/testify/mock" +) + +// MockProfile is an autogenerated mock type for the Profile type +type MockProfile struct { + mock.Mock +} + +// Attributes provides a mock function with given fields: +func (_m *MockProfile) Attributes() appstoreconnect.ProfileAttributes { + ret := _m.Called() + + var r0 appstoreconnect.ProfileAttributes + if rf, ok := ret.Get(0).(func() appstoreconnect.ProfileAttributes); ok { + r0 = rf() + } else { + r0, ok = ret.Get(0).(appstoreconnect.ProfileAttributes) + if !ok { + } + } + + return r0 +} + +// BundleID provides a mock function with given fields: +func (_m *MockProfile) BundleID() (appstoreconnect.BundleID, error) { + ret := _m.Called() + + var r0 appstoreconnect.BundleID + if rf, ok := ret.Get(0).(func() appstoreconnect.BundleID); ok { + r0 = rf() + } else { + r0, ok = ret.Get(0).(appstoreconnect.BundleID) + if !ok { + } + } + + var r1 error + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// CertificateIDs provides a mock function with given fields: +func (_m *MockProfile) CertificateIDs() ([]string, error) { + ret := _m.Called() + + var r0 []string + if rf, ok := ret.Get(0).(func() []string); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0, ok = ret.Get(0).([]string) + if !ok { + } + } + } + + var r1 error + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// DeviceIDs provides a mock function with given fields: +func (_m *MockProfile) DeviceIDs() ([]string, error) { + ret := _m.Called() + + var r0 []string + if rf, ok := ret.Get(0).(func() []string); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0, ok = ret.Get(0).([]string) + if !ok { + } + } + } + + var r1 error + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Entitlements provides a mock function with given fields: +func (_m *MockProfile) Entitlements() (Entitlements, error) { + ret := _m.Called() + + var r0 Entitlements + if rf, ok := ret.Get(0).(func() Entitlements); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0, ok = ret.Get(0).(Entitlements) + if !ok { + } + } + } + + var r1 error + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ID provides a mock function with given fields: +func (_m *MockProfile) ID() string { + ret := _m.Called() + + var r0 string + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0, ok = ret.Get(0).(string) + if !ok { + } + } + + return r0 +} diff --git a/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/models.go b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/models.go new file mode 100644 index 0000000..2fa7208 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/models.go @@ -0,0 +1,55 @@ +package autocodesign + +import ( + "github.com/bitrise-io/go-xcode/v2/autocodesign/devportalclient/appstoreconnect" +) + +// CertificateTypeByDistribution ... +var CertificateTypeByDistribution = map[DistributionType]appstoreconnect.CertificateType{ + Development: appstoreconnect.IOSDevelopment, + AppStore: appstoreconnect.IOSDistribution, + AdHoc: appstoreconnect.IOSDistribution, + Enterprise: appstoreconnect.IOSDistribution, +} + +// ProfileTypeToPlatform ... +var ProfileTypeToPlatform = map[appstoreconnect.ProfileType]Platform{ + appstoreconnect.IOSAppDevelopment: IOS, + appstoreconnect.IOSAppStore: IOS, + appstoreconnect.IOSAppAdHoc: IOS, + appstoreconnect.IOSAppInHouse: IOS, + + appstoreconnect.TvOSAppDevelopment: TVOS, + appstoreconnect.TvOSAppStore: TVOS, + appstoreconnect.TvOSAppAdHoc: TVOS, + appstoreconnect.TvOSAppInHouse: TVOS, +} + +// ProfileTypeToDistribution ... +var ProfileTypeToDistribution = map[appstoreconnect.ProfileType]DistributionType{ + appstoreconnect.IOSAppDevelopment: Development, + appstoreconnect.IOSAppStore: AppStore, + appstoreconnect.IOSAppAdHoc: AdHoc, + appstoreconnect.IOSAppInHouse: Enterprise, + + appstoreconnect.TvOSAppDevelopment: Development, + appstoreconnect.TvOSAppStore: AppStore, + appstoreconnect.TvOSAppAdHoc: AdHoc, + appstoreconnect.TvOSAppInHouse: Enterprise, +} + +// PlatformToProfileTypeByDistribution ... +var PlatformToProfileTypeByDistribution = map[Platform]map[DistributionType]appstoreconnect.ProfileType{ + IOS: { + Development: appstoreconnect.IOSAppDevelopment, + AppStore: appstoreconnect.IOSAppStore, + AdHoc: appstoreconnect.IOSAppAdHoc, + Enterprise: appstoreconnect.IOSAppInHouse, + }, + TVOS: { + Development: appstoreconnect.TvOSAppDevelopment, + AppStore: appstoreconnect.TvOSAppStore, + AdHoc: appstoreconnect.TvOSAppAdHoc, + Enterprise: appstoreconnect.TvOSAppInHouse, + }, +} diff --git a/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/profiledownloader/profiledownloader.go b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/profiledownloader/profiledownloader.go new file mode 100644 index 0000000..3bb4210 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/profiledownloader/profiledownloader.go @@ -0,0 +1,69 @@ +package profiledownloader + +import ( + "context" + "fmt" + "net/http" + "time" + + "github.com/bitrise-io/go-steputils/input" + "github.com/bitrise-io/go-utils/filedownloader" + "github.com/bitrise-io/go-xcode/profileutil" + "github.com/bitrise-io/go-xcode/v2/autocodesign" + "github.com/bitrise-io/go-xcode/v2/autocodesign/localcodesignasset" +) + +type downloader struct { + urls []string + client *http.Client +} + +// New returns an implementation that can download from remote, local file paths +func New(profileURLs []string, client *http.Client) autocodesign.ProfileProvider { + return downloader{ + urls: profileURLs, + client: client, + } +} + +// IsAvailable returns true if there are available remote profiles to download +func (d downloader) IsAvailable() bool { + return len(d.urls) != 0 +} + +// GetProfiles downloads remote profiles and returns their contents +func (d downloader) GetProfiles() ([]autocodesign.LocalProfile, error) { + var profiles []autocodesign.LocalProfile + + for _, url := range d.urls { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) + defer cancel() + + downloader := filedownloader.NewWithContext(ctx, d.client) + fileProvider := input.NewFileProvider(downloader) + + content, err := fileProvider.Contents(url) + if err != nil { + return nil, err + } else if content == nil { + return nil, fmt.Errorf("profile (%s) is empty", url) + } + + parsedProfile, err := profileutil.ProvisioningProfileFromContent(content) + if err != nil { + return nil, fmt.Errorf("invalid pkcs7 file format: %w", err) + } + + profileInfo, err := profileutil.NewProvisioningProfileInfo(*parsedProfile) + if err != nil { + return nil, fmt.Errorf("unknown provisioning profile format: %w", err) + } + + profiles = append(profiles, autocodesign.LocalProfile{ + Profile: localcodesignasset.NewProfile(profileInfo, content), + Info: profileInfo, + }) + } + + return profiles, nil +} diff --git a/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/profiles.go b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/profiles.go new file mode 100644 index 0000000..a84679d --- /dev/null +++ b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/profiles.go @@ -0,0 +1,510 @@ +package autocodesign + +import ( + "errors" + "fmt" + "strings" + "time" + + "github.com/bitrise-io/go-utils/log" + "github.com/bitrise-io/go-utils/retry" + "github.com/bitrise-io/go-utils/sliceutil" + "github.com/bitrise-io/go-xcode/profileutil" + "github.com/bitrise-io/go-xcode/v2/autocodesign/devportalclient/appstoreconnect" + "github.com/bitrise-io/go-xcode/xcodeproject/serialized" +) + +func appIDName(bundleID string) string { + prefix := "" + if strings.HasSuffix(bundleID, ".*") { + prefix = "Wildcard " + } + r := strings.NewReplacer(".", " ", "_", " ", "-", " ", "*", " ") + return prefix + "Bitrise " + r.Replace(bundleID) +} + +func ensureProfiles(profileClient DevPortalClient, distrType DistributionType, + certsByType map[appstoreconnect.CertificateType][]Certificate, app AppLayout, + devPortalDeviceIDs []string, minProfileDaysValid int) (*AppCodesignAssets, error) { + // Ensure Profiles + + bundleIDByBundleIDIdentifer := map[string]*appstoreconnect.BundleID{} + + containersByBundleID := map[string][]string{} + + profileManager := profileManager{ + client: profileClient, + bundleIDByBundleIDIdentifer: bundleIDByBundleIDIdentifer, + containersByBundleID: containersByBundleID, + } + + fmt.Println() + log.Infof("Checking %s provisioning profiles", distrType) + + certificate, err := SelectCertificate(certsByType, distrType) + if err != nil { + return nil, err + } + + codesignAssets := AppCodesignAssets{ + ArchivableTargetProfilesByBundleID: map[string]Profile{}, + UITestTargetProfilesByBundleID: map[string]Profile{}, + Certificate: certificate.CertificateInfo, + } + + certType := CertificateTypeByDistribution[distrType] + certs := certsByType[certType] + + var certIDs []string + for _, cert := range certs { + certIDs = append(certIDs, cert.ID) + } + + platformProfileTypes, ok := PlatformToProfileTypeByDistribution[app.Platform] + if !ok { + return nil, fmt.Errorf("no profiles for platform: %s", app.Platform) + } + + profileType := platformProfileTypes[distrType] + + for bundleIDIdentifier, entitlements := range app.EntitlementsByArchivableTargetBundleID { + var profileDeviceIDs []string + if DistributionTypeRequiresDeviceList([]DistributionType{distrType}) { + profileDeviceIDs = devPortalDeviceIDs + } + + profile, err := profileManager.ensureProfileWithRetry(profileType, bundleIDIdentifier, entitlements, certIDs, profileDeviceIDs, minProfileDaysValid) + if err != nil { + return nil, err + } + codesignAssets.ArchivableTargetProfilesByBundleID[bundleIDIdentifier] = *profile + } + + if len(app.UITestTargetBundleIDs) > 0 && distrType == Development { + // Capabilities are not supported for UITest targets. + // Xcode managed signing uses Wildcard Provisioning Profiles for UITest target signing. + for _, bundleIDIdentifier := range app.UITestTargetBundleIDs { + wildcardBundleID, err := CreateWildcardBundleID(bundleIDIdentifier) + if err != nil { + return nil, fmt.Errorf("could not create wildcard bundle id: %s", err) + } + + // Capabilities are not supported for UITest targets. + profile, err := profileManager.ensureProfileWithRetry(profileType, wildcardBundleID, nil, certIDs, devPortalDeviceIDs, minProfileDaysValid) + if err != nil { + return nil, err + } + codesignAssets.UITestTargetProfilesByBundleID[bundleIDIdentifier] = *profile + } + } + + if len(profileManager.containersByBundleID) > 0 { + iCloudContainers := "" + for bundleID, containers := range containersByBundleID { + iCloudContainers = fmt.Sprintf("%s, containers:\n", bundleID) + for _, container := range containers { + iCloudContainers += fmt.Sprintf("- %s\n", container) + } + iCloudContainers += "\n" + } + + return nil, &DetailedError{ + ErrorMessage: "", + Title: "Unable to automatically assign iCloud containers to the following app IDs:", + Description: iCloudContainers, + Recommendation: "You have to manually add the listed containers to your app ID at: https://developer.apple.com/account/resources/identifiers/list.", + } + } + + return &codesignAssets, nil +} + +type profileManager struct { + client DevPortalClient + bundleIDByBundleIDIdentifer map[string]*appstoreconnect.BundleID + containersByBundleID map[string][]string +} + +func (m profileManager) ensureBundleID(bundleIDIdentifier string, entitlements Entitlements) (*appstoreconnect.BundleID, error) { + fmt.Println() + log.Infof(" Searching for app ID for bundle ID: %s", bundleIDIdentifier) + + bundleID, ok := m.bundleIDByBundleIDIdentifer[bundleIDIdentifier] + if !ok { + var err error + bundleID, err = m.client.FindBundleID(bundleIDIdentifier) + if err != nil { + return nil, fmt.Errorf("failed to find bundle ID: %w", err) + } + } + + if bundleID == nil && isAppClip(entitlements) { + return nil, ErrAppClipAppID{} + } + + if bundleID != nil { + log.Printf(" app ID found: %s", bundleID.Attributes.Name) + + m.bundleIDByBundleIDIdentifer[bundleIDIdentifier] = bundleID + + // Check if BundleID is sync with the project + err := m.client.CheckBundleIDEntitlements(*bundleID, entitlements) + if err != nil { + if mErr, ok := err.(NonmatchingProfileError); ok { + if isAppClip(entitlements) && hasSignInWithAppleEntitlement(entitlements) { + return nil, ErrAppClipAppIDWithAppleSigning{} + } + + log.Warnf(" app ID capabilities invalid: %s", mErr.Reason) + log.Warnf(" app ID capabilities are not in sync with the project capabilities, synchronizing...") + if err := m.client.SyncBundleID(*bundleID, entitlements); err != nil { + return nil, fmt.Errorf("failed to update bundle ID capabilities: %w", err) + } + + return bundleID, nil + } + + return nil, fmt.Errorf("failed to validate bundle ID: %w", err) + } + + log.Printf(" app ID capabilities are in sync with the project capabilities") + + return bundleID, nil + } + + // Create BundleID + log.Warnf(" app ID not found, generating...") + + bundleID, err := m.client.CreateBundleID(bundleIDIdentifier, appIDName(bundleIDIdentifier)) + if err != nil { + return nil, fmt.Errorf("failed to create bundle ID: %w", err) + } + + containers, err := entitlements.ICloudContainers() + if err != nil { + return nil, fmt.Errorf("failed to get list of iCloud containers: %w", err) + } + + if len(containers) > 0 { + m.containersByBundleID[bundleIDIdentifier] = containers + log.Errorf(" app ID created but couldn't add iCloud containers: %v", containers) + } + + if err := m.client.SyncBundleID(*bundleID, entitlements); err != nil { + return nil, fmt.Errorf("failed to update bundle ID capabilities: %w", err) + } + + m.bundleIDByBundleIDIdentifer[bundleIDIdentifier] = bundleID + + return bundleID, nil +} + +func (m profileManager) ensureProfileWithRetry(profileType appstoreconnect.ProfileType, bundleIDIdentifier string, entitlements Entitlements, certIDs, deviceIDs []string, minProfileDaysValid int) (*Profile, error) { + var profile *Profile + // Accessing the same Apple Developer Portal team can cause race conditions (parallel CI runs for example). + // Between the time of finding and downloading a profile, it could have been deleted for example. + if err := retry.Times(5).Wait(10 * time.Second).TryWithAbort(func(attempt uint) (error, bool) { + if attempt > 0 { + fmt.Println() + log.Printf(" Retrying profile preparation (attempt %d)", attempt) + } + + var err error + profile, err = m.ensureProfile(profileType, bundleIDIdentifier, entitlements, certIDs, deviceIDs, minProfileDaysValid) + if err != nil { + if ok := errors.As(err, &ProfilesInconsistentError{}); ok { + log.Warnf(" %s", err) + return err, false + } + + return err, true + } + + return nil, false + }); err != nil { + return nil, err + } + + return profile, nil +} + +func (m profileManager) ensureProfile(profileType appstoreconnect.ProfileType, bundleIDIdentifier string, entitlements Entitlements, certIDs, deviceIDs []string, minProfileDaysValid int) (*Profile, error) { + fmt.Println() + log.Infof(" Checking bundle id: %s", bundleIDIdentifier) + log.Printf(" capabilities:") + for k, v := range entitlements { + log.Printf(" - %s: %v", k, v) + } + + // Search for Bitrise managed Profile + name := profileName(profileType, bundleIDIdentifier) + profile, err := m.client.FindProfile(name, profileType) + if err != nil { + return nil, fmt.Errorf("failed to find profile: %w", err) + } + + if profile == nil { + log.Warnf(" profile does not exist, generating...") + } else { + log.Printf(" Bitrise managed profile found: %s ID: %s UUID: %s Expiry: %s", profile.Attributes().Name, profile.ID(), profile.Attributes().UUID, time.Time(profile.Attributes().ExpirationDate)) + + if profile.Attributes().ProfileState == appstoreconnect.Active { + // Check if Bitrise managed Profile is sync with the project + err := checkProfile(m.client, profile, entitlements, deviceIDs, certIDs, minProfileDaysValid) + if err != nil { + if mErr, ok := err.(NonmatchingProfileError); ok { + log.Warnf(" the profile is not in sync with the project requirements (%s), regenerating ...", mErr.Reason) + } else { + return nil, fmt.Errorf("failed to check if profile is valid: %w", err) + } + } else { // Profile matches + log.Donef(" profile is in sync with the project requirements") + return &profile, nil + } + } + + if profile.Attributes().ProfileState == appstoreconnect.Invalid { + // If the profile's bundle id gets modified, the profile turns in Invalid state. + log.Warnf(" the profile state is invalid, regenerating ...") + } + + if err := m.client.DeleteProfile(profile.ID()); err != nil { + return nil, fmt.Errorf("failed to delete profile: %w", err) + } + } + + // Search for BundleID + bundleID, err := m.ensureBundleID(bundleIDIdentifier, entitlements) + if err != nil { + return nil, fmt.Errorf("failed to ensure application identifier for %s: %w", bundleIDIdentifier, err) + } + + // Create Bitrise managed Profile + fmt.Println() + log.Infof(" Creating profile for bundle id: %s", bundleID.Attributes.Name) + + profile, err = m.client.CreateProfile(name, profileType, *bundleID, certIDs, deviceIDs) + if err != nil { + return nil, fmt.Errorf("failed to create profile: %w", err) + } + + log.Donef(" profile created: %s", profile.Attributes().Name) + return &profile, nil +} + +func isAppClip(entitlements Entitlements) bool { + for key := range entitlements { + if key == appstoreconnect.ParentApplicationIdentifierEntitlementKey { + return true + } + } + return false +} + +func hasSignInWithAppleEntitlement(entitlements Entitlements) bool { + for key := range entitlements { + if key == appstoreconnect.SignInWithAppleEntitlementKey { + return true + } + } + return false +} + +// DistributionTypeRequiresDeviceList returns true if the provided distribution method requires a provisioning profile with a device list. +func DistributionTypeRequiresDeviceList(distrTypes []DistributionType) bool { + for _, distrType := range distrTypes { + if distrType == Development || distrType == AdHoc { + return true + } + } + return false +} + +// CreateWildcardBundleID creates a wildcard bundle identifier, by replacing the provided bundle id's last component with an asterisk (*). +func CreateWildcardBundleID(bundleID string) (string, error) { + idx := strings.LastIndex(bundleID, ".") + if idx == -1 { + return "", fmt.Errorf("invalid bundle id (%s): does not contain *", bundleID) + } + + return bundleID[:idx] + ".*", nil +} + +// profileName generates profile name with layout: Bitrise - () +func profileName(profileType appstoreconnect.ProfileType, bundleID string) string { + platform, ok := ProfileTypeToPlatform[profileType] + if !ok { + panic(fmt.Sprintf("unknown profile type: %s", profileType)) + } + + distribution, ok := ProfileTypeToDistribution[profileType] + if !ok { + panic(fmt.Sprintf("unknown profile type: %s", profileType)) + } + + prefix := "" + if strings.HasSuffix(bundleID, ".*") { + // `*` char is not allowed in Profile name. + bundleID = strings.TrimSuffix(bundleID, ".*") + prefix = "Wildcard " + } + + return fmt.Sprintf("%sBitrise %s %s - (%s)", prefix, platform, distribution, bundleID) +} + +func checkProfileEntitlements(client DevPortalClient, prof Profile, appEntitlements Entitlements) error { + profileEnts, err := prof.Entitlements() + if err != nil { + return err + } + + missingContainers, err := FindMissingContainers(appEntitlements, profileEnts) + if err != nil { + return fmt.Errorf("failed to check missing containers: %s", err) + } + if len(missingContainers) > 0 { + return NonmatchingProfileError{ + Reason: fmt.Sprintf("project uses containers that are missing from the provisioning profile: %v", missingContainers), + } + } + + bundleID, err := prof.BundleID() + if err != nil { + return err + } + + return client.CheckBundleIDEntitlements(bundleID, appEntitlements) +} + +// ParseRawProfileEntitlements ... +func ParseRawProfileEntitlements(profileContents []byte) (Entitlements, error) { + pkcs, err := profileutil.ProvisioningProfileFromContent(profileContents) + if err != nil { + return nil, fmt.Errorf("failed to parse pkcs7 from profile content: %s", err) + } + + profile, err := profileutil.NewProvisioningProfileInfo(*pkcs) + if err != nil { + return nil, fmt.Errorf("failed to parse profile info from pkcs7 content: %s", err) + } + return Entitlements(profile.Entitlements), nil +} + +// FindMissingContainers ... +func FindMissingContainers(projectEnts, profileEnts Entitlements) ([]string, error) { + projContainerIDs, err := serialized.Object(projectEnts).StringSlice(ICloudIdentifiersEntitlementKey) + if err != nil { + if serialized.IsKeyNotFoundError(err) { + return nil, nil // project has no container + } + return nil, err + } + + // project has containers, so the profile should have at least the same + + profContainerIDs, err := serialized.Object(profileEnts).StringSlice(ICloudIdentifiersEntitlementKey) + if err != nil { + if serialized.IsKeyNotFoundError(err) { + return projContainerIDs, nil + } + return nil, err + } + + // project and profile also has containers, check if profile contains the containers the project need + + var missing []string + for _, projContainerID := range projContainerIDs { + var found bool + for _, profContainerID := range profContainerIDs { + if projContainerID == profContainerID { + found = true + break + } + } + if !found { + missing = append(missing, projContainerID) + } + } + + return missing, nil +} + +func checkProfileCertificates(profileCertificateIDs []string, certificateIDs []string) error { + for _, id := range certificateIDs { + if !sliceutil.IsStringInSlice(id, profileCertificateIDs) { + return NonmatchingProfileError{ + Reason: fmt.Sprintf("certificate with ID (%s) not included in the profile", id), + } + } + } + return nil +} + +func checkProfileDevices(profileDeviceIDs []string, deviceIDs []string) error { + for _, id := range deviceIDs { + if !sliceutil.IsStringInSlice(id, profileDeviceIDs) { + return NonmatchingProfileError{ + Reason: fmt.Sprintf("device with ID (%s) not included in the profile", id), + } + } + } + + return nil +} + +func isProfileExpired(prof Profile, minProfileDaysValid int) bool { + relativeExpiryTime := time.Now() + if minProfileDaysValid > 0 { + relativeExpiryTime = relativeExpiryTime.Add(time.Duration(minProfileDaysValid) * 24 * time.Hour) + } + return time.Time(prof.Attributes().ExpirationDate).Before(relativeExpiryTime) +} + +func checkProfile(client DevPortalClient, prof Profile, entitlements Entitlements, deviceIDs, certificateIDs []string, minProfileDaysValid int) error { + if isProfileExpired(prof, minProfileDaysValid) { + return NonmatchingProfileError{ + Reason: fmt.Sprintf("profile expired, or will expire in less then %d day(s)", minProfileDaysValid), + } + } + + if err := checkProfileEntitlements(client, prof, entitlements); err != nil { + return err + } + + profileCertificateIDs, err := prof.CertificateIDs() + if err != nil { + return err + } + if err := checkProfileCertificates(profileCertificateIDs, certificateIDs); err != nil { + return err + } + + profileDeviceIDs, err := prof.DeviceIDs() + if err != nil { + return err + } + return checkProfileDevices(profileDeviceIDs, deviceIDs) +} + +// SelectCertificate selects the first certificate with the given distribution type. +func SelectCertificate(certsByType map[appstoreconnect.CertificateType][]Certificate, distrType DistributionType) (*Certificate, error) { + certType := CertificateTypeByDistribution[distrType] + certs := certsByType[certType] + + if len(certs) == 0 { + return nil, fmt.Errorf("no valid certificate provided for distribution type: %s", distrType) + } + + if len(certs) > 1 { + log.Warnf("Multiple certificates provided for distribution type: %s", distrType) + for _, c := range certs { + log.Warnf("- %s", c.CertificateInfo.CommonName) + } + } + + selectedCertificate := certs[0] + + log.Warnf("Using certificate for %s distribution: %s", distrType, selectedCertificate.CertificateInfo.CommonName) + + return &selectedCertificate, nil +} diff --git a/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/utils.go b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/utils.go new file mode 100644 index 0000000..dc1aed4 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-xcode/v2/autocodesign/utils.go @@ -0,0 +1,70 @@ +package autocodesign + +import ( + "fmt" + "time" + + "github.com/bitrise-io/go-utils/log" +) + +func mergeCodeSignAssets(base, additional *AppCodesignAssets) *AppCodesignAssets { + if additional == nil { + return base + } + + if base == nil { + return additional + } + + if additional.ArchivableTargetProfilesByBundleID == nil { + additional.ArchivableTargetProfilesByBundleID = base.ArchivableTargetProfilesByBundleID + } else { + for bundleID, profile := range base.ArchivableTargetProfilesByBundleID { + additional.ArchivableTargetProfilesByBundleID[bundleID] = profile + } + } + + if additional.UITestTargetProfilesByBundleID == nil { + additional.UITestTargetProfilesByBundleID = base.UITestTargetProfilesByBundleID + } else { + for bundleID, profile := range base.UITestTargetProfilesByBundleID { + additional.UITestTargetProfilesByBundleID[bundleID] = profile + } + } + + base = additional + + return base +} + +func printMissingCodeSignAssets(missingCodesignAssets *AppLayout) { + fmt.Println() + log.Infof("Local code signing assets not found for:") + log.Printf("Archivable targets (%d)", len(missingCodesignAssets.EntitlementsByArchivableTargetBundleID)) + for bundleID := range missingCodesignAssets.EntitlementsByArchivableTargetBundleID { + log.Printf("- %s", bundleID) + } + log.Printf("UITest targets (%d)", len(missingCodesignAssets.UITestTargetBundleIDs)) + for _, bundleID := range missingCodesignAssets.UITestTargetBundleIDs { + log.Printf("- %s", bundleID) + } +} + +func printExistingCodesignAssets(assets *AppCodesignAssets, distrType DistributionType) { + if assets == nil { + return + } + + fmt.Println() + log.Infof("Local code signing assets for %s distribution:", distrType) + log.Printf("Certificate: %s (team name: %s, serial: %s)", assets.Certificate.CommonName, assets.Certificate.TeamName, assets.Certificate.Serial) + log.Printf("Archivable targets (%d)", len(assets.ArchivableTargetProfilesByBundleID)) + for bundleID, profile := range assets.ArchivableTargetProfilesByBundleID { + log.Printf("- %s: %s (ID: %s UUID: %s Expiry: %s)", bundleID, profile.Attributes().Name, profile.ID(), profile.Attributes().UUID, time.Time(profile.Attributes().ExpirationDate)) + } + + log.Printf("UITest targets (%d)", len(assets.UITestTargetProfilesByBundleID)) + for bundleID, profile := range assets.UITestTargetProfilesByBundleID { + log.Printf("- %s: %s (ID: %s UUID: %s Expiry: %s)", bundleID, profile.Attributes().Name, profile.ID(), profile.Attributes().UUID, time.Time(profile.Attributes().ExpirationDate)) + } +} diff --git a/vendor/github.com/bitrise-io/go-xcode/xcodeproject/serialized/error.go b/vendor/github.com/bitrise-io/go-xcode/xcodeproject/serialized/error.go new file mode 100644 index 0000000..ea63009 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-xcode/xcodeproject/serialized/error.go @@ -0,0 +1,54 @@ +package serialized + +import "fmt" + +// KeyNotFoundError ... +type KeyNotFoundError struct { + key string + object Object +} + +// Error ... +func (e KeyNotFoundError) Error() string { + return fmt.Sprintf("key: %T(%#v) not found in: %T(%#v)", e.key, e.key, e.object, e.object) +} + +// NewKeyNotFoundError ... +func NewKeyNotFoundError(key string, object Object) KeyNotFoundError { + return KeyNotFoundError{key: key, object: object} +} + +// IsKeyNotFoundError ... +func IsKeyNotFoundError(err error) bool { + if err == nil { + return false + } + _, ok := err.(KeyNotFoundError) + return ok +} + +// TypeCastError ... +type TypeCastError struct { + key string + value interface{} + expectedType string +} + +// NewTypeCastError ... +func NewTypeCastError(key string, value interface{}, expected interface{}) TypeCastError { + return TypeCastError{key: key, value: value, expectedType: fmt.Sprintf("%T", expected)} +} + +// IsTypeCastError ... +func IsTypeCastError(err error) bool { + if err == nil { + return false + } + _, ok := err.(TypeCastError) + return ok +} + +// Error ... +func (e TypeCastError) Error() string { + return fmt.Sprintf("value: %T(%#v) for key: %T(%#v) can not be casted to: %s", e.value, e.value, e.key, e.key, e.expectedType) +} diff --git a/vendor/github.com/bitrise-io/go-xcode/xcodeproject/serialized/serialized.go b/vendor/github.com/bitrise-io/go-xcode/xcodeproject/serialized/serialized.go new file mode 100644 index 0000000..ce329f2 --- /dev/null +++ b/vendor/github.com/bitrise-io/go-xcode/xcodeproject/serialized/serialized.go @@ -0,0 +1,92 @@ +package serialized + +// Object ... +type Object map[string]interface{} + +// Keys ... +func (o Object) Keys() []string { + var keys []string + for key := range o { + keys = append(keys, key) + } + return keys +} + +// Value ... +func (o Object) Value(key string) (interface{}, error) { + value, ok := o[key] + if !ok { + return nil, NewKeyNotFoundError(key, o) + } + return value, nil +} + +// String ... +func (o Object) String(key string) (string, error) { + value, err := o.Value(key) + if err != nil { + return "", err + } + + casted, ok := value.(string) + if !ok { + return "", NewTypeCastError(key, value, "") + } + + return casted, nil +} + +// Int64 returns a value with int64 type from the map +func (o Object) Int64(key string) (int64, error) { + value, err := o.Value(key) + if err != nil { + return -1, err + } + + casted, ok := value.(int64) + if !ok { + return -1, NewTypeCastError(key, value, 0) + } + + return casted, nil +} + +// StringSlice ... +func (o Object) StringSlice(key string) ([]string, error) { + value, err := o.Value(key) + if err != nil { + return nil, err + } + + casted, ok := value.([]interface{}) + if !ok { + return nil, NewTypeCastError(key, value, []interface{}{}) + } + + slice := []string{} + for _, v := range casted { + item, ok := v.(string) + if !ok { + return nil, NewTypeCastError(key, casted, "") + } + + slice = append(slice, item) + } + + return slice, nil +} + +// Object ... +func (o Object) Object(key string) (Object, error) { + value, err := o.Value(key) + if err != nil { + return nil, err + } + + casted, ok := value.(map[string]interface{}) + if !ok { + return nil, NewTypeCastError(key, value, map[string]interface{}{}) + } + + return casted, nil +} diff --git a/vendor/github.com/bitrise-io/pkcs12/renovate.json b/vendor/github.com/bitrise-io/pkcs12/renovate.json new file mode 100644 index 0000000..7369fcf --- /dev/null +++ b/vendor/github.com/bitrise-io/pkcs12/renovate.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "local>bitrise-io/renovate-config" + ] +} diff --git a/vendor/github.com/golang-jwt/jwt/v4/.gitignore b/vendor/github.com/golang-jwt/jwt/v4/.gitignore new file mode 100644 index 0000000..09573e0 --- /dev/null +++ b/vendor/github.com/golang-jwt/jwt/v4/.gitignore @@ -0,0 +1,4 @@ +.DS_Store +bin +.idea/ + diff --git a/vendor/github.com/golang-jwt/jwt/v4/LICENSE b/vendor/github.com/golang-jwt/jwt/v4/LICENSE new file mode 100644 index 0000000..35dbc25 --- /dev/null +++ b/vendor/github.com/golang-jwt/jwt/v4/LICENSE @@ -0,0 +1,9 @@ +Copyright (c) 2012 Dave Grijalva +Copyright (c) 2021 golang-jwt maintainers + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + diff --git a/vendor/github.com/golang-jwt/jwt/v4/MIGRATION_GUIDE.md b/vendor/github.com/golang-jwt/jwt/v4/MIGRATION_GUIDE.md new file mode 100644 index 0000000..32966f5 --- /dev/null +++ b/vendor/github.com/golang-jwt/jwt/v4/MIGRATION_GUIDE.md @@ -0,0 +1,22 @@ +## Migration Guide (v4.0.0) + +Starting from [v4.0.0](https://github.com/golang-jwt/jwt/releases/tag/v4.0.0), the import path will be: + + "github.com/golang-jwt/jwt/v4" + +The `/v4` version will be backwards compatible with existing `v3.x.y` tags in this repo, as well as +`github.com/dgrijalva/jwt-go`. For most users this should be a drop-in replacement, if you're having +troubles migrating, please open an issue. + +You can replace all occurrences of `github.com/dgrijalva/jwt-go` or `github.com/golang-jwt/jwt` with `github.com/golang-jwt/jwt/v4`, either manually or by using tools such as `sed` or `gofmt`. + +And then you'd typically run: + +``` +go get github.com/golang-jwt/jwt/v4 +go mod tidy +``` + +## Older releases (before v3.2.0) + +The original migration guide for older releases can be found at https://github.com/dgrijalva/jwt-go/blob/master/MIGRATION_GUIDE.md. diff --git a/vendor/github.com/golang-jwt/jwt/v4/README.md b/vendor/github.com/golang-jwt/jwt/v4/README.md new file mode 100644 index 0000000..01b2164 --- /dev/null +++ b/vendor/github.com/golang-jwt/jwt/v4/README.md @@ -0,0 +1,123 @@ +# jwt-go + +[![build](https://github.com/golang-jwt/jwt/actions/workflows/build.yml/badge.svg)](https://github.com/golang-jwt/jwt/actions/workflows/build.yml) +[![Go Reference](https://pkg.go.dev/badge/github.com/golang-jwt/jwt/v4.svg)](https://pkg.go.dev/github.com/golang-jwt/jwt/v4) + +A [go](http://www.golang.org) (or 'golang' for search engine friendliness) implementation of [JSON Web Tokens](https://datatracker.ietf.org/doc/html/rfc7519). + +Starting with [v4.0.0](https://github.com/golang-jwt/jwt/releases/tag/v4.0.0) this project adds Go module support, but maintains backwards compatibility with older `v3.x.y` tags and upstream `github.com/dgrijalva/jwt-go`. +See the [`MIGRATION_GUIDE.md`](./MIGRATION_GUIDE.md) for more information. + +> After the original author of the library suggested migrating the maintenance of `jwt-go`, a dedicated team of open source maintainers decided to clone the existing library into this repository. See [dgrijalva/jwt-go#462](https://github.com/dgrijalva/jwt-go/issues/462) for a detailed discussion on this topic. + + +**SECURITY NOTICE:** Some older versions of Go have a security issue in the crypto/elliptic. Recommendation is to upgrade to at least 1.15 See issue [dgrijalva/jwt-go#216](https://github.com/dgrijalva/jwt-go/issues/216) for more detail. + +**SECURITY NOTICE:** It's important that you [validate the `alg` presented is what you expect](https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/). This library attempts to make it easy to do the right thing by requiring key types match the expected alg, but you should take the extra step to verify it in your usage. See the examples provided. + +### Supported Go versions + +Our support of Go versions is aligned with Go's [version release policy](https://golang.org/doc/devel/release#policy). +So we will support a major version of Go until there are two newer major releases. +We no longer support building jwt-go with unsupported Go versions, as these contain security vulnerabilities +which will not be fixed. + +## What the heck is a JWT? + +JWT.io has [a great introduction](https://jwt.io/introduction) to JSON Web Tokens. + +In short, it's a signed JSON object that does something useful (for example, authentication). It's commonly used for `Bearer` tokens in Oauth 2. A token is made of three parts, separated by `.`'s. The first two parts are JSON objects, that have been [base64url](https://datatracker.ietf.org/doc/html/rfc4648) encoded. The last part is the signature, encoded the same way. + +The first part is called the header. It contains the necessary information for verifying the last part, the signature. For example, which encryption method was used for signing and what key was used. + +The part in the middle is the interesting bit. It's called the Claims and contains the actual stuff you care about. Refer to [RFC 7519](https://datatracker.ietf.org/doc/html/rfc7519) for information about reserved keys and the proper way to add your own. + +## What's in the box? + +This library supports the parsing and verification as well as the generation and signing of JWTs. Current supported signing algorithms are HMAC SHA, RSA, RSA-PSS, and ECDSA, though hooks are present for adding your own. + +## Examples + +See [the project documentation](https://pkg.go.dev/github.com/golang-jwt/jwt) for examples of usage: + +* [Simple example of parsing and validating a token](https://pkg.go.dev/github.com/golang-jwt/jwt#example-Parse-Hmac) +* [Simple example of building and signing a token](https://pkg.go.dev/github.com/golang-jwt/jwt#example-New-Hmac) +* [Directory of Examples](https://pkg.go.dev/github.com/golang-jwt/jwt#pkg-examples) + +## Extensions + +This library publishes all the necessary components for adding your own signing methods. Simply implement the `SigningMethod` interface and register a factory method using `RegisterSigningMethod`. + +A common use case would be integrating with different 3rd party signature providers, like key management services from various cloud providers or Hardware Security Modules (HSMs). + +| Extension | Purpose | Repo | +|-----------|----------------------------------------------------------------------------------------------|--------------------------------------------| +| GCP | Integrates with multiple Google Cloud Platform signing tools (AppEngine, IAM API, Cloud KMS) | https://github.com/someone1/gcp-jwt-go | +| AWS | Integrates with AWS Key Management Service, KMS | https://github.com/matelang/jwt-go-aws-kms | + +*Disclaimer*: Unless otherwise specified, these integrations are maintained by third parties and should not be considered as a primary offer by any of the mentioned cloud providers + +## Compliance + +This library was last reviewed to comply with [RFC 7519](https://datatracker.ietf.org/doc/html/rfc7519) dated May 2015 with a few notable differences: + +* In order to protect against accidental use of [Unsecured JWTs](https://datatracker.ietf.org/doc/html/rfc7519#section-6), tokens using `alg=none` will only be accepted if the constant `jwt.UnsafeAllowNoneSignatureType` is provided as the key. + +## Project Status & Versioning + +This library is considered production ready. Feedback and feature requests are appreciated. The API should be considered stable. There should be very few backwards-incompatible changes outside of major version updates (and only with good reason). + +This project uses [Semantic Versioning 2.0.0](http://semver.org). Accepted pull requests will land on `main`. Periodically, versions will be tagged from `main`. You can find all the releases on [the project releases page](https://github.com/golang-jwt/jwt/releases). + +**BREAKING CHANGES:*** +A full list of breaking changes is available in `VERSION_HISTORY.md`. See `MIGRATION_GUIDE.md` for more information on updating your code. + +## Usage Tips + +### Signing vs Encryption + +A token is simply a JSON object that is signed by its author. this tells you exactly two things about the data: + +* The author of the token was in the possession of the signing secret +* The data has not been modified since it was signed + +It's important to know that JWT does not provide encryption, which means anyone who has access to the token can read its contents. If you need to protect (encrypt) the data, there is a companion spec, `JWE`, that provides this functionality. JWE is currently outside the scope of this library. + +### Choosing a Signing Method + +There are several signing methods available, and you should probably take the time to learn about the various options before choosing one. The principal design decision is most likely going to be symmetric vs asymmetric. + +Symmetric signing methods, such as HSA, use only a single secret. This is probably the simplest signing method to use since any `[]byte` can be used as a valid secret. They are also slightly computationally faster to use, though this rarely is enough to matter. Symmetric signing methods work the best when both producers and consumers of tokens are trusted, or even the same system. Since the same secret is used to both sign and validate tokens, you can't easily distribute the key for validation. + +Asymmetric signing methods, such as RSA, use different keys for signing and verifying tokens. This makes it possible to produce tokens with a private key, and allow any consumer to access the public key for verification. + +### Signing Methods and Key Types + +Each signing method expects a different object type for its signing keys. See the package documentation for details. Here are the most common ones: + +* The [HMAC signing method](https://pkg.go.dev/github.com/golang-jwt/jwt#SigningMethodHMAC) (`HS256`,`HS384`,`HS512`) expect `[]byte` values for signing and validation +* The [RSA signing method](https://pkg.go.dev/github.com/golang-jwt/jwt#SigningMethodRSA) (`RS256`,`RS384`,`RS512`) expect `*rsa.PrivateKey` for signing and `*rsa.PublicKey` for validation +* The [ECDSA signing method](https://pkg.go.dev/github.com/golang-jwt/jwt#SigningMethodECDSA) (`ES256`,`ES384`,`ES512`) expect `*ecdsa.PrivateKey` for signing and `*ecdsa.PublicKey` for validation +* The [EdDSA signing method](https://pkg.go.dev/github.com/golang-jwt/jwt#SigningMethodEd25519) (`Ed25519`) expect `ed25519.PrivateKey` for signing and `ed25519.PublicKey` for validation + +### JWT and OAuth + +It's worth mentioning that OAuth and JWT are not the same thing. A JWT token is simply a signed JSON object. It can be used anywhere such a thing is useful. There is some confusion, though, as JWT is the most common type of bearer token used in OAuth2 authentication. + +Without going too far down the rabbit hole, here's a description of the interaction of these technologies: + +* OAuth is a protocol for allowing an identity provider to be separate from the service a user is logging in to. For example, whenever you use Facebook to log into a different service (Yelp, Spotify, etc), you are using OAuth. +* OAuth defines several options for passing around authentication data. One popular method is called a "bearer token". A bearer token is simply a string that _should_ only be held by an authenticated user. Thus, simply presenting this token proves your identity. You can probably derive from here why a JWT might make a good bearer token. +* Because bearer tokens are used for authentication, it's important they're kept secret. This is why transactions that use bearer tokens typically happen over SSL. + +### Troubleshooting + +This library uses descriptive error messages whenever possible. If you are not getting the expected result, have a look at the errors. The most common place people get stuck is providing the correct type of key to the parser. See the above section on signing methods and key types. + +## More + +Documentation can be found [on pkg.go.dev](https://pkg.go.dev/github.com/golang-jwt/jwt). + +The command line utility included in this project (cmd/jwt) provides a straightforward example of token creation and parsing as well as a useful tool for debugging your own integration. You'll also find several implementation examples in the documentation. + +[golang-jwt](https://github.com/orgs/golang-jwt) incorporates a modified version of the JWT logo, which is distributed under the terms of the [MIT License](https://github.com/jsonwebtoken/jsonwebtoken.github.io/blob/master/LICENSE.txt). diff --git a/vendor/github.com/golang-jwt/jwt/v4/VERSION_HISTORY.md b/vendor/github.com/golang-jwt/jwt/v4/VERSION_HISTORY.md new file mode 100644 index 0000000..afbfc4e --- /dev/null +++ b/vendor/github.com/golang-jwt/jwt/v4/VERSION_HISTORY.md @@ -0,0 +1,135 @@ +## `jwt-go` Version History + +#### 4.0.0 + +* Introduces support for Go modules. The `v4` version will be backwards compatible with `v3.x.y`. + +#### 3.2.2 + +* Starting from this release, we are adopting the policy to support the most 2 recent versions of Go currently available. By the time of this release, this is Go 1.15 and 1.16 ([#28](https://github.com/golang-jwt/jwt/pull/28)). +* Fixed a potential issue that could occur when the verification of `exp`, `iat` or `nbf` was not required and contained invalid contents, i.e. non-numeric/date. Thanks for @thaJeztah for making us aware of that and @giorgos-f3 for originally reporting it to the formtech fork ([#40](https://github.com/golang-jwt/jwt/pull/40)). +* Added support for EdDSA / ED25519 ([#36](https://github.com/golang-jwt/jwt/pull/36)). +* Optimized allocations ([#33](https://github.com/golang-jwt/jwt/pull/33)). + +#### 3.2.1 + +* **Import Path Change**: See MIGRATION_GUIDE.md for tips on updating your code + * Changed the import path from `github.com/dgrijalva/jwt-go` to `github.com/golang-jwt/jwt` +* Fixed type confusing issue between `string` and `[]string` in `VerifyAudience` ([#12](https://github.com/golang-jwt/jwt/pull/12)). This fixes CVE-2020-26160 + +#### 3.2.0 + +* Added method `ParseUnverified` to allow users to split up the tasks of parsing and validation +* HMAC signing method returns `ErrInvalidKeyType` instead of `ErrInvalidKey` where appropriate +* Added options to `request.ParseFromRequest`, which allows for an arbitrary list of modifiers to parsing behavior. Initial set include `WithClaims` and `WithParser`. Existing usage of this function will continue to work as before. +* Deprecated `ParseFromRequestWithClaims` to simplify API in the future. + +#### 3.1.0 + +* Improvements to `jwt` command line tool +* Added `SkipClaimsValidation` option to `Parser` +* Documentation updates + +#### 3.0.0 + +* **Compatibility Breaking Changes**: See MIGRATION_GUIDE.md for tips on updating your code + * Dropped support for `[]byte` keys when using RSA signing methods. This convenience feature could contribute to security vulnerabilities involving mismatched key types with signing methods. + * `ParseFromRequest` has been moved to `request` subpackage and usage has changed + * The `Claims` property on `Token` is now type `Claims` instead of `map[string]interface{}`. The default value is type `MapClaims`, which is an alias to `map[string]interface{}`. This makes it possible to use a custom type when decoding claims. +* Other Additions and Changes + * Added `Claims` interface type to allow users to decode the claims into a custom type + * Added `ParseWithClaims`, which takes a third argument of type `Claims`. Use this function instead of `Parse` if you have a custom type you'd like to decode into. + * Dramatically improved the functionality and flexibility of `ParseFromRequest`, which is now in the `request` subpackage + * Added `ParseFromRequestWithClaims` which is the `FromRequest` equivalent of `ParseWithClaims` + * Added new interface type `Extractor`, which is used for extracting JWT strings from http requests. Used with `ParseFromRequest` and `ParseFromRequestWithClaims`. + * Added several new, more specific, validation errors to error type bitmask + * Moved examples from README to executable example files + * Signing method registry is now thread safe + * Added new property to `ValidationError`, which contains the raw error returned by calls made by parse/verify (such as those returned by keyfunc or json parser) + +#### 2.7.0 + +This will likely be the last backwards compatible release before 3.0.0, excluding essential bug fixes. + +* Added new option `-show` to the `jwt` command that will just output the decoded token without verifying +* Error text for expired tokens includes how long it's been expired +* Fixed incorrect error returned from `ParseRSAPublicKeyFromPEM` +* Documentation updates + +#### 2.6.0 + +* Exposed inner error within ValidationError +* Fixed validation errors when using UseJSONNumber flag +* Added several unit tests + +#### 2.5.0 + +* Added support for signing method none. You shouldn't use this. The API tries to make this clear. +* Updated/fixed some documentation +* Added more helpful error message when trying to parse tokens that begin with `BEARER ` + +#### 2.4.0 + +* Added new type, Parser, to allow for configuration of various parsing parameters + * You can now specify a list of valid signing methods. Anything outside this set will be rejected. + * You can now opt to use the `json.Number` type instead of `float64` when parsing token JSON +* Added support for [Travis CI](https://travis-ci.org/dgrijalva/jwt-go) +* Fixed some bugs with ECDSA parsing + +#### 2.3.0 + +* Added support for ECDSA signing methods +* Added support for RSA PSS signing methods (requires go v1.4) + +#### 2.2.0 + +* Gracefully handle a `nil` `Keyfunc` being passed to `Parse`. Result will now be the parsed token and an error, instead of a panic. + +#### 2.1.0 + +Backwards compatible API change that was missed in 2.0.0. + +* The `SignedString` method on `Token` now takes `interface{}` instead of `[]byte` + +#### 2.0.0 + +There were two major reasons for breaking backwards compatibility with this update. The first was a refactor required to expand the width of the RSA and HMAC-SHA signing implementations. There will likely be no required code changes to support this change. + +The second update, while unfortunately requiring a small change in integration, is required to open up this library to other signing methods. Not all keys used for all signing methods have a single standard on-disk representation. Requiring `[]byte` as the type for all keys proved too limiting. Additionally, this implementation allows for pre-parsed tokens to be reused, which might matter in an application that parses a high volume of tokens with a small set of keys. Backwards compatibilty has been maintained for passing `[]byte` to the RSA signing methods, but they will also accept `*rsa.PublicKey` and `*rsa.PrivateKey`. + +It is likely the only integration change required here will be to change `func(t *jwt.Token) ([]byte, error)` to `func(t *jwt.Token) (interface{}, error)` when calling `Parse`. + +* **Compatibility Breaking Changes** + * `SigningMethodHS256` is now `*SigningMethodHMAC` instead of `type struct` + * `SigningMethodRS256` is now `*SigningMethodRSA` instead of `type struct` + * `KeyFunc` now returns `interface{}` instead of `[]byte` + * `SigningMethod.Sign` now takes `interface{}` instead of `[]byte` for the key + * `SigningMethod.Verify` now takes `interface{}` instead of `[]byte` for the key +* Renamed type `SigningMethodHS256` to `SigningMethodHMAC`. Specific sizes are now just instances of this type. + * Added public package global `SigningMethodHS256` + * Added public package global `SigningMethodHS384` + * Added public package global `SigningMethodHS512` +* Renamed type `SigningMethodRS256` to `SigningMethodRSA`. Specific sizes are now just instances of this type. + * Added public package global `SigningMethodRS256` + * Added public package global `SigningMethodRS384` + * Added public package global `SigningMethodRS512` +* Moved sample private key for HMAC tests from an inline value to a file on disk. Value is unchanged. +* Refactored the RSA implementation to be easier to read +* Exposed helper methods `ParseRSAPrivateKeyFromPEM` and `ParseRSAPublicKeyFromPEM` + +#### 1.0.2 + +* Fixed bug in parsing public keys from certificates +* Added more tests around the parsing of keys for RS256 +* Code refactoring in RS256 implementation. No functional changes + +#### 1.0.1 + +* Fixed panic if RS256 signing method was passed an invalid key + +#### 1.0.0 + +* First versioned release +* API stabilized +* Supports creating, signing, parsing, and validating JWT tokens +* Supports RS256 and HS256 signing methods diff --git a/vendor/github.com/golang-jwt/jwt/v4/claims.go b/vendor/github.com/golang-jwt/jwt/v4/claims.go new file mode 100644 index 0000000..9d95cad --- /dev/null +++ b/vendor/github.com/golang-jwt/jwt/v4/claims.go @@ -0,0 +1,273 @@ +package jwt + +import ( + "crypto/subtle" + "fmt" + "time" +) + +// Claims must just have a Valid method that determines +// if the token is invalid for any supported reason +type Claims interface { + Valid() error +} + +// RegisteredClaims are a structured version of the JWT Claims Set, +// restricted to Registered Claim Names, as referenced at +// https://datatracker.ietf.org/doc/html/rfc7519#section-4.1 +// +// This type can be used on its own, but then additional private and +// public claims embedded in the JWT will not be parsed. The typical usecase +// therefore is to embedded this in a user-defined claim type. +// +// See examples for how to use this with your own claim types. +type RegisteredClaims struct { + // the `iss` (Issuer) claim. See https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.1 + Issuer string `json:"iss,omitempty"` + + // the `sub` (Subject) claim. See https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.2 + Subject string `json:"sub,omitempty"` + + // the `aud` (Audience) claim. See https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.3 + Audience ClaimStrings `json:"aud,omitempty"` + + // the `exp` (Expiration Time) claim. See https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.4 + ExpiresAt *NumericDate `json:"exp,omitempty"` + + // the `nbf` (Not Before) claim. See https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.5 + NotBefore *NumericDate `json:"nbf,omitempty"` + + // the `iat` (Issued At) claim. See https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.6 + IssuedAt *NumericDate `json:"iat,omitempty"` + + // the `jti` (JWT ID) claim. See https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.7 + ID string `json:"jti,omitempty"` +} + +// Valid validates time based claims "exp, iat, nbf". +// There is no accounting for clock skew. +// As well, if any of the above claims are not in the token, it will still +// be considered a valid claim. +func (c RegisteredClaims) Valid() error { + vErr := new(ValidationError) + now := TimeFunc() + + // The claims below are optional, by default, so if they are set to the + // default value in Go, let's not fail the verification for them. + if !c.VerifyExpiresAt(now, false) { + delta := now.Sub(c.ExpiresAt.Time) + vErr.Inner = fmt.Errorf("%s by %s", ErrTokenExpired, delta) + vErr.Errors |= ValidationErrorExpired + } + + if !c.VerifyIssuedAt(now, false) { + vErr.Inner = ErrTokenUsedBeforeIssued + vErr.Errors |= ValidationErrorIssuedAt + } + + if !c.VerifyNotBefore(now, false) { + vErr.Inner = ErrTokenNotValidYet + vErr.Errors |= ValidationErrorNotValidYet + } + + if vErr.valid() { + return nil + } + + return vErr +} + +// VerifyAudience compares the aud claim against cmp. +// If required is false, this method will return true if the value matches or is unset +func (c *RegisteredClaims) VerifyAudience(cmp string, req bool) bool { + return verifyAud(c.Audience, cmp, req) +} + +// VerifyExpiresAt compares the exp claim against cmp (cmp < exp). +// If req is false, it will return true, if exp is unset. +func (c *RegisteredClaims) VerifyExpiresAt(cmp time.Time, req bool) bool { + if c.ExpiresAt == nil { + return verifyExp(nil, cmp, req) + } + + return verifyExp(&c.ExpiresAt.Time, cmp, req) +} + +// VerifyIssuedAt compares the iat claim against cmp (cmp >= iat). +// If req is false, it will return true, if iat is unset. +func (c *RegisteredClaims) VerifyIssuedAt(cmp time.Time, req bool) bool { + if c.IssuedAt == nil { + return verifyIat(nil, cmp, req) + } + + return verifyIat(&c.IssuedAt.Time, cmp, req) +} + +// VerifyNotBefore compares the nbf claim against cmp (cmp >= nbf). +// If req is false, it will return true, if nbf is unset. +func (c *RegisteredClaims) VerifyNotBefore(cmp time.Time, req bool) bool { + if c.NotBefore == nil { + return verifyNbf(nil, cmp, req) + } + + return verifyNbf(&c.NotBefore.Time, cmp, req) +} + +// VerifyIssuer compares the iss claim against cmp. +// If required is false, this method will return true if the value matches or is unset +func (c *RegisteredClaims) VerifyIssuer(cmp string, req bool) bool { + return verifyIss(c.Issuer, cmp, req) +} + +// StandardClaims are a structured version of the JWT Claims Set, as referenced at +// https://datatracker.ietf.org/doc/html/rfc7519#section-4. They do not follow the +// specification exactly, since they were based on an earlier draft of the +// specification and not updated. The main difference is that they only +// support integer-based date fields and singular audiences. This might lead to +// incompatibilities with other JWT implementations. The use of this is discouraged, instead +// the newer RegisteredClaims struct should be used. +// +// Deprecated: Use RegisteredClaims instead for a forward-compatible way to access registered claims in a struct. +type StandardClaims struct { + Audience string `json:"aud,omitempty"` + ExpiresAt int64 `json:"exp,omitempty"` + Id string `json:"jti,omitempty"` + IssuedAt int64 `json:"iat,omitempty"` + Issuer string `json:"iss,omitempty"` + NotBefore int64 `json:"nbf,omitempty"` + Subject string `json:"sub,omitempty"` +} + +// Valid validates time based claims "exp, iat, nbf". There is no accounting for clock skew. +// As well, if any of the above claims are not in the token, it will still +// be considered a valid claim. +func (c StandardClaims) Valid() error { + vErr := new(ValidationError) + now := TimeFunc().Unix() + + // The claims below are optional, by default, so if they are set to the + // default value in Go, let's not fail the verification for them. + if !c.VerifyExpiresAt(now, false) { + delta := time.Unix(now, 0).Sub(time.Unix(c.ExpiresAt, 0)) + vErr.Inner = fmt.Errorf("%s by %s", ErrTokenExpired, delta) + vErr.Errors |= ValidationErrorExpired + } + + if !c.VerifyIssuedAt(now, false) { + vErr.Inner = ErrTokenUsedBeforeIssued + vErr.Errors |= ValidationErrorIssuedAt + } + + if !c.VerifyNotBefore(now, false) { + vErr.Inner = ErrTokenNotValidYet + vErr.Errors |= ValidationErrorNotValidYet + } + + if vErr.valid() { + return nil + } + + return vErr +} + +// VerifyAudience compares the aud claim against cmp. +// If required is false, this method will return true if the value matches or is unset +func (c *StandardClaims) VerifyAudience(cmp string, req bool) bool { + return verifyAud([]string{c.Audience}, cmp, req) +} + +// VerifyExpiresAt compares the exp claim against cmp (cmp < exp). +// If req is false, it will return true, if exp is unset. +func (c *StandardClaims) VerifyExpiresAt(cmp int64, req bool) bool { + if c.ExpiresAt == 0 { + return verifyExp(nil, time.Unix(cmp, 0), req) + } + + t := time.Unix(c.ExpiresAt, 0) + return verifyExp(&t, time.Unix(cmp, 0), req) +} + +// VerifyIssuedAt compares the iat claim against cmp (cmp >= iat). +// If req is false, it will return true, if iat is unset. +func (c *StandardClaims) VerifyIssuedAt(cmp int64, req bool) bool { + if c.IssuedAt == 0 { + return verifyIat(nil, time.Unix(cmp, 0), req) + } + + t := time.Unix(c.IssuedAt, 0) + return verifyIat(&t, time.Unix(cmp, 0), req) +} + +// VerifyNotBefore compares the nbf claim against cmp (cmp >= nbf). +// If req is false, it will return true, if nbf is unset. +func (c *StandardClaims) VerifyNotBefore(cmp int64, req bool) bool { + if c.NotBefore == 0 { + return verifyNbf(nil, time.Unix(cmp, 0), req) + } + + t := time.Unix(c.NotBefore, 0) + return verifyNbf(&t, time.Unix(cmp, 0), req) +} + +// VerifyIssuer compares the iss claim against cmp. +// If required is false, this method will return true if the value matches or is unset +func (c *StandardClaims) VerifyIssuer(cmp string, req bool) bool { + return verifyIss(c.Issuer, cmp, req) +} + +// ----- helpers + +func verifyAud(aud []string, cmp string, required bool) bool { + if len(aud) == 0 { + return !required + } + // use a var here to keep constant time compare when looping over a number of claims + result := false + + var stringClaims string + for _, a := range aud { + if subtle.ConstantTimeCompare([]byte(a), []byte(cmp)) != 0 { + result = true + } + stringClaims = stringClaims + a + } + + // case where "" is sent in one or many aud claims + if len(stringClaims) == 0 { + return !required + } + + return result +} + +func verifyExp(exp *time.Time, now time.Time, required bool) bool { + if exp == nil { + return !required + } + return now.Before(*exp) +} + +func verifyIat(iat *time.Time, now time.Time, required bool) bool { + if iat == nil { + return !required + } + return now.After(*iat) || now.Equal(*iat) +} + +func verifyNbf(nbf *time.Time, now time.Time, required bool) bool { + if nbf == nil { + return !required + } + return now.After(*nbf) || now.Equal(*nbf) +} + +func verifyIss(iss string, cmp string, required bool) bool { + if iss == "" { + return !required + } + if subtle.ConstantTimeCompare([]byte(iss), []byte(cmp)) != 0 { + return true + } else { + return false + } +} diff --git a/vendor/github.com/golang-jwt/jwt/v4/doc.go b/vendor/github.com/golang-jwt/jwt/v4/doc.go new file mode 100644 index 0000000..a86dc1a --- /dev/null +++ b/vendor/github.com/golang-jwt/jwt/v4/doc.go @@ -0,0 +1,4 @@ +// Package jwt is a Go implementation of JSON Web Tokens: http://self-issued.info/docs/draft-jones-json-web-token.html +// +// See README.md for more info. +package jwt diff --git a/vendor/github.com/golang-jwt/jwt/v4/ecdsa.go b/vendor/github.com/golang-jwt/jwt/v4/ecdsa.go new file mode 100644 index 0000000..eac023f --- /dev/null +++ b/vendor/github.com/golang-jwt/jwt/v4/ecdsa.go @@ -0,0 +1,142 @@ +package jwt + +import ( + "crypto" + "crypto/ecdsa" + "crypto/rand" + "errors" + "math/big" +) + +var ( + // Sadly this is missing from crypto/ecdsa compared to crypto/rsa + ErrECDSAVerification = errors.New("crypto/ecdsa: verification error") +) + +// SigningMethodECDSA implements the ECDSA family of signing methods. +// Expects *ecdsa.PrivateKey for signing and *ecdsa.PublicKey for verification +type SigningMethodECDSA struct { + Name string + Hash crypto.Hash + KeySize int + CurveBits int +} + +// Specific instances for EC256 and company +var ( + SigningMethodES256 *SigningMethodECDSA + SigningMethodES384 *SigningMethodECDSA + SigningMethodES512 *SigningMethodECDSA +) + +func init() { + // ES256 + SigningMethodES256 = &SigningMethodECDSA{"ES256", crypto.SHA256, 32, 256} + RegisterSigningMethod(SigningMethodES256.Alg(), func() SigningMethod { + return SigningMethodES256 + }) + + // ES384 + SigningMethodES384 = &SigningMethodECDSA{"ES384", crypto.SHA384, 48, 384} + RegisterSigningMethod(SigningMethodES384.Alg(), func() SigningMethod { + return SigningMethodES384 + }) + + // ES512 + SigningMethodES512 = &SigningMethodECDSA{"ES512", crypto.SHA512, 66, 521} + RegisterSigningMethod(SigningMethodES512.Alg(), func() SigningMethod { + return SigningMethodES512 + }) +} + +func (m *SigningMethodECDSA) Alg() string { + return m.Name +} + +// Verify implements token verification for the SigningMethod. +// For this verify method, key must be an ecdsa.PublicKey struct +func (m *SigningMethodECDSA) Verify(signingString, signature string, key interface{}) error { + var err error + + // Decode the signature + var sig []byte + if sig, err = DecodeSegment(signature); err != nil { + return err + } + + // Get the key + var ecdsaKey *ecdsa.PublicKey + switch k := key.(type) { + case *ecdsa.PublicKey: + ecdsaKey = k + default: + return ErrInvalidKeyType + } + + if len(sig) != 2*m.KeySize { + return ErrECDSAVerification + } + + r := big.NewInt(0).SetBytes(sig[:m.KeySize]) + s := big.NewInt(0).SetBytes(sig[m.KeySize:]) + + // Create hasher + if !m.Hash.Available() { + return ErrHashUnavailable + } + hasher := m.Hash.New() + hasher.Write([]byte(signingString)) + + // Verify the signature + if verifystatus := ecdsa.Verify(ecdsaKey, hasher.Sum(nil), r, s); verifystatus { + return nil + } + + return ErrECDSAVerification +} + +// Sign implements token signing for the SigningMethod. +// For this signing method, key must be an ecdsa.PrivateKey struct +func (m *SigningMethodECDSA) Sign(signingString string, key interface{}) (string, error) { + // Get the key + var ecdsaKey *ecdsa.PrivateKey + switch k := key.(type) { + case *ecdsa.PrivateKey: + ecdsaKey = k + default: + return "", ErrInvalidKeyType + } + + // Create the hasher + if !m.Hash.Available() { + return "", ErrHashUnavailable + } + + hasher := m.Hash.New() + hasher.Write([]byte(signingString)) + + // Sign the string and return r, s + if r, s, err := ecdsa.Sign(rand.Reader, ecdsaKey, hasher.Sum(nil)); err == nil { + curveBits := ecdsaKey.Curve.Params().BitSize + + if m.CurveBits != curveBits { + return "", ErrInvalidKey + } + + keyBytes := curveBits / 8 + if curveBits%8 > 0 { + keyBytes += 1 + } + + // We serialize the outputs (r and s) into big-endian byte arrays + // padded with zeros on the left to make sure the sizes work out. + // Output must be 2*keyBytes long. + out := make([]byte, 2*keyBytes) + r.FillBytes(out[0:keyBytes]) // r is assigned to the first half of output. + s.FillBytes(out[keyBytes:]) // s is assigned to the second half of output. + + return EncodeSegment(out), nil + } else { + return "", err + } +} diff --git a/vendor/github.com/golang-jwt/jwt/v4/ecdsa_utils.go b/vendor/github.com/golang-jwt/jwt/v4/ecdsa_utils.go new file mode 100644 index 0000000..5700636 --- /dev/null +++ b/vendor/github.com/golang-jwt/jwt/v4/ecdsa_utils.go @@ -0,0 +1,69 @@ +package jwt + +import ( + "crypto/ecdsa" + "crypto/x509" + "encoding/pem" + "errors" +) + +var ( + ErrNotECPublicKey = errors.New("key is not a valid ECDSA public key") + ErrNotECPrivateKey = errors.New("key is not a valid ECDSA private key") +) + +// ParseECPrivateKeyFromPEM parses a PEM encoded Elliptic Curve Private Key Structure +func ParseECPrivateKeyFromPEM(key []byte) (*ecdsa.PrivateKey, error) { + var err error + + // Parse PEM block + var block *pem.Block + if block, _ = pem.Decode(key); block == nil { + return nil, ErrKeyMustBePEMEncoded + } + + // Parse the key + var parsedKey interface{} + if parsedKey, err = x509.ParseECPrivateKey(block.Bytes); err != nil { + if parsedKey, err = x509.ParsePKCS8PrivateKey(block.Bytes); err != nil { + return nil, err + } + } + + var pkey *ecdsa.PrivateKey + var ok bool + if pkey, ok = parsedKey.(*ecdsa.PrivateKey); !ok { + return nil, ErrNotECPrivateKey + } + + return pkey, nil +} + +// ParseECPublicKeyFromPEM parses a PEM encoded PKCS1 or PKCS8 public key +func ParseECPublicKeyFromPEM(key []byte) (*ecdsa.PublicKey, error) { + var err error + + // Parse PEM block + var block *pem.Block + if block, _ = pem.Decode(key); block == nil { + return nil, ErrKeyMustBePEMEncoded + } + + // Parse the key + var parsedKey interface{} + if parsedKey, err = x509.ParsePKIXPublicKey(block.Bytes); err != nil { + if cert, err := x509.ParseCertificate(block.Bytes); err == nil { + parsedKey = cert.PublicKey + } else { + return nil, err + } + } + + var pkey *ecdsa.PublicKey + var ok bool + if pkey, ok = parsedKey.(*ecdsa.PublicKey); !ok { + return nil, ErrNotECPublicKey + } + + return pkey, nil +} diff --git a/vendor/github.com/golang-jwt/jwt/v4/ed25519.go b/vendor/github.com/golang-jwt/jwt/v4/ed25519.go new file mode 100644 index 0000000..07d3aac --- /dev/null +++ b/vendor/github.com/golang-jwt/jwt/v4/ed25519.go @@ -0,0 +1,85 @@ +package jwt + +import ( + "errors" + + "crypto" + "crypto/ed25519" + "crypto/rand" +) + +var ( + ErrEd25519Verification = errors.New("ed25519: verification error") +) + +// SigningMethodEd25519 implements the EdDSA family. +// Expects ed25519.PrivateKey for signing and ed25519.PublicKey for verification +type SigningMethodEd25519 struct{} + +// Specific instance for EdDSA +var ( + SigningMethodEdDSA *SigningMethodEd25519 +) + +func init() { + SigningMethodEdDSA = &SigningMethodEd25519{} + RegisterSigningMethod(SigningMethodEdDSA.Alg(), func() SigningMethod { + return SigningMethodEdDSA + }) +} + +func (m *SigningMethodEd25519) Alg() string { + return "EdDSA" +} + +// Verify implements token verification for the SigningMethod. +// For this verify method, key must be an ed25519.PublicKey +func (m *SigningMethodEd25519) Verify(signingString, signature string, key interface{}) error { + var err error + var ed25519Key ed25519.PublicKey + var ok bool + + if ed25519Key, ok = key.(ed25519.PublicKey); !ok { + return ErrInvalidKeyType + } + + if len(ed25519Key) != ed25519.PublicKeySize { + return ErrInvalidKey + } + + // Decode the signature + var sig []byte + if sig, err = DecodeSegment(signature); err != nil { + return err + } + + // Verify the signature + if !ed25519.Verify(ed25519Key, []byte(signingString), sig) { + return ErrEd25519Verification + } + + return nil +} + +// Sign implements token signing for the SigningMethod. +// For this signing method, key must be an ed25519.PrivateKey +func (m *SigningMethodEd25519) Sign(signingString string, key interface{}) (string, error) { + var ed25519Key crypto.Signer + var ok bool + + if ed25519Key, ok = key.(crypto.Signer); !ok { + return "", ErrInvalidKeyType + } + + if _, ok := ed25519Key.Public().(ed25519.PublicKey); !ok { + return "", ErrInvalidKey + } + + // Sign the string and return the encoded result + // ed25519 performs a two-pass hash as part of its algorithm. Therefore, we need to pass a non-prehashed message into the Sign function, as indicated by crypto.Hash(0) + sig, err := ed25519Key.Sign(rand.Reader, []byte(signingString), crypto.Hash(0)) + if err != nil { + return "", err + } + return EncodeSegment(sig), nil +} diff --git a/vendor/github.com/golang-jwt/jwt/v4/ed25519_utils.go b/vendor/github.com/golang-jwt/jwt/v4/ed25519_utils.go new file mode 100644 index 0000000..cdb5e68 --- /dev/null +++ b/vendor/github.com/golang-jwt/jwt/v4/ed25519_utils.go @@ -0,0 +1,64 @@ +package jwt + +import ( + "crypto" + "crypto/ed25519" + "crypto/x509" + "encoding/pem" + "errors" +) + +var ( + ErrNotEdPrivateKey = errors.New("key is not a valid Ed25519 private key") + ErrNotEdPublicKey = errors.New("key is not a valid Ed25519 public key") +) + +// ParseEdPrivateKeyFromPEM parses a PEM-encoded Edwards curve private key +func ParseEdPrivateKeyFromPEM(key []byte) (crypto.PrivateKey, error) { + var err error + + // Parse PEM block + var block *pem.Block + if block, _ = pem.Decode(key); block == nil { + return nil, ErrKeyMustBePEMEncoded + } + + // Parse the key + var parsedKey interface{} + if parsedKey, err = x509.ParsePKCS8PrivateKey(block.Bytes); err != nil { + return nil, err + } + + var pkey ed25519.PrivateKey + var ok bool + if pkey, ok = parsedKey.(ed25519.PrivateKey); !ok { + return nil, ErrNotEdPrivateKey + } + + return pkey, nil +} + +// ParseEdPublicKeyFromPEM parses a PEM-encoded Edwards curve public key +func ParseEdPublicKeyFromPEM(key []byte) (crypto.PublicKey, error) { + var err error + + // Parse PEM block + var block *pem.Block + if block, _ = pem.Decode(key); block == nil { + return nil, ErrKeyMustBePEMEncoded + } + + // Parse the key + var parsedKey interface{} + if parsedKey, err = x509.ParsePKIXPublicKey(block.Bytes); err != nil { + return nil, err + } + + var pkey ed25519.PublicKey + var ok bool + if pkey, ok = parsedKey.(ed25519.PublicKey); !ok { + return nil, ErrNotEdPublicKey + } + + return pkey, nil +} diff --git a/vendor/github.com/golang-jwt/jwt/v4/errors.go b/vendor/github.com/golang-jwt/jwt/v4/errors.go new file mode 100644 index 0000000..10ac883 --- /dev/null +++ b/vendor/github.com/golang-jwt/jwt/v4/errors.go @@ -0,0 +1,112 @@ +package jwt + +import ( + "errors" +) + +// Error constants +var ( + ErrInvalidKey = errors.New("key is invalid") + ErrInvalidKeyType = errors.New("key is of invalid type") + ErrHashUnavailable = errors.New("the requested hash function is unavailable") + + ErrTokenMalformed = errors.New("token is malformed") + ErrTokenUnverifiable = errors.New("token is unverifiable") + ErrTokenSignatureInvalid = errors.New("token signature is invalid") + + ErrTokenInvalidAudience = errors.New("token has invalid audience") + ErrTokenExpired = errors.New("token is expired") + ErrTokenUsedBeforeIssued = errors.New("token used before issued") + ErrTokenInvalidIssuer = errors.New("token has invalid issuer") + ErrTokenNotValidYet = errors.New("token is not valid yet") + ErrTokenInvalidId = errors.New("token has invalid id") + ErrTokenInvalidClaims = errors.New("token has invalid claims") +) + +// The errors that might occur when parsing and validating a token +const ( + ValidationErrorMalformed uint32 = 1 << iota // Token is malformed + ValidationErrorUnverifiable // Token could not be verified because of signing problems + ValidationErrorSignatureInvalid // Signature validation failed + + // Standard Claim validation errors + ValidationErrorAudience // AUD validation failed + ValidationErrorExpired // EXP validation failed + ValidationErrorIssuedAt // IAT validation failed + ValidationErrorIssuer // ISS validation failed + ValidationErrorNotValidYet // NBF validation failed + ValidationErrorId // JTI validation failed + ValidationErrorClaimsInvalid // Generic claims validation error +) + +// NewValidationError is a helper for constructing a ValidationError with a string error message +func NewValidationError(errorText string, errorFlags uint32) *ValidationError { + return &ValidationError{ + text: errorText, + Errors: errorFlags, + } +} + +// ValidationError represents an error from Parse if token is not valid +type ValidationError struct { + Inner error // stores the error returned by external dependencies, i.e.: KeyFunc + Errors uint32 // bitfield. see ValidationError... constants + text string // errors that do not have a valid error just have text +} + +// Error is the implementation of the err interface. +func (e ValidationError) Error() string { + if e.Inner != nil { + return e.Inner.Error() + } else if e.text != "" { + return e.text + } else { + return "token is invalid" + } +} + +// Unwrap gives errors.Is and errors.As access to the inner error. +func (e *ValidationError) Unwrap() error { + return e.Inner +} + +// No errors +func (e *ValidationError) valid() bool { + return e.Errors == 0 +} + +// Is checks if this ValidationError is of the supplied error. We are first checking for the exact error message +// by comparing the inner error message. If that fails, we compare using the error flags. This way we can use +// custom error messages (mainly for backwards compatability) and still leverage errors.Is using the global error variables. +func (e *ValidationError) Is(err error) bool { + // Check, if our inner error is a direct match + if errors.Is(errors.Unwrap(e), err) { + return true + } + + // Otherwise, we need to match using our error flags + switch err { + case ErrTokenMalformed: + return e.Errors&ValidationErrorMalformed != 0 + case ErrTokenUnverifiable: + return e.Errors&ValidationErrorUnverifiable != 0 + case ErrTokenSignatureInvalid: + return e.Errors&ValidationErrorSignatureInvalid != 0 + case ErrTokenInvalidAudience: + return e.Errors&ValidationErrorAudience != 0 + case ErrTokenExpired: + return e.Errors&ValidationErrorExpired != 0 + case ErrTokenUsedBeforeIssued: + return e.Errors&ValidationErrorIssuedAt != 0 + case ErrTokenInvalidIssuer: + return e.Errors&ValidationErrorIssuer != 0 + case ErrTokenNotValidYet: + return e.Errors&ValidationErrorNotValidYet != 0 + case ErrTokenInvalidId: + return e.Errors&ValidationErrorId != 0 + case ErrTokenInvalidClaims: + return e.Errors&ValidationErrorClaimsInvalid != 0 + } + + return false +} diff --git a/vendor/github.com/golang-jwt/jwt/v4/go.mod b/vendor/github.com/golang-jwt/jwt/v4/go.mod new file mode 100644 index 0000000..2f215c5 --- /dev/null +++ b/vendor/github.com/golang-jwt/jwt/v4/go.mod @@ -0,0 +1,7 @@ +module github.com/golang-jwt/jwt/v4 + +go 1.16 + +retract ( + v4.4.0 // Contains a backwards incompatible change to the Claims interface. +) diff --git a/vendor/github.com/golang-jwt/jwt/v4/go.sum b/vendor/github.com/golang-jwt/jwt/v4/go.sum new file mode 100644 index 0000000..e69de29 diff --git a/vendor/github.com/golang-jwt/jwt/v4/hmac.go b/vendor/github.com/golang-jwt/jwt/v4/hmac.go new file mode 100644 index 0000000..011f68a --- /dev/null +++ b/vendor/github.com/golang-jwt/jwt/v4/hmac.go @@ -0,0 +1,95 @@ +package jwt + +import ( + "crypto" + "crypto/hmac" + "errors" +) + +// SigningMethodHMAC implements the HMAC-SHA family of signing methods. +// Expects key type of []byte for both signing and validation +type SigningMethodHMAC struct { + Name string + Hash crypto.Hash +} + +// Specific instances for HS256 and company +var ( + SigningMethodHS256 *SigningMethodHMAC + SigningMethodHS384 *SigningMethodHMAC + SigningMethodHS512 *SigningMethodHMAC + ErrSignatureInvalid = errors.New("signature is invalid") +) + +func init() { + // HS256 + SigningMethodHS256 = &SigningMethodHMAC{"HS256", crypto.SHA256} + RegisterSigningMethod(SigningMethodHS256.Alg(), func() SigningMethod { + return SigningMethodHS256 + }) + + // HS384 + SigningMethodHS384 = &SigningMethodHMAC{"HS384", crypto.SHA384} + RegisterSigningMethod(SigningMethodHS384.Alg(), func() SigningMethod { + return SigningMethodHS384 + }) + + // HS512 + SigningMethodHS512 = &SigningMethodHMAC{"HS512", crypto.SHA512} + RegisterSigningMethod(SigningMethodHS512.Alg(), func() SigningMethod { + return SigningMethodHS512 + }) +} + +func (m *SigningMethodHMAC) Alg() string { + return m.Name +} + +// Verify implements token verification for the SigningMethod. Returns nil if the signature is valid. +func (m *SigningMethodHMAC) Verify(signingString, signature string, key interface{}) error { + // Verify the key is the right type + keyBytes, ok := key.([]byte) + if !ok { + return ErrInvalidKeyType + } + + // Decode signature, for comparison + sig, err := DecodeSegment(signature) + if err != nil { + return err + } + + // Can we use the specified hashing method? + if !m.Hash.Available() { + return ErrHashUnavailable + } + + // This signing method is symmetric, so we validate the signature + // by reproducing the signature from the signing string and key, then + // comparing that against the provided signature. + hasher := hmac.New(m.Hash.New, keyBytes) + hasher.Write([]byte(signingString)) + if !hmac.Equal(sig, hasher.Sum(nil)) { + return ErrSignatureInvalid + } + + // No validation errors. Signature is good. + return nil +} + +// Sign implements token signing for the SigningMethod. +// Key must be []byte +func (m *SigningMethodHMAC) Sign(signingString string, key interface{}) (string, error) { + if keyBytes, ok := key.([]byte); ok { + if !m.Hash.Available() { + return "", ErrHashUnavailable + } + + hasher := hmac.New(m.Hash.New, keyBytes) + hasher.Write([]byte(signingString)) + + return EncodeSegment(hasher.Sum(nil)), nil + } + + return "", ErrInvalidKeyType +} diff --git a/vendor/github.com/golang-jwt/jwt/v4/map_claims.go b/vendor/github.com/golang-jwt/jwt/v4/map_claims.go new file mode 100644 index 0000000..2700d64 --- /dev/null +++ b/vendor/github.com/golang-jwt/jwt/v4/map_claims.go @@ -0,0 +1,151 @@ +package jwt + +import ( + "encoding/json" + "errors" + "time" + // "fmt" +) + +// MapClaims is a claims type that uses the map[string]interface{} for JSON decoding. +// This is the default claims type if you don't supply one +type MapClaims map[string]interface{} + +// VerifyAudience Compares the aud claim against cmp. +// If required is false, this method will return true if the value matches or is unset +func (m MapClaims) VerifyAudience(cmp string, req bool) bool { + var aud []string + switch v := m["aud"].(type) { + case string: + aud = append(aud, v) + case []string: + aud = v + case []interface{}: + for _, a := range v { + vs, ok := a.(string) + if !ok { + return false + } + aud = append(aud, vs) + } + } + return verifyAud(aud, cmp, req) +} + +// VerifyExpiresAt compares the exp claim against cmp (cmp <= exp). +// If req is false, it will return true, if exp is unset. +func (m MapClaims) VerifyExpiresAt(cmp int64, req bool) bool { + cmpTime := time.Unix(cmp, 0) + + v, ok := m["exp"] + if !ok { + return !req + } + + switch exp := v.(type) { + case float64: + if exp == 0 { + return verifyExp(nil, cmpTime, req) + } + + return verifyExp(&newNumericDateFromSeconds(exp).Time, cmpTime, req) + case json.Number: + v, _ := exp.Float64() + + return verifyExp(&newNumericDateFromSeconds(v).Time, cmpTime, req) + } + + return false +} + +// VerifyIssuedAt compares the exp claim against cmp (cmp >= iat). +// If req is false, it will return true, if iat is unset. +func (m MapClaims) VerifyIssuedAt(cmp int64, req bool) bool { + cmpTime := time.Unix(cmp, 0) + + v, ok := m["iat"] + if !ok { + return !req + } + + switch iat := v.(type) { + case float64: + if iat == 0 { + return verifyIat(nil, cmpTime, req) + } + + return verifyIat(&newNumericDateFromSeconds(iat).Time, cmpTime, req) + case json.Number: + v, _ := iat.Float64() + + return verifyIat(&newNumericDateFromSeconds(v).Time, cmpTime, req) + } + + return false +} + +// VerifyNotBefore compares the nbf claim against cmp (cmp >= nbf). +// If req is false, it will return true, if nbf is unset. +func (m MapClaims) VerifyNotBefore(cmp int64, req bool) bool { + cmpTime := time.Unix(cmp, 0) + + v, ok := m["nbf"] + if !ok { + return !req + } + + switch nbf := v.(type) { + case float64: + if nbf == 0 { + return verifyNbf(nil, cmpTime, req) + } + + return verifyNbf(&newNumericDateFromSeconds(nbf).Time, cmpTime, req) + case json.Number: + v, _ := nbf.Float64() + + return verifyNbf(&newNumericDateFromSeconds(v).Time, cmpTime, req) + } + + return false +} + +// VerifyIssuer compares the iss claim against cmp. +// If required is false, this method will return true if the value matches or is unset +func (m MapClaims) VerifyIssuer(cmp string, req bool) bool { + iss, _ := m["iss"].(string) + return verifyIss(iss, cmp, req) +} + +// Valid validates time based claims "exp, iat, nbf". +// There is no accounting for clock skew. +// As well, if any of the above claims are not in the token, it will still +// be considered a valid claim. +func (m MapClaims) Valid() error { + vErr := new(ValidationError) + now := TimeFunc().Unix() + + if !m.VerifyExpiresAt(now, false) { + // TODO(oxisto): this should be replaced with ErrTokenExpired + vErr.Inner = errors.New("Token is expired") + vErr.Errors |= ValidationErrorExpired + } + + if !m.VerifyIssuedAt(now, false) { + // TODO(oxisto): this should be replaced with ErrTokenUsedBeforeIssued + vErr.Inner = errors.New("Token used before issued") + vErr.Errors |= ValidationErrorIssuedAt + } + + if !m.VerifyNotBefore(now, false) { + // TODO(oxisto): this should be replaced with ErrTokenNotValidYet + vErr.Inner = errors.New("Token is not valid yet") + vErr.Errors |= ValidationErrorNotValidYet + } + + if vErr.valid() { + return nil + } + + return vErr +} diff --git a/vendor/github.com/golang-jwt/jwt/v4/none.go b/vendor/github.com/golang-jwt/jwt/v4/none.go new file mode 100644 index 0000000..f19835d --- /dev/null +++ b/vendor/github.com/golang-jwt/jwt/v4/none.go @@ -0,0 +1,52 @@ +package jwt + +// SigningMethodNone implements the none signing method. This is required by the spec +// but you probably should never use it. +var SigningMethodNone *signingMethodNone + +const UnsafeAllowNoneSignatureType unsafeNoneMagicConstant = "none signing method allowed" + +var NoneSignatureTypeDisallowedError error + +type signingMethodNone struct{} +type unsafeNoneMagicConstant string + +func init() { + SigningMethodNone = &signingMethodNone{} + NoneSignatureTypeDisallowedError = NewValidationError("'none' signature type is not allowed", ValidationErrorSignatureInvalid) + + RegisterSigningMethod(SigningMethodNone.Alg(), func() SigningMethod { + return SigningMethodNone + }) +} + +func (m *signingMethodNone) Alg() string { + return "none" +} + +// Only allow 'none' alg type if UnsafeAllowNoneSignatureType is specified as the key +func (m *signingMethodNone) Verify(signingString, signature string, key interface{}) (err error) { + // Key must be UnsafeAllowNoneSignatureType to prevent accidentally + // accepting 'none' signing method + if _, ok := key.(unsafeNoneMagicConstant); !ok { + return NoneSignatureTypeDisallowedError + } + // If signing method is none, signature must be an empty string + if signature != "" { + return NewValidationError( + "'none' signing method with non-empty signature", + ValidationErrorSignatureInvalid, + ) + } + + // Accept 'none' signing method. + return nil +} + +// Only allow 'none' signing if UnsafeAllowNoneSignatureType is specified as the key +func (m *signingMethodNone) Sign(signingString string, key interface{}) (string, error) { + if _, ok := key.(unsafeNoneMagicConstant); ok { + return "", nil + } + return "", NoneSignatureTypeDisallowedError +} diff --git a/vendor/github.com/golang-jwt/jwt/v4/parser.go b/vendor/github.com/golang-jwt/jwt/v4/parser.go new file mode 100644 index 0000000..2f61a69 --- /dev/null +++ b/vendor/github.com/golang-jwt/jwt/v4/parser.go @@ -0,0 +1,170 @@ +package jwt + +import ( + "bytes" + "encoding/json" + "fmt" + "strings" +) + +type Parser struct { + // If populated, only these methods will be considered valid. + // + // Deprecated: In future releases, this field will not be exported anymore and should be set with an option to NewParser instead. + ValidMethods []string + + // Use JSON Number format in JSON decoder. + // + // Deprecated: In future releases, this field will not be exported anymore and should be set with an option to NewParser instead. + UseJSONNumber bool + + // Skip claims validation during token parsing. + // + // Deprecated: In future releases, this field will not be exported anymore and should be set with an option to NewParser instead. + SkipClaimsValidation bool +} + +// NewParser creates a new Parser with the specified options +func NewParser(options ...ParserOption) *Parser { + p := &Parser{} + + // loop through our parsing options and apply them + for _, option := range options { + option(p) + } + + return p +} + +// Parse parses, validates, verifies the signature and returns the parsed token. +// keyFunc will receive the parsed token and should return the key for validating. +func (p *Parser) Parse(tokenString string, keyFunc Keyfunc) (*Token, error) { + return p.ParseWithClaims(tokenString, MapClaims{}, keyFunc) +} + +func (p *Parser) ParseWithClaims(tokenString string, claims Claims, keyFunc Keyfunc) (*Token, error) { + token, parts, err := p.ParseUnverified(tokenString, claims) + if err != nil { + return token, err + } + + // Verify signing method is in the required set + if p.ValidMethods != nil { + var signingMethodValid = false + var alg = token.Method.Alg() + for _, m := range p.ValidMethods { + if m == alg { + signingMethodValid = true + break + } + } + if !signingMethodValid { + // signing method is not in the listed set + return token, NewValidationError(fmt.Sprintf("signing method %v is invalid", alg), ValidationErrorSignatureInvalid) + } + } + + // Lookup key + var key interface{} + if keyFunc == nil { + // keyFunc was not provided. short circuiting validation + return token, NewValidationError("no Keyfunc was provided.", ValidationErrorUnverifiable) + } + if key, err = keyFunc(token); err != nil { + // keyFunc returned an error + if ve, ok := err.(*ValidationError); ok { + return token, ve + } + return token, &ValidationError{Inner: err, Errors: ValidationErrorUnverifiable} + } + + vErr := &ValidationError{} + + // Validate Claims + if !p.SkipClaimsValidation { + if err := token.Claims.Valid(); err != nil { + + // If the Claims Valid returned an error, check if it is a validation error, + // If it was another error type, create a ValidationError with a generic ClaimsInvalid flag set + if e, ok := err.(*ValidationError); !ok { + vErr = &ValidationError{Inner: err, Errors: ValidationErrorClaimsInvalid} + } else { + vErr = e + } + } + } + + // Perform validation + token.Signature = parts[2] + if err = token.Method.Verify(strings.Join(parts[0:2], "."), token.Signature, key); err != nil { + vErr.Inner = err + vErr.Errors |= ValidationErrorSignatureInvalid + } + + if vErr.valid() { + token.Valid = true + return token, nil + } + + return token, vErr +} + +// ParseUnverified parses the token but doesn't validate the signature. +// +// WARNING: Don't use this method unless you know what you're doing. +// +// It's only ever useful in cases where you know the signature is valid (because it has +// been checked previously in the stack) and you want to extract values from it. +func (p *Parser) ParseUnverified(tokenString string, claims Claims) (token *Token, parts []string, err error) { + parts = strings.Split(tokenString, ".") + if len(parts) != 3 { + return nil, parts, NewValidationError("token contains an invalid number of segments", ValidationErrorMalformed) + } + + token = &Token{Raw: tokenString} + + // parse Header + var headerBytes []byte + if headerBytes, err = DecodeSegment(parts[0]); err != nil { + if strings.HasPrefix(strings.ToLower(tokenString), "bearer ") { + return token, parts, NewValidationError("tokenstring should not contain 'bearer '", ValidationErrorMalformed) + } + return token, parts, &ValidationError{Inner: err, Errors: ValidationErrorMalformed} + } + if err = json.Unmarshal(headerBytes, &token.Header); err != nil { + return token, parts, &ValidationError{Inner: err, Errors: ValidationErrorMalformed} + } + + // parse Claims + var claimBytes []byte + token.Claims = claims + + if claimBytes, err = DecodeSegment(parts[1]); err != nil { + return token, parts, &ValidationError{Inner: err, Errors: ValidationErrorMalformed} + } + dec := json.NewDecoder(bytes.NewBuffer(claimBytes)) + if p.UseJSONNumber { + dec.UseNumber() + } + // JSON Decode. Special case for map type to avoid weird pointer behavior + if c, ok := token.Claims.(MapClaims); ok { + err = dec.Decode(&c) + } else { + err = dec.Decode(&claims) + } + // Handle decode error + if err != nil { + return token, parts, &ValidationError{Inner: err, Errors: ValidationErrorMalformed} + } + + // Lookup signature method + if method, ok := token.Header["alg"].(string); ok { + if token.Method = GetSigningMethod(method); token.Method == nil { + return token, parts, NewValidationError("signing method (alg) is unavailable.", ValidationErrorUnverifiable) + } + } else { + return token, parts, NewValidationError("signing method (alg) is unspecified.", ValidationErrorUnverifiable) + } + + return token, parts, nil +} diff --git a/vendor/github.com/golang-jwt/jwt/v4/parser_option.go b/vendor/github.com/golang-jwt/jwt/v4/parser_option.go new file mode 100644 index 0000000..6ea6f95 --- /dev/null +++ b/vendor/github.com/golang-jwt/jwt/v4/parser_option.go @@ -0,0 +1,29 @@ +package jwt + +// ParserOption is used to implement functional-style options that modify the behavior of the parser. To add +// new options, just create a function (ideally beginning with With or Without) that returns an anonymous function that +// takes a *Parser type as input and manipulates its configuration accordingly. +type ParserOption func(*Parser) + +// WithValidMethods is an option to supply algorithm methods that the parser will check. Only those methods will be considered valid. +// It is heavily encouraged to use this option in order to prevent attacks such as https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/. +func WithValidMethods(methods []string) ParserOption { + return func(p *Parser) { + p.ValidMethods = methods + } +} + +// WithJSONNumber is an option to configure the underlying JSON parser with UseNumber +func WithJSONNumber() ParserOption { + return func(p *Parser) { + p.UseJSONNumber = true + } +} + +// WithoutClaimsValidation is an option to disable claims validation. This option should only be used if you exactly know +// what you are doing. +func WithoutClaimsValidation() ParserOption { + return func(p *Parser) { + p.SkipClaimsValidation = true + } +} diff --git a/vendor/github.com/golang-jwt/jwt/v4/rsa.go b/vendor/github.com/golang-jwt/jwt/v4/rsa.go new file mode 100644 index 0000000..b910b19 --- /dev/null +++ b/vendor/github.com/golang-jwt/jwt/v4/rsa.go @@ -0,0 +1,101 @@ +package jwt + +import ( + "crypto" + "crypto/rand" + "crypto/rsa" +) + +// SigningMethodRSA implements the RSA family of signing methods. +// Expects *rsa.PrivateKey for signing and *rsa.PublicKey for validation +type SigningMethodRSA struct { + Name string + Hash crypto.Hash +} + +// Specific instances for RS256 and company +var ( + SigningMethodRS256 *SigningMethodRSA + SigningMethodRS384 *SigningMethodRSA + SigningMethodRS512 *SigningMethodRSA +) + +func init() { + // RS256 + SigningMethodRS256 = &SigningMethodRSA{"RS256", crypto.SHA256} + RegisterSigningMethod(SigningMethodRS256.Alg(), func() SigningMethod { + return SigningMethodRS256 + }) + + // RS384 + SigningMethodRS384 = &SigningMethodRSA{"RS384", crypto.SHA384} + RegisterSigningMethod(SigningMethodRS384.Alg(), func() SigningMethod { + return SigningMethodRS384 + }) + + // RS512 + SigningMethodRS512 = &SigningMethodRSA{"RS512", crypto.SHA512} + RegisterSigningMethod(SigningMethodRS512.Alg(), func() SigningMethod { + return SigningMethodRS512 + }) +} + +func (m *SigningMethodRSA) Alg() string { + return m.Name +} + +// Verify implements token verification for the SigningMethod +// For this signing method, must be an *rsa.PublicKey structure. +func (m *SigningMethodRSA) Verify(signingString, signature string, key interface{}) error { + var err error + + // Decode the signature + var sig []byte + if sig, err = DecodeSegment(signature); err != nil { + return err + } + + var rsaKey *rsa.PublicKey + var ok bool + + if rsaKey, ok = key.(*rsa.PublicKey); !ok { + return ErrInvalidKeyType + } + + // Create hasher + if !m.Hash.Available() { + return ErrHashUnavailable + } + hasher := m.Hash.New() + hasher.Write([]byte(signingString)) + + // Verify the signature + return rsa.VerifyPKCS1v15(rsaKey, m.Hash, hasher.Sum(nil), sig) +} + +// Sign implements token signing for the SigningMethod +// For this signing method, must be an *rsa.PrivateKey structure. +func (m *SigningMethodRSA) Sign(signingString string, key interface{}) (string, error) { + var rsaKey *rsa.PrivateKey + var ok bool + + // Validate type of key + if rsaKey, ok = key.(*rsa.PrivateKey); !ok { + return "", ErrInvalidKey + } + + // Create the hasher + if !m.Hash.Available() { + return "", ErrHashUnavailable + } + + hasher := m.Hash.New() + hasher.Write([]byte(signingString)) + + // Sign the string and return the encoded bytes + if sigBytes, err := rsa.SignPKCS1v15(rand.Reader, rsaKey, m.Hash, hasher.Sum(nil)); err == nil { + return EncodeSegment(sigBytes), nil + } else { + return "", err + } +} diff --git a/vendor/github.com/golang-jwt/jwt/v4/rsa_pss.go b/vendor/github.com/golang-jwt/jwt/v4/rsa_pss.go new file mode 100644 index 0000000..5a8502f --- /dev/null +++ b/vendor/github.com/golang-jwt/jwt/v4/rsa_pss.go @@ -0,0 +1,142 @@ +// +build go1.4 + +package jwt + +import ( + "crypto" + "crypto/rand" + "crypto/rsa" +) + +// SigningMethodRSAPSS implements the RSAPSS family of signing methods signing methods +type SigningMethodRSAPSS struct { + *SigningMethodRSA + Options *rsa.PSSOptions + // VerifyOptions is optional. If set overrides Options for rsa.VerifyPPS. + // Used to accept tokens signed with rsa.PSSSaltLengthAuto, what doesn't follow + // https://tools.ietf.org/html/rfc7518#section-3.5 but was used previously. + // See https://github.com/dgrijalva/jwt-go/issues/285#issuecomment-437451244 for details. + VerifyOptions *rsa.PSSOptions +} + +// Specific instances for RS/PS and company. +var ( + SigningMethodPS256 *SigningMethodRSAPSS + SigningMethodPS384 *SigningMethodRSAPSS + SigningMethodPS512 *SigningMethodRSAPSS +) + +func init() { + // PS256 + SigningMethodPS256 = &SigningMethodRSAPSS{ + SigningMethodRSA: &SigningMethodRSA{ + Name: "PS256", + Hash: crypto.SHA256, + }, + Options: &rsa.PSSOptions{ + SaltLength: rsa.PSSSaltLengthEqualsHash, + }, + VerifyOptions: &rsa.PSSOptions{ + SaltLength: rsa.PSSSaltLengthAuto, + }, + } + RegisterSigningMethod(SigningMethodPS256.Alg(), func() SigningMethod { + return SigningMethodPS256 + }) + + // PS384 + SigningMethodPS384 = &SigningMethodRSAPSS{ + SigningMethodRSA: &SigningMethodRSA{ + Name: "PS384", + Hash: crypto.SHA384, + }, + Options: &rsa.PSSOptions{ + SaltLength: rsa.PSSSaltLengthEqualsHash, + }, + VerifyOptions: &rsa.PSSOptions{ + SaltLength: rsa.PSSSaltLengthAuto, + }, + } + RegisterSigningMethod(SigningMethodPS384.Alg(), func() SigningMethod { + return SigningMethodPS384 + }) + + // PS512 + SigningMethodPS512 = &SigningMethodRSAPSS{ + SigningMethodRSA: &SigningMethodRSA{ + Name: "PS512", + Hash: crypto.SHA512, + }, + Options: &rsa.PSSOptions{ + SaltLength: rsa.PSSSaltLengthEqualsHash, + }, + VerifyOptions: &rsa.PSSOptions{ + SaltLength: rsa.PSSSaltLengthAuto, + }, + } + RegisterSigningMethod(SigningMethodPS512.Alg(), func() SigningMethod { + return SigningMethodPS512 + }) +} + +// Verify implements token verification for the SigningMethod. +// For this verify method, key must be an rsa.PublicKey struct +func (m *SigningMethodRSAPSS) Verify(signingString, signature string, key interface{}) error { + var err error + + // Decode the signature + var sig []byte + if sig, err = DecodeSegment(signature); err != nil { + return err + } + + var rsaKey *rsa.PublicKey + switch k := key.(type) { + case *rsa.PublicKey: + rsaKey = k + default: + return ErrInvalidKey + } + + // Create hasher + if !m.Hash.Available() { + return ErrHashUnavailable + } + hasher := m.Hash.New() + hasher.Write([]byte(signingString)) + + opts := m.Options + if m.VerifyOptions != nil { + opts = m.VerifyOptions + } + + return rsa.VerifyPSS(rsaKey, m.Hash, hasher.Sum(nil), sig, opts) +} + +// Sign implements token signing for the SigningMethod. +// For this signing method, key must be an rsa.PrivateKey struct +func (m *SigningMethodRSAPSS) Sign(signingString string, key interface{}) (string, error) { + var rsaKey *rsa.PrivateKey + + switch k := key.(type) { + case *rsa.PrivateKey: + rsaKey = k + default: + return "", ErrInvalidKeyType + } + + // Create the hasher + if !m.Hash.Available() { + return "", ErrHashUnavailable + } + + hasher := m.Hash.New() + hasher.Write([]byte(signingString)) + + // Sign the string and return the encoded bytes + if sigBytes, err := rsa.SignPSS(rand.Reader, rsaKey, m.Hash, hasher.Sum(nil), m.Options); err == nil { + return EncodeSegment(sigBytes), nil + } else { + return "", err + } +} diff --git a/vendor/github.com/golang-jwt/jwt/v4/rsa_utils.go b/vendor/github.com/golang-jwt/jwt/v4/rsa_utils.go new file mode 100644 index 0000000..1966c45 --- /dev/null +++ b/vendor/github.com/golang-jwt/jwt/v4/rsa_utils.go @@ -0,0 +1,105 @@ +package jwt + +import ( + "crypto/rsa" + "crypto/x509" + "encoding/pem" + "errors" +) + +var ( + ErrKeyMustBePEMEncoded = errors.New("invalid key: Key must be a PEM encoded PKCS1 or PKCS8 key") + ErrNotRSAPrivateKey = errors.New("key is not a valid RSA private key") + ErrNotRSAPublicKey = errors.New("key is not a valid RSA public key") +) + +// ParseRSAPrivateKeyFromPEM parses a PEM encoded PKCS1 or PKCS8 private key +func ParseRSAPrivateKeyFromPEM(key []byte) (*rsa.PrivateKey, error) { + var err error + + // Parse PEM block + var block *pem.Block + if block, _ = pem.Decode(key); block == nil { + return nil, ErrKeyMustBePEMEncoded + } + + var parsedKey interface{} + if parsedKey, err = x509.ParsePKCS1PrivateKey(block.Bytes); err != nil { + if parsedKey, err = x509.ParsePKCS8PrivateKey(block.Bytes); err != nil { + return nil, err + } + } + + var pkey *rsa.PrivateKey + var ok bool + if pkey, ok = parsedKey.(*rsa.PrivateKey); !ok { + return nil, ErrNotRSAPrivateKey + } + + return pkey, nil +} + +// ParseRSAPrivateKeyFromPEMWithPassword parses a PEM encoded PKCS1 or PKCS8 private key protected with password +// +// Deprecated: This function is deprecated and should not be used anymore. It uses the deprecated x509.DecryptPEMBlock +// function, which was deprecated since RFC 1423 is regarded insecure by design. Unfortunately, there is no alternative +// in the Go standard library for now. See https://github.com/golang/go/issues/8860. +func ParseRSAPrivateKeyFromPEMWithPassword(key []byte, password string) (*rsa.PrivateKey, error) { + var err error + + // Parse PEM block + var block *pem.Block + if block, _ = pem.Decode(key); block == nil { + return nil, ErrKeyMustBePEMEncoded + } + + var parsedKey interface{} + + var blockDecrypted []byte + if blockDecrypted, err = x509.DecryptPEMBlock(block, []byte(password)); err != nil { + return nil, err + } + + if parsedKey, err = x509.ParsePKCS1PrivateKey(blockDecrypted); err != nil { + if parsedKey, err = x509.ParsePKCS8PrivateKey(blockDecrypted); err != nil { + return nil, err + } + } + + var pkey *rsa.PrivateKey + var ok bool + if pkey, ok = parsedKey.(*rsa.PrivateKey); !ok { + return nil, ErrNotRSAPrivateKey + } + + return pkey, nil +} + +// ParseRSAPublicKeyFromPEM parses a PEM encoded PKCS1 or PKCS8 public key +func ParseRSAPublicKeyFromPEM(key []byte) (*rsa.PublicKey, error) { + var err error + + // Parse PEM block + var block *pem.Block + if block, _ = pem.Decode(key); block == nil { + return nil, ErrKeyMustBePEMEncoded + } + + // Parse the key + var parsedKey interface{} + if parsedKey, err = x509.ParsePKIXPublicKey(block.Bytes); err != nil { + if cert, err := x509.ParseCertificate(block.Bytes); err == nil { + parsedKey = cert.PublicKey + } else { + return nil, err + } + } + + var pkey *rsa.PublicKey + var ok bool + if pkey, ok = parsedKey.(*rsa.PublicKey); !ok { + return nil, ErrNotRSAPublicKey + } + + return pkey, nil +} diff --git a/vendor/github.com/golang-jwt/jwt/v4/signing_method.go b/vendor/github.com/golang-jwt/jwt/v4/signing_method.go new file mode 100644 index 0000000..241ae9c --- /dev/null +++ b/vendor/github.com/golang-jwt/jwt/v4/signing_method.go @@ -0,0 +1,46 @@ +package jwt + +import ( + "sync" +) + +var signingMethods = map[string]func() SigningMethod{} +var signingMethodLock = new(sync.RWMutex) + +// SigningMethod can be used add new methods for signing or verifying tokens. +type SigningMethod interface { + Verify(signingString, signature string, key interface{}) error // Returns nil if signature is valid + Sign(signingString string, key interface{}) (string, error) // Returns encoded signature or error + Alg() string // returns the alg identifier for this method (example: 'HS256') +} + +// RegisterSigningMethod registers the "alg" name and a factory function for signing method. +// This is typically done during init() in the method's implementation +func RegisterSigningMethod(alg string, f func() SigningMethod) { + signingMethodLock.Lock() + defer signingMethodLock.Unlock() + + signingMethods[alg] = f +} + +// GetSigningMethod retrieves a signing method from an "alg" string +func GetSigningMethod(alg string) (method SigningMethod) { + signingMethodLock.RLock() + defer signingMethodLock.RUnlock() + + if methodF, ok := signingMethods[alg]; ok { + method = methodF() + } + return +} + +// GetAlgorithms returns a list of registered "alg" names +func GetAlgorithms() (algs []string) { + signingMethodLock.RLock() + defer signingMethodLock.RUnlock() + + for alg := range signingMethods { + algs = append(algs, alg) + } + return +} diff --git a/vendor/github.com/golang-jwt/jwt/v4/staticcheck.conf b/vendor/github.com/golang-jwt/jwt/v4/staticcheck.conf new file mode 100644 index 0000000..53745d5 --- /dev/null +++ b/vendor/github.com/golang-jwt/jwt/v4/staticcheck.conf @@ -0,0 +1 @@ +checks = ["all", "-ST1000", "-ST1003", "-ST1016", "-ST1023"] diff --git a/vendor/github.com/golang-jwt/jwt/v4/token.go b/vendor/github.com/golang-jwt/jwt/v4/token.go new file mode 100644 index 0000000..09b4cde --- /dev/null +++ b/vendor/github.com/golang-jwt/jwt/v4/token.go @@ -0,0 +1,128 @@ +package jwt + +import ( + "encoding/base64" + "encoding/json" + "strings" + "time" +) + + +// DecodePaddingAllowed will switch the codec used for decoding JWTs respectively. Note that the JWS RFC7515 +// states that the tokens will utilize a Base64url encoding with no padding. Unfortunately, some implementations +// of JWT are producing non-standard tokens, and thus require support for decoding. Note that this is a global +// variable, and updating it will change the behavior on a package level, and is also NOT go-routine safe. +// To use the non-recommended decoding, set this boolean to `true` prior to using this package. +var DecodePaddingAllowed bool + +// TimeFunc provides the current time when parsing token to validate "exp" claim (expiration time). +// You can override it to use another time value. This is useful for testing or if your +// server uses a different time zone than your tokens. +var TimeFunc = time.Now + +// Keyfunc will be used by the Parse methods as a callback function to supply +// the key for verification. The function receives the parsed, +// but unverified Token. This allows you to use properties in the +// Header of the token (such as `kid`) to identify which key to use. +type Keyfunc func(*Token) (interface{}, error) + +// Token represents a JWT Token. Different fields will be used depending on whether you're +// creating or parsing/verifying a token. +type Token struct { + Raw string // The raw token. Populated when you Parse a token + Method SigningMethod // The signing method used or to be used + Header map[string]interface{} // The first segment of the token + Claims Claims // The second segment of the token + Signature string // The third segment of the token. Populated when you Parse a token + Valid bool // Is the token valid? Populated when you Parse/Verify a token +} + +// New creates a new Token with the specified signing method and an empty map of claims. +func New(method SigningMethod) *Token { + return NewWithClaims(method, MapClaims{}) +} + +// NewWithClaims creates a new Token with the specified signing method and claims. +func NewWithClaims(method SigningMethod, claims Claims) *Token { + return &Token{ + Header: map[string]interface{}{ + "typ": "JWT", + "alg": method.Alg(), + }, + Claims: claims, + Method: method, + } +} + +// SignedString creates and returns a complete, signed JWT. +// The token is signed using the SigningMethod specified in the token. +func (t *Token) SignedString(key interface{}) (string, error) { + var sig, sstr string + var err error + if sstr, err = t.SigningString(); err != nil { + return "", err + } + if sig, err = t.Method.Sign(sstr, key); err != nil { + return "", err + } + return strings.Join([]string{sstr, sig}, "."), nil +} + +// SigningString generates the signing string. This is the +// most expensive part of the whole deal. Unless you +// need this for something special, just go straight for +// the SignedString. +func (t *Token) SigningString() (string, error) { + var err error + var jsonValue []byte + + if jsonValue, err = json.Marshal(t.Header); err != nil { + return "", err + } + header := EncodeSegment(jsonValue) + + if jsonValue, err = json.Marshal(t.Claims); err != nil { + return "", err + } + claim := EncodeSegment(jsonValue) + + return strings.Join([]string{header, claim}, "."), nil +} + +// Parse parses, validates, verifies the signature and returns the parsed token. +// keyFunc will receive the parsed token and should return the cryptographic key +// for verifying the signature. +// The caller is strongly encouraged to set the WithValidMethods option to +// validate the 'alg' claim in the token matches the expected algorithm. +// For more details about the importance of validating the 'alg' claim, +// see https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/ +func Parse(tokenString string, keyFunc Keyfunc, options ...ParserOption) (*Token, error) { + return NewParser(options...).Parse(tokenString, keyFunc) +} + +func ParseWithClaims(tokenString string, claims Claims, keyFunc Keyfunc, options ...ParserOption) (*Token, error) { + return NewParser(options...).ParseWithClaims(tokenString, claims, keyFunc) +} + +// EncodeSegment encodes a JWT specific base64url encoding with padding stripped +// +// Deprecated: In a future release, we will demote this function to a non-exported function, since it +// should only be used internally +func EncodeSegment(seg []byte) string { + return base64.RawURLEncoding.EncodeToString(seg) +} + +// DecodeSegment decodes a JWT specific base64url encoding with padding stripped +// +// Deprecated: In a future release, we will demote this function to a non-exported function, since it +// should only be used internally +func DecodeSegment(seg string) ([]byte, error) { + if DecodePaddingAllowed { + if l := len(seg) % 4; l > 0 { + seg += strings.Repeat("=", 4-l) + } + return base64.URLEncoding.DecodeString(seg) + } + + return base64.RawURLEncoding.DecodeString(seg) +} diff --git a/vendor/github.com/golang-jwt/jwt/v4/types.go b/vendor/github.com/golang-jwt/jwt/v4/types.go new file mode 100644 index 0000000..2c647fd --- /dev/null +++ b/vendor/github.com/golang-jwt/jwt/v4/types.go @@ -0,0 +1,131 @@ +package jwt + +import ( + "encoding/json" + "fmt" + "math" + "reflect" + "strconv" + "time" +) + +// TimePrecision sets the precision of times and dates within this library. +// This has an influence on the precision of times when comparing expiry or +// other related time fields. Furthermore, it is also the precision of times +// when serializing. +// +// For backwards compatibility the default precision is set to seconds, so that +// no fractional timestamps are generated. +var TimePrecision = time.Second + +// MarshalSingleStringAsArray modifies the behaviour of the ClaimStrings type, especially +// its MarshalJSON function. +// +// If it is set to true (the default), it will always serialize the type as an +// array of strings, even if it just contains one element, defaulting to the behaviour +// of the underlying []string. If it is set to false, it will serialize to a single +// string, if it contains one element. Otherwise, it will serialize to an array of strings. +var MarshalSingleStringAsArray = true + +// NumericDate represents a JSON numeric date value, as referenced at +// https://datatracker.ietf.org/doc/html/rfc7519#section-2. +type NumericDate struct { + time.Time +} + +// NewNumericDate constructs a new *NumericDate from a standard library time.Time struct. +// It will truncate the timestamp according to the precision specified in TimePrecision. +func NewNumericDate(t time.Time) *NumericDate { + return &NumericDate{t.Truncate(TimePrecision)} +} + +// newNumericDateFromSeconds creates a new *NumericDate out of a float64 representing a +// UNIX epoch with the float fraction representing non-integer seconds. +func newNumericDateFromSeconds(f float64) *NumericDate { + round, frac := math.Modf(f) + return NewNumericDate(time.Unix(int64(round), int64(frac*1e9))) +} + +// MarshalJSON is an implementation of the json.RawMessage interface and serializes the UNIX epoch +// represented in NumericDate to a byte array, using the precision specified in TimePrecision. +func (date NumericDate) MarshalJSON() (b []byte, err error) { + var prec int + if TimePrecision < time.Second { + prec = int(math.Log10(float64(time.Second) / float64(TimePrecision))) + } + f := float64(date.Truncate(TimePrecision).UnixNano()) / float64(time.Second) + + return []byte(strconv.FormatFloat(f, 'f', prec, 64)), nil +} + +// UnmarshalJSON is an implementation of the json.RawMessage interface and deserializses a +// NumericDate from a JSON representation, i.e. a json.Number. This number represents an UNIX epoch +// with either integer or non-integer seconds. +func (date *NumericDate) UnmarshalJSON(b []byte) (err error) { + var ( + number json.Number + f float64 + ) + + if err = json.Unmarshal(b, &number); err != nil { + return fmt.Errorf("could not parse NumericData: %w", err) + } + + if f, err = number.Float64(); err != nil { + return fmt.Errorf("could not convert json number value to float: %w", err) + } + + n := newNumericDateFromSeconds(f) + *date = *n + + return nil +} + +// ClaimStrings is basically just a slice of strings, but it can be either serialized from a string array or just a string. +// This type is necessary, since the "aud" claim can either be a single string or an array. +type ClaimStrings []string + +func (s *ClaimStrings) UnmarshalJSON(data []byte) (err error) { + var value interface{} + + if err = json.Unmarshal(data, &value); err != nil { + return err + } + + var aud []string + + switch v := value.(type) { + case string: + aud = append(aud, v) + case []string: + aud = ClaimStrings(v) + case []interface{}: + for _, vv := range v { + vs, ok := vv.(string) + if !ok { + return &json.UnsupportedTypeError{Type: reflect.TypeOf(vv)} + } + aud = append(aud, vs) + } + case nil: + return nil + default: + return &json.UnsupportedTypeError{Type: reflect.TypeOf(v)} + } + + *s = aud + + return +} + +func (s ClaimStrings) MarshalJSON() (b []byte, err error) { + // This handles a special case in the JWT RFC. If the string array, e.g. used by the "aud" field, + // only contains one element, it MAY be serialized as a single string. This may or may not be + // desired based on the ecosystem of other JWT library used, so we make it configurable by the + // variable MarshalSingleStringAsArray. + if len(s) == 1 && !MarshalSingleStringAsArray { + return json.Marshal(s[0]) + } + + return json.Marshal([]string(s)) +} diff --git a/vendor/github.com/google/go-querystring/LICENSE b/vendor/github.com/google/go-querystring/LICENSE new file mode 100644 index 0000000..ae121a1 --- /dev/null +++ b/vendor/github.com/google/go-querystring/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2013 Google. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/google/go-querystring/query/encode.go b/vendor/github.com/google/go-querystring/query/encode.go new file mode 100644 index 0000000..91198f8 --- /dev/null +++ b/vendor/github.com/google/go-querystring/query/encode.go @@ -0,0 +1,357 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package query implements encoding of structs into URL query parameters. +// +// As a simple example: +// +// type Options struct { +// Query string `url:"q"` +// ShowAll bool `url:"all"` +// Page int `url:"page"` +// } +// +// opt := Options{ "foo", true, 2 } +// v, _ := query.Values(opt) +// fmt.Print(v.Encode()) // will output: "q=foo&all=true&page=2" +// +// The exact mapping between Go values and url.Values is described in the +// documentation for the Values() function. +package query + +import ( + "bytes" + "fmt" + "net/url" + "reflect" + "strconv" + "strings" + "time" +) + +var timeType = reflect.TypeOf(time.Time{}) + +var encoderType = reflect.TypeOf(new(Encoder)).Elem() + +// Encoder is an interface implemented by any type that wishes to encode +// itself into URL values in a non-standard way. +type Encoder interface { + EncodeValues(key string, v *url.Values) error +} + +// Values returns the url.Values encoding of v. +// +// Values expects to be passed a struct, and traverses it recursively using the +// following encoding rules. +// +// Each exported struct field is encoded as a URL parameter unless +// +// - the field's tag is "-", or +// - the field is empty and its tag specifies the "omitempty" option +// +// The empty values are false, 0, any nil pointer or interface value, any array +// slice, map, or string of length zero, and any type (such as time.Time) that +// returns true for IsZero(). +// +// The URL parameter name defaults to the struct field name but can be +// specified in the struct field's tag value. The "url" key in the struct +// field's tag value is the key name, followed by an optional comma and +// options. For example: +// +// // Field is ignored by this package. +// Field int `url:"-"` +// +// // Field appears as URL parameter "myName". +// Field int `url:"myName"` +// +// // Field appears as URL parameter "myName" and the field is omitted if +// // its value is empty +// Field int `url:"myName,omitempty"` +// +// // Field appears as URL parameter "Field" (the default), but the field +// // is skipped if empty. Note the leading comma. +// Field int `url:",omitempty"` +// +// For encoding individual field values, the following type-dependent rules +// apply: +// +// Boolean values default to encoding as the strings "true" or "false". +// Including the "int" option signals that the field should be encoded as the +// strings "1" or "0". +// +// time.Time values default to encoding as RFC3339 timestamps. Including the +// "unix" option signals that the field should be encoded as a Unix time (see +// time.Unix()). The "unixmilli" and "unixnano" options will encode the number +// of milliseconds and nanoseconds, respectively, since January 1, 1970 (see +// time.UnixNano()). Including the "layout" struct tag (separate from the +// "url" tag) will use the value of the "layout" tag as a layout passed to +// time.Format. For example: +// +// // Encode a time.Time as YYYY-MM-DD +// Field time.Time `layout:"2006-01-02"` +// +// Slice and Array values default to encoding as multiple URL values of the +// same name. Including the "comma" option signals that the field should be +// encoded as a single comma-delimited value. Including the "space" option +// similarly encodes the value as a single space-delimited string. Including +// the "semicolon" option will encode the value as a semicolon-delimited string. +// Including the "brackets" option signals that the multiple URL values should +// have "[]" appended to the value name. "numbered" will append a number to +// the end of each incidence of the value name, example: +// name0=value0&name1=value1, etc. Including the "del" struct tag (separate +// from the "url" tag) will use the value of the "del" tag as the delimiter. +// For example: +// +// // Encode a slice of bools as ints ("1" for true, "0" for false), +// // separated by exclamation points "!". +// Field []bool `url:",int" del:"!"` +// +// Anonymous struct fields are usually encoded as if their inner exported +// fields were fields in the outer struct, subject to the standard Go +// visibility rules. An anonymous struct field with a name given in its URL +// tag is treated as having that name, rather than being anonymous. +// +// Non-nil pointer values are encoded as the value pointed to. +// +// Nested structs are encoded including parent fields in value names for +// scoping. e.g: +// +// "user[name]=acme&user[addr][postcode]=1234&user[addr][city]=SFO" +// +// All other values are encoded using their default string representation. +// +// Multiple fields that encode to the same URL parameter name will be included +// as multiple URL values of the same name. +func Values(v interface{}) (url.Values, error) { + values := make(url.Values) + val := reflect.ValueOf(v) + for val.Kind() == reflect.Ptr { + if val.IsNil() { + return values, nil + } + val = val.Elem() + } + + if v == nil { + return values, nil + } + + if val.Kind() != reflect.Struct { + return nil, fmt.Errorf("query: Values() expects struct input. Got %v", val.Kind()) + } + + err := reflectValue(values, val, "") + return values, err +} + +// reflectValue populates the values parameter from the struct fields in val. +// Embedded structs are followed recursively (using the rules defined in the +// Values function documentation) breadth-first. +func reflectValue(values url.Values, val reflect.Value, scope string) error { + var embedded []reflect.Value + + typ := val.Type() + for i := 0; i < typ.NumField(); i++ { + sf := typ.Field(i) + if sf.PkgPath != "" && !sf.Anonymous { // unexported + continue + } + + sv := val.Field(i) + tag := sf.Tag.Get("url") + if tag == "-" { + continue + } + name, opts := parseTag(tag) + + if name == "" { + if sf.Anonymous { + v := reflect.Indirect(sv) + if v.IsValid() && v.Kind() == reflect.Struct { + // save embedded struct for later processing + embedded = append(embedded, v) + continue + } + } + + name = sf.Name + } + + if scope != "" { + name = scope + "[" + name + "]" + } + + if opts.Contains("omitempty") && isEmptyValue(sv) { + continue + } + + if sv.Type().Implements(encoderType) { + // if sv is a nil pointer and the custom encoder is defined on a non-pointer + // method receiver, set sv to the zero value of the underlying type + if !reflect.Indirect(sv).IsValid() && sv.Type().Elem().Implements(encoderType) { + sv = reflect.New(sv.Type().Elem()) + } + + m := sv.Interface().(Encoder) + if err := m.EncodeValues(name, &values); err != nil { + return err + } + continue + } + + // recursively dereference pointers. break on nil pointers + for sv.Kind() == reflect.Ptr { + if sv.IsNil() { + break + } + sv = sv.Elem() + } + + if sv.Kind() == reflect.Slice || sv.Kind() == reflect.Array { + var del string + if opts.Contains("comma") { + del = "," + } else if opts.Contains("space") { + del = " " + } else if opts.Contains("semicolon") { + del = ";" + } else if opts.Contains("brackets") { + name = name + "[]" + } else { + del = sf.Tag.Get("del") + } + + if del != "" { + s := new(bytes.Buffer) + first := true + for i := 0; i < sv.Len(); i++ { + if first { + first = false + } else { + s.WriteString(del) + } + s.WriteString(valueString(sv.Index(i), opts, sf)) + } + values.Add(name, s.String()) + } else { + for i := 0; i < sv.Len(); i++ { + k := name + if opts.Contains("numbered") { + k = fmt.Sprintf("%s%d", name, i) + } + values.Add(k, valueString(sv.Index(i), opts, sf)) + } + } + continue + } + + if sv.Type() == timeType { + values.Add(name, valueString(sv, opts, sf)) + continue + } + + if sv.Kind() == reflect.Struct { + if err := reflectValue(values, sv, name); err != nil { + return err + } + continue + } + + values.Add(name, valueString(sv, opts, sf)) + } + + for _, f := range embedded { + if err := reflectValue(values, f, scope); err != nil { + return err + } + } + + return nil +} + +// valueString returns the string representation of a value. +func valueString(v reflect.Value, opts tagOptions, sf reflect.StructField) string { + for v.Kind() == reflect.Ptr { + if v.IsNil() { + return "" + } + v = v.Elem() + } + + if v.Kind() == reflect.Bool && opts.Contains("int") { + if v.Bool() { + return "1" + } + return "0" + } + + if v.Type() == timeType { + t := v.Interface().(time.Time) + if opts.Contains("unix") { + return strconv.FormatInt(t.Unix(), 10) + } + if opts.Contains("unixmilli") { + return strconv.FormatInt((t.UnixNano() / 1e6), 10) + } + if opts.Contains("unixnano") { + return strconv.FormatInt(t.UnixNano(), 10) + } + if layout := sf.Tag.Get("layout"); layout != "" { + return t.Format(layout) + } + return t.Format(time.RFC3339) + } + + return fmt.Sprint(v.Interface()) +} + +// isEmptyValue checks if a value should be considered empty for the purposes +// of omitting fields with the "omitempty" option. +func isEmptyValue(v reflect.Value) bool { + switch v.Kind() { + case reflect.Array, reflect.Map, reflect.Slice, reflect.String: + return v.Len() == 0 + case reflect.Bool: + return !v.Bool() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return v.Int() == 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return v.Uint() == 0 + case reflect.Float32, reflect.Float64: + return v.Float() == 0 + case reflect.Interface, reflect.Ptr: + return v.IsNil() + } + + type zeroable interface { + IsZero() bool + } + + if z, ok := v.Interface().(zeroable); ok { + return z.IsZero() + } + + return false +} + +// tagOptions is the string following a comma in a struct field's "url" tag, or +// the empty string. It does not include the leading comma. +type tagOptions []string + +// parseTag splits a struct field's url tag into its name and comma-separated +// options. +func parseTag(tag string) (string, tagOptions) { + s := strings.Split(tag, ",") + return s[0], s[1:] +} + +// Contains checks whether the tagOptions contains the specified option. +func (o tagOptions) Contains(option string) bool { + for _, s := range o { + if s == option { + return true + } + } + return false +} diff --git a/vendor/github.com/hashicorp/go-cleanhttp/LICENSE b/vendor/github.com/hashicorp/go-cleanhttp/LICENSE new file mode 100644 index 0000000..e87a115 --- /dev/null +++ b/vendor/github.com/hashicorp/go-cleanhttp/LICENSE @@ -0,0 +1,363 @@ +Mozilla Public License, version 2.0 + +1. Definitions + +1.1. "Contributor" + + means each individual or legal entity that creates, contributes to the + creation of, or owns Covered Software. + +1.2. "Contributor Version" + + means the combination of the Contributions of others (if any) used by a + Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + + means Source Code Form to which the initial Contributor has attached the + notice in Exhibit A, the Executable Form of such Source Code Form, and + Modifications of such Source Code Form, in each case including portions + thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + a. that the initial Contributor has attached the notice described in + Exhibit B to the Covered Software; or + + b. that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the terms of + a Secondary License. + +1.6. "Executable Form" + + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + + means a work that combines Covered Software with other material, in a + separate file or files, that is not Covered Software. + +1.8. "License" + + means this document. + +1.9. "Licensable" + + means having the right to grant, to the maximum extent possible, whether + at the time of the initial grant or subsequently, any and all of the + rights conveyed by this License. + +1.10. "Modifications" + + means any of the following: + + a. any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered Software; or + + b. any new file in Source Code Form that contains any Covered Software. + +1.11. "Patent Claims" of a Contributor + + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the License, + by the making, using, selling, offering for sale, having made, import, + or transfer of either its Contributions or its Contributor Version. + +1.12. "Secondary License" + + means either the GNU General Public License, Version 2.0, the GNU Lesser + General Public License, Version 2.1, the GNU Affero General Public + License, Version 3.0, or any later versions of those licenses. + +1.13. "Source Code Form" + + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that controls, is + controlled by, or is under common control with You. For purposes of this + definition, "control" means (a) the power, direct or indirect, to cause + the direction or management of such entity, whether by contract or + otherwise, or (b) ownership of more than fifty percent (50%) of the + outstanding shares or beneficial ownership of such entity. + + +2. License Grants and Conditions + +2.1. Grants + + Each Contributor hereby grants You a world-wide, royalty-free, + non-exclusive license: + + a. under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + + b. under Patent Claims of such Contributor to make, use, sell, offer for + sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + + The licenses granted in Section 2.1 with respect to any Contribution + become effective for each Contribution on the date the Contributor first + distributes such Contribution. + +2.3. Limitations on Grant Scope + + The licenses granted in this Section 2 are the only rights granted under + this License. No additional rights or licenses will be implied from the + distribution or licensing of Covered Software under this License. + Notwithstanding Section 2.1(b) above, no patent license is granted by a + Contributor: + + a. for any code that a Contributor has removed from Covered Software; or + + b. for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + + c. under Patent Claims infringed by Covered Software in the absence of + its Contributions. + + This License does not grant any rights in the trademarks, service marks, + or logos of any Contributor (except as may be necessary to comply with + the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + + No Contributor makes additional grants as a result of Your choice to + distribute the Covered Software under a subsequent version of this + License (see Section 10.2) or under the terms of a Secondary License (if + permitted under the terms of Section 3.3). + +2.5. Representation + + Each Contributor represents that the Contributor believes its + Contributions are its original creation(s) or it has sufficient rights to + grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + + This License is not intended to limit any rights You have under + applicable copyright doctrines of fair use, fair dealing, or other + equivalents. + +2.7. Conditions + + Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in + Section 2.1. + + +3. Responsibilities + +3.1. Distribution of Source Form + + All distribution of Covered Software in Source Code Form, including any + Modifications that You create or to which You contribute, must be under + the terms of this License. You must inform recipients that the Source + Code Form of the Covered Software is governed by the terms of this + License, and how they can obtain a copy of this License. You may not + attempt to alter or restrict the recipients' rights in the Source Code + Form. + +3.2. Distribution of Executable Form + + If You distribute Covered Software in Executable Form then: + + a. such Covered Software must also be made available in Source Code Form, + as described in Section 3.1, and You must inform recipients of the + Executable Form how they can obtain a copy of such Source Code Form by + reasonable means in a timely manner, at a charge no more than the cost + of distribution to the recipient; and + + b. You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter the + recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + + You may create and distribute a Larger Work under terms of Your choice, + provided that You also comply with the requirements of this License for + the Covered Software. If the Larger Work is a combination of Covered + Software with a work governed by one or more Secondary Licenses, and the + Covered Software is not Incompatible With Secondary Licenses, this + License permits You to additionally distribute such Covered Software + under the terms of such Secondary License(s), so that the recipient of + the Larger Work may, at their option, further distribute the Covered + Software under the terms of either this License or such Secondary + License(s). + +3.4. Notices + + You may not remove or alter the substance of any license notices + (including copyright notices, patent notices, disclaimers of warranty, or + limitations of liability) contained within the Source Code Form of the + Covered Software, except that You may alter any license notices to the + extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + + You may choose to offer, and to charge a fee for, warranty, support, + indemnity or liability obligations to one or more recipients of Covered + Software. However, You may do so only on Your own behalf, and not on + behalf of any Contributor. You must make it absolutely clear that any + such warranty, support, indemnity, or liability obligation is offered by + You alone, and You hereby agree to indemnify every Contributor for any + liability incurred by such Contributor as a result of warranty, support, + indemnity or liability terms You offer. You may include additional + disclaimers of warranty and limitations of liability specific to any + jurisdiction. + +4. Inability to Comply Due to Statute or Regulation + + If it is impossible for You to comply with any of the terms of this License + with respect to some or all of the Covered Software due to statute, + judicial order, or regulation then You must: (a) comply with the terms of + this License to the maximum extent possible; and (b) describe the + limitations and the code they affect. Such description must be placed in a + text file included with all distributions of the Covered Software under + this License. Except to the extent prohibited by statute or regulation, + such description must be sufficiently detailed for a recipient of ordinary + skill to be able to understand it. + +5. Termination + +5.1. The rights granted under this License will terminate automatically if You + fail to comply with any of its terms. However, if You become compliant, + then the rights granted under this License from a particular Contributor + are reinstated (a) provisionally, unless and until such Contributor + explicitly and finally terminates Your grants, and (b) on an ongoing + basis, if such Contributor fails to notify You of the non-compliance by + some reasonable means prior to 60 days after You have come back into + compliance. Moreover, Your grants from a particular Contributor are + reinstated on an ongoing basis if such Contributor notifies You of the + non-compliance by some reasonable means, this is the first time You have + received notice of non-compliance with this License from such + Contributor, and You become compliant prior to 30 days after Your receipt + of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent + infringement claim (excluding declaratory judgment actions, + counter-claims, and cross-claims) alleging that a Contributor Version + directly or indirectly infringes any patent, then the rights granted to + You by any and all Contributors for the Covered Software under Section + 2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user + license agreements (excluding distributors and resellers) which have been + validly granted by You or Your distributors under this License prior to + termination shall survive termination. + +6. Disclaimer of Warranty + + Covered Software is provided under this License on an "as is" basis, + without warranty of any kind, either expressed, implied, or statutory, + including, without limitation, warranties that the Covered Software is free + of defects, merchantable, fit for a particular purpose or non-infringing. + The entire risk as to the quality and performance of the Covered Software + is with You. Should any Covered Software prove defective in any respect, + You (not any Contributor) assume the cost of any necessary servicing, + repair, or correction. This disclaimer of warranty constitutes an essential + part of this License. No use of any Covered Software is authorized under + this License except under this disclaimer. + +7. Limitation of Liability + + Under no circumstances and under no legal theory, whether tort (including + negligence), contract, or otherwise, shall any Contributor, or anyone who + distributes Covered Software as permitted above, be liable to You for any + direct, indirect, special, incidental, or consequential damages of any + character including, without limitation, damages for lost profits, loss of + goodwill, work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses, even if such party shall have been + informed of the possibility of such damages. This limitation of liability + shall not apply to liability for death or personal injury resulting from + such party's negligence to the extent applicable law prohibits such + limitation. Some jurisdictions do not allow the exclusion or limitation of + incidental or consequential damages, so this exclusion and limitation may + not apply to You. + +8. Litigation + + Any litigation relating to this License may be brought only in the courts + of a jurisdiction where the defendant maintains its principal place of + business and such litigation shall be governed by laws of that + jurisdiction, without reference to its conflict-of-law provisions. Nothing + in this Section shall prevent a party's ability to bring cross-claims or + counter-claims. + +9. Miscellaneous + + This License represents the complete agreement concerning the subject + matter hereof. If any provision of this License is held to be + unenforceable, such provision shall be reformed only to the extent + necessary to make it enforceable. Any law or regulation which provides that + the language of a contract shall be construed against the drafter shall not + be used to construe this License against a Contributor. + + +10. Versions of the License + +10.1. New Versions + + Mozilla Foundation is the license steward. Except as provided in Section + 10.3, no one other than the license steward has the right to modify or + publish new versions of this License. Each version will be given a + distinguishing version number. + +10.2. Effect of New Versions + + You may distribute the Covered Software under the terms of the version + of the License under which You originally received the Covered Software, + or under the terms of any subsequent version published by the license + steward. + +10.3. Modified Versions + + If you create software not governed by this License, and you want to + create a new license for such software, you may create and use a + modified version of this License if you rename the license and remove + any references to the name of the license steward (except to note that + such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary + Licenses If You choose to distribute Source Code Form that is + Incompatible With Secondary Licenses under the terms of this version of + the License, the notice described in Exhibit B of this License must be + attached. + +Exhibit A - Source Code Form License Notice + + This Source Code Form is subject to the + terms of the Mozilla Public License, v. + 2.0. If a copy of the MPL was not + distributed with this file, You can + obtain one at + http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular file, +then You may include the notice in a location (such as a LICENSE file in a +relevant directory) where a recipient would be likely to look for such a +notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice + + This Source Code Form is "Incompatible + With Secondary Licenses", as defined by + the Mozilla Public License, v. 2.0. + diff --git a/vendor/github.com/hashicorp/go-cleanhttp/README.md b/vendor/github.com/hashicorp/go-cleanhttp/README.md new file mode 100644 index 0000000..036e531 --- /dev/null +++ b/vendor/github.com/hashicorp/go-cleanhttp/README.md @@ -0,0 +1,30 @@ +# cleanhttp + +Functions for accessing "clean" Go http.Client values + +------------- + +The Go standard library contains a default `http.Client` called +`http.DefaultClient`. It is a common idiom in Go code to start with +`http.DefaultClient` and tweak it as necessary, and in fact, this is +encouraged; from the `http` package documentation: + +> The Client's Transport typically has internal state (cached TCP connections), +so Clients should be reused instead of created as needed. Clients are safe for +concurrent use by multiple goroutines. + +Unfortunately, this is a shared value, and it is not uncommon for libraries to +assume that they are free to modify it at will. With enough dependencies, it +can be very easy to encounter strange problems and race conditions due to +manipulation of this shared value across libraries and goroutines (clients are +safe for concurrent use, but writing values to the client struct itself is not +protected). + +Making things worse is the fact that a bare `http.Client` will use a default +`http.Transport` called `http.DefaultTransport`, which is another global value +that behaves the same way. So it is not simply enough to replace +`http.DefaultClient` with `&http.Client{}`. + +This repository provides some simple functions to get a "clean" `http.Client` +-- one that uses the same default values as the Go standard library, but +returns a client that does not share any state with other clients. diff --git a/vendor/github.com/hashicorp/go-cleanhttp/cleanhttp.go b/vendor/github.com/hashicorp/go-cleanhttp/cleanhttp.go new file mode 100644 index 0000000..fe28d15 --- /dev/null +++ b/vendor/github.com/hashicorp/go-cleanhttp/cleanhttp.go @@ -0,0 +1,58 @@ +package cleanhttp + +import ( + "net" + "net/http" + "runtime" + "time" +) + +// DefaultTransport returns a new http.Transport with similar default values to +// http.DefaultTransport, but with idle connections and keepalives disabled. +func DefaultTransport() *http.Transport { + transport := DefaultPooledTransport() + transport.DisableKeepAlives = true + transport.MaxIdleConnsPerHost = -1 + return transport +} + +// DefaultPooledTransport returns a new http.Transport with similar default +// values to http.DefaultTransport. Do not use this for transient transports as +// it can leak file descriptors over time. Only use this for transports that +// will be re-used for the same host(s). +func DefaultPooledTransport() *http.Transport { + transport := &http.Transport{ + Proxy: http.ProxyFromEnvironment, + DialContext: (&net.Dialer{ + Timeout: 30 * time.Second, + KeepAlive: 30 * time.Second, + DualStack: true, + }).DialContext, + MaxIdleConns: 100, + IdleConnTimeout: 90 * time.Second, + TLSHandshakeTimeout: 10 * time.Second, + ExpectContinueTimeout: 1 * time.Second, + ForceAttemptHTTP2: true, + MaxIdleConnsPerHost: runtime.GOMAXPROCS(0) + 1, + } + return transport +} + +// DefaultClient returns a new http.Client with similar default values to +// http.Client, but with a non-shared Transport, idle connections disabled, and +// keepalives disabled. +func DefaultClient() *http.Client { + return &http.Client{ + Transport: DefaultTransport(), + } +} + +// DefaultPooledClient returns a new http.Client with similar default values to +// http.Client, but with a shared Transport. Do not use this function for +// transient clients as it can leak file descriptors over time. Only use this +// for clients that will be re-used for the same host(s). +func DefaultPooledClient() *http.Client { + return &http.Client{ + Transport: DefaultPooledTransport(), + } +} diff --git a/vendor/github.com/hashicorp/go-cleanhttp/doc.go b/vendor/github.com/hashicorp/go-cleanhttp/doc.go new file mode 100644 index 0000000..0584109 --- /dev/null +++ b/vendor/github.com/hashicorp/go-cleanhttp/doc.go @@ -0,0 +1,20 @@ +// Package cleanhttp offers convenience utilities for acquiring "clean" +// http.Transport and http.Client structs. +// +// Values set on http.DefaultClient and http.DefaultTransport affect all +// callers. This can have detrimental effects, esepcially in TLS contexts, +// where client or root certificates set to talk to multiple endpoints can end +// up displacing each other, leading to hard-to-debug issues. This package +// provides non-shared http.Client and http.Transport structs to ensure that +// the configuration will not be overwritten by other parts of the application +// or dependencies. +// +// The DefaultClient and DefaultTransport functions disable idle connections +// and keepalives. Without ensuring that idle connections are closed before +// garbage collection, short-term clients/transports can leak file descriptors, +// eventually leading to "too many open files" errors. If you will be +// connecting to the same hosts repeatedly from the same client, you can use +// DefaultPooledClient to receive a client that has connection pooling +// semantics similar to http.DefaultClient. +// +package cleanhttp diff --git a/vendor/github.com/hashicorp/go-cleanhttp/go.mod b/vendor/github.com/hashicorp/go-cleanhttp/go.mod new file mode 100644 index 0000000..005ccde --- /dev/null +++ b/vendor/github.com/hashicorp/go-cleanhttp/go.mod @@ -0,0 +1,3 @@ +module github.com/hashicorp/go-cleanhttp + +go 1.13 diff --git a/vendor/github.com/hashicorp/go-cleanhttp/handlers.go b/vendor/github.com/hashicorp/go-cleanhttp/handlers.go new file mode 100644 index 0000000..3c845dc --- /dev/null +++ b/vendor/github.com/hashicorp/go-cleanhttp/handlers.go @@ -0,0 +1,48 @@ +package cleanhttp + +import ( + "net/http" + "strings" + "unicode" +) + +// HandlerInput provides input options to cleanhttp's handlers +type HandlerInput struct { + ErrStatus int +} + +// PrintablePathCheckHandler is a middleware that ensures the request path +// contains only printable runes. +func PrintablePathCheckHandler(next http.Handler, input *HandlerInput) http.Handler { + // Nil-check on input to make it optional + if input == nil { + input = &HandlerInput{ + ErrStatus: http.StatusBadRequest, + } + } + + // Default to http.StatusBadRequest on error + if input.ErrStatus == 0 { + input.ErrStatus = http.StatusBadRequest + } + + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r != nil { + // Check URL path for non-printable characters + idx := strings.IndexFunc(r.URL.Path, func(c rune) bool { + return !unicode.IsPrint(c) + }) + + if idx != -1 { + w.WriteHeader(input.ErrStatus) + return + } + + if next != nil { + next.ServeHTTP(w, r) + } + } + + return + }) +} diff --git a/vendor/github.com/hashicorp/go-retryablehttp/.gitignore b/vendor/github.com/hashicorp/go-retryablehttp/.gitignore new file mode 100644 index 0000000..4e309e0 --- /dev/null +++ b/vendor/github.com/hashicorp/go-retryablehttp/.gitignore @@ -0,0 +1,4 @@ +.idea/ +*.iml +*.test +.vscode/ \ No newline at end of file diff --git a/vendor/github.com/hashicorp/go-retryablehttp/LICENSE b/vendor/github.com/hashicorp/go-retryablehttp/LICENSE new file mode 100644 index 0000000..e87a115 --- /dev/null +++ b/vendor/github.com/hashicorp/go-retryablehttp/LICENSE @@ -0,0 +1,363 @@ +Mozilla Public License, version 2.0 + +1. Definitions + +1.1. "Contributor" + + means each individual or legal entity that creates, contributes to the + creation of, or owns Covered Software. + +1.2. "Contributor Version" + + means the combination of the Contributions of others (if any) used by a + Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + + means Source Code Form to which the initial Contributor has attached the + notice in Exhibit A, the Executable Form of such Source Code Form, and + Modifications of such Source Code Form, in each case including portions + thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + a. that the initial Contributor has attached the notice described in + Exhibit B to the Covered Software; or + + b. that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the terms of + a Secondary License. + +1.6. "Executable Form" + + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + + means a work that combines Covered Software with other material, in a + separate file or files, that is not Covered Software. + +1.8. "License" + + means this document. + +1.9. "Licensable" + + means having the right to grant, to the maximum extent possible, whether + at the time of the initial grant or subsequently, any and all of the + rights conveyed by this License. + +1.10. "Modifications" + + means any of the following: + + a. any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered Software; or + + b. any new file in Source Code Form that contains any Covered Software. + +1.11. "Patent Claims" of a Contributor + + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the License, + by the making, using, selling, offering for sale, having made, import, + or transfer of either its Contributions or its Contributor Version. + +1.12. "Secondary License" + + means either the GNU General Public License, Version 2.0, the GNU Lesser + General Public License, Version 2.1, the GNU Affero General Public + License, Version 3.0, or any later versions of those licenses. + +1.13. "Source Code Form" + + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that controls, is + controlled by, or is under common control with You. For purposes of this + definition, "control" means (a) the power, direct or indirect, to cause + the direction or management of such entity, whether by contract or + otherwise, or (b) ownership of more than fifty percent (50%) of the + outstanding shares or beneficial ownership of such entity. + + +2. License Grants and Conditions + +2.1. Grants + + Each Contributor hereby grants You a world-wide, royalty-free, + non-exclusive license: + + a. under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + + b. under Patent Claims of such Contributor to make, use, sell, offer for + sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + + The licenses granted in Section 2.1 with respect to any Contribution + become effective for each Contribution on the date the Contributor first + distributes such Contribution. + +2.3. Limitations on Grant Scope + + The licenses granted in this Section 2 are the only rights granted under + this License. No additional rights or licenses will be implied from the + distribution or licensing of Covered Software under this License. + Notwithstanding Section 2.1(b) above, no patent license is granted by a + Contributor: + + a. for any code that a Contributor has removed from Covered Software; or + + b. for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + + c. under Patent Claims infringed by Covered Software in the absence of + its Contributions. + + This License does not grant any rights in the trademarks, service marks, + or logos of any Contributor (except as may be necessary to comply with + the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + + No Contributor makes additional grants as a result of Your choice to + distribute the Covered Software under a subsequent version of this + License (see Section 10.2) or under the terms of a Secondary License (if + permitted under the terms of Section 3.3). + +2.5. Representation + + Each Contributor represents that the Contributor believes its + Contributions are its original creation(s) or it has sufficient rights to + grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + + This License is not intended to limit any rights You have under + applicable copyright doctrines of fair use, fair dealing, or other + equivalents. + +2.7. Conditions + + Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in + Section 2.1. + + +3. Responsibilities + +3.1. Distribution of Source Form + + All distribution of Covered Software in Source Code Form, including any + Modifications that You create or to which You contribute, must be under + the terms of this License. You must inform recipients that the Source + Code Form of the Covered Software is governed by the terms of this + License, and how they can obtain a copy of this License. You may not + attempt to alter or restrict the recipients' rights in the Source Code + Form. + +3.2. Distribution of Executable Form + + If You distribute Covered Software in Executable Form then: + + a. such Covered Software must also be made available in Source Code Form, + as described in Section 3.1, and You must inform recipients of the + Executable Form how they can obtain a copy of such Source Code Form by + reasonable means in a timely manner, at a charge no more than the cost + of distribution to the recipient; and + + b. You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter the + recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + + You may create and distribute a Larger Work under terms of Your choice, + provided that You also comply with the requirements of this License for + the Covered Software. If the Larger Work is a combination of Covered + Software with a work governed by one or more Secondary Licenses, and the + Covered Software is not Incompatible With Secondary Licenses, this + License permits You to additionally distribute such Covered Software + under the terms of such Secondary License(s), so that the recipient of + the Larger Work may, at their option, further distribute the Covered + Software under the terms of either this License or such Secondary + License(s). + +3.4. Notices + + You may not remove or alter the substance of any license notices + (including copyright notices, patent notices, disclaimers of warranty, or + limitations of liability) contained within the Source Code Form of the + Covered Software, except that You may alter any license notices to the + extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + + You may choose to offer, and to charge a fee for, warranty, support, + indemnity or liability obligations to one or more recipients of Covered + Software. However, You may do so only on Your own behalf, and not on + behalf of any Contributor. You must make it absolutely clear that any + such warranty, support, indemnity, or liability obligation is offered by + You alone, and You hereby agree to indemnify every Contributor for any + liability incurred by such Contributor as a result of warranty, support, + indemnity or liability terms You offer. You may include additional + disclaimers of warranty and limitations of liability specific to any + jurisdiction. + +4. Inability to Comply Due to Statute or Regulation + + If it is impossible for You to comply with any of the terms of this License + with respect to some or all of the Covered Software due to statute, + judicial order, or regulation then You must: (a) comply with the terms of + this License to the maximum extent possible; and (b) describe the + limitations and the code they affect. Such description must be placed in a + text file included with all distributions of the Covered Software under + this License. Except to the extent prohibited by statute or regulation, + such description must be sufficiently detailed for a recipient of ordinary + skill to be able to understand it. + +5. Termination + +5.1. The rights granted under this License will terminate automatically if You + fail to comply with any of its terms. However, if You become compliant, + then the rights granted under this License from a particular Contributor + are reinstated (a) provisionally, unless and until such Contributor + explicitly and finally terminates Your grants, and (b) on an ongoing + basis, if such Contributor fails to notify You of the non-compliance by + some reasonable means prior to 60 days after You have come back into + compliance. Moreover, Your grants from a particular Contributor are + reinstated on an ongoing basis if such Contributor notifies You of the + non-compliance by some reasonable means, this is the first time You have + received notice of non-compliance with this License from such + Contributor, and You become compliant prior to 30 days after Your receipt + of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent + infringement claim (excluding declaratory judgment actions, + counter-claims, and cross-claims) alleging that a Contributor Version + directly or indirectly infringes any patent, then the rights granted to + You by any and all Contributors for the Covered Software under Section + 2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user + license agreements (excluding distributors and resellers) which have been + validly granted by You or Your distributors under this License prior to + termination shall survive termination. + +6. Disclaimer of Warranty + + Covered Software is provided under this License on an "as is" basis, + without warranty of any kind, either expressed, implied, or statutory, + including, without limitation, warranties that the Covered Software is free + of defects, merchantable, fit for a particular purpose or non-infringing. + The entire risk as to the quality and performance of the Covered Software + is with You. Should any Covered Software prove defective in any respect, + You (not any Contributor) assume the cost of any necessary servicing, + repair, or correction. This disclaimer of warranty constitutes an essential + part of this License. No use of any Covered Software is authorized under + this License except under this disclaimer. + +7. Limitation of Liability + + Under no circumstances and under no legal theory, whether tort (including + negligence), contract, or otherwise, shall any Contributor, or anyone who + distributes Covered Software as permitted above, be liable to You for any + direct, indirect, special, incidental, or consequential damages of any + character including, without limitation, damages for lost profits, loss of + goodwill, work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses, even if such party shall have been + informed of the possibility of such damages. This limitation of liability + shall not apply to liability for death or personal injury resulting from + such party's negligence to the extent applicable law prohibits such + limitation. Some jurisdictions do not allow the exclusion or limitation of + incidental or consequential damages, so this exclusion and limitation may + not apply to You. + +8. Litigation + + Any litigation relating to this License may be brought only in the courts + of a jurisdiction where the defendant maintains its principal place of + business and such litigation shall be governed by laws of that + jurisdiction, without reference to its conflict-of-law provisions. Nothing + in this Section shall prevent a party's ability to bring cross-claims or + counter-claims. + +9. Miscellaneous + + This License represents the complete agreement concerning the subject + matter hereof. If any provision of this License is held to be + unenforceable, such provision shall be reformed only to the extent + necessary to make it enforceable. Any law or regulation which provides that + the language of a contract shall be construed against the drafter shall not + be used to construe this License against a Contributor. + + +10. Versions of the License + +10.1. New Versions + + Mozilla Foundation is the license steward. Except as provided in Section + 10.3, no one other than the license steward has the right to modify or + publish new versions of this License. Each version will be given a + distinguishing version number. + +10.2. Effect of New Versions + + You may distribute the Covered Software under the terms of the version + of the License under which You originally received the Covered Software, + or under the terms of any subsequent version published by the license + steward. + +10.3. Modified Versions + + If you create software not governed by this License, and you want to + create a new license for such software, you may create and use a + modified version of this License if you rename the license and remove + any references to the name of the license steward (except to note that + such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary + Licenses If You choose to distribute Source Code Form that is + Incompatible With Secondary Licenses under the terms of this version of + the License, the notice described in Exhibit B of this License must be + attached. + +Exhibit A - Source Code Form License Notice + + This Source Code Form is subject to the + terms of the Mozilla Public License, v. + 2.0. If a copy of the MPL was not + distributed with this file, You can + obtain one at + http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular file, +then You may include the notice in a location (such as a LICENSE file in a +relevant directory) where a recipient would be likely to look for such a +notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice + + This Source Code Form is "Incompatible + With Secondary Licenses", as defined by + the Mozilla Public License, v. 2.0. + diff --git a/vendor/github.com/hashicorp/go-retryablehttp/Makefile b/vendor/github.com/hashicorp/go-retryablehttp/Makefile new file mode 100644 index 0000000..da17640 --- /dev/null +++ b/vendor/github.com/hashicorp/go-retryablehttp/Makefile @@ -0,0 +1,11 @@ +default: test + +test: + go vet ./... + go test -race ./... + +updatedeps: + go get -f -t -u ./... + go get -f -u ./... + +.PHONY: default test updatedeps diff --git a/vendor/github.com/hashicorp/go-retryablehttp/README.md b/vendor/github.com/hashicorp/go-retryablehttp/README.md new file mode 100644 index 0000000..09f5eaf --- /dev/null +++ b/vendor/github.com/hashicorp/go-retryablehttp/README.md @@ -0,0 +1,81 @@ +go-retryablehttp +================ + +[![Build Status](http://img.shields.io/travis/hashicorp/go-retryablehttp.svg?style=flat-square)][travis] +[![Go Documentation](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)][godocs] + +[travis]: http://travis-ci.org/hashicorp/go-retryablehttp +[godocs]: http://godoc.org/github.com/hashicorp/go-retryablehttp + +The `retryablehttp` package provides a familiar HTTP client interface with +automatic retries and exponential backoff. It is a thin wrapper over the +standard `net/http` client library and exposes nearly the same public API. This +makes `retryablehttp` very easy to drop into existing programs. + +`retryablehttp` performs automatic retries under certain conditions. Mainly, if +an error is returned by the client (connection errors, etc.), or if a 500-range +response code is received (except 501), then a retry is invoked after a wait +period. Otherwise, the response is returned and left to the caller to +interpret. + +The main difference from `net/http` is that requests which take a request body +(POST/PUT et. al) can have the body provided in a number of ways (some more or +less efficient) that allow "rewinding" the request body if the initial request +fails so that the full request can be attempted again. See the +[godoc](http://godoc.org/github.com/hashicorp/go-retryablehttp) for more +details. + +Version 0.6.0 and before are compatible with Go prior to 1.12. From 0.6.1 onward, Go 1.12+ is required. +From 0.6.7 onward, Go 1.13+ is required. + +Example Use +=========== + +Using this library should look almost identical to what you would do with +`net/http`. The most simple example of a GET request is shown below: + +```go +resp, err := retryablehttp.Get("/foo") +if err != nil { + panic(err) +} +``` + +The returned response object is an `*http.Response`, the same thing you would +usually get from `net/http`. Had the request failed one or more times, the above +call would block and retry with exponential backoff. + +## Retrying cases that fail after a seeming success + +It's possible for a request to succeed in the sense that the expected response headers are received, but then to encounter network-level errors while reading the response body. In go-retryablehttp's most basic usage, this error would not be retryable, due to the out-of-band handling of the response body. In some cases it may be desirable to handle the response body as part of the retryable operation. + +A toy example (which will retry the full request and succeed on the second attempt) is shown below: + +```go +c := retryablehttp.NewClient() +r := retryablehttp.NewRequest("GET", "://foo", nil) +handlerShouldRetry := true +r.SetResponseHandler(func(*http.Response) error { + if !handlerShouldRetry { + return nil + } + handlerShouldRetry = false + return errors.New("retryable error") +}) +``` + +## Getting a stdlib `*http.Client` with retries + +It's possible to convert a `*retryablehttp.Client` directly to a `*http.Client`. +This makes use of retryablehttp broadly applicable with minimal effort. Simply +configure a `*retryablehttp.Client` as you wish, and then call `StandardClient()`: + +```go +retryClient := retryablehttp.NewClient() +retryClient.RetryMax = 10 + +standardClient := retryClient.StandardClient() // *http.Client +``` + +For more usage and examples see the +[godoc](http://godoc.org/github.com/hashicorp/go-retryablehttp). diff --git a/vendor/github.com/hashicorp/go-retryablehttp/client.go b/vendor/github.com/hashicorp/go-retryablehttp/client.go new file mode 100644 index 0000000..57116e9 --- /dev/null +++ b/vendor/github.com/hashicorp/go-retryablehttp/client.go @@ -0,0 +1,815 @@ +// Package retryablehttp provides a familiar HTTP client interface with +// automatic retries and exponential backoff. It is a thin wrapper over the +// standard net/http client library and exposes nearly the same public API. +// This makes retryablehttp very easy to drop into existing programs. +// +// retryablehttp performs automatic retries under certain conditions. Mainly, if +// an error is returned by the client (connection errors etc), or if a 500-range +// response is received, then a retry is invoked. Otherwise, the response is +// returned and left to the caller to interpret. +// +// Requests which take a request body should provide a non-nil function +// parameter. The best choice is to provide either a function satisfying +// ReaderFunc which provides multiple io.Readers in an efficient manner, a +// *bytes.Buffer (the underlying raw byte slice will be used) or a raw byte +// slice. As it is a reference type, and we will wrap it as needed by readers, +// we can efficiently re-use the request body without needing to copy it. If an +// io.Reader (such as a *bytes.Reader) is provided, the full body will be read +// prior to the first request, and will be efficiently re-used for any retries. +// ReadSeeker can be used, but some users have observed occasional data races +// between the net/http library and the Seek functionality of some +// implementations of ReadSeeker, so should be avoided if possible. +package retryablehttp + +import ( + "bytes" + "context" + "crypto/x509" + "fmt" + "io" + "io/ioutil" + "log" + "math" + "math/rand" + "net/http" + "net/url" + "os" + "regexp" + "strconv" + "strings" + "sync" + "time" + + cleanhttp "github.com/hashicorp/go-cleanhttp" +) + +var ( + // Default retry configuration + defaultRetryWaitMin = 1 * time.Second + defaultRetryWaitMax = 30 * time.Second + defaultRetryMax = 4 + + // defaultLogger is the logger provided with defaultClient + defaultLogger = log.New(os.Stderr, "", log.LstdFlags) + + // defaultClient is used for performing requests without explicitly making + // a new client. It is purposely private to avoid modifications. + defaultClient = NewClient() + + // We need to consume response bodies to maintain http connections, but + // limit the size we consume to respReadLimit. + respReadLimit = int64(4096) + + // A regular expression to match the error returned by net/http when the + // configured number of redirects is exhausted. This error isn't typed + // specifically so we resort to matching on the error string. + redirectsErrorRe = regexp.MustCompile(`stopped after \d+ redirects\z`) + + // A regular expression to match the error returned by net/http when the + // scheme specified in the URL is invalid. This error isn't typed + // specifically so we resort to matching on the error string. + schemeErrorRe = regexp.MustCompile(`unsupported protocol scheme`) + + // A regular expression to match the error returned by net/http when the + // TLS certificate is not trusted. This error isn't typed + // specifically so we resort to matching on the error string. + notTrustedErrorRe = regexp.MustCompile(`certificate is not trusted`) +) + +// ReaderFunc is the type of function that can be given natively to NewRequest +type ReaderFunc func() (io.Reader, error) + +// ResponseHandlerFunc is a type of function that takes in a Response, and does something with it. +// It only runs if the initial part of the request was successful. +// If an error is returned, the client's retry policy will be used to determine whether to retry the whole request. +type ResponseHandlerFunc func(*http.Response) error + +// LenReader is an interface implemented by many in-memory io.Reader's. Used +// for automatically sending the right Content-Length header when possible. +type LenReader interface { + Len() int +} + +// Request wraps the metadata needed to create HTTP requests. +type Request struct { + // body is a seekable reader over the request body payload. This is + // used to rewind the request data in between retries. + body ReaderFunc + + responseHandler ResponseHandlerFunc + + // Embed an HTTP request directly. This makes a *Request act exactly + // like an *http.Request so that all meta methods are supported. + *http.Request +} + +// WithContext returns wrapped Request with a shallow copy of underlying *http.Request +// with its context changed to ctx. The provided ctx must be non-nil. +func (r *Request) WithContext(ctx context.Context) *Request { + return &Request{ + body: r.body, + responseHandler: r.responseHandler, + Request: r.Request.WithContext(ctx), + } +} + +// SetResponseHandler allows setting the response handler. +func (r *Request) SetResponseHandler(fn ResponseHandlerFunc) { + r.responseHandler = fn +} + +// BodyBytes allows accessing the request body. It is an analogue to +// http.Request's Body variable, but it returns a copy of the underlying data +// rather than consuming it. +// +// This function is not thread-safe; do not call it at the same time as another +// call, or at the same time this request is being used with Client.Do. +func (r *Request) BodyBytes() ([]byte, error) { + if r.body == nil { + return nil, nil + } + body, err := r.body() + if err != nil { + return nil, err + } + buf := new(bytes.Buffer) + _, err = buf.ReadFrom(body) + if err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +// SetBody allows setting the request body. +// +// It is useful if a new body needs to be set without constructing a new Request. +func (r *Request) SetBody(rawBody interface{}) error { + bodyReader, contentLength, err := getBodyReaderAndContentLength(rawBody) + if err != nil { + return err + } + r.body = bodyReader + r.ContentLength = contentLength + return nil +} + +// WriteTo allows copying the request body into a writer. +// +// It writes data to w until there's no more data to write or +// when an error occurs. The return int64 value is the number of bytes +// written. Any error encountered during the write is also returned. +// The signature matches io.WriterTo interface. +func (r *Request) WriteTo(w io.Writer) (int64, error) { + body, err := r.body() + if err != nil { + return 0, err + } + if c, ok := body.(io.Closer); ok { + defer c.Close() + } + return io.Copy(w, body) +} + +func getBodyReaderAndContentLength(rawBody interface{}) (ReaderFunc, int64, error) { + var bodyReader ReaderFunc + var contentLength int64 + + switch body := rawBody.(type) { + // If they gave us a function already, great! Use it. + case ReaderFunc: + bodyReader = body + tmp, err := body() + if err != nil { + return nil, 0, err + } + if lr, ok := tmp.(LenReader); ok { + contentLength = int64(lr.Len()) + } + if c, ok := tmp.(io.Closer); ok { + c.Close() + } + + case func() (io.Reader, error): + bodyReader = body + tmp, err := body() + if err != nil { + return nil, 0, err + } + if lr, ok := tmp.(LenReader); ok { + contentLength = int64(lr.Len()) + } + if c, ok := tmp.(io.Closer); ok { + c.Close() + } + + // If a regular byte slice, we can read it over and over via new + // readers + case []byte: + buf := body + bodyReader = func() (io.Reader, error) { + return bytes.NewReader(buf), nil + } + contentLength = int64(len(buf)) + + // If a bytes.Buffer we can read the underlying byte slice over and + // over + case *bytes.Buffer: + buf := body + bodyReader = func() (io.Reader, error) { + return bytes.NewReader(buf.Bytes()), nil + } + contentLength = int64(buf.Len()) + + // We prioritize *bytes.Reader here because we don't really want to + // deal with it seeking so want it to match here instead of the + // io.ReadSeeker case. + case *bytes.Reader: + buf, err := ioutil.ReadAll(body) + if err != nil { + return nil, 0, err + } + bodyReader = func() (io.Reader, error) { + return bytes.NewReader(buf), nil + } + contentLength = int64(len(buf)) + + // Compat case + case io.ReadSeeker: + raw := body + bodyReader = func() (io.Reader, error) { + _, err := raw.Seek(0, 0) + return ioutil.NopCloser(raw), err + } + if lr, ok := raw.(LenReader); ok { + contentLength = int64(lr.Len()) + } + + // Read all in so we can reset + case io.Reader: + buf, err := ioutil.ReadAll(body) + if err != nil { + return nil, 0, err + } + bodyReader = func() (io.Reader, error) { + return bytes.NewReader(buf), nil + } + contentLength = int64(len(buf)) + + // No body provided, nothing to do + case nil: + + // Unrecognized type + default: + return nil, 0, fmt.Errorf("cannot handle type %T", rawBody) + } + return bodyReader, contentLength, nil +} + +// FromRequest wraps an http.Request in a retryablehttp.Request +func FromRequest(r *http.Request) (*Request, error) { + bodyReader, _, err := getBodyReaderAndContentLength(r.Body) + if err != nil { + return nil, err + } + // Could assert contentLength == r.ContentLength + return &Request{body: bodyReader, Request: r}, nil +} + +// NewRequest creates a new wrapped request. +func NewRequest(method, url string, rawBody interface{}) (*Request, error) { + return NewRequestWithContext(context.Background(), method, url, rawBody) +} + +// NewRequestWithContext creates a new wrapped request with the provided context. +// +// The context controls the entire lifetime of a request and its response: +// obtaining a connection, sending the request, and reading the response headers and body. +func NewRequestWithContext(ctx context.Context, method, url string, rawBody interface{}) (*Request, error) { + bodyReader, contentLength, err := getBodyReaderAndContentLength(rawBody) + if err != nil { + return nil, err + } + + httpReq, err := http.NewRequestWithContext(ctx, method, url, nil) + if err != nil { + return nil, err + } + httpReq.ContentLength = contentLength + + return &Request{body: bodyReader, Request: httpReq}, nil +} + +// Logger interface allows to use other loggers than +// standard log.Logger. +type Logger interface { + Printf(string, ...interface{}) +} + +// 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 { + Error(msg string, keysAndValues ...interface{}) + Info(msg string, keysAndValues ...interface{}) + Debug(msg string, keysAndValues ...interface{}) + Warn(msg string, keysAndValues ...interface{}) +} + +// hookLogger adapts an LeveledLogger to Logger for use by the existing hook functions +// without changing the API. +type hookLogger struct { + LeveledLogger +} + +func (h hookLogger) Printf(s string, args ...interface{}) { + h.Info(fmt.Sprintf(s, args...)) +} + +// RequestLogHook allows a function to run before each retry. The HTTP +// request which will be made, and the retry number (0 for the initial +// request) are available to users. The internal logger is exposed to +// consumers. +type RequestLogHook func(Logger, *http.Request, int) + +// ResponseLogHook is like RequestLogHook, but allows running a function +// on each HTTP response. This function will be invoked at the end of +// every HTTP request executed, regardless of whether a subsequent retry +// needs to be performed or not. If the response body is read or closed +// from this method, this will affect the response returned from Do(). +type ResponseLogHook func(Logger, *http.Response) + +// CheckRetry specifies a policy for handling retries. It is called +// following each request with the response and error values returned by +// the http.Client. If CheckRetry returns false, the Client stops retrying +// and returns the response to the caller. If CheckRetry returns an error, +// that error value is returned in lieu of the error from the request. The +// Client will close any response body when retrying, but if the retry is +// aborted it is up to the CheckRetry callback to properly close any +// response body before returning. +type CheckRetry func(ctx context.Context, resp *http.Response, err error) (bool, error) + +// Backoff specifies a policy for how long to wait between retries. +// It is called after a failing request to determine the amount of time +// that should pass before trying again. +type Backoff func(min, max time.Duration, attemptNum int, resp *http.Response) time.Duration + +// ErrorHandler is called if retries are expired, containing the last status +// from the http library. If not specified, default behavior for the library is +// to close the body and return an error indicating how many tries were +// attempted. If overriding this, be sure to close the body if needed. +type ErrorHandler func(resp *http.Response, err error, numTries int) (*http.Response, error) + +// Client is used to make HTTP requests. It adds additional functionality +// like automatic retries to tolerate minor outages. +type Client struct { + HTTPClient *http.Client // Internal HTTP client. + Logger interface{} // Customer logger instance. Can be either Logger or LeveledLogger + + RetryWaitMin time.Duration // Minimum time to wait + RetryWaitMax time.Duration // Maximum time to wait + RetryMax int // Maximum number of retries + + // RequestLogHook allows a user-supplied function to be called + // before each retry. + RequestLogHook RequestLogHook + + // ResponseLogHook allows a user-supplied function to be called + // with the response from each HTTP request executed. + ResponseLogHook ResponseLogHook + + // CheckRetry specifies the policy for handling retries, and is called + // after each request. The default policy is DefaultRetryPolicy. + CheckRetry CheckRetry + + // Backoff specifies the policy for how long to wait between retries + Backoff Backoff + + // ErrorHandler specifies the custom error handler to use, if any + ErrorHandler ErrorHandler + + loggerInit sync.Once + clientInit sync.Once +} + +// NewClient creates a new Client with default settings. +func NewClient() *Client { + return &Client{ + HTTPClient: cleanhttp.DefaultPooledClient(), + Logger: defaultLogger, + RetryWaitMin: defaultRetryWaitMin, + RetryWaitMax: defaultRetryWaitMax, + RetryMax: defaultRetryMax, + CheckRetry: DefaultRetryPolicy, + Backoff: DefaultBackoff, + } +} + +func (c *Client) logger() interface{} { + c.loggerInit.Do(func() { + if c.Logger == nil { + return + } + + switch c.Logger.(type) { + case Logger, LeveledLogger: + // ok + default: + // This should happen in dev when they are setting Logger and work on code, not in prod. + panic(fmt.Sprintf("invalid logger type passed, must be Logger or LeveledLogger, was %T", c.Logger)) + } + }) + + return c.Logger +} + +// DefaultRetryPolicy provides a default callback for Client.CheckRetry, which +// will retry on connection errors and server errors. +func DefaultRetryPolicy(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() + } + + // don't propagate other errors + shouldRetry, _ := baseRetryPolicy(resp, err) + return shouldRetry, 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() + } + + return baseRetryPolicy(resp, err) +} + +func baseRetryPolicy(resp *http.Response, err error) (bool, error) { + 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 notTrustedErrorRe.MatchString(v.Error()) { + return false, v + } + if _, ok := v.Err.(x509.UnknownAuthorityError); ok { + return false, v + } + } + + // The error is likely recoverable so retry. + 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 + // 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 != http.StatusNotImplemented) { + return true, fmt.Errorf("unexpected HTTP status %s", resp.Status) + } + + return false, nil +} + +// DefaultBackoff provides a default callback for Client.Backoff which +// will perform exponential backoff based on the attempt number and limited +// 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 { + if resp != nil { + if resp.StatusCode == http.StatusTooManyRequests || resp.StatusCode == http.StatusServiceUnavailable { + 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) + sleep := time.Duration(mult) + if float64(sleep) != mult || sleep > max { + sleep = max + } + return sleep +} + +// LinearJitterBackoff provides a callback for Client.Backoff which will +// perform linear backoff based on the attempt number and with jitter to +// prevent a thundering herd. +// +// min and max here are *not* absolute values. The number to be multiplied by +// the attempt number will be chosen at random from between them, thus they are +// bounding the jitter. +// +// For instance: +// * To get strictly linear backoff of one second increasing each retry, set +// both to one second (1s, 2s, 3s, 4s, ...) +// * To get a small amount of jitter centered around one second increasing each +// retry, set to around one second, such as a min of 800ms and max of 1200ms +// (892ms, 2102ms, 2945ms, 4312ms, ...) +// * To get extreme jitter, set to a very wide spread, such as a min of 100ms +// and a max of 20s (15382ms, 292ms, 51321ms, 35234ms, ...) +func LinearJitterBackoff(min, max time.Duration, attemptNum int, resp *http.Response) time.Duration { + // attemptNum always starts at zero but we want to start at 1 for multiplication + attemptNum++ + + if max <= min { + // Unclear what to do here, or they are the same, so return min * + // attemptNum + return min * time.Duration(attemptNum) + } + + // Seed rand; doing this every time is fine + rand := rand.New(rand.NewSource(int64(time.Now().Nanosecond()))) + + // Pick a random number that lies somewhere between the min and max and + // multiply by the attemptNum. attemptNum starts at zero so we always + // increment here. We first get a random percentage, then apply that to the + // difference between min and max, and add to min. + jitter := rand.Float64() * float64(max-min) + jitterMin := int64(jitter) + int64(min) + return time.Duration(jitterMin * int64(attemptNum)) +} + +// PassthroughErrorHandler is an ErrorHandler that directly passes through the +// values from the net/http library for the final request. The body is not +// closed. +func PassthroughErrorHandler(resp *http.Response, err error, _ int) (*http.Response, error) { + return resp, err +} + +// Do wraps calling an HTTP method with retries. +func (c *Client) Do(req *Request) (*http.Response, error) { + c.clientInit.Do(func() { + if c.HTTPClient == nil { + c.HTTPClient = cleanhttp.DefaultPooledClient() + } + }) + + logger := c.logger() + + if logger != nil { + switch v := logger.(type) { + case LeveledLogger: + 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 attempt int + var shouldRetry bool + var doErr, respErr, checkErr error + + for i := 0; ; i++ { + doErr, respErr = nil, nil + attempt++ + + // Always rewind the request body when non-nil. + if req.body != nil { + body, err := req.body() + if err != nil { + c.HTTPClient.CloseIdleConnections() + return resp, err + } + if c, ok := body.(io.ReadCloser); ok { + req.Body = c + } else { + req.Body = ioutil.NopCloser(body) + } + } + + if c.RequestLogHook != nil { + switch v := logger.(type) { + case LeveledLogger: + c.RequestLogHook(hookLogger{v}, req.Request, i) + case Logger: + c.RequestLogHook(v, req.Request, i) + default: + c.RequestLogHook(nil, req.Request, i) + } + } + + // Attempt the request + resp, doErr = c.HTTPClient.Do(req.Request) + + // Check if we should continue with retries. + shouldRetry, checkErr = c.CheckRetry(req.Context(), resp, doErr) + if !shouldRetry && doErr == nil && req.responseHandler != nil { + respErr = req.responseHandler(resp) + shouldRetry, checkErr = c.CheckRetry(req.Context(), resp, respErr) + } + + err := doErr + if respErr != nil { + err = respErr + } + if err != nil { + switch v := logger.(type) { + case LeveledLogger: + v.Error("request failed", "error", err, "method", req.Method, "url", req.URL) + case Logger: + v.Printf("[ERR] %s %s request failed: %v", req.Method, req.URL, err) + } + } else { + // Call this here to maintain the behavior of logging all requests, + // even if CheckRetry signals to stop. + if c.ResponseLogHook != nil { + // Call the response logger function if provided. + switch v := logger.(type) { + case LeveledLogger: + c.ResponseLogHook(hookLogger{v}, resp) + case Logger: + c.ResponseLogHook(v, resp) + default: + c.ResponseLogHook(nil, resp) + } + } + } + + if !shouldRetry { + break + } + + // We do this before drainBody because there's no need for the I/O if + // we're breaking out + remain := c.RetryMax - i + if remain <= 0 { + break + } + + // We're going to retry, consume any response to reuse the connection. + if doErr == nil { + c.drainBody(resp.Body) + } + + wait := c.Backoff(c.RetryWaitMin, c.RetryWaitMax, i, resp) + if logger != nil { + desc := fmt.Sprintf("%s %s", req.Method, req.URL) + if resp != nil { + desc = fmt.Sprintf("%s (status: %d)", desc, resp.StatusCode) + } + switch v := logger.(type) { + case LeveledLogger: + v.Debug("retrying request", "request", desc, "timeout", wait, "remaining", remain) + case Logger: + v.Printf("[DEBUG] %s: retrying in %s (%d left)", desc, wait, remain) + } + } + timer := time.NewTimer(wait) + select { + case <-req.Context().Done(): + timer.Stop() + c.HTTPClient.CloseIdleConnections() + return nil, req.Context().Err() + case <-timer.C: + } + + // 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 && respErr == nil && checkErr == nil && !shouldRetry { + return resp, nil + } + + defer c.HTTPClient.CloseIdleConnections() + + var err error + if checkErr != nil { + err = checkErr + } else if respErr != nil { + err = respErr + } else { + err = doErr + } + + if c.ErrorHandler != nil { + return c.ErrorHandler(resp, err, attempt) + } + + // By default, we close the response body and return an error without + // returning the response + if resp != nil { + c.drainBody(resp.Body) + } + + // this means CheckRetry thought the request was a failure, but didn't + // 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. +func (c *Client) drainBody(body io.ReadCloser) { + defer body.Close() + _, err := io.Copy(ioutil.Discard, io.LimitReader(body, respReadLimit)) + if err != nil { + if c.logger() != nil { + switch v := c.logger().(type) { + case LeveledLogger: + v.Error("error reading response body", "error", err) + case Logger: + v.Printf("[ERR] error reading response body: %v", err) + } + } + } +} + +// Get is a shortcut for doing a GET request without making a new client. +func Get(url string) (*http.Response, error) { + return defaultClient.Get(url) +} + +// Get is a convenience helper for doing simple GET requests. +func (c *Client) Get(url string) (*http.Response, error) { + req, err := NewRequest("GET", url, nil) + if err != nil { + return nil, err + } + return c.Do(req) +} + +// Head is a shortcut for doing a HEAD request without making a new client. +func Head(url string) (*http.Response, error) { + return defaultClient.Head(url) +} + +// Head is a convenience method for doing simple HEAD requests. +func (c *Client) Head(url string) (*http.Response, error) { + req, err := NewRequest("HEAD", url, nil) + if err != nil { + return nil, err + } + return c.Do(req) +} + +// Post is a shortcut for doing a POST request without making a new client. +func Post(url, bodyType string, body interface{}) (*http.Response, error) { + return defaultClient.Post(url, bodyType, body) +} + +// Post is a convenience method for doing simple POST requests. +func (c *Client) Post(url, bodyType string, body interface{}) (*http.Response, error) { + req, err := NewRequest("POST", url, body) + if err != nil { + return nil, err + } + req.Header.Set("Content-Type", bodyType) + return c.Do(req) +} + +// PostForm is a shortcut to perform a POST with form data without creating +// a new client. +func PostForm(url string, data url.Values) (*http.Response, error) { + return defaultClient.PostForm(url, data) +} + +// PostForm is a convenience method for doing simple POST operations using +// pre-filled url.Values form data. +func (c *Client) PostForm(url string, data url.Values) (*http.Response, error) { + return c.Post(url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode())) +} + +// StandardClient returns a stdlib *http.Client with a custom Transport, which +// shims in a *retryablehttp.Client for added retries. +func (c *Client) StandardClient() *http.Client { + return &http.Client{ + Transport: &RoundTripper{Client: c}, + } +} diff --git a/vendor/github.com/hashicorp/go-retryablehttp/go.mod b/vendor/github.com/hashicorp/go-retryablehttp/go.mod new file mode 100644 index 0000000..7cc02b7 --- /dev/null +++ b/vendor/github.com/hashicorp/go-retryablehttp/go.mod @@ -0,0 +1,8 @@ +module github.com/hashicorp/go-retryablehttp + +require ( + github.com/hashicorp/go-cleanhttp v0.5.1 + github.com/hashicorp/go-hclog v0.9.2 +) + +go 1.13 diff --git a/vendor/github.com/hashicorp/go-retryablehttp/go.sum b/vendor/github.com/hashicorp/go-retryablehttp/go.sum new file mode 100644 index 0000000..71afe56 --- /dev/null +++ b/vendor/github.com/hashicorp/go-retryablehttp/go.sum @@ -0,0 +1,10 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI= +github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= diff --git a/vendor/github.com/hashicorp/go-retryablehttp/roundtripper.go b/vendor/github.com/hashicorp/go-retryablehttp/roundtripper.go new file mode 100644 index 0000000..8f3ee35 --- /dev/null +++ b/vendor/github.com/hashicorp/go-retryablehttp/roundtripper.go @@ -0,0 +1,52 @@ +package retryablehttp + +import ( + "errors" + "net/http" + "net/url" + "sync" +) + +// RoundTripper implements the http.RoundTripper interface, using a retrying +// HTTP client to execute requests. +// +// It is important to note that retryablehttp doesn't always act exactly as a +// RoundTripper should. This is highly dependent on the retryable client's +// configuration. +type RoundTripper struct { + // The client to use during requests. If nil, the default retryablehttp + // client and settings will be used. + Client *Client + + // once ensures that the logic to initialize the default client runs at + // most once, in a single thread. + once sync.Once +} + +// init initializes the underlying retryable client. +func (rt *RoundTripper) init() { + if rt.Client == nil { + rt.Client = NewClient() + } +} + +// RoundTrip satisfies the http.RoundTripper interface. +func (rt *RoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { + rt.once.Do(rt.init) + + // Convert the request to be retryable. + retryableReq, err := FromRequest(req) + if err != nil { + return nil, err + } + + // Execute the request. + 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 +} diff --git a/vendor/github.com/hashicorp/go-version/CHANGELOG.md b/vendor/github.com/hashicorp/go-version/CHANGELOG.md index dbae7f7..0945500 100644 --- a/vendor/github.com/hashicorp/go-version/CHANGELOG.md +++ b/vendor/github.com/hashicorp/go-version/CHANGELOG.md @@ -1,3 +1,10 @@ +# 1.4.0 (January 5, 2022) + +FEATURES: + + - Introduce `MustConstraints()` ([#87](https://github.com/hashicorp/go-version/pull/87)) + - `Constraints`: Introduce `Equals()` and `sort.Interface` methods ([#88](https://github.com/hashicorp/go-version/pull/88)) + # 1.3.0 (March 31, 2021) Please note that CHANGELOG.md does not exist in the source code prior to this release. diff --git a/vendor/github.com/hashicorp/go-version/README.md b/vendor/github.com/hashicorp/go-version/README.md index 851a337..4d25050 100644 --- a/vendor/github.com/hashicorp/go-version/README.md +++ b/vendor/github.com/hashicorp/go-version/README.md @@ -1,5 +1,5 @@ # Versioning Library for Go -[![Build Status](https://circleci.com/gh/hashicorp/go-version/tree/master.svg?style=svg)](https://circleci.com/gh/hashicorp/go-version/tree/master) +[![Build Status](https://circleci.com/gh/hashicorp/go-version/tree/main.svg?style=svg)](https://circleci.com/gh/hashicorp/go-version/tree/main) [![GoDoc](https://godoc.org/github.com/hashicorp/go-version?status.svg)](https://godoc.org/github.com/hashicorp/go-version) go-version is a library for parsing versions and version constraints, diff --git a/vendor/github.com/hashicorp/go-version/constraint.go b/vendor/github.com/hashicorp/go-version/constraint.go index d055759..1d88090 100644 --- a/vendor/github.com/hashicorp/go-version/constraint.go +++ b/vendor/github.com/hashicorp/go-version/constraint.go @@ -4,6 +4,7 @@ import ( "fmt" "reflect" "regexp" + "sort" "strings" ) @@ -11,30 +12,40 @@ import ( // ">= 1.0". type Constraint struct { f constraintFunc + op operator check *Version original string } +func (c *Constraint) Equals(con *Constraint) bool { + return c.op == con.op && c.check.Equal(con.check) +} + // Constraints is a slice of constraints. We make a custom type so that // we can add methods to it. type Constraints []*Constraint type constraintFunc func(v, c *Version) bool -var constraintOperators map[string]constraintFunc +var constraintOperators map[string]constraintOperation + +type constraintOperation struct { + op operator + f constraintFunc +} var constraintRegexp *regexp.Regexp func init() { - constraintOperators = map[string]constraintFunc{ - "": constraintEqual, - "=": constraintEqual, - "!=": constraintNotEqual, - ">": constraintGreaterThan, - "<": constraintLessThan, - ">=": constraintGreaterThanEqual, - "<=": constraintLessThanEqual, - "~>": constraintPessimistic, + constraintOperators = map[string]constraintOperation{ + "": {op: equal, f: constraintEqual}, + "=": {op: equal, f: constraintEqual}, + "!=": {op: notEqual, f: constraintNotEqual}, + ">": {op: greaterThan, f: constraintGreaterThan}, + "<": {op: lessThan, f: constraintLessThan}, + ">=": {op: greaterThanEqual, f: constraintGreaterThanEqual}, + "<=": {op: lessThanEqual, f: constraintLessThanEqual}, + "~>": {op: pessimistic, f: constraintPessimistic}, } ops := make([]string, 0, len(constraintOperators)) @@ -66,6 +77,16 @@ func NewConstraint(v string) (Constraints, error) { return Constraints(result), nil } +// MustConstraints is a helper that wraps a call to a function +// returning (Constraints, error) and panics if error is non-nil. +func MustConstraints(c Constraints, err error) Constraints { + if err != nil { + panic(err) + } + + return c +} + // Check tests if a version satisfies all the constraints. func (cs Constraints) Check(v *Version) bool { for _, c := range cs { @@ -77,6 +98,56 @@ func (cs Constraints) Check(v *Version) bool { return true } +// Equals compares Constraints with other Constraints +// for equality. This may not represent logical equivalence +// of compared constraints. +// e.g. even though '>0.1,>0.2' is logically equivalent +// to '>0.2' it is *NOT* treated as equal. +// +// Missing operator is treated as equal to '=', whitespaces +// are ignored and constraints are sorted before comaparison. +func (cs Constraints) Equals(c Constraints) bool { + if len(cs) != len(c) { + return false + } + + // make copies to retain order of the original slices + left := make(Constraints, len(cs)) + copy(left, cs) + sort.Stable(left) + right := make(Constraints, len(c)) + copy(right, c) + sort.Stable(right) + + // compare sorted slices + for i, con := range left { + if !con.Equals(right[i]) { + return false + } + } + + return true +} + +func (cs Constraints) Len() int { + return len(cs) +} + +func (cs Constraints) Less(i, j int) bool { + if cs[i].op < cs[j].op { + return true + } + if cs[i].op > cs[j].op { + return false + } + + return cs[i].check.LessThan(cs[j].check) +} + +func (cs Constraints) Swap(i, j int) { + cs[i], cs[j] = cs[j], cs[i] +} + // Returns the string format of the constraints func (cs Constraints) String() string { csStr := make([]string, len(cs)) @@ -107,8 +178,11 @@ func parseSingle(v string) (*Constraint, error) { return nil, err } + cop := constraintOperators[matches[1]] + return &Constraint{ - f: constraintOperators[matches[1]], + f: cop.f, + op: cop.op, check: check, original: v, }, nil @@ -138,6 +212,18 @@ func prereleaseCheck(v, c *Version) bool { // Constraint functions //------------------------------------------------------------------- +type operator rune + +const ( + equal operator = '=' + notEqual operator = '≠' + greaterThan operator = '>' + lessThan operator = '<' + greaterThanEqual operator = '≥' + lessThanEqual operator = '≤' + pessimistic operator = '~' +) + func constraintEqual(v, c *Version) bool { return v.Equal(c) } diff --git a/vendor/github.com/hashicorp/go-version/version.go b/vendor/github.com/hashicorp/go-version/version.go index 8068834..e87df69 100644 --- a/vendor/github.com/hashicorp/go-version/version.go +++ b/vendor/github.com/hashicorp/go-version/version.go @@ -64,7 +64,6 @@ func newVersion(v string, pattern *regexp.Regexp) (*Version, error) { } segmentsStr := strings.Split(matches[1], ".") segments := make([]int64, len(segmentsStr)) - si := 0 for i, str := range segmentsStr { val, err := strconv.ParseInt(str, 10, 64) if err != nil { @@ -72,8 +71,7 @@ func newVersion(v string, pattern *regexp.Regexp) (*Version, error) { "Error parsing version: %s", err) } - segments[i] = int64(val) - si++ + segments[i] = val } // Even though we could support more than three segments, if we @@ -92,7 +90,7 @@ func newVersion(v string, pattern *regexp.Regexp) (*Version, error) { metadata: matches[10], pre: pre, segments: segments, - si: si, + si: len(segmentsStr), original: v, }, nil } @@ -390,3 +388,20 @@ func (v *Version) String() string { func (v *Version) Original() string { return v.original } + +// UnmarshalText implements encoding.TextUnmarshaler interface. +func (v *Version) UnmarshalText(b []byte) error { + temp, err := NewVersion(string(b)) + if err != nil { + return err + } + + *v = *temp + + return nil +} + +// MarshalText implements encoding.TextMarshaler interface. +func (v *Version) MarshalText() ([]byte, error) { + return []byte(v.String()), nil +} diff --git a/vendor/github.com/stretchr/objx/.codeclimate.yml b/vendor/github.com/stretchr/objx/.codeclimate.yml new file mode 100644 index 0000000..559fa39 --- /dev/null +++ b/vendor/github.com/stretchr/objx/.codeclimate.yml @@ -0,0 +1,21 @@ +engines: + gofmt: + enabled: true + golint: + enabled: true + govet: + enabled: true + +exclude_patterns: +- ".github/" +- "vendor/" +- "codegen/" +- "*.yml" +- ".*.yml" +- "*.md" +- "Gopkg.*" +- "doc.go" +- "type_specific_codegen_test.go" +- "type_specific_codegen.go" +- ".gitignore" +- "LICENSE" diff --git a/vendor/github.com/stretchr/objx/.gitignore b/vendor/github.com/stretchr/objx/.gitignore new file mode 100644 index 0000000..ea58090 --- /dev/null +++ b/vendor/github.com/stretchr/objx/.gitignore @@ -0,0 +1,11 @@ +# Binaries for programs and plugins +*.exe +*.dll +*.so +*.dylib + +# Test binary, build with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out diff --git a/vendor/github.com/stretchr/objx/LICENSE b/vendor/github.com/stretchr/objx/LICENSE new file mode 100644 index 0000000..44d4d9d --- /dev/null +++ b/vendor/github.com/stretchr/objx/LICENSE @@ -0,0 +1,22 @@ +The MIT License + +Copyright (c) 2014 Stretchr, Inc. +Copyright (c) 2017-2018 objx contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/stretchr/objx/README.md b/vendor/github.com/stretchr/objx/README.md new file mode 100644 index 0000000..246660b --- /dev/null +++ b/vendor/github.com/stretchr/objx/README.md @@ -0,0 +1,80 @@ +# Objx +[![Build Status](https://travis-ci.org/stretchr/objx.svg?branch=master)](https://travis-ci.org/stretchr/objx) +[![Go Report Card](https://goreportcard.com/badge/github.com/stretchr/objx)](https://goreportcard.com/report/github.com/stretchr/objx) +[![Maintainability](https://api.codeclimate.com/v1/badges/1d64bc6c8474c2074f2b/maintainability)](https://codeclimate.com/github/stretchr/objx/maintainability) +[![Test Coverage](https://api.codeclimate.com/v1/badges/1d64bc6c8474c2074f2b/test_coverage)](https://codeclimate.com/github/stretchr/objx/test_coverage) +[![Sourcegraph](https://sourcegraph.com/github.com/stretchr/objx/-/badge.svg)](https://sourcegraph.com/github.com/stretchr/objx) +[![GoDoc](https://godoc.org/github.com/stretchr/objx?status.svg)](https://godoc.org/github.com/stretchr/objx) + +Objx - Go package for dealing with maps, slices, JSON and other data. + +Get started: + +- Install Objx with [one line of code](#installation), or [update it with another](#staying-up-to-date) +- Check out the API Documentation http://godoc.org/github.com/stretchr/objx + +## Overview +Objx provides the `objx.Map` type, which is a `map[string]interface{}` that exposes a powerful `Get` method (among others) that allows you to easily and quickly get access to data within the map, without having to worry too much about type assertions, missing data, default values etc. + +### Pattern +Objx uses a preditable pattern to make access data from within `map[string]interface{}` easy. Call one of the `objx.` functions to create your `objx.Map` to get going: + + m, err := objx.FromJSON(json) + +NOTE: Any methods or functions with the `Must` prefix will panic if something goes wrong, the rest will be optimistic and try to figure things out without panicking. + +Use `Get` to access the value you're interested in. You can use dot and array +notation too: + + m.Get("places[0].latlng") + +Once you have sought the `Value` you're interested in, you can use the `Is*` methods to determine its type. + + if m.Get("code").IsStr() { // Your code... } + +Or you can just assume the type, and use one of the strong type methods to extract the real value: + + m.Get("code").Int() + +If there's no value there (or if it's the wrong type) then a default value will be returned, or you can be explicit about the default value. + + Get("code").Int(-1) + +If you're dealing with a slice of data as a value, Objx provides many useful methods for iterating, manipulating and selecting that data. You can find out more by exploring the index below. + +### Reading data +A simple example of how to use Objx: + + // Use MustFromJSON to make an objx.Map from some JSON + m := objx.MustFromJSON(`{"name": "Mat", "age": 30}`) + + // Get the details + name := m.Get("name").Str() + age := m.Get("age").Int() + + // Get their nickname (or use their name if they don't have one) + nickname := m.Get("nickname").Str(name) + +### Ranging +Since `objx.Map` is a `map[string]interface{}` you can treat it as such. For example, to `range` the data, do what you would expect: + + m := objx.MustFromJSON(json) + for key, value := range m { + // Your code... + } + +## Installation +To install Objx, use go get: + + go get github.com/stretchr/objx + +### Staying up to date +To update Objx to the latest version, run: + + go get -u github.com/stretchr/objx + +### Supported go versions +We support the lastest three major Go versions, which are 1.10, 1.11 and 1.12 at the moment. + +## Contributing +Please feel free to submit issues, fork the repository and send pull requests! diff --git a/vendor/github.com/stretchr/objx/Taskfile.yml b/vendor/github.com/stretchr/objx/Taskfile.yml new file mode 100644 index 0000000..a749ac5 --- /dev/null +++ b/vendor/github.com/stretchr/objx/Taskfile.yml @@ -0,0 +1,30 @@ +version: '2' + +env: + GOFLAGS: -mod=vendor + +tasks: + default: + deps: [test] + + lint: + desc: Checks code style + cmds: + - gofmt -d -s *.go + - go vet ./... + silent: true + + lint-fix: + desc: Fixes code style + cmds: + - gofmt -w -s *.go + + test: + desc: Runs go tests + cmds: + - go test -race ./... + + test-coverage: + desc: Runs go tests and calucates test coverage + cmds: + - go test -race -coverprofile=c.out ./... diff --git a/vendor/github.com/stretchr/objx/accessors.go b/vendor/github.com/stretchr/objx/accessors.go new file mode 100644 index 0000000..4c60455 --- /dev/null +++ b/vendor/github.com/stretchr/objx/accessors.go @@ -0,0 +1,197 @@ +package objx + +import ( + "reflect" + "regexp" + "strconv" + "strings" +) + +const ( + // PathSeparator is the character used to separate the elements + // of the keypath. + // + // For example, `location.address.city` + PathSeparator string = "." + + // arrayAccesRegexString is the regex used to extract the array number + // from the access path + arrayAccesRegexString = `^(.+)\[([0-9]+)\]$` + + // mapAccessRegexString is the regex used to extract the map key + // from the access path + mapAccessRegexString = `^([^\[]*)\[([^\]]+)\](.*)$` +) + +// arrayAccesRegex is the compiled arrayAccesRegexString +var arrayAccesRegex = regexp.MustCompile(arrayAccesRegexString) + +// mapAccessRegex is the compiled mapAccessRegexString +var mapAccessRegex = regexp.MustCompile(mapAccessRegexString) + +// Get gets the value using the specified selector and +// returns it inside a new Obj object. +// +// If it cannot find the value, Get will return a nil +// value inside an instance of Obj. +// +// Get can only operate directly on map[string]interface{} and []interface. +// +// Example +// +// To access the title of the third chapter of the second book, do: +// +// o.Get("books[1].chapters[2].title") +func (m Map) Get(selector string) *Value { + rawObj := access(m, selector, nil, false) + return &Value{data: rawObj} +} + +// Set sets the value using the specified selector and +// returns the object on which Set was called. +// +// Set can only operate directly on map[string]interface{} and []interface +// +// Example +// +// To set the title of the third chapter of the second book, do: +// +// o.Set("books[1].chapters[2].title","Time to Go") +func (m Map) Set(selector string, value interface{}) Map { + access(m, selector, value, true) + return m +} + +// getIndex returns the index, which is hold in s by two braches. +// It also returns s withour the index part, e.g. name[1] will return (1, name). +// If no index is found, -1 is returned +func getIndex(s string) (int, string) { + arrayMatches := arrayAccesRegex.FindStringSubmatch(s) + if len(arrayMatches) > 0 { + // Get the key into the map + selector := arrayMatches[1] + // Get the index into the array at the key + // We know this cannt fail because arrayMatches[2] is an int for sure + index, _ := strconv.Atoi(arrayMatches[2]) + return index, selector + } + return -1, s +} + +// getKey returns the key which is held in s by two brackets. +// It also returns the next selector. +func getKey(s string) (string, string) { + selSegs := strings.SplitN(s, PathSeparator, 2) + thisSel := selSegs[0] + nextSel := "" + + if len(selSegs) > 1 { + nextSel = selSegs[1] + } + + mapMatches := mapAccessRegex.FindStringSubmatch(s) + if len(mapMatches) > 0 { + if _, err := strconv.Atoi(mapMatches[2]); err != nil { + thisSel = mapMatches[1] + nextSel = "[" + mapMatches[2] + "]" + mapMatches[3] + + if thisSel == "" { + thisSel = mapMatches[2] + nextSel = mapMatches[3] + } + + if nextSel == "" { + selSegs = []string{"", ""} + } else if nextSel[0] == '.' { + nextSel = nextSel[1:] + } + } + } + + return thisSel, nextSel +} + +// access accesses the object using the selector and performs the +// appropriate action. +func access(current interface{}, selector string, value interface{}, isSet bool) interface{} { + thisSel, nextSel := getKey(selector) + + indexes := []int{} + for strings.Contains(thisSel, "[") { + prevSel := thisSel + index := -1 + index, thisSel = getIndex(thisSel) + indexes = append(indexes, index) + if prevSel == thisSel { + break + } + } + + if curMap, ok := current.(Map); ok { + current = map[string]interface{}(curMap) + } + // get the object in question + switch current.(type) { + case map[string]interface{}: + curMSI := current.(map[string]interface{}) + if nextSel == "" && isSet { + curMSI[thisSel] = value + return nil + } + + _, ok := curMSI[thisSel].(map[string]interface{}) + if !ok { + _, ok = curMSI[thisSel].(Map) + } + + if (curMSI[thisSel] == nil || !ok) && len(indexes) == 0 && isSet { + curMSI[thisSel] = map[string]interface{}{} + } + + current = curMSI[thisSel] + default: + current = nil + } + + // do we need to access the item of an array? + if len(indexes) > 0 { + num := len(indexes) + for num > 0 { + num-- + index := indexes[num] + indexes = indexes[:num] + if array, ok := interSlice(current); ok { + if index < len(array) { + current = array[index] + } else { + current = nil + break + } + } + } + } + + if nextSel != "" { + current = access(current, nextSel, value, isSet) + } + return current +} + +func interSlice(slice interface{}) ([]interface{}, bool) { + if array, ok := slice.([]interface{}); ok { + return array, ok + } + + s := reflect.ValueOf(slice) + if s.Kind() != reflect.Slice { + return nil, false + } + + ret := make([]interface{}, s.Len()) + + for i := 0; i < s.Len(); i++ { + ret[i] = s.Index(i).Interface() + } + + return ret, true +} diff --git a/vendor/github.com/stretchr/objx/conversions.go b/vendor/github.com/stretchr/objx/conversions.go new file mode 100644 index 0000000..080aa46 --- /dev/null +++ b/vendor/github.com/stretchr/objx/conversions.go @@ -0,0 +1,280 @@ +package objx + +import ( + "bytes" + "encoding/base64" + "encoding/json" + "errors" + "fmt" + "net/url" + "strconv" +) + +// SignatureSeparator is the character that is used to +// separate the Base64 string from the security signature. +const SignatureSeparator = "_" + +// URLValuesSliceKeySuffix is the character that is used to +// specify a suffic for slices parsed by URLValues. +// If the suffix is set to "[i]", then the index of the slice +// is used in place of i +// Ex: Suffix "[]" would have the form a[]=b&a[]=c +// OR Suffix "[i]" would have the form a[0]=b&a[1]=c +// OR Suffix "" would have the form a=b&a=c +var urlValuesSliceKeySuffix = "[]" + +const ( + URLValuesSliceKeySuffixEmpty = "" + URLValuesSliceKeySuffixArray = "[]" + URLValuesSliceKeySuffixIndex = "[i]" +) + +// SetURLValuesSliceKeySuffix sets the character that is used to +// specify a suffic for slices parsed by URLValues. +// If the suffix is set to "[i]", then the index of the slice +// is used in place of i +// Ex: Suffix "[]" would have the form a[]=b&a[]=c +// OR Suffix "[i]" would have the form a[0]=b&a[1]=c +// OR Suffix "" would have the form a=b&a=c +func SetURLValuesSliceKeySuffix(s string) error { + if s == URLValuesSliceKeySuffixEmpty || s == URLValuesSliceKeySuffixArray || s == URLValuesSliceKeySuffixIndex { + urlValuesSliceKeySuffix = s + return nil + } + + return errors.New("objx: Invalid URLValuesSliceKeySuffix provided.") +} + +// JSON converts the contained object to a JSON string +// representation +func (m Map) JSON() (string, error) { + for k, v := range m { + m[k] = cleanUp(v) + } + + result, err := json.Marshal(m) + if err != nil { + err = errors.New("objx: JSON encode failed with: " + err.Error()) + } + return string(result), err +} + +func cleanUpInterfaceArray(in []interface{}) []interface{} { + result := make([]interface{}, len(in)) + for i, v := range in { + result[i] = cleanUp(v) + } + return result +} + +func cleanUpInterfaceMap(in map[interface{}]interface{}) Map { + result := Map{} + for k, v := range in { + result[fmt.Sprintf("%v", k)] = cleanUp(v) + } + return result +} + +func cleanUpStringMap(in map[string]interface{}) Map { + result := Map{} + for k, v := range in { + result[k] = cleanUp(v) + } + return result +} + +func cleanUpMSIArray(in []map[string]interface{}) []Map { + result := make([]Map, len(in)) + for i, v := range in { + result[i] = cleanUpStringMap(v) + } + return result +} + +func cleanUpMapArray(in []Map) []Map { + result := make([]Map, len(in)) + for i, v := range in { + result[i] = cleanUpStringMap(v) + } + return result +} + +func cleanUp(v interface{}) interface{} { + switch v := v.(type) { + case []interface{}: + return cleanUpInterfaceArray(v) + case []map[string]interface{}: + return cleanUpMSIArray(v) + case map[interface{}]interface{}: + return cleanUpInterfaceMap(v) + case Map: + return cleanUpStringMap(v) + case []Map: + return cleanUpMapArray(v) + default: + return v + } +} + +// MustJSON converts the contained object to a JSON string +// representation and panics if there is an error +func (m Map) MustJSON() string { + result, err := m.JSON() + if err != nil { + panic(err.Error()) + } + return result +} + +// Base64 converts the contained object to a Base64 string +// representation of the JSON string representation +func (m Map) Base64() (string, error) { + var buf bytes.Buffer + + jsonData, err := m.JSON() + if err != nil { + return "", err + } + + encoder := base64.NewEncoder(base64.StdEncoding, &buf) + _, _ = encoder.Write([]byte(jsonData)) + _ = encoder.Close() + + return buf.String(), nil +} + +// MustBase64 converts the contained object to a Base64 string +// representation of the JSON string representation and panics +// if there is an error +func (m Map) MustBase64() string { + result, err := m.Base64() + if err != nil { + panic(err.Error()) + } + return result +} + +// SignedBase64 converts the contained object to a Base64 string +// representation of the JSON string representation and signs it +// using the provided key. +func (m Map) SignedBase64(key string) (string, error) { + base64, err := m.Base64() + if err != nil { + return "", err + } + + sig := HashWithKey(base64, key) + return base64 + SignatureSeparator + sig, nil +} + +// MustSignedBase64 converts the contained object to a Base64 string +// representation of the JSON string representation and signs it +// using the provided key and panics if there is an error +func (m Map) MustSignedBase64(key string) string { + result, err := m.SignedBase64(key) + if err != nil { + panic(err.Error()) + } + return result +} + +/* + URL Query + ------------------------------------------------ +*/ + +// URLValues creates a url.Values object from an Obj. This +// function requires that the wrapped object be a map[string]interface{} +func (m Map) URLValues() url.Values { + vals := make(url.Values) + + m.parseURLValues(m, vals, "") + + return vals +} + +func (m Map) parseURLValues(queryMap Map, vals url.Values, key string) { + useSliceIndex := false + if urlValuesSliceKeySuffix == "[i]" { + useSliceIndex = true + } + + for k, v := range queryMap { + val := &Value{data: v} + switch { + case val.IsObjxMap(): + if key == "" { + m.parseURLValues(val.ObjxMap(), vals, k) + } else { + m.parseURLValues(val.ObjxMap(), vals, key+"["+k+"]") + } + case val.IsObjxMapSlice(): + sliceKey := k + if key != "" { + sliceKey = key + "[" + k + "]" + } + + if useSliceIndex { + for i, sv := range val.MustObjxMapSlice() { + sk := sliceKey + "[" + strconv.FormatInt(int64(i), 10) + "]" + m.parseURLValues(sv, vals, sk) + } + } else { + sliceKey = sliceKey + urlValuesSliceKeySuffix + for _, sv := range val.MustObjxMapSlice() { + m.parseURLValues(sv, vals, sliceKey) + } + } + case val.IsMSISlice(): + sliceKey := k + if key != "" { + sliceKey = key + "[" + k + "]" + } + + if useSliceIndex { + for i, sv := range val.MustMSISlice() { + sk := sliceKey + "[" + strconv.FormatInt(int64(i), 10) + "]" + m.parseURLValues(New(sv), vals, sk) + } + } else { + sliceKey = sliceKey + urlValuesSliceKeySuffix + for _, sv := range val.MustMSISlice() { + m.parseURLValues(New(sv), vals, sliceKey) + } + } + case val.IsStrSlice(), val.IsBoolSlice(), + val.IsFloat32Slice(), val.IsFloat64Slice(), + val.IsIntSlice(), val.IsInt8Slice(), val.IsInt16Slice(), val.IsInt32Slice(), val.IsInt64Slice(), + val.IsUintSlice(), val.IsUint8Slice(), val.IsUint16Slice(), val.IsUint32Slice(), val.IsUint64Slice(): + + sliceKey := k + if key != "" { + sliceKey = key + "[" + k + "]" + } + + if useSliceIndex { + for i, sv := range val.StringSlice() { + sk := sliceKey + "[" + strconv.FormatInt(int64(i), 10) + "]" + vals.Set(sk, sv) + } + } else { + sliceKey = sliceKey + urlValuesSliceKeySuffix + vals[sliceKey] = val.StringSlice() + } + + default: + if key == "" { + vals.Set(k, val.String()) + } else { + vals.Set(key+"["+k+"]", val.String()) + } + } + } +} + +// URLQuery gets an encoded URL query representing the given +// Obj. This function requires that the wrapped object be a +// map[string]interface{} +func (m Map) URLQuery() (string, error) { + return m.URLValues().Encode(), nil +} diff --git a/vendor/github.com/stretchr/objx/doc.go b/vendor/github.com/stretchr/objx/doc.go new file mode 100644 index 0000000..6d6af1a --- /dev/null +++ b/vendor/github.com/stretchr/objx/doc.go @@ -0,0 +1,66 @@ +/* +Objx - Go package for dealing with maps, slices, JSON and other data. + +Overview + +Objx provides the `objx.Map` type, which is a `map[string]interface{}` that exposes +a powerful `Get` method (among others) that allows you to easily and quickly get +access to data within the map, without having to worry too much about type assertions, +missing data, default values etc. + +Pattern + +Objx uses a preditable pattern to make access data from within `map[string]interface{}` easy. +Call one of the `objx.` functions to create your `objx.Map` to get going: + + m, err := objx.FromJSON(json) + +NOTE: Any methods or functions with the `Must` prefix will panic if something goes wrong, +the rest will be optimistic and try to figure things out without panicking. + +Use `Get` to access the value you're interested in. You can use dot and array +notation too: + + m.Get("places[0].latlng") + +Once you have sought the `Value` you're interested in, you can use the `Is*` methods to determine its type. + + if m.Get("code").IsStr() { // Your code... } + +Or you can just assume the type, and use one of the strong type methods to extract the real value: + + m.Get("code").Int() + +If there's no value there (or if it's the wrong type) then a default value will be returned, +or you can be explicit about the default value. + + Get("code").Int(-1) + +If you're dealing with a slice of data as a value, Objx provides many useful methods for iterating, +manipulating and selecting that data. You can find out more by exploring the index below. + +Reading data + +A simple example of how to use Objx: + + // Use MustFromJSON to make an objx.Map from some JSON + m := objx.MustFromJSON(`{"name": "Mat", "age": 30}`) + + // Get the details + name := m.Get("name").Str() + age := m.Get("age").Int() + + // Get their nickname (or use their name if they don't have one) + nickname := m.Get("nickname").Str(name) + +Ranging + +Since `objx.Map` is a `map[string]interface{}` you can treat it as such. +For example, to `range` the data, do what you would expect: + + m := objx.MustFromJSON(json) + for key, value := range m { + // Your code... + } +*/ +package objx diff --git a/vendor/github.com/stretchr/objx/go.mod b/vendor/github.com/stretchr/objx/go.mod new file mode 100644 index 0000000..45a55d2 --- /dev/null +++ b/vendor/github.com/stretchr/objx/go.mod @@ -0,0 +1,8 @@ +module github.com/stretchr/objx + +go 1.12 + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/stretchr/testify v1.7.1 +) diff --git a/vendor/github.com/stretchr/objx/go.sum b/vendor/github.com/stretchr/objx/go.sum new file mode 100644 index 0000000..c731dbc --- /dev/null +++ b/vendor/github.com/stretchr/objx/go.sum @@ -0,0 +1,12 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/vendor/github.com/stretchr/objx/map.go b/vendor/github.com/stretchr/objx/map.go new file mode 100644 index 0000000..a64712a --- /dev/null +++ b/vendor/github.com/stretchr/objx/map.go @@ -0,0 +1,215 @@ +package objx + +import ( + "encoding/base64" + "encoding/json" + "errors" + "io/ioutil" + "net/url" + "strings" +) + +// MSIConvertable is an interface that defines methods for converting your +// custom types to a map[string]interface{} representation. +type MSIConvertable interface { + // MSI gets a map[string]interface{} (msi) representing the + // object. + MSI() map[string]interface{} +} + +// Map provides extended functionality for working with +// untyped data, in particular map[string]interface (msi). +type Map map[string]interface{} + +// Value returns the internal value instance +func (m Map) Value() *Value { + return &Value{data: m} +} + +// Nil represents a nil Map. +var Nil = New(nil) + +// New creates a new Map containing the map[string]interface{} in the data argument. +// If the data argument is not a map[string]interface, New attempts to call the +// MSI() method on the MSIConvertable interface to create one. +func New(data interface{}) Map { + if _, ok := data.(map[string]interface{}); !ok { + if converter, ok := data.(MSIConvertable); ok { + data = converter.MSI() + } else { + return nil + } + } + return Map(data.(map[string]interface{})) +} + +// MSI creates a map[string]interface{} and puts it inside a new Map. +// +// The arguments follow a key, value pattern. +// +// +// Returns nil if any key argument is non-string or if there are an odd number of arguments. +// +// Example +// +// To easily create Maps: +// +// m := objx.MSI("name", "Mat", "age", 29, "subobj", objx.MSI("active", true)) +// +// // creates an Map equivalent to +// m := objx.Map{"name": "Mat", "age": 29, "subobj": objx.Map{"active": true}} +func MSI(keyAndValuePairs ...interface{}) Map { + newMap := Map{} + keyAndValuePairsLen := len(keyAndValuePairs) + if keyAndValuePairsLen%2 != 0 { + return nil + } + for i := 0; i < keyAndValuePairsLen; i = i + 2 { + key := keyAndValuePairs[i] + value := keyAndValuePairs[i+1] + + // make sure the key is a string + keyString, keyStringOK := key.(string) + if !keyStringOK { + return nil + } + newMap[keyString] = value + } + return newMap +} + +// ****** Conversion Constructors + +// MustFromJSON creates a new Map containing the data specified in the +// jsonString. +// +// Panics if the JSON is invalid. +func MustFromJSON(jsonString string) Map { + o, err := FromJSON(jsonString) + if err != nil { + panic("objx: MustFromJSON failed with error: " + err.Error()) + } + return o +} + +// MustFromJSONSlice creates a new slice of Map containing the data specified in the +// jsonString. Works with jsons with a top level array +// +// Panics if the JSON is invalid. +func MustFromJSONSlice(jsonString string) []Map { + slice, err := FromJSONSlice(jsonString) + if err != nil { + panic("objx: MustFromJSONSlice failed with error: " + err.Error()) + } + return slice +} + +// FromJSON creates a new Map containing the data specified in the +// jsonString. +// +// Returns an error if the JSON is invalid. +func FromJSON(jsonString string) (Map, error) { + var m Map + err := json.Unmarshal([]byte(jsonString), &m) + if err != nil { + return Nil, err + } + return m, nil +} + +// FromJSONSlice creates a new slice of Map containing the data specified in the +// jsonString. Works with jsons with a top level array +// +// Returns an error if the JSON is invalid. +func FromJSONSlice(jsonString string) ([]Map, error) { + var slice []Map + err := json.Unmarshal([]byte(jsonString), &slice) + if err != nil { + return nil, err + } + return slice, nil +} + +// FromBase64 creates a new Obj containing the data specified +// in the Base64 string. +// +// The string is an encoded JSON string returned by Base64 +func FromBase64(base64String string) (Map, error) { + decoder := base64.NewDecoder(base64.StdEncoding, strings.NewReader(base64String)) + decoded, err := ioutil.ReadAll(decoder) + if err != nil { + return nil, err + } + return FromJSON(string(decoded)) +} + +// MustFromBase64 creates a new Obj containing the data specified +// in the Base64 string and panics if there is an error. +// +// The string is an encoded JSON string returned by Base64 +func MustFromBase64(base64String string) Map { + result, err := FromBase64(base64String) + if err != nil { + panic("objx: MustFromBase64 failed with error: " + err.Error()) + } + return result +} + +// FromSignedBase64 creates a new Obj containing the data specified +// in the Base64 string. +// +// The string is an encoded JSON string returned by SignedBase64 +func FromSignedBase64(base64String, key string) (Map, error) { + parts := strings.Split(base64String, SignatureSeparator) + if len(parts) != 2 { + return nil, errors.New("objx: Signed base64 string is malformed") + } + + sig := HashWithKey(parts[0], key) + if parts[1] != sig { + return nil, errors.New("objx: Signature for base64 data does not match") + } + return FromBase64(parts[0]) +} + +// MustFromSignedBase64 creates a new Obj containing the data specified +// in the Base64 string and panics if there is an error. +// +// The string is an encoded JSON string returned by Base64 +func MustFromSignedBase64(base64String, key string) Map { + result, err := FromSignedBase64(base64String, key) + if err != nil { + panic("objx: MustFromSignedBase64 failed with error: " + err.Error()) + } + return result +} + +// FromURLQuery generates a new Obj by parsing the specified +// query. +// +// For queries with multiple values, the first value is selected. +func FromURLQuery(query string) (Map, error) { + vals, err := url.ParseQuery(query) + if err != nil { + return nil, err + } + m := Map{} + for k, vals := range vals { + m[k] = vals[0] + } + return m, nil +} + +// MustFromURLQuery generates a new Obj by parsing the specified +// query. +// +// For queries with multiple values, the first value is selected. +// +// Panics if it encounters an error +func MustFromURLQuery(query string) Map { + o, err := FromURLQuery(query) + if err != nil { + panic("objx: MustFromURLQuery failed with error: " + err.Error()) + } + return o +} diff --git a/vendor/github.com/stretchr/objx/mutations.go b/vendor/github.com/stretchr/objx/mutations.go new file mode 100644 index 0000000..c3400a3 --- /dev/null +++ b/vendor/github.com/stretchr/objx/mutations.go @@ -0,0 +1,77 @@ +package objx + +// Exclude returns a new Map with the keys in the specified []string +// excluded. +func (m Map) Exclude(exclude []string) Map { + excluded := make(Map) + for k, v := range m { + if !contains(exclude, k) { + excluded[k] = v + } + } + return excluded +} + +// Copy creates a shallow copy of the Obj. +func (m Map) Copy() Map { + copied := Map{} + for k, v := range m { + copied[k] = v + } + return copied +} + +// Merge blends the specified map with a copy of this map and returns the result. +// +// Keys that appear in both will be selected from the specified map. +// This method requires that the wrapped object be a map[string]interface{} +func (m Map) Merge(merge Map) Map { + return m.Copy().MergeHere(merge) +} + +// MergeHere blends the specified map with this map and returns the current map. +// +// Keys that appear in both will be selected from the specified map. The original map +// will be modified. This method requires that +// the wrapped object be a map[string]interface{} +func (m Map) MergeHere(merge Map) Map { + for k, v := range merge { + m[k] = v + } + return m +} + +// Transform builds a new Obj giving the transformer a chance +// to change the keys and values as it goes. This method requires that +// the wrapped object be a map[string]interface{} +func (m Map) Transform(transformer func(key string, value interface{}) (string, interface{})) Map { + newMap := Map{} + for k, v := range m { + modifiedKey, modifiedVal := transformer(k, v) + newMap[modifiedKey] = modifiedVal + } + return newMap +} + +// TransformKeys builds a new map using the specified key mapping. +// +// Unspecified keys will be unaltered. +// This method requires that the wrapped object be a map[string]interface{} +func (m Map) TransformKeys(mapping map[string]string) Map { + return m.Transform(func(key string, value interface{}) (string, interface{}) { + if newKey, ok := mapping[key]; ok { + return newKey, value + } + return key, value + }) +} + +// Checks if a string slice contains a string +func contains(s []string, e string) bool { + for _, a := range s { + if a == e { + return true + } + } + return false +} diff --git a/vendor/github.com/stretchr/objx/security.go b/vendor/github.com/stretchr/objx/security.go new file mode 100644 index 0000000..692be8e --- /dev/null +++ b/vendor/github.com/stretchr/objx/security.go @@ -0,0 +1,12 @@ +package objx + +import ( + "crypto/sha1" + "encoding/hex" +) + +// HashWithKey hashes the specified string using the security key +func HashWithKey(data, key string) string { + d := sha1.Sum([]byte(data + ":" + key)) + return hex.EncodeToString(d[:]) +} diff --git a/vendor/github.com/stretchr/objx/tests.go b/vendor/github.com/stretchr/objx/tests.go new file mode 100644 index 0000000..d9e0b47 --- /dev/null +++ b/vendor/github.com/stretchr/objx/tests.go @@ -0,0 +1,17 @@ +package objx + +// Has gets whether there is something at the specified selector +// or not. +// +// If m is nil, Has will always return false. +func (m Map) Has(selector string) bool { + if m == nil { + return false + } + return !m.Get(selector).IsNil() +} + +// IsNil gets whether the data is nil or not. +func (v *Value) IsNil() bool { + return v == nil || v.data == nil +} diff --git a/vendor/github.com/stretchr/objx/type_specific.go b/vendor/github.com/stretchr/objx/type_specific.go new file mode 100644 index 0000000..80f88d9 --- /dev/null +++ b/vendor/github.com/stretchr/objx/type_specific.go @@ -0,0 +1,346 @@ +package objx + +/* + MSI (map[string]interface{} and []map[string]interface{}) +*/ + +// MSI gets the value as a map[string]interface{}, returns the optionalDefault +// value or a system default object if the value is the wrong type. +func (v *Value) MSI(optionalDefault ...map[string]interface{}) map[string]interface{} { + if s, ok := v.data.(map[string]interface{}); ok { + return s + } + if s, ok := v.data.(Map); ok { + return map[string]interface{}(s) + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil +} + +// MustMSI gets the value as a map[string]interface{}. +// +// Panics if the object is not a map[string]interface{}. +func (v *Value) MustMSI() map[string]interface{} { + if s, ok := v.data.(Map); ok { + return map[string]interface{}(s) + } + return v.data.(map[string]interface{}) +} + +// MSISlice gets the value as a []map[string]interface{}, returns the optionalDefault +// value or nil if the value is not a []map[string]interface{}. +func (v *Value) MSISlice(optionalDefault ...[]map[string]interface{}) []map[string]interface{} { + if s, ok := v.data.([]map[string]interface{}); ok { + return s + } + + s := v.ObjxMapSlice() + if s == nil { + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil + } + + result := make([]map[string]interface{}, len(s)) + for i := range s { + result[i] = s[i].Value().MSI() + } + return result +} + +// MustMSISlice gets the value as a []map[string]interface{}. +// +// Panics if the object is not a []map[string]interface{}. +func (v *Value) MustMSISlice() []map[string]interface{} { + if s := v.MSISlice(); s != nil { + return s + } + + return v.data.([]map[string]interface{}) +} + +// IsMSI gets whether the object contained is a map[string]interface{} or not. +func (v *Value) IsMSI() bool { + _, ok := v.data.(map[string]interface{}) + if !ok { + _, ok = v.data.(Map) + } + return ok +} + +// IsMSISlice gets whether the object contained is a []map[string]interface{} or not. +func (v *Value) IsMSISlice() bool { + _, ok := v.data.([]map[string]interface{}) + if !ok { + _, ok = v.data.([]Map) + if !ok { + s, ok := v.data.([]interface{}) + if ok { + for i := range s { + switch s[i].(type) { + case Map: + case map[string]interface{}: + default: + return false + } + } + return true + } + } + } + return ok +} + +// EachMSI calls the specified callback for each object +// in the []map[string]interface{}. +// +// Panics if the object is the wrong type. +func (v *Value) EachMSI(callback func(int, map[string]interface{}) bool) *Value { + for index, val := range v.MustMSISlice() { + carryon := callback(index, val) + if !carryon { + break + } + } + return v +} + +// WhereMSI uses the specified decider function to select items +// from the []map[string]interface{}. The object contained in the result will contain +// only the selected items. +func (v *Value) WhereMSI(decider func(int, map[string]interface{}) bool) *Value { + var selected []map[string]interface{} + v.EachMSI(func(index int, val map[string]interface{}) bool { + shouldSelect := decider(index, val) + if !shouldSelect { + selected = append(selected, val) + } + return true + }) + return &Value{data: selected} +} + +// GroupMSI uses the specified grouper function to group the items +// keyed by the return of the grouper. The object contained in the +// result will contain a map[string][]map[string]interface{}. +func (v *Value) GroupMSI(grouper func(int, map[string]interface{}) string) *Value { + groups := make(map[string][]map[string]interface{}) + v.EachMSI(func(index int, val map[string]interface{}) bool { + group := grouper(index, val) + if _, ok := groups[group]; !ok { + groups[group] = make([]map[string]interface{}, 0) + } + groups[group] = append(groups[group], val) + return true + }) + return &Value{data: groups} +} + +// ReplaceMSI uses the specified function to replace each map[string]interface{}s +// by iterating each item. The data in the returned result will be a +// []map[string]interface{} containing the replaced items. +func (v *Value) ReplaceMSI(replacer func(int, map[string]interface{}) map[string]interface{}) *Value { + arr := v.MustMSISlice() + replaced := make([]map[string]interface{}, len(arr)) + v.EachMSI(func(index int, val map[string]interface{}) bool { + replaced[index] = replacer(index, val) + return true + }) + return &Value{data: replaced} +} + +// CollectMSI uses the specified collector function to collect a value +// for each of the map[string]interface{}s in the slice. The data returned will be a +// []interface{}. +func (v *Value) CollectMSI(collector func(int, map[string]interface{}) interface{}) *Value { + arr := v.MustMSISlice() + collected := make([]interface{}, len(arr)) + v.EachMSI(func(index int, val map[string]interface{}) bool { + collected[index] = collector(index, val) + return true + }) + return &Value{data: collected} +} + +/* + ObjxMap ((Map) and [](Map)) +*/ + +// ObjxMap gets the value as a (Map), returns the optionalDefault +// value or a system default object if the value is the wrong type. +func (v *Value) ObjxMap(optionalDefault ...(Map)) Map { + if s, ok := v.data.((Map)); ok { + return s + } + if s, ok := v.data.(map[string]interface{}); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return New(nil) +} + +// MustObjxMap gets the value as a (Map). +// +// Panics if the object is not a (Map). +func (v *Value) MustObjxMap() Map { + if s, ok := v.data.(map[string]interface{}); ok { + return s + } + return v.data.((Map)) +} + +// ObjxMapSlice gets the value as a [](Map), returns the optionalDefault +// value or nil if the value is not a [](Map). +func (v *Value) ObjxMapSlice(optionalDefault ...[](Map)) [](Map) { + if s, ok := v.data.([]Map); ok { + return s + } + + if s, ok := v.data.([]map[string]interface{}); ok { + result := make([]Map, len(s)) + for i := range s { + result[i] = s[i] + } + return result + } + + s, ok := v.data.([]interface{}) + if !ok { + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil + } + + result := make([]Map, len(s)) + for i := range s { + switch s[i].(type) { + case Map: + result[i] = s[i].(Map) + case map[string]interface{}: + result[i] = New(s[i]) + default: + return nil + } + } + return result +} + +// MustObjxMapSlice gets the value as a [](Map). +// +// Panics if the object is not a [](Map). +func (v *Value) MustObjxMapSlice() [](Map) { + if s := v.ObjxMapSlice(); s != nil { + return s + } + return v.data.([](Map)) +} + +// IsObjxMap gets whether the object contained is a (Map) or not. +func (v *Value) IsObjxMap() bool { + _, ok := v.data.((Map)) + if !ok { + _, ok = v.data.(map[string]interface{}) + } + return ok +} + +// IsObjxMapSlice gets whether the object contained is a [](Map) or not. +func (v *Value) IsObjxMapSlice() bool { + _, ok := v.data.([](Map)) + if !ok { + _, ok = v.data.([]map[string]interface{}) + if !ok { + s, ok := v.data.([]interface{}) + if ok { + for i := range s { + switch s[i].(type) { + case Map: + case map[string]interface{}: + default: + return false + } + } + return true + } + } + } + + return ok +} + +// EachObjxMap calls the specified callback for each object +// in the [](Map). +// +// Panics if the object is the wrong type. +func (v *Value) EachObjxMap(callback func(int, Map) bool) *Value { + for index, val := range v.MustObjxMapSlice() { + carryon := callback(index, val) + if !carryon { + break + } + } + return v +} + +// WhereObjxMap uses the specified decider function to select items +// from the [](Map). The object contained in the result will contain +// only the selected items. +func (v *Value) WhereObjxMap(decider func(int, Map) bool) *Value { + var selected [](Map) + v.EachObjxMap(func(index int, val Map) bool { + shouldSelect := decider(index, val) + if !shouldSelect { + selected = append(selected, val) + } + return true + }) + return &Value{data: selected} +} + +// GroupObjxMap uses the specified grouper function to group the items +// keyed by the return of the grouper. The object contained in the +// result will contain a map[string][](Map). +func (v *Value) GroupObjxMap(grouper func(int, Map) string) *Value { + groups := make(map[string][](Map)) + v.EachObjxMap(func(index int, val Map) bool { + group := grouper(index, val) + if _, ok := groups[group]; !ok { + groups[group] = make([](Map), 0) + } + groups[group] = append(groups[group], val) + return true + }) + return &Value{data: groups} +} + +// ReplaceObjxMap uses the specified function to replace each (Map)s +// by iterating each item. The data in the returned result will be a +// [](Map) containing the replaced items. +func (v *Value) ReplaceObjxMap(replacer func(int, Map) Map) *Value { + arr := v.MustObjxMapSlice() + replaced := make([](Map), len(arr)) + v.EachObjxMap(func(index int, val Map) bool { + replaced[index] = replacer(index, val) + return true + }) + return &Value{data: replaced} +} + +// CollectObjxMap uses the specified collector function to collect a value +// for each of the (Map)s in the slice. The data returned will be a +// []interface{}. +func (v *Value) CollectObjxMap(collector func(int, Map) interface{}) *Value { + arr := v.MustObjxMapSlice() + collected := make([]interface{}, len(arr)) + v.EachObjxMap(func(index int, val Map) bool { + collected[index] = collector(index, val) + return true + }) + return &Value{data: collected} +} diff --git a/vendor/github.com/stretchr/objx/type_specific_codegen.go b/vendor/github.com/stretchr/objx/type_specific_codegen.go new file mode 100644 index 0000000..4585045 --- /dev/null +++ b/vendor/github.com/stretchr/objx/type_specific_codegen.go @@ -0,0 +1,2261 @@ +package objx + +/* + Inter (interface{} and []interface{}) +*/ + +// Inter gets the value as a interface{}, returns the optionalDefault +// value or a system default object if the value is the wrong type. +func (v *Value) Inter(optionalDefault ...interface{}) interface{} { + if s, ok := v.data.(interface{}); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil +} + +// MustInter gets the value as a interface{}. +// +// Panics if the object is not a interface{}. +func (v *Value) MustInter() interface{} { + return v.data.(interface{}) +} + +// InterSlice gets the value as a []interface{}, returns the optionalDefault +// value or nil if the value is not a []interface{}. +func (v *Value) InterSlice(optionalDefault ...[]interface{}) []interface{} { + if s, ok := v.data.([]interface{}); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil +} + +// MustInterSlice gets the value as a []interface{}. +// +// Panics if the object is not a []interface{}. +func (v *Value) MustInterSlice() []interface{} { + return v.data.([]interface{}) +} + +// IsInter gets whether the object contained is a interface{} or not. +func (v *Value) IsInter() bool { + _, ok := v.data.(interface{}) + return ok +} + +// IsInterSlice gets whether the object contained is a []interface{} or not. +func (v *Value) IsInterSlice() bool { + _, ok := v.data.([]interface{}) + return ok +} + +// EachInter calls the specified callback for each object +// in the []interface{}. +// +// Panics if the object is the wrong type. +func (v *Value) EachInter(callback func(int, interface{}) bool) *Value { + for index, val := range v.MustInterSlice() { + carryon := callback(index, val) + if !carryon { + break + } + } + return v +} + +// WhereInter uses the specified decider function to select items +// from the []interface{}. The object contained in the result will contain +// only the selected items. +func (v *Value) WhereInter(decider func(int, interface{}) bool) *Value { + var selected []interface{} + v.EachInter(func(index int, val interface{}) bool { + shouldSelect := decider(index, val) + if !shouldSelect { + selected = append(selected, val) + } + return true + }) + return &Value{data: selected} +} + +// GroupInter uses the specified grouper function to group the items +// keyed by the return of the grouper. The object contained in the +// result will contain a map[string][]interface{}. +func (v *Value) GroupInter(grouper func(int, interface{}) string) *Value { + groups := make(map[string][]interface{}) + v.EachInter(func(index int, val interface{}) bool { + group := grouper(index, val) + if _, ok := groups[group]; !ok { + groups[group] = make([]interface{}, 0) + } + groups[group] = append(groups[group], val) + return true + }) + return &Value{data: groups} +} + +// ReplaceInter uses the specified function to replace each interface{}s +// by iterating each item. The data in the returned result will be a +// []interface{} containing the replaced items. +func (v *Value) ReplaceInter(replacer func(int, interface{}) interface{}) *Value { + arr := v.MustInterSlice() + replaced := make([]interface{}, len(arr)) + v.EachInter(func(index int, val interface{}) bool { + replaced[index] = replacer(index, val) + return true + }) + return &Value{data: replaced} +} + +// CollectInter uses the specified collector function to collect a value +// for each of the interface{}s in the slice. The data returned will be a +// []interface{}. +func (v *Value) CollectInter(collector func(int, interface{}) interface{}) *Value { + arr := v.MustInterSlice() + collected := make([]interface{}, len(arr)) + v.EachInter(func(index int, val interface{}) bool { + collected[index] = collector(index, val) + return true + }) + return &Value{data: collected} +} + +/* + Bool (bool and []bool) +*/ + +// Bool gets the value as a bool, returns the optionalDefault +// value or a system default object if the value is the wrong type. +func (v *Value) Bool(optionalDefault ...bool) bool { + if s, ok := v.data.(bool); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return false +} + +// MustBool gets the value as a bool. +// +// Panics if the object is not a bool. +func (v *Value) MustBool() bool { + return v.data.(bool) +} + +// BoolSlice gets the value as a []bool, returns the optionalDefault +// value or nil if the value is not a []bool. +func (v *Value) BoolSlice(optionalDefault ...[]bool) []bool { + if s, ok := v.data.([]bool); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil +} + +// MustBoolSlice gets the value as a []bool. +// +// Panics if the object is not a []bool. +func (v *Value) MustBoolSlice() []bool { + return v.data.([]bool) +} + +// IsBool gets whether the object contained is a bool or not. +func (v *Value) IsBool() bool { + _, ok := v.data.(bool) + return ok +} + +// IsBoolSlice gets whether the object contained is a []bool or not. +func (v *Value) IsBoolSlice() bool { + _, ok := v.data.([]bool) + return ok +} + +// EachBool calls the specified callback for each object +// in the []bool. +// +// Panics if the object is the wrong type. +func (v *Value) EachBool(callback func(int, bool) bool) *Value { + for index, val := range v.MustBoolSlice() { + carryon := callback(index, val) + if !carryon { + break + } + } + return v +} + +// WhereBool uses the specified decider function to select items +// from the []bool. The object contained in the result will contain +// only the selected items. +func (v *Value) WhereBool(decider func(int, bool) bool) *Value { + var selected []bool + v.EachBool(func(index int, val bool) bool { + shouldSelect := decider(index, val) + if !shouldSelect { + selected = append(selected, val) + } + return true + }) + return &Value{data: selected} +} + +// GroupBool uses the specified grouper function to group the items +// keyed by the return of the grouper. The object contained in the +// result will contain a map[string][]bool. +func (v *Value) GroupBool(grouper func(int, bool) string) *Value { + groups := make(map[string][]bool) + v.EachBool(func(index int, val bool) bool { + group := grouper(index, val) + if _, ok := groups[group]; !ok { + groups[group] = make([]bool, 0) + } + groups[group] = append(groups[group], val) + return true + }) + return &Value{data: groups} +} + +// ReplaceBool uses the specified function to replace each bools +// by iterating each item. The data in the returned result will be a +// []bool containing the replaced items. +func (v *Value) ReplaceBool(replacer func(int, bool) bool) *Value { + arr := v.MustBoolSlice() + replaced := make([]bool, len(arr)) + v.EachBool(func(index int, val bool) bool { + replaced[index] = replacer(index, val) + return true + }) + return &Value{data: replaced} +} + +// CollectBool uses the specified collector function to collect a value +// for each of the bools in the slice. The data returned will be a +// []interface{}. +func (v *Value) CollectBool(collector func(int, bool) interface{}) *Value { + arr := v.MustBoolSlice() + collected := make([]interface{}, len(arr)) + v.EachBool(func(index int, val bool) bool { + collected[index] = collector(index, val) + return true + }) + return &Value{data: collected} +} + +/* + Str (string and []string) +*/ + +// Str gets the value as a string, returns the optionalDefault +// value or a system default object if the value is the wrong type. +func (v *Value) Str(optionalDefault ...string) string { + if s, ok := v.data.(string); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return "" +} + +// MustStr gets the value as a string. +// +// Panics if the object is not a string. +func (v *Value) MustStr() string { + return v.data.(string) +} + +// StrSlice gets the value as a []string, returns the optionalDefault +// value or nil if the value is not a []string. +func (v *Value) StrSlice(optionalDefault ...[]string) []string { + if s, ok := v.data.([]string); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil +} + +// MustStrSlice gets the value as a []string. +// +// Panics if the object is not a []string. +func (v *Value) MustStrSlice() []string { + return v.data.([]string) +} + +// IsStr gets whether the object contained is a string or not. +func (v *Value) IsStr() bool { + _, ok := v.data.(string) + return ok +} + +// IsStrSlice gets whether the object contained is a []string or not. +func (v *Value) IsStrSlice() bool { + _, ok := v.data.([]string) + return ok +} + +// EachStr calls the specified callback for each object +// in the []string. +// +// Panics if the object is the wrong type. +func (v *Value) EachStr(callback func(int, string) bool) *Value { + for index, val := range v.MustStrSlice() { + carryon := callback(index, val) + if !carryon { + break + } + } + return v +} + +// WhereStr uses the specified decider function to select items +// from the []string. The object contained in the result will contain +// only the selected items. +func (v *Value) WhereStr(decider func(int, string) bool) *Value { + var selected []string + v.EachStr(func(index int, val string) bool { + shouldSelect := decider(index, val) + if !shouldSelect { + selected = append(selected, val) + } + return true + }) + return &Value{data: selected} +} + +// GroupStr uses the specified grouper function to group the items +// keyed by the return of the grouper. The object contained in the +// result will contain a map[string][]string. +func (v *Value) GroupStr(grouper func(int, string) string) *Value { + groups := make(map[string][]string) + v.EachStr(func(index int, val string) bool { + group := grouper(index, val) + if _, ok := groups[group]; !ok { + groups[group] = make([]string, 0) + } + groups[group] = append(groups[group], val) + return true + }) + return &Value{data: groups} +} + +// ReplaceStr uses the specified function to replace each strings +// by iterating each item. The data in the returned result will be a +// []string containing the replaced items. +func (v *Value) ReplaceStr(replacer func(int, string) string) *Value { + arr := v.MustStrSlice() + replaced := make([]string, len(arr)) + v.EachStr(func(index int, val string) bool { + replaced[index] = replacer(index, val) + return true + }) + return &Value{data: replaced} +} + +// CollectStr uses the specified collector function to collect a value +// for each of the strings in the slice. The data returned will be a +// []interface{}. +func (v *Value) CollectStr(collector func(int, string) interface{}) *Value { + arr := v.MustStrSlice() + collected := make([]interface{}, len(arr)) + v.EachStr(func(index int, val string) bool { + collected[index] = collector(index, val) + return true + }) + return &Value{data: collected} +} + +/* + Int (int and []int) +*/ + +// Int gets the value as a int, returns the optionalDefault +// value or a system default object if the value is the wrong type. +func (v *Value) Int(optionalDefault ...int) int { + if s, ok := v.data.(int); ok { + return s + } + if s, ok := v.data.(float64); ok { + if float64(int(s)) == s { + return int(s) + } + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return 0 +} + +// MustInt gets the value as a int. +// +// Panics if the object is not a int. +func (v *Value) MustInt() int { + if s, ok := v.data.(float64); ok { + if float64(int(s)) == s { + return int(s) + } + } + return v.data.(int) +} + +// IntSlice gets the value as a []int, returns the optionalDefault +// value or nil if the value is not a []int. +func (v *Value) IntSlice(optionalDefault ...[]int) []int { + if s, ok := v.data.([]int); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil +} + +// MustIntSlice gets the value as a []int. +// +// Panics if the object is not a []int. +func (v *Value) MustIntSlice() []int { + return v.data.([]int) +} + +// IsInt gets whether the object contained is a int or not. +func (v *Value) IsInt() bool { + _, ok := v.data.(int) + return ok +} + +// IsIntSlice gets whether the object contained is a []int or not. +func (v *Value) IsIntSlice() bool { + _, ok := v.data.([]int) + return ok +} + +// EachInt calls the specified callback for each object +// in the []int. +// +// Panics if the object is the wrong type. +func (v *Value) EachInt(callback func(int, int) bool) *Value { + for index, val := range v.MustIntSlice() { + carryon := callback(index, val) + if !carryon { + break + } + } + return v +} + +// WhereInt uses the specified decider function to select items +// from the []int. The object contained in the result will contain +// only the selected items. +func (v *Value) WhereInt(decider func(int, int) bool) *Value { + var selected []int + v.EachInt(func(index int, val int) bool { + shouldSelect := decider(index, val) + if !shouldSelect { + selected = append(selected, val) + } + return true + }) + return &Value{data: selected} +} + +// GroupInt uses the specified grouper function to group the items +// keyed by the return of the grouper. The object contained in the +// result will contain a map[string][]int. +func (v *Value) GroupInt(grouper func(int, int) string) *Value { + groups := make(map[string][]int) + v.EachInt(func(index int, val int) bool { + group := grouper(index, val) + if _, ok := groups[group]; !ok { + groups[group] = make([]int, 0) + } + groups[group] = append(groups[group], val) + return true + }) + return &Value{data: groups} +} + +// ReplaceInt uses the specified function to replace each ints +// by iterating each item. The data in the returned result will be a +// []int containing the replaced items. +func (v *Value) ReplaceInt(replacer func(int, int) int) *Value { + arr := v.MustIntSlice() + replaced := make([]int, len(arr)) + v.EachInt(func(index int, val int) bool { + replaced[index] = replacer(index, val) + return true + }) + return &Value{data: replaced} +} + +// CollectInt uses the specified collector function to collect a value +// for each of the ints in the slice. The data returned will be a +// []interface{}. +func (v *Value) CollectInt(collector func(int, int) interface{}) *Value { + arr := v.MustIntSlice() + collected := make([]interface{}, len(arr)) + v.EachInt(func(index int, val int) bool { + collected[index] = collector(index, val) + return true + }) + return &Value{data: collected} +} + +/* + Int8 (int8 and []int8) +*/ + +// Int8 gets the value as a int8, returns the optionalDefault +// value or a system default object if the value is the wrong type. +func (v *Value) Int8(optionalDefault ...int8) int8 { + if s, ok := v.data.(int8); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return 0 +} + +// MustInt8 gets the value as a int8. +// +// Panics if the object is not a int8. +func (v *Value) MustInt8() int8 { + return v.data.(int8) +} + +// Int8Slice gets the value as a []int8, returns the optionalDefault +// value or nil if the value is not a []int8. +func (v *Value) Int8Slice(optionalDefault ...[]int8) []int8 { + if s, ok := v.data.([]int8); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil +} + +// MustInt8Slice gets the value as a []int8. +// +// Panics if the object is not a []int8. +func (v *Value) MustInt8Slice() []int8 { + return v.data.([]int8) +} + +// IsInt8 gets whether the object contained is a int8 or not. +func (v *Value) IsInt8() bool { + _, ok := v.data.(int8) + return ok +} + +// IsInt8Slice gets whether the object contained is a []int8 or not. +func (v *Value) IsInt8Slice() bool { + _, ok := v.data.([]int8) + return ok +} + +// EachInt8 calls the specified callback for each object +// in the []int8. +// +// Panics if the object is the wrong type. +func (v *Value) EachInt8(callback func(int, int8) bool) *Value { + for index, val := range v.MustInt8Slice() { + carryon := callback(index, val) + if !carryon { + break + } + } + return v +} + +// WhereInt8 uses the specified decider function to select items +// from the []int8. The object contained in the result will contain +// only the selected items. +func (v *Value) WhereInt8(decider func(int, int8) bool) *Value { + var selected []int8 + v.EachInt8(func(index int, val int8) bool { + shouldSelect := decider(index, val) + if !shouldSelect { + selected = append(selected, val) + } + return true + }) + return &Value{data: selected} +} + +// GroupInt8 uses the specified grouper function to group the items +// keyed by the return of the grouper. The object contained in the +// result will contain a map[string][]int8. +func (v *Value) GroupInt8(grouper func(int, int8) string) *Value { + groups := make(map[string][]int8) + v.EachInt8(func(index int, val int8) bool { + group := grouper(index, val) + if _, ok := groups[group]; !ok { + groups[group] = make([]int8, 0) + } + groups[group] = append(groups[group], val) + return true + }) + return &Value{data: groups} +} + +// ReplaceInt8 uses the specified function to replace each int8s +// by iterating each item. The data in the returned result will be a +// []int8 containing the replaced items. +func (v *Value) ReplaceInt8(replacer func(int, int8) int8) *Value { + arr := v.MustInt8Slice() + replaced := make([]int8, len(arr)) + v.EachInt8(func(index int, val int8) bool { + replaced[index] = replacer(index, val) + return true + }) + return &Value{data: replaced} +} + +// CollectInt8 uses the specified collector function to collect a value +// for each of the int8s in the slice. The data returned will be a +// []interface{}. +func (v *Value) CollectInt8(collector func(int, int8) interface{}) *Value { + arr := v.MustInt8Slice() + collected := make([]interface{}, len(arr)) + v.EachInt8(func(index int, val int8) bool { + collected[index] = collector(index, val) + return true + }) + return &Value{data: collected} +} + +/* + Int16 (int16 and []int16) +*/ + +// Int16 gets the value as a int16, returns the optionalDefault +// value or a system default object if the value is the wrong type. +func (v *Value) Int16(optionalDefault ...int16) int16 { + if s, ok := v.data.(int16); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return 0 +} + +// MustInt16 gets the value as a int16. +// +// Panics if the object is not a int16. +func (v *Value) MustInt16() int16 { + return v.data.(int16) +} + +// Int16Slice gets the value as a []int16, returns the optionalDefault +// value or nil if the value is not a []int16. +func (v *Value) Int16Slice(optionalDefault ...[]int16) []int16 { + if s, ok := v.data.([]int16); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil +} + +// MustInt16Slice gets the value as a []int16. +// +// Panics if the object is not a []int16. +func (v *Value) MustInt16Slice() []int16 { + return v.data.([]int16) +} + +// IsInt16 gets whether the object contained is a int16 or not. +func (v *Value) IsInt16() bool { + _, ok := v.data.(int16) + return ok +} + +// IsInt16Slice gets whether the object contained is a []int16 or not. +func (v *Value) IsInt16Slice() bool { + _, ok := v.data.([]int16) + return ok +} + +// EachInt16 calls the specified callback for each object +// in the []int16. +// +// Panics if the object is the wrong type. +func (v *Value) EachInt16(callback func(int, int16) bool) *Value { + for index, val := range v.MustInt16Slice() { + carryon := callback(index, val) + if !carryon { + break + } + } + return v +} + +// WhereInt16 uses the specified decider function to select items +// from the []int16. The object contained in the result will contain +// only the selected items. +func (v *Value) WhereInt16(decider func(int, int16) bool) *Value { + var selected []int16 + v.EachInt16(func(index int, val int16) bool { + shouldSelect := decider(index, val) + if !shouldSelect { + selected = append(selected, val) + } + return true + }) + return &Value{data: selected} +} + +// GroupInt16 uses the specified grouper function to group the items +// keyed by the return of the grouper. The object contained in the +// result will contain a map[string][]int16. +func (v *Value) GroupInt16(grouper func(int, int16) string) *Value { + groups := make(map[string][]int16) + v.EachInt16(func(index int, val int16) bool { + group := grouper(index, val) + if _, ok := groups[group]; !ok { + groups[group] = make([]int16, 0) + } + groups[group] = append(groups[group], val) + return true + }) + return &Value{data: groups} +} + +// ReplaceInt16 uses the specified function to replace each int16s +// by iterating each item. The data in the returned result will be a +// []int16 containing the replaced items. +func (v *Value) ReplaceInt16(replacer func(int, int16) int16) *Value { + arr := v.MustInt16Slice() + replaced := make([]int16, len(arr)) + v.EachInt16(func(index int, val int16) bool { + replaced[index] = replacer(index, val) + return true + }) + return &Value{data: replaced} +} + +// CollectInt16 uses the specified collector function to collect a value +// for each of the int16s in the slice. The data returned will be a +// []interface{}. +func (v *Value) CollectInt16(collector func(int, int16) interface{}) *Value { + arr := v.MustInt16Slice() + collected := make([]interface{}, len(arr)) + v.EachInt16(func(index int, val int16) bool { + collected[index] = collector(index, val) + return true + }) + return &Value{data: collected} +} + +/* + Int32 (int32 and []int32) +*/ + +// Int32 gets the value as a int32, returns the optionalDefault +// value or a system default object if the value is the wrong type. +func (v *Value) Int32(optionalDefault ...int32) int32 { + if s, ok := v.data.(int32); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return 0 +} + +// MustInt32 gets the value as a int32. +// +// Panics if the object is not a int32. +func (v *Value) MustInt32() int32 { + return v.data.(int32) +} + +// Int32Slice gets the value as a []int32, returns the optionalDefault +// value or nil if the value is not a []int32. +func (v *Value) Int32Slice(optionalDefault ...[]int32) []int32 { + if s, ok := v.data.([]int32); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil +} + +// MustInt32Slice gets the value as a []int32. +// +// Panics if the object is not a []int32. +func (v *Value) MustInt32Slice() []int32 { + return v.data.([]int32) +} + +// IsInt32 gets whether the object contained is a int32 or not. +func (v *Value) IsInt32() bool { + _, ok := v.data.(int32) + return ok +} + +// IsInt32Slice gets whether the object contained is a []int32 or not. +func (v *Value) IsInt32Slice() bool { + _, ok := v.data.([]int32) + return ok +} + +// EachInt32 calls the specified callback for each object +// in the []int32. +// +// Panics if the object is the wrong type. +func (v *Value) EachInt32(callback func(int, int32) bool) *Value { + for index, val := range v.MustInt32Slice() { + carryon := callback(index, val) + if !carryon { + break + } + } + return v +} + +// WhereInt32 uses the specified decider function to select items +// from the []int32. The object contained in the result will contain +// only the selected items. +func (v *Value) WhereInt32(decider func(int, int32) bool) *Value { + var selected []int32 + v.EachInt32(func(index int, val int32) bool { + shouldSelect := decider(index, val) + if !shouldSelect { + selected = append(selected, val) + } + return true + }) + return &Value{data: selected} +} + +// GroupInt32 uses the specified grouper function to group the items +// keyed by the return of the grouper. The object contained in the +// result will contain a map[string][]int32. +func (v *Value) GroupInt32(grouper func(int, int32) string) *Value { + groups := make(map[string][]int32) + v.EachInt32(func(index int, val int32) bool { + group := grouper(index, val) + if _, ok := groups[group]; !ok { + groups[group] = make([]int32, 0) + } + groups[group] = append(groups[group], val) + return true + }) + return &Value{data: groups} +} + +// ReplaceInt32 uses the specified function to replace each int32s +// by iterating each item. The data in the returned result will be a +// []int32 containing the replaced items. +func (v *Value) ReplaceInt32(replacer func(int, int32) int32) *Value { + arr := v.MustInt32Slice() + replaced := make([]int32, len(arr)) + v.EachInt32(func(index int, val int32) bool { + replaced[index] = replacer(index, val) + return true + }) + return &Value{data: replaced} +} + +// CollectInt32 uses the specified collector function to collect a value +// for each of the int32s in the slice. The data returned will be a +// []interface{}. +func (v *Value) CollectInt32(collector func(int, int32) interface{}) *Value { + arr := v.MustInt32Slice() + collected := make([]interface{}, len(arr)) + v.EachInt32(func(index int, val int32) bool { + collected[index] = collector(index, val) + return true + }) + return &Value{data: collected} +} + +/* + Int64 (int64 and []int64) +*/ + +// Int64 gets the value as a int64, returns the optionalDefault +// value or a system default object if the value is the wrong type. +func (v *Value) Int64(optionalDefault ...int64) int64 { + if s, ok := v.data.(int64); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return 0 +} + +// MustInt64 gets the value as a int64. +// +// Panics if the object is not a int64. +func (v *Value) MustInt64() int64 { + return v.data.(int64) +} + +// Int64Slice gets the value as a []int64, returns the optionalDefault +// value or nil if the value is not a []int64. +func (v *Value) Int64Slice(optionalDefault ...[]int64) []int64 { + if s, ok := v.data.([]int64); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil +} + +// MustInt64Slice gets the value as a []int64. +// +// Panics if the object is not a []int64. +func (v *Value) MustInt64Slice() []int64 { + return v.data.([]int64) +} + +// IsInt64 gets whether the object contained is a int64 or not. +func (v *Value) IsInt64() bool { + _, ok := v.data.(int64) + return ok +} + +// IsInt64Slice gets whether the object contained is a []int64 or not. +func (v *Value) IsInt64Slice() bool { + _, ok := v.data.([]int64) + return ok +} + +// EachInt64 calls the specified callback for each object +// in the []int64. +// +// Panics if the object is the wrong type. +func (v *Value) EachInt64(callback func(int, int64) bool) *Value { + for index, val := range v.MustInt64Slice() { + carryon := callback(index, val) + if !carryon { + break + } + } + return v +} + +// WhereInt64 uses the specified decider function to select items +// from the []int64. The object contained in the result will contain +// only the selected items. +func (v *Value) WhereInt64(decider func(int, int64) bool) *Value { + var selected []int64 + v.EachInt64(func(index int, val int64) bool { + shouldSelect := decider(index, val) + if !shouldSelect { + selected = append(selected, val) + } + return true + }) + return &Value{data: selected} +} + +// GroupInt64 uses the specified grouper function to group the items +// keyed by the return of the grouper. The object contained in the +// result will contain a map[string][]int64. +func (v *Value) GroupInt64(grouper func(int, int64) string) *Value { + groups := make(map[string][]int64) + v.EachInt64(func(index int, val int64) bool { + group := grouper(index, val) + if _, ok := groups[group]; !ok { + groups[group] = make([]int64, 0) + } + groups[group] = append(groups[group], val) + return true + }) + return &Value{data: groups} +} + +// ReplaceInt64 uses the specified function to replace each int64s +// by iterating each item. The data in the returned result will be a +// []int64 containing the replaced items. +func (v *Value) ReplaceInt64(replacer func(int, int64) int64) *Value { + arr := v.MustInt64Slice() + replaced := make([]int64, len(arr)) + v.EachInt64(func(index int, val int64) bool { + replaced[index] = replacer(index, val) + return true + }) + return &Value{data: replaced} +} + +// CollectInt64 uses the specified collector function to collect a value +// for each of the int64s in the slice. The data returned will be a +// []interface{}. +func (v *Value) CollectInt64(collector func(int, int64) interface{}) *Value { + arr := v.MustInt64Slice() + collected := make([]interface{}, len(arr)) + v.EachInt64(func(index int, val int64) bool { + collected[index] = collector(index, val) + return true + }) + return &Value{data: collected} +} + +/* + Uint (uint and []uint) +*/ + +// Uint gets the value as a uint, returns the optionalDefault +// value or a system default object if the value is the wrong type. +func (v *Value) Uint(optionalDefault ...uint) uint { + if s, ok := v.data.(uint); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return 0 +} + +// MustUint gets the value as a uint. +// +// Panics if the object is not a uint. +func (v *Value) MustUint() uint { + return v.data.(uint) +} + +// UintSlice gets the value as a []uint, returns the optionalDefault +// value or nil if the value is not a []uint. +func (v *Value) UintSlice(optionalDefault ...[]uint) []uint { + if s, ok := v.data.([]uint); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil +} + +// MustUintSlice gets the value as a []uint. +// +// Panics if the object is not a []uint. +func (v *Value) MustUintSlice() []uint { + return v.data.([]uint) +} + +// IsUint gets whether the object contained is a uint or not. +func (v *Value) IsUint() bool { + _, ok := v.data.(uint) + return ok +} + +// IsUintSlice gets whether the object contained is a []uint or not. +func (v *Value) IsUintSlice() bool { + _, ok := v.data.([]uint) + return ok +} + +// EachUint calls the specified callback for each object +// in the []uint. +// +// Panics if the object is the wrong type. +func (v *Value) EachUint(callback func(int, uint) bool) *Value { + for index, val := range v.MustUintSlice() { + carryon := callback(index, val) + if !carryon { + break + } + } + return v +} + +// WhereUint uses the specified decider function to select items +// from the []uint. The object contained in the result will contain +// only the selected items. +func (v *Value) WhereUint(decider func(int, uint) bool) *Value { + var selected []uint + v.EachUint(func(index int, val uint) bool { + shouldSelect := decider(index, val) + if !shouldSelect { + selected = append(selected, val) + } + return true + }) + return &Value{data: selected} +} + +// GroupUint uses the specified grouper function to group the items +// keyed by the return of the grouper. The object contained in the +// result will contain a map[string][]uint. +func (v *Value) GroupUint(grouper func(int, uint) string) *Value { + groups := make(map[string][]uint) + v.EachUint(func(index int, val uint) bool { + group := grouper(index, val) + if _, ok := groups[group]; !ok { + groups[group] = make([]uint, 0) + } + groups[group] = append(groups[group], val) + return true + }) + return &Value{data: groups} +} + +// ReplaceUint uses the specified function to replace each uints +// by iterating each item. The data in the returned result will be a +// []uint containing the replaced items. +func (v *Value) ReplaceUint(replacer func(int, uint) uint) *Value { + arr := v.MustUintSlice() + replaced := make([]uint, len(arr)) + v.EachUint(func(index int, val uint) bool { + replaced[index] = replacer(index, val) + return true + }) + return &Value{data: replaced} +} + +// CollectUint uses the specified collector function to collect a value +// for each of the uints in the slice. The data returned will be a +// []interface{}. +func (v *Value) CollectUint(collector func(int, uint) interface{}) *Value { + arr := v.MustUintSlice() + collected := make([]interface{}, len(arr)) + v.EachUint(func(index int, val uint) bool { + collected[index] = collector(index, val) + return true + }) + return &Value{data: collected} +} + +/* + Uint8 (uint8 and []uint8) +*/ + +// Uint8 gets the value as a uint8, returns the optionalDefault +// value or a system default object if the value is the wrong type. +func (v *Value) Uint8(optionalDefault ...uint8) uint8 { + if s, ok := v.data.(uint8); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return 0 +} + +// MustUint8 gets the value as a uint8. +// +// Panics if the object is not a uint8. +func (v *Value) MustUint8() uint8 { + return v.data.(uint8) +} + +// Uint8Slice gets the value as a []uint8, returns the optionalDefault +// value or nil if the value is not a []uint8. +func (v *Value) Uint8Slice(optionalDefault ...[]uint8) []uint8 { + if s, ok := v.data.([]uint8); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil +} + +// MustUint8Slice gets the value as a []uint8. +// +// Panics if the object is not a []uint8. +func (v *Value) MustUint8Slice() []uint8 { + return v.data.([]uint8) +} + +// IsUint8 gets whether the object contained is a uint8 or not. +func (v *Value) IsUint8() bool { + _, ok := v.data.(uint8) + return ok +} + +// IsUint8Slice gets whether the object contained is a []uint8 or not. +func (v *Value) IsUint8Slice() bool { + _, ok := v.data.([]uint8) + return ok +} + +// EachUint8 calls the specified callback for each object +// in the []uint8. +// +// Panics if the object is the wrong type. +func (v *Value) EachUint8(callback func(int, uint8) bool) *Value { + for index, val := range v.MustUint8Slice() { + carryon := callback(index, val) + if !carryon { + break + } + } + return v +} + +// WhereUint8 uses the specified decider function to select items +// from the []uint8. The object contained in the result will contain +// only the selected items. +func (v *Value) WhereUint8(decider func(int, uint8) bool) *Value { + var selected []uint8 + v.EachUint8(func(index int, val uint8) bool { + shouldSelect := decider(index, val) + if !shouldSelect { + selected = append(selected, val) + } + return true + }) + return &Value{data: selected} +} + +// GroupUint8 uses the specified grouper function to group the items +// keyed by the return of the grouper. The object contained in the +// result will contain a map[string][]uint8. +func (v *Value) GroupUint8(grouper func(int, uint8) string) *Value { + groups := make(map[string][]uint8) + v.EachUint8(func(index int, val uint8) bool { + group := grouper(index, val) + if _, ok := groups[group]; !ok { + groups[group] = make([]uint8, 0) + } + groups[group] = append(groups[group], val) + return true + }) + return &Value{data: groups} +} + +// ReplaceUint8 uses the specified function to replace each uint8s +// by iterating each item. The data in the returned result will be a +// []uint8 containing the replaced items. +func (v *Value) ReplaceUint8(replacer func(int, uint8) uint8) *Value { + arr := v.MustUint8Slice() + replaced := make([]uint8, len(arr)) + v.EachUint8(func(index int, val uint8) bool { + replaced[index] = replacer(index, val) + return true + }) + return &Value{data: replaced} +} + +// CollectUint8 uses the specified collector function to collect a value +// for each of the uint8s in the slice. The data returned will be a +// []interface{}. +func (v *Value) CollectUint8(collector func(int, uint8) interface{}) *Value { + arr := v.MustUint8Slice() + collected := make([]interface{}, len(arr)) + v.EachUint8(func(index int, val uint8) bool { + collected[index] = collector(index, val) + return true + }) + return &Value{data: collected} +} + +/* + Uint16 (uint16 and []uint16) +*/ + +// Uint16 gets the value as a uint16, returns the optionalDefault +// value or a system default object if the value is the wrong type. +func (v *Value) Uint16(optionalDefault ...uint16) uint16 { + if s, ok := v.data.(uint16); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return 0 +} + +// MustUint16 gets the value as a uint16. +// +// Panics if the object is not a uint16. +func (v *Value) MustUint16() uint16 { + return v.data.(uint16) +} + +// Uint16Slice gets the value as a []uint16, returns the optionalDefault +// value or nil if the value is not a []uint16. +func (v *Value) Uint16Slice(optionalDefault ...[]uint16) []uint16 { + if s, ok := v.data.([]uint16); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil +} + +// MustUint16Slice gets the value as a []uint16. +// +// Panics if the object is not a []uint16. +func (v *Value) MustUint16Slice() []uint16 { + return v.data.([]uint16) +} + +// IsUint16 gets whether the object contained is a uint16 or not. +func (v *Value) IsUint16() bool { + _, ok := v.data.(uint16) + return ok +} + +// IsUint16Slice gets whether the object contained is a []uint16 or not. +func (v *Value) IsUint16Slice() bool { + _, ok := v.data.([]uint16) + return ok +} + +// EachUint16 calls the specified callback for each object +// in the []uint16. +// +// Panics if the object is the wrong type. +func (v *Value) EachUint16(callback func(int, uint16) bool) *Value { + for index, val := range v.MustUint16Slice() { + carryon := callback(index, val) + if !carryon { + break + } + } + return v +} + +// WhereUint16 uses the specified decider function to select items +// from the []uint16. The object contained in the result will contain +// only the selected items. +func (v *Value) WhereUint16(decider func(int, uint16) bool) *Value { + var selected []uint16 + v.EachUint16(func(index int, val uint16) bool { + shouldSelect := decider(index, val) + if !shouldSelect { + selected = append(selected, val) + } + return true + }) + return &Value{data: selected} +} + +// GroupUint16 uses the specified grouper function to group the items +// keyed by the return of the grouper. The object contained in the +// result will contain a map[string][]uint16. +func (v *Value) GroupUint16(grouper func(int, uint16) string) *Value { + groups := make(map[string][]uint16) + v.EachUint16(func(index int, val uint16) bool { + group := grouper(index, val) + if _, ok := groups[group]; !ok { + groups[group] = make([]uint16, 0) + } + groups[group] = append(groups[group], val) + return true + }) + return &Value{data: groups} +} + +// ReplaceUint16 uses the specified function to replace each uint16s +// by iterating each item. The data in the returned result will be a +// []uint16 containing the replaced items. +func (v *Value) ReplaceUint16(replacer func(int, uint16) uint16) *Value { + arr := v.MustUint16Slice() + replaced := make([]uint16, len(arr)) + v.EachUint16(func(index int, val uint16) bool { + replaced[index] = replacer(index, val) + return true + }) + return &Value{data: replaced} +} + +// CollectUint16 uses the specified collector function to collect a value +// for each of the uint16s in the slice. The data returned will be a +// []interface{}. +func (v *Value) CollectUint16(collector func(int, uint16) interface{}) *Value { + arr := v.MustUint16Slice() + collected := make([]interface{}, len(arr)) + v.EachUint16(func(index int, val uint16) bool { + collected[index] = collector(index, val) + return true + }) + return &Value{data: collected} +} + +/* + Uint32 (uint32 and []uint32) +*/ + +// Uint32 gets the value as a uint32, returns the optionalDefault +// value or a system default object if the value is the wrong type. +func (v *Value) Uint32(optionalDefault ...uint32) uint32 { + if s, ok := v.data.(uint32); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return 0 +} + +// MustUint32 gets the value as a uint32. +// +// Panics if the object is not a uint32. +func (v *Value) MustUint32() uint32 { + return v.data.(uint32) +} + +// Uint32Slice gets the value as a []uint32, returns the optionalDefault +// value or nil if the value is not a []uint32. +func (v *Value) Uint32Slice(optionalDefault ...[]uint32) []uint32 { + if s, ok := v.data.([]uint32); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil +} + +// MustUint32Slice gets the value as a []uint32. +// +// Panics if the object is not a []uint32. +func (v *Value) MustUint32Slice() []uint32 { + return v.data.([]uint32) +} + +// IsUint32 gets whether the object contained is a uint32 or not. +func (v *Value) IsUint32() bool { + _, ok := v.data.(uint32) + return ok +} + +// IsUint32Slice gets whether the object contained is a []uint32 or not. +func (v *Value) IsUint32Slice() bool { + _, ok := v.data.([]uint32) + return ok +} + +// EachUint32 calls the specified callback for each object +// in the []uint32. +// +// Panics if the object is the wrong type. +func (v *Value) EachUint32(callback func(int, uint32) bool) *Value { + for index, val := range v.MustUint32Slice() { + carryon := callback(index, val) + if !carryon { + break + } + } + return v +} + +// WhereUint32 uses the specified decider function to select items +// from the []uint32. The object contained in the result will contain +// only the selected items. +func (v *Value) WhereUint32(decider func(int, uint32) bool) *Value { + var selected []uint32 + v.EachUint32(func(index int, val uint32) bool { + shouldSelect := decider(index, val) + if !shouldSelect { + selected = append(selected, val) + } + return true + }) + return &Value{data: selected} +} + +// GroupUint32 uses the specified grouper function to group the items +// keyed by the return of the grouper. The object contained in the +// result will contain a map[string][]uint32. +func (v *Value) GroupUint32(grouper func(int, uint32) string) *Value { + groups := make(map[string][]uint32) + v.EachUint32(func(index int, val uint32) bool { + group := grouper(index, val) + if _, ok := groups[group]; !ok { + groups[group] = make([]uint32, 0) + } + groups[group] = append(groups[group], val) + return true + }) + return &Value{data: groups} +} + +// ReplaceUint32 uses the specified function to replace each uint32s +// by iterating each item. The data in the returned result will be a +// []uint32 containing the replaced items. +func (v *Value) ReplaceUint32(replacer func(int, uint32) uint32) *Value { + arr := v.MustUint32Slice() + replaced := make([]uint32, len(arr)) + v.EachUint32(func(index int, val uint32) bool { + replaced[index] = replacer(index, val) + return true + }) + return &Value{data: replaced} +} + +// CollectUint32 uses the specified collector function to collect a value +// for each of the uint32s in the slice. The data returned will be a +// []interface{}. +func (v *Value) CollectUint32(collector func(int, uint32) interface{}) *Value { + arr := v.MustUint32Slice() + collected := make([]interface{}, len(arr)) + v.EachUint32(func(index int, val uint32) bool { + collected[index] = collector(index, val) + return true + }) + return &Value{data: collected} +} + +/* + Uint64 (uint64 and []uint64) +*/ + +// Uint64 gets the value as a uint64, returns the optionalDefault +// value or a system default object if the value is the wrong type. +func (v *Value) Uint64(optionalDefault ...uint64) uint64 { + if s, ok := v.data.(uint64); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return 0 +} + +// MustUint64 gets the value as a uint64. +// +// Panics if the object is not a uint64. +func (v *Value) MustUint64() uint64 { + return v.data.(uint64) +} + +// Uint64Slice gets the value as a []uint64, returns the optionalDefault +// value or nil if the value is not a []uint64. +func (v *Value) Uint64Slice(optionalDefault ...[]uint64) []uint64 { + if s, ok := v.data.([]uint64); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil +} + +// MustUint64Slice gets the value as a []uint64. +// +// Panics if the object is not a []uint64. +func (v *Value) MustUint64Slice() []uint64 { + return v.data.([]uint64) +} + +// IsUint64 gets whether the object contained is a uint64 or not. +func (v *Value) IsUint64() bool { + _, ok := v.data.(uint64) + return ok +} + +// IsUint64Slice gets whether the object contained is a []uint64 or not. +func (v *Value) IsUint64Slice() bool { + _, ok := v.data.([]uint64) + return ok +} + +// EachUint64 calls the specified callback for each object +// in the []uint64. +// +// Panics if the object is the wrong type. +func (v *Value) EachUint64(callback func(int, uint64) bool) *Value { + for index, val := range v.MustUint64Slice() { + carryon := callback(index, val) + if !carryon { + break + } + } + return v +} + +// WhereUint64 uses the specified decider function to select items +// from the []uint64. The object contained in the result will contain +// only the selected items. +func (v *Value) WhereUint64(decider func(int, uint64) bool) *Value { + var selected []uint64 + v.EachUint64(func(index int, val uint64) bool { + shouldSelect := decider(index, val) + if !shouldSelect { + selected = append(selected, val) + } + return true + }) + return &Value{data: selected} +} + +// GroupUint64 uses the specified grouper function to group the items +// keyed by the return of the grouper. The object contained in the +// result will contain a map[string][]uint64. +func (v *Value) GroupUint64(grouper func(int, uint64) string) *Value { + groups := make(map[string][]uint64) + v.EachUint64(func(index int, val uint64) bool { + group := grouper(index, val) + if _, ok := groups[group]; !ok { + groups[group] = make([]uint64, 0) + } + groups[group] = append(groups[group], val) + return true + }) + return &Value{data: groups} +} + +// ReplaceUint64 uses the specified function to replace each uint64s +// by iterating each item. The data in the returned result will be a +// []uint64 containing the replaced items. +func (v *Value) ReplaceUint64(replacer func(int, uint64) uint64) *Value { + arr := v.MustUint64Slice() + replaced := make([]uint64, len(arr)) + v.EachUint64(func(index int, val uint64) bool { + replaced[index] = replacer(index, val) + return true + }) + return &Value{data: replaced} +} + +// CollectUint64 uses the specified collector function to collect a value +// for each of the uint64s in the slice. The data returned will be a +// []interface{}. +func (v *Value) CollectUint64(collector func(int, uint64) interface{}) *Value { + arr := v.MustUint64Slice() + collected := make([]interface{}, len(arr)) + v.EachUint64(func(index int, val uint64) bool { + collected[index] = collector(index, val) + return true + }) + return &Value{data: collected} +} + +/* + Uintptr (uintptr and []uintptr) +*/ + +// Uintptr gets the value as a uintptr, returns the optionalDefault +// value or a system default object if the value is the wrong type. +func (v *Value) Uintptr(optionalDefault ...uintptr) uintptr { + if s, ok := v.data.(uintptr); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return 0 +} + +// MustUintptr gets the value as a uintptr. +// +// Panics if the object is not a uintptr. +func (v *Value) MustUintptr() uintptr { + return v.data.(uintptr) +} + +// UintptrSlice gets the value as a []uintptr, returns the optionalDefault +// value or nil if the value is not a []uintptr. +func (v *Value) UintptrSlice(optionalDefault ...[]uintptr) []uintptr { + if s, ok := v.data.([]uintptr); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil +} + +// MustUintptrSlice gets the value as a []uintptr. +// +// Panics if the object is not a []uintptr. +func (v *Value) MustUintptrSlice() []uintptr { + return v.data.([]uintptr) +} + +// IsUintptr gets whether the object contained is a uintptr or not. +func (v *Value) IsUintptr() bool { + _, ok := v.data.(uintptr) + return ok +} + +// IsUintptrSlice gets whether the object contained is a []uintptr or not. +func (v *Value) IsUintptrSlice() bool { + _, ok := v.data.([]uintptr) + return ok +} + +// EachUintptr calls the specified callback for each object +// in the []uintptr. +// +// Panics if the object is the wrong type. +func (v *Value) EachUintptr(callback func(int, uintptr) bool) *Value { + for index, val := range v.MustUintptrSlice() { + carryon := callback(index, val) + if !carryon { + break + } + } + return v +} + +// WhereUintptr uses the specified decider function to select items +// from the []uintptr. The object contained in the result will contain +// only the selected items. +func (v *Value) WhereUintptr(decider func(int, uintptr) bool) *Value { + var selected []uintptr + v.EachUintptr(func(index int, val uintptr) bool { + shouldSelect := decider(index, val) + if !shouldSelect { + selected = append(selected, val) + } + return true + }) + return &Value{data: selected} +} + +// GroupUintptr uses the specified grouper function to group the items +// keyed by the return of the grouper. The object contained in the +// result will contain a map[string][]uintptr. +func (v *Value) GroupUintptr(grouper func(int, uintptr) string) *Value { + groups := make(map[string][]uintptr) + v.EachUintptr(func(index int, val uintptr) bool { + group := grouper(index, val) + if _, ok := groups[group]; !ok { + groups[group] = make([]uintptr, 0) + } + groups[group] = append(groups[group], val) + return true + }) + return &Value{data: groups} +} + +// ReplaceUintptr uses the specified function to replace each uintptrs +// by iterating each item. The data in the returned result will be a +// []uintptr containing the replaced items. +func (v *Value) ReplaceUintptr(replacer func(int, uintptr) uintptr) *Value { + arr := v.MustUintptrSlice() + replaced := make([]uintptr, len(arr)) + v.EachUintptr(func(index int, val uintptr) bool { + replaced[index] = replacer(index, val) + return true + }) + return &Value{data: replaced} +} + +// CollectUintptr uses the specified collector function to collect a value +// for each of the uintptrs in the slice. The data returned will be a +// []interface{}. +func (v *Value) CollectUintptr(collector func(int, uintptr) interface{}) *Value { + arr := v.MustUintptrSlice() + collected := make([]interface{}, len(arr)) + v.EachUintptr(func(index int, val uintptr) bool { + collected[index] = collector(index, val) + return true + }) + return &Value{data: collected} +} + +/* + Float32 (float32 and []float32) +*/ + +// Float32 gets the value as a float32, returns the optionalDefault +// value or a system default object if the value is the wrong type. +func (v *Value) Float32(optionalDefault ...float32) float32 { + if s, ok := v.data.(float32); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return 0 +} + +// MustFloat32 gets the value as a float32. +// +// Panics if the object is not a float32. +func (v *Value) MustFloat32() float32 { + return v.data.(float32) +} + +// Float32Slice gets the value as a []float32, returns the optionalDefault +// value or nil if the value is not a []float32. +func (v *Value) Float32Slice(optionalDefault ...[]float32) []float32 { + if s, ok := v.data.([]float32); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil +} + +// MustFloat32Slice gets the value as a []float32. +// +// Panics if the object is not a []float32. +func (v *Value) MustFloat32Slice() []float32 { + return v.data.([]float32) +} + +// IsFloat32 gets whether the object contained is a float32 or not. +func (v *Value) IsFloat32() bool { + _, ok := v.data.(float32) + return ok +} + +// IsFloat32Slice gets whether the object contained is a []float32 or not. +func (v *Value) IsFloat32Slice() bool { + _, ok := v.data.([]float32) + return ok +} + +// EachFloat32 calls the specified callback for each object +// in the []float32. +// +// Panics if the object is the wrong type. +func (v *Value) EachFloat32(callback func(int, float32) bool) *Value { + for index, val := range v.MustFloat32Slice() { + carryon := callback(index, val) + if !carryon { + break + } + } + return v +} + +// WhereFloat32 uses the specified decider function to select items +// from the []float32. The object contained in the result will contain +// only the selected items. +func (v *Value) WhereFloat32(decider func(int, float32) bool) *Value { + var selected []float32 + v.EachFloat32(func(index int, val float32) bool { + shouldSelect := decider(index, val) + if !shouldSelect { + selected = append(selected, val) + } + return true + }) + return &Value{data: selected} +} + +// GroupFloat32 uses the specified grouper function to group the items +// keyed by the return of the grouper. The object contained in the +// result will contain a map[string][]float32. +func (v *Value) GroupFloat32(grouper func(int, float32) string) *Value { + groups := make(map[string][]float32) + v.EachFloat32(func(index int, val float32) bool { + group := grouper(index, val) + if _, ok := groups[group]; !ok { + groups[group] = make([]float32, 0) + } + groups[group] = append(groups[group], val) + return true + }) + return &Value{data: groups} +} + +// ReplaceFloat32 uses the specified function to replace each float32s +// by iterating each item. The data in the returned result will be a +// []float32 containing the replaced items. +func (v *Value) ReplaceFloat32(replacer func(int, float32) float32) *Value { + arr := v.MustFloat32Slice() + replaced := make([]float32, len(arr)) + v.EachFloat32(func(index int, val float32) bool { + replaced[index] = replacer(index, val) + return true + }) + return &Value{data: replaced} +} + +// CollectFloat32 uses the specified collector function to collect a value +// for each of the float32s in the slice. The data returned will be a +// []interface{}. +func (v *Value) CollectFloat32(collector func(int, float32) interface{}) *Value { + arr := v.MustFloat32Slice() + collected := make([]interface{}, len(arr)) + v.EachFloat32(func(index int, val float32) bool { + collected[index] = collector(index, val) + return true + }) + return &Value{data: collected} +} + +/* + Float64 (float64 and []float64) +*/ + +// Float64 gets the value as a float64, returns the optionalDefault +// value or a system default object if the value is the wrong type. +func (v *Value) Float64(optionalDefault ...float64) float64 { + if s, ok := v.data.(float64); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return 0 +} + +// MustFloat64 gets the value as a float64. +// +// Panics if the object is not a float64. +func (v *Value) MustFloat64() float64 { + return v.data.(float64) +} + +// Float64Slice gets the value as a []float64, returns the optionalDefault +// value or nil if the value is not a []float64. +func (v *Value) Float64Slice(optionalDefault ...[]float64) []float64 { + if s, ok := v.data.([]float64); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil +} + +// MustFloat64Slice gets the value as a []float64. +// +// Panics if the object is not a []float64. +func (v *Value) MustFloat64Slice() []float64 { + return v.data.([]float64) +} + +// IsFloat64 gets whether the object contained is a float64 or not. +func (v *Value) IsFloat64() bool { + _, ok := v.data.(float64) + return ok +} + +// IsFloat64Slice gets whether the object contained is a []float64 or not. +func (v *Value) IsFloat64Slice() bool { + _, ok := v.data.([]float64) + return ok +} + +// EachFloat64 calls the specified callback for each object +// in the []float64. +// +// Panics if the object is the wrong type. +func (v *Value) EachFloat64(callback func(int, float64) bool) *Value { + for index, val := range v.MustFloat64Slice() { + carryon := callback(index, val) + if !carryon { + break + } + } + return v +} + +// WhereFloat64 uses the specified decider function to select items +// from the []float64. The object contained in the result will contain +// only the selected items. +func (v *Value) WhereFloat64(decider func(int, float64) bool) *Value { + var selected []float64 + v.EachFloat64(func(index int, val float64) bool { + shouldSelect := decider(index, val) + if !shouldSelect { + selected = append(selected, val) + } + return true + }) + return &Value{data: selected} +} + +// GroupFloat64 uses the specified grouper function to group the items +// keyed by the return of the grouper. The object contained in the +// result will contain a map[string][]float64. +func (v *Value) GroupFloat64(grouper func(int, float64) string) *Value { + groups := make(map[string][]float64) + v.EachFloat64(func(index int, val float64) bool { + group := grouper(index, val) + if _, ok := groups[group]; !ok { + groups[group] = make([]float64, 0) + } + groups[group] = append(groups[group], val) + return true + }) + return &Value{data: groups} +} + +// ReplaceFloat64 uses the specified function to replace each float64s +// by iterating each item. The data in the returned result will be a +// []float64 containing the replaced items. +func (v *Value) ReplaceFloat64(replacer func(int, float64) float64) *Value { + arr := v.MustFloat64Slice() + replaced := make([]float64, len(arr)) + v.EachFloat64(func(index int, val float64) bool { + replaced[index] = replacer(index, val) + return true + }) + return &Value{data: replaced} +} + +// CollectFloat64 uses the specified collector function to collect a value +// for each of the float64s in the slice. The data returned will be a +// []interface{}. +func (v *Value) CollectFloat64(collector func(int, float64) interface{}) *Value { + arr := v.MustFloat64Slice() + collected := make([]interface{}, len(arr)) + v.EachFloat64(func(index int, val float64) bool { + collected[index] = collector(index, val) + return true + }) + return &Value{data: collected} +} + +/* + Complex64 (complex64 and []complex64) +*/ + +// Complex64 gets the value as a complex64, returns the optionalDefault +// value or a system default object if the value is the wrong type. +func (v *Value) Complex64(optionalDefault ...complex64) complex64 { + if s, ok := v.data.(complex64); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return 0 +} + +// MustComplex64 gets the value as a complex64. +// +// Panics if the object is not a complex64. +func (v *Value) MustComplex64() complex64 { + return v.data.(complex64) +} + +// Complex64Slice gets the value as a []complex64, returns the optionalDefault +// value or nil if the value is not a []complex64. +func (v *Value) Complex64Slice(optionalDefault ...[]complex64) []complex64 { + if s, ok := v.data.([]complex64); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil +} + +// MustComplex64Slice gets the value as a []complex64. +// +// Panics if the object is not a []complex64. +func (v *Value) MustComplex64Slice() []complex64 { + return v.data.([]complex64) +} + +// IsComplex64 gets whether the object contained is a complex64 or not. +func (v *Value) IsComplex64() bool { + _, ok := v.data.(complex64) + return ok +} + +// IsComplex64Slice gets whether the object contained is a []complex64 or not. +func (v *Value) IsComplex64Slice() bool { + _, ok := v.data.([]complex64) + return ok +} + +// EachComplex64 calls the specified callback for each object +// in the []complex64. +// +// Panics if the object is the wrong type. +func (v *Value) EachComplex64(callback func(int, complex64) bool) *Value { + for index, val := range v.MustComplex64Slice() { + carryon := callback(index, val) + if !carryon { + break + } + } + return v +} + +// WhereComplex64 uses the specified decider function to select items +// from the []complex64. The object contained in the result will contain +// only the selected items. +func (v *Value) WhereComplex64(decider func(int, complex64) bool) *Value { + var selected []complex64 + v.EachComplex64(func(index int, val complex64) bool { + shouldSelect := decider(index, val) + if !shouldSelect { + selected = append(selected, val) + } + return true + }) + return &Value{data: selected} +} + +// GroupComplex64 uses the specified grouper function to group the items +// keyed by the return of the grouper. The object contained in the +// result will contain a map[string][]complex64. +func (v *Value) GroupComplex64(grouper func(int, complex64) string) *Value { + groups := make(map[string][]complex64) + v.EachComplex64(func(index int, val complex64) bool { + group := grouper(index, val) + if _, ok := groups[group]; !ok { + groups[group] = make([]complex64, 0) + } + groups[group] = append(groups[group], val) + return true + }) + return &Value{data: groups} +} + +// ReplaceComplex64 uses the specified function to replace each complex64s +// by iterating each item. The data in the returned result will be a +// []complex64 containing the replaced items. +func (v *Value) ReplaceComplex64(replacer func(int, complex64) complex64) *Value { + arr := v.MustComplex64Slice() + replaced := make([]complex64, len(arr)) + v.EachComplex64(func(index int, val complex64) bool { + replaced[index] = replacer(index, val) + return true + }) + return &Value{data: replaced} +} + +// CollectComplex64 uses the specified collector function to collect a value +// for each of the complex64s in the slice. The data returned will be a +// []interface{}. +func (v *Value) CollectComplex64(collector func(int, complex64) interface{}) *Value { + arr := v.MustComplex64Slice() + collected := make([]interface{}, len(arr)) + v.EachComplex64(func(index int, val complex64) bool { + collected[index] = collector(index, val) + return true + }) + return &Value{data: collected} +} + +/* + Complex128 (complex128 and []complex128) +*/ + +// Complex128 gets the value as a complex128, returns the optionalDefault +// value or a system default object if the value is the wrong type. +func (v *Value) Complex128(optionalDefault ...complex128) complex128 { + if s, ok := v.data.(complex128); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return 0 +} + +// MustComplex128 gets the value as a complex128. +// +// Panics if the object is not a complex128. +func (v *Value) MustComplex128() complex128 { + return v.data.(complex128) +} + +// Complex128Slice gets the value as a []complex128, returns the optionalDefault +// value or nil if the value is not a []complex128. +func (v *Value) Complex128Slice(optionalDefault ...[]complex128) []complex128 { + if s, ok := v.data.([]complex128); ok { + return s + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + return nil +} + +// MustComplex128Slice gets the value as a []complex128. +// +// Panics if the object is not a []complex128. +func (v *Value) MustComplex128Slice() []complex128 { + return v.data.([]complex128) +} + +// IsComplex128 gets whether the object contained is a complex128 or not. +func (v *Value) IsComplex128() bool { + _, ok := v.data.(complex128) + return ok +} + +// IsComplex128Slice gets whether the object contained is a []complex128 or not. +func (v *Value) IsComplex128Slice() bool { + _, ok := v.data.([]complex128) + return ok +} + +// EachComplex128 calls the specified callback for each object +// in the []complex128. +// +// Panics if the object is the wrong type. +func (v *Value) EachComplex128(callback func(int, complex128) bool) *Value { + for index, val := range v.MustComplex128Slice() { + carryon := callback(index, val) + if !carryon { + break + } + } + return v +} + +// WhereComplex128 uses the specified decider function to select items +// from the []complex128. The object contained in the result will contain +// only the selected items. +func (v *Value) WhereComplex128(decider func(int, complex128) bool) *Value { + var selected []complex128 + v.EachComplex128(func(index int, val complex128) bool { + shouldSelect := decider(index, val) + if !shouldSelect { + selected = append(selected, val) + } + return true + }) + return &Value{data: selected} +} + +// GroupComplex128 uses the specified grouper function to group the items +// keyed by the return of the grouper. The object contained in the +// result will contain a map[string][]complex128. +func (v *Value) GroupComplex128(grouper func(int, complex128) string) *Value { + groups := make(map[string][]complex128) + v.EachComplex128(func(index int, val complex128) bool { + group := grouper(index, val) + if _, ok := groups[group]; !ok { + groups[group] = make([]complex128, 0) + } + groups[group] = append(groups[group], val) + return true + }) + return &Value{data: groups} +} + +// ReplaceComplex128 uses the specified function to replace each complex128s +// by iterating each item. The data in the returned result will be a +// []complex128 containing the replaced items. +func (v *Value) ReplaceComplex128(replacer func(int, complex128) complex128) *Value { + arr := v.MustComplex128Slice() + replaced := make([]complex128, len(arr)) + v.EachComplex128(func(index int, val complex128) bool { + replaced[index] = replacer(index, val) + return true + }) + return &Value{data: replaced} +} + +// CollectComplex128 uses the specified collector function to collect a value +// for each of the complex128s in the slice. The data returned will be a +// []interface{}. +func (v *Value) CollectComplex128(collector func(int, complex128) interface{}) *Value { + arr := v.MustComplex128Slice() + collected := make([]interface{}, len(arr)) + v.EachComplex128(func(index int, val complex128) bool { + collected[index] = collector(index, val) + return true + }) + return &Value{data: collected} +} diff --git a/vendor/github.com/stretchr/objx/value.go b/vendor/github.com/stretchr/objx/value.go new file mode 100644 index 0000000..4e5f9b7 --- /dev/null +++ b/vendor/github.com/stretchr/objx/value.go @@ -0,0 +1,159 @@ +package objx + +import ( + "fmt" + "strconv" +) + +// Value provides methods for extracting interface{} data in various +// types. +type Value struct { + // data contains the raw data being managed by this Value + data interface{} +} + +// Data returns the raw data contained by this Value +func (v *Value) Data() interface{} { + return v.data +} + +// String returns the value always as a string +func (v *Value) String() string { + switch { + case v.IsNil(): + return "" + case v.IsStr(): + return v.Str() + case v.IsBool(): + return strconv.FormatBool(v.Bool()) + case v.IsFloat32(): + return strconv.FormatFloat(float64(v.Float32()), 'f', -1, 32) + case v.IsFloat64(): + return strconv.FormatFloat(v.Float64(), 'f', -1, 64) + case v.IsInt(): + return strconv.FormatInt(int64(v.Int()), 10) + case v.IsInt8(): + return strconv.FormatInt(int64(v.Int8()), 10) + case v.IsInt16(): + return strconv.FormatInt(int64(v.Int16()), 10) + case v.IsInt32(): + return strconv.FormatInt(int64(v.Int32()), 10) + case v.IsInt64(): + return strconv.FormatInt(v.Int64(), 10) + case v.IsUint(): + return strconv.FormatUint(uint64(v.Uint()), 10) + case v.IsUint8(): + return strconv.FormatUint(uint64(v.Uint8()), 10) + case v.IsUint16(): + return strconv.FormatUint(uint64(v.Uint16()), 10) + case v.IsUint32(): + return strconv.FormatUint(uint64(v.Uint32()), 10) + case v.IsUint64(): + return strconv.FormatUint(v.Uint64(), 10) + } + return fmt.Sprintf("%#v", v.Data()) +} + +// StringSlice returns the value always as a []string +func (v *Value) StringSlice(optionalDefault ...[]string) []string { + switch { + case v.IsStrSlice(): + return v.MustStrSlice() + case v.IsBoolSlice(): + slice := v.MustBoolSlice() + vals := make([]string, len(slice)) + for i, iv := range slice { + vals[i] = strconv.FormatBool(iv) + } + return vals + case v.IsFloat32Slice(): + slice := v.MustFloat32Slice() + vals := make([]string, len(slice)) + for i, iv := range slice { + vals[i] = strconv.FormatFloat(float64(iv), 'f', -1, 32) + } + return vals + case v.IsFloat64Slice(): + slice := v.MustFloat64Slice() + vals := make([]string, len(slice)) + for i, iv := range slice { + vals[i] = strconv.FormatFloat(iv, 'f', -1, 64) + } + return vals + case v.IsIntSlice(): + slice := v.MustIntSlice() + vals := make([]string, len(slice)) + for i, iv := range slice { + vals[i] = strconv.FormatInt(int64(iv), 10) + } + return vals + case v.IsInt8Slice(): + slice := v.MustInt8Slice() + vals := make([]string, len(slice)) + for i, iv := range slice { + vals[i] = strconv.FormatInt(int64(iv), 10) + } + return vals + case v.IsInt16Slice(): + slice := v.MustInt16Slice() + vals := make([]string, len(slice)) + for i, iv := range slice { + vals[i] = strconv.FormatInt(int64(iv), 10) + } + return vals + case v.IsInt32Slice(): + slice := v.MustInt32Slice() + vals := make([]string, len(slice)) + for i, iv := range slice { + vals[i] = strconv.FormatInt(int64(iv), 10) + } + return vals + case v.IsInt64Slice(): + slice := v.MustInt64Slice() + vals := make([]string, len(slice)) + for i, iv := range slice { + vals[i] = strconv.FormatInt(iv, 10) + } + return vals + case v.IsUintSlice(): + slice := v.MustUintSlice() + vals := make([]string, len(slice)) + for i, iv := range slice { + vals[i] = strconv.FormatUint(uint64(iv), 10) + } + return vals + case v.IsUint8Slice(): + slice := v.MustUint8Slice() + vals := make([]string, len(slice)) + for i, iv := range slice { + vals[i] = strconv.FormatUint(uint64(iv), 10) + } + return vals + case v.IsUint16Slice(): + slice := v.MustUint16Slice() + vals := make([]string, len(slice)) + for i, iv := range slice { + vals[i] = strconv.FormatUint(uint64(iv), 10) + } + return vals + case v.IsUint32Slice(): + slice := v.MustUint32Slice() + vals := make([]string, len(slice)) + for i, iv := range slice { + vals[i] = strconv.FormatUint(uint64(iv), 10) + } + return vals + case v.IsUint64Slice(): + slice := v.MustUint64Slice() + vals := make([]string, len(slice)) + for i, iv := range slice { + vals[i] = strconv.FormatUint(iv, 10) + } + return vals + } + if len(optionalDefault) == 1 { + return optionalDefault[0] + } + + return []string{} +} diff --git a/vendor/github.com/stretchr/testify/assert/assertion_compare.go b/vendor/github.com/stretchr/testify/assert/assertion_compare.go index 41649d2..3bb22a9 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_compare.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_compare.go @@ -3,6 +3,7 @@ package assert import ( "fmt" "reflect" + "time" ) type CompareType int @@ -30,6 +31,8 @@ var ( float64Type = reflect.TypeOf(float64(1)) stringType = reflect.TypeOf("") + + timeType = reflect.TypeOf(time.Time{}) ) func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) { @@ -299,6 +302,27 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) { return compareLess, true } } + // Check for known struct types we can check for compare results. + case reflect.Struct: + { + // All structs enter here. We're not interested in most types. + if !canConvert(obj1Value, timeType) { + break + } + + // time.Time can compared! + timeObj1, ok := obj1.(time.Time) + if !ok { + timeObj1 = obj1Value.Convert(timeType).Interface().(time.Time) + } + + timeObj2, ok := obj2.(time.Time) + if !ok { + timeObj2 = obj2Value.Convert(timeType).Interface().(time.Time) + } + + return compare(timeObj1.UnixNano(), timeObj2.UnixNano(), reflect.Int64) + } } return compareEqual, false @@ -310,7 +334,10 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) { // assert.Greater(t, float64(2), float64(1)) // assert.Greater(t, "b", "a") func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { - return compareTwoValues(t, e1, e2, []CompareType{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs) + if h, ok := t.(tHelper); ok { + h.Helper() + } + return compareTwoValues(t, e1, e2, []CompareType{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs...) } // GreaterOrEqual asserts that the first element is greater than or equal to the second @@ -320,7 +347,10 @@ func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface // assert.GreaterOrEqual(t, "b", "a") // assert.GreaterOrEqual(t, "b", "b") func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { - return compareTwoValues(t, e1, e2, []CompareType{compareGreater, compareEqual}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs) + if h, ok := t.(tHelper); ok { + h.Helper() + } + return compareTwoValues(t, e1, e2, []CompareType{compareGreater, compareEqual}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs...) } // Less asserts that the first element is less than the second @@ -329,7 +359,10 @@ func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...in // assert.Less(t, float64(1), float64(2)) // assert.Less(t, "a", "b") func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { - return compareTwoValues(t, e1, e2, []CompareType{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs) + if h, ok := t.(tHelper); ok { + h.Helper() + } + return compareTwoValues(t, e1, e2, []CompareType{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs...) } // LessOrEqual asserts that the first element is less than or equal to the second @@ -339,7 +372,10 @@ func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) // assert.LessOrEqual(t, "a", "b") // assert.LessOrEqual(t, "b", "b") func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { - return compareTwoValues(t, e1, e2, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs) + if h, ok := t.(tHelper); ok { + h.Helper() + } + return compareTwoValues(t, e1, e2, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs...) } // Positive asserts that the specified element is positive @@ -347,8 +383,11 @@ func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...inter // assert.Positive(t, 1) // assert.Positive(t, 1.23) func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } zero := reflect.Zero(reflect.TypeOf(e)) - return compareTwoValues(t, e, zero.Interface(), []CompareType{compareGreater}, "\"%v\" is not positive", msgAndArgs) + return compareTwoValues(t, e, zero.Interface(), []CompareType{compareGreater}, "\"%v\" is not positive", msgAndArgs...) } // Negative asserts that the specified element is negative @@ -356,8 +395,11 @@ func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) bool { // assert.Negative(t, -1) // assert.Negative(t, -1.23) func Negative(t TestingT, e interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } zero := reflect.Zero(reflect.TypeOf(e)) - return compareTwoValues(t, e, zero.Interface(), []CompareType{compareLess}, "\"%v\" is not negative", msgAndArgs) + return compareTwoValues(t, e, zero.Interface(), []CompareType{compareLess}, "\"%v\" is not negative", msgAndArgs...) } func compareTwoValues(t TestingT, e1 interface{}, e2 interface{}, allowedComparesResults []CompareType, failMessage string, msgAndArgs ...interface{}) bool { diff --git a/vendor/github.com/stretchr/testify/assert/assertion_compare_can_convert.go b/vendor/github.com/stretchr/testify/assert/assertion_compare_can_convert.go new file mode 100644 index 0000000..df22c47 --- /dev/null +++ b/vendor/github.com/stretchr/testify/assert/assertion_compare_can_convert.go @@ -0,0 +1,16 @@ +//go:build go1.17 +// +build go1.17 + +// TODO: once support for Go 1.16 is dropped, this file can be +// merged/removed with assertion_compare_go1.17_test.go and +// assertion_compare_legacy.go + +package assert + +import "reflect" + +// Wrapper around reflect.Value.CanConvert, for compatability +// reasons. +func canConvert(value reflect.Value, to reflect.Type) bool { + return value.CanConvert(to) +} diff --git a/vendor/github.com/stretchr/testify/assert/assertion_compare_legacy.go b/vendor/github.com/stretchr/testify/assert/assertion_compare_legacy.go new file mode 100644 index 0000000..1701af2 --- /dev/null +++ b/vendor/github.com/stretchr/testify/assert/assertion_compare_legacy.go @@ -0,0 +1,16 @@ +//go:build !go1.17 +// +build !go1.17 + +// TODO: once support for Go 1.16 is dropped, this file can be +// merged/removed with assertion_compare_go1.17_test.go and +// assertion_compare_can_convert.go + +package assert + +import "reflect" + +// Older versions of Go does not have the reflect.Value.CanConvert +// method. +func canConvert(value reflect.Value, to reflect.Type) bool { + return false +} diff --git a/vendor/github.com/stretchr/testify/assert/assertion_format.go b/vendor/github.com/stretchr/testify/assert/assertion_format.go index 4dfd122..27e2420 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_format.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_format.go @@ -123,6 +123,18 @@ func ErrorAsf(t TestingT, err error, target interface{}, msg string, args ...int return ErrorAs(t, err, target, append([]interface{}{msg}, args...)...) } +// ErrorContainsf asserts that a function returned an error (i.e. not `nil`) +// and that the error contains the specified substring. +// +// actualObj, err := SomeFunction() +// assert.ErrorContainsf(t, err, expectedErrorSubString, "error message %s", "formatted") +func ErrorContainsf(t TestingT, theError error, contains string, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return ErrorContains(t, theError, contains, append([]interface{}{msg}, args...)...) +} + // ErrorIsf asserts that at least one of the errors in err's chain matches target. // This is a wrapper for errors.Is. func ErrorIsf(t TestingT, err error, target error, msg string, args ...interface{}) bool { diff --git a/vendor/github.com/stretchr/testify/assert/assertion_forward.go b/vendor/github.com/stretchr/testify/assert/assertion_forward.go index 25337a6..d9ea368 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_forward.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_forward.go @@ -222,6 +222,30 @@ func (a *Assertions) ErrorAsf(err error, target interface{}, msg string, args .. return ErrorAsf(a.t, err, target, msg, args...) } +// ErrorContains asserts that a function returned an error (i.e. not `nil`) +// and that the error contains the specified substring. +// +// actualObj, err := SomeFunction() +// a.ErrorContains(err, expectedErrorSubString) +func (a *Assertions) ErrorContains(theError error, contains string, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return ErrorContains(a.t, theError, contains, msgAndArgs...) +} + +// ErrorContainsf asserts that a function returned an error (i.e. not `nil`) +// and that the error contains the specified substring. +// +// actualObj, err := SomeFunction() +// a.ErrorContainsf(err, expectedErrorSubString, "error message %s", "formatted") +func (a *Assertions) ErrorContainsf(theError error, contains string, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return ErrorContainsf(a.t, theError, contains, msg, args...) +} + // ErrorIs asserts that at least one of the errors in err's chain matches target. // This is a wrapper for errors.Is. func (a *Assertions) ErrorIs(err error, target error, msgAndArgs ...interface{}) bool { diff --git a/vendor/github.com/stretchr/testify/assert/assertion_order.go b/vendor/github.com/stretchr/testify/assert/assertion_order.go index 1c3b471..7594487 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_order.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_order.go @@ -50,7 +50,7 @@ func isOrdered(t TestingT, object interface{}, allowedComparesResults []CompareT // assert.IsIncreasing(t, []float{1, 2}) // assert.IsIncreasing(t, []string{"a", "b"}) func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { - return isOrdered(t, object, []CompareType{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs) + return isOrdered(t, object, []CompareType{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs...) } // IsNonIncreasing asserts that the collection is not increasing @@ -59,7 +59,7 @@ func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) boo // assert.IsNonIncreasing(t, []float{2, 1}) // assert.IsNonIncreasing(t, []string{"b", "a"}) func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { - return isOrdered(t, object, []CompareType{compareEqual, compareGreater}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs) + return isOrdered(t, object, []CompareType{compareEqual, compareGreater}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs...) } // IsDecreasing asserts that the collection is decreasing @@ -68,7 +68,7 @@ func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) // assert.IsDecreasing(t, []float{2, 1}) // assert.IsDecreasing(t, []string{"b", "a"}) func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { - return isOrdered(t, object, []CompareType{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs) + return isOrdered(t, object, []CompareType{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs...) } // IsNonDecreasing asserts that the collection is not decreasing @@ -77,5 +77,5 @@ func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) boo // assert.IsNonDecreasing(t, []float{1, 2}) // assert.IsNonDecreasing(t, []string{"a", "b"}) func IsNonDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { - return isOrdered(t, object, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs) + return isOrdered(t, object, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs...) } diff --git a/vendor/github.com/stretchr/testify/assert/assertions.go b/vendor/github.com/stretchr/testify/assert/assertions.go index bcac440..0357b22 100644 --- a/vendor/github.com/stretchr/testify/assert/assertions.go +++ b/vendor/github.com/stretchr/testify/assert/assertions.go @@ -718,10 +718,14 @@ func NotEqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...inte // return (false, false) if impossible. // return (true, false) if element was not found. // return (true, true) if element was found. -func includeElement(list interface{}, element interface{}) (ok, found bool) { +func containsElement(list interface{}, element interface{}) (ok, found bool) { listValue := reflect.ValueOf(list) - listKind := reflect.TypeOf(list).Kind() + listType := reflect.TypeOf(list) + if listType == nil { + return false, false + } + listKind := listType.Kind() defer func() { if e := recover(); e != nil { ok = false @@ -764,7 +768,7 @@ func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bo h.Helper() } - ok, found := includeElement(s, contains) + ok, found := containsElement(s, contains) if !ok { return Fail(t, fmt.Sprintf("%#v could not be applied builtin len()", s), msgAndArgs...) } @@ -787,7 +791,7 @@ func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) h.Helper() } - ok, found := includeElement(s, contains) + ok, found := containsElement(s, contains) if !ok { return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", s), msgAndArgs...) } @@ -831,7 +835,7 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok for i := 0; i < subsetValue.Len(); i++ { element := subsetValue.Index(i).Interface() - ok, found := includeElement(list, element) + ok, found := containsElement(list, element) if !ok { return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", list), msgAndArgs...) } @@ -852,7 +856,7 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) h.Helper() } if subset == nil { - return Fail(t, fmt.Sprintf("nil is the empty set which is a subset of every set"), msgAndArgs...) + return Fail(t, "nil is the empty set which is a subset of every set", msgAndArgs...) } subsetValue := reflect.ValueOf(subset) @@ -875,7 +879,7 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) for i := 0; i < subsetValue.Len(); i++ { element := subsetValue.Index(i).Interface() - ok, found := includeElement(list, element) + ok, found := containsElement(list, element) if !ok { return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", list), msgAndArgs...) } @@ -1000,27 +1004,21 @@ func Condition(t TestingT, comp Comparison, msgAndArgs ...interface{}) bool { type PanicTestFunc func() // didPanic returns true if the function passed to it panics. Otherwise, it returns false. -func didPanic(f PanicTestFunc) (bool, interface{}, string) { - - didPanic := false - var message interface{} - var stack string - func() { - - defer func() { - if message = recover(); message != nil { - didPanic = true - stack = string(debug.Stack()) - } - }() - - // call the target function - f() +func didPanic(f PanicTestFunc) (didPanic bool, message interface{}, stack string) { + didPanic = true + defer func() { + message = recover() + if didPanic { + stack = string(debug.Stack()) + } }() - return didPanic, message, stack + // call the target function + f() + didPanic = false + return } // Panics asserts that the code inside the specified PanicTestFunc panics. @@ -1161,11 +1159,15 @@ func InDelta(t TestingT, expected, actual interface{}, delta float64, msgAndArgs bf, bok := toFloat(actual) if !aok || !bok { - return Fail(t, fmt.Sprintf("Parameters must be numerical"), msgAndArgs...) + return Fail(t, "Parameters must be numerical", msgAndArgs...) + } + + if math.IsNaN(af) && math.IsNaN(bf) { + return true } if math.IsNaN(af) { - return Fail(t, fmt.Sprintf("Expected must not be NaN"), msgAndArgs...) + return Fail(t, "Expected must not be NaN", msgAndArgs...) } if math.IsNaN(bf) { @@ -1188,7 +1190,7 @@ func InDeltaSlice(t TestingT, expected, actual interface{}, delta float64, msgAn if expected == nil || actual == nil || reflect.TypeOf(actual).Kind() != reflect.Slice || reflect.TypeOf(expected).Kind() != reflect.Slice { - return Fail(t, fmt.Sprintf("Parameters must be slice"), msgAndArgs...) + return Fail(t, "Parameters must be slice", msgAndArgs...) } actualSlice := reflect.ValueOf(actual) @@ -1250,8 +1252,12 @@ func InDeltaMapValues(t TestingT, expected, actual interface{}, delta float64, m func calcRelativeError(expected, actual interface{}) (float64, error) { af, aok := toFloat(expected) - if !aok { - return 0, fmt.Errorf("expected value %q cannot be converted to float", expected) + bf, bok := toFloat(actual) + if !aok || !bok { + return 0, fmt.Errorf("Parameters must be numerical") + } + if math.IsNaN(af) && math.IsNaN(bf) { + return 0, nil } if math.IsNaN(af) { return 0, errors.New("expected value must not be NaN") @@ -1259,10 +1265,6 @@ func calcRelativeError(expected, actual interface{}) (float64, error) { if af == 0 { return 0, fmt.Errorf("expected value must have a value other than zero to calculate the relative error") } - bf, bok := toFloat(actual) - if !bok { - return 0, fmt.Errorf("actual value %q cannot be converted to float", actual) - } if math.IsNaN(bf) { return 0, errors.New("actual value must not be NaN") } @@ -1298,7 +1300,7 @@ func InEpsilonSlice(t TestingT, expected, actual interface{}, epsilon float64, m if expected == nil || actual == nil || reflect.TypeOf(actual).Kind() != reflect.Slice || reflect.TypeOf(expected).Kind() != reflect.Slice { - return Fail(t, fmt.Sprintf("Parameters must be slice"), msgAndArgs...) + return Fail(t, "Parameters must be slice", msgAndArgs...) } actualSlice := reflect.ValueOf(actual) @@ -1375,6 +1377,27 @@ func EqualError(t TestingT, theError error, errString string, msgAndArgs ...inte return true } +// ErrorContains asserts that a function returned an error (i.e. not `nil`) +// and that the error contains the specified substring. +// +// actualObj, err := SomeFunction() +// assert.ErrorContains(t, err, expectedErrorSubString) +func ErrorContains(t TestingT, theError error, contains string, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if !Error(t, theError, msgAndArgs...) { + return false + } + + actual := theError.Error() + if !strings.Contains(actual, contains) { + return Fail(t, fmt.Sprintf("Error %#v does not contain %#v", actual, contains), msgAndArgs...) + } + + return true +} + // matchRegexp return true if a specified regexp matches a string. func matchRegexp(rx interface{}, str interface{}) bool { @@ -1588,12 +1611,17 @@ func diff(expected interface{}, actual interface{}) string { } var e, a string - if et != reflect.TypeOf("") { - e = spewConfig.Sdump(expected) - a = spewConfig.Sdump(actual) - } else { + + switch et { + case reflect.TypeOf(""): e = reflect.ValueOf(expected).String() a = reflect.ValueOf(actual).String() + case reflect.TypeOf(time.Time{}): + e = spewConfigStringerEnabled.Sdump(expected) + a = spewConfigStringerEnabled.Sdump(actual) + default: + e = spewConfig.Sdump(expected) + a = spewConfig.Sdump(actual) } diff, _ := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{ @@ -1625,6 +1653,14 @@ var spewConfig = spew.ConfigState{ MaxDepth: 10, } +var spewConfigStringerEnabled = spew.ConfigState{ + Indent: " ", + DisablePointerAddresses: true, + DisableCapacities: true, + SortKeys: true, + MaxDepth: 10, +} + type tHelper interface { Helper() } diff --git a/vendor/github.com/stretchr/testify/mock/doc.go b/vendor/github.com/stretchr/testify/mock/doc.go new file mode 100644 index 0000000..7324128 --- /dev/null +++ b/vendor/github.com/stretchr/testify/mock/doc.go @@ -0,0 +1,44 @@ +// Package mock provides a system by which it is possible to mock your objects +// and verify calls are happening as expected. +// +// Example Usage +// +// The mock package provides an object, Mock, that tracks activity on another object. It is usually +// embedded into a test object as shown below: +// +// type MyTestObject struct { +// // add a Mock object instance +// mock.Mock +// +// // other fields go here as normal +// } +// +// When implementing the methods of an interface, you wire your functions up +// to call the Mock.Called(args...) method, and return the appropriate values. +// +// For example, to mock a method that saves the name and age of a person and returns +// the year of their birth or an error, you might write this: +// +// func (o *MyTestObject) SavePersonDetails(firstname, lastname string, age int) (int, error) { +// args := o.Called(firstname, lastname, age) +// return args.Int(0), args.Error(1) +// } +// +// The Int, Error and Bool methods are examples of strongly typed getters that take the argument +// index position. Given this argument list: +// +// (12, true, "Something") +// +// You could read them out strongly typed like this: +// +// args.Int(0) +// args.Bool(1) +// args.String(2) +// +// For objects of your own type, use the generic Arguments.Get(index) method and make a type assertion: +// +// return args.Get(0).(*MyObject), args.Get(1).(*AnotherObjectOfMine) +// +// This may cause a panic if the object you are getting is nil (the type assertion will fail), in those +// cases you should check for nil first. +package mock diff --git a/vendor/github.com/stretchr/testify/mock/mock.go b/vendor/github.com/stretchr/testify/mock/mock.go new file mode 100644 index 0000000..853da6c --- /dev/null +++ b/vendor/github.com/stretchr/testify/mock/mock.go @@ -0,0 +1,1016 @@ +package mock + +import ( + "errors" + "fmt" + "reflect" + "regexp" + "runtime" + "strings" + "sync" + "time" + + "github.com/davecgh/go-spew/spew" + "github.com/pmezard/go-difflib/difflib" + "github.com/stretchr/objx" + "github.com/stretchr/testify/assert" +) + +// TestingT is an interface wrapper around *testing.T +type TestingT interface { + Logf(format string, args ...interface{}) + Errorf(format string, args ...interface{}) + FailNow() +} + +/* + Call +*/ + +// Call represents a method call and is used for setting expectations, +// as well as recording activity. +type Call struct { + Parent *Mock + + // The name of the method that was or will be called. + Method string + + // Holds the arguments of the method. + Arguments Arguments + + // Holds the arguments that should be returned when + // this method is called. + ReturnArguments Arguments + + // Holds the caller info for the On() call + callerInfo []string + + // The number of times to return the return arguments when setting + // expectations. 0 means to always return the value. + Repeatability int + + // Amount of times this call has been called + totalCalls int + + // Call to this method can be optional + optional bool + + // Holds a channel that will be used to block the Return until it either + // receives a message or is closed. nil means it returns immediately. + WaitFor <-chan time.Time + + waitTime time.Duration + + // Holds a handler used to manipulate arguments content that are passed by + // reference. It's useful when mocking methods such as unmarshalers or + // decoders. + RunFn func(Arguments) + + // PanicMsg holds msg to be used to mock panic on the function call + // if the PanicMsg is set to a non nil string the function call will panic + // irrespective of other settings + PanicMsg *string +} + +func newCall(parent *Mock, methodName string, callerInfo []string, methodArguments ...interface{}) *Call { + return &Call{ + Parent: parent, + Method: methodName, + Arguments: methodArguments, + ReturnArguments: make([]interface{}, 0), + callerInfo: callerInfo, + Repeatability: 0, + WaitFor: nil, + RunFn: nil, + PanicMsg: nil, + } +} + +func (c *Call) lock() { + c.Parent.mutex.Lock() +} + +func (c *Call) unlock() { + c.Parent.mutex.Unlock() +} + +// Return specifies the return arguments for the expectation. +// +// Mock.On("DoSomething").Return(errors.New("failed")) +func (c *Call) Return(returnArguments ...interface{}) *Call { + c.lock() + defer c.unlock() + + c.ReturnArguments = returnArguments + + return c +} + +// Panic specifies if the functon call should fail and the panic message +// +// Mock.On("DoSomething").Panic("test panic") +func (c *Call) Panic(msg string) *Call { + c.lock() + defer c.unlock() + + c.PanicMsg = &msg + + return c +} + +// Once indicates that that the mock should only return the value once. +// +// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Once() +func (c *Call) Once() *Call { + return c.Times(1) +} + +// Twice indicates that that the mock should only return the value twice. +// +// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Twice() +func (c *Call) Twice() *Call { + return c.Times(2) +} + +// Times indicates that that the mock should only return the indicated number +// of times. +// +// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Times(5) +func (c *Call) Times(i int) *Call { + c.lock() + defer c.unlock() + c.Repeatability = i + return c +} + +// WaitUntil sets the channel that will block the mock's return until its closed +// or a message is received. +// +// Mock.On("MyMethod", arg1, arg2).WaitUntil(time.After(time.Second)) +func (c *Call) WaitUntil(w <-chan time.Time) *Call { + c.lock() + defer c.unlock() + c.WaitFor = w + return c +} + +// After sets how long to block until the call returns +// +// Mock.On("MyMethod", arg1, arg2).After(time.Second) +func (c *Call) After(d time.Duration) *Call { + c.lock() + defer c.unlock() + c.waitTime = d + return c +} + +// Run sets a handler to be called before returning. It can be used when +// mocking a method (such as an unmarshaler) that takes a pointer to a struct and +// sets properties in such struct +// +// Mock.On("Unmarshal", AnythingOfType("*map[string]interface{}")).Return().Run(func(args Arguments) { +// arg := args.Get(0).(*map[string]interface{}) +// arg["foo"] = "bar" +// }) +func (c *Call) Run(fn func(args Arguments)) *Call { + c.lock() + defer c.unlock() + c.RunFn = fn + return c +} + +// Maybe allows the method call to be optional. Not calling an optional method +// will not cause an error while asserting expectations +func (c *Call) Maybe() *Call { + c.lock() + defer c.unlock() + c.optional = true + return c +} + +// On chains a new expectation description onto the mocked interface. This +// allows syntax like. +// +// Mock. +// On("MyMethod", 1).Return(nil). +// On("MyOtherMethod", 'a', 'b', 'c').Return(errors.New("Some Error")) +//go:noinline +func (c *Call) On(methodName string, arguments ...interface{}) *Call { + return c.Parent.On(methodName, arguments...) +} + +// Mock is the workhorse used to track activity on another object. +// For an example of its usage, refer to the "Example Usage" section at the top +// of this document. +type Mock struct { + // Represents the calls that are expected of + // an object. + ExpectedCalls []*Call + + // Holds the calls that were made to this mocked object. + Calls []Call + + // test is An optional variable that holds the test struct, to be used when an + // invalid mock call was made. + test TestingT + + // TestData holds any data that might be useful for testing. Testify ignores + // this data completely allowing you to do whatever you like with it. + testData objx.Map + + mutex sync.Mutex +} + +// String provides a %v format string for Mock. +// Note: this is used implicitly by Arguments.Diff if a Mock is passed. +// It exists because go's default %v formatting traverses the struct +// without acquiring the mutex, which is detected by go test -race. +func (m *Mock) String() string { + return fmt.Sprintf("%[1]T<%[1]p>", m) +} + +// TestData holds any data that might be useful for testing. Testify ignores +// this data completely allowing you to do whatever you like with it. +func (m *Mock) TestData() objx.Map { + + if m.testData == nil { + m.testData = make(objx.Map) + } + + return m.testData +} + +/* + Setting expectations +*/ + +// Test sets the test struct variable of the mock object +func (m *Mock) Test(t TestingT) { + m.mutex.Lock() + defer m.mutex.Unlock() + m.test = t +} + +// fail fails the current test with the given formatted format and args. +// In case that a test was defined, it uses the test APIs for failing a test, +// otherwise it uses panic. +func (m *Mock) fail(format string, args ...interface{}) { + m.mutex.Lock() + defer m.mutex.Unlock() + + if m.test == nil { + panic(fmt.Sprintf(format, args...)) + } + m.test.Errorf(format, args...) + m.test.FailNow() +} + +// On starts a description of an expectation of the specified method +// being called. +// +// Mock.On("MyMethod", arg1, arg2) +func (m *Mock) On(methodName string, arguments ...interface{}) *Call { + for _, arg := range arguments { + if v := reflect.ValueOf(arg); v.Kind() == reflect.Func { + panic(fmt.Sprintf("cannot use Func in expectations. Use mock.AnythingOfType(\"%T\")", arg)) + } + } + + m.mutex.Lock() + defer m.mutex.Unlock() + c := newCall(m, methodName, assert.CallerInfo(), arguments...) + m.ExpectedCalls = append(m.ExpectedCalls, c) + return c +} + +// /* +// Recording and responding to activity +// */ + +func (m *Mock) findExpectedCall(method string, arguments ...interface{}) (int, *Call) { + var expectedCall *Call + + for i, call := range m.ExpectedCalls { + if call.Method == method { + _, diffCount := call.Arguments.Diff(arguments) + if diffCount == 0 { + expectedCall = call + if call.Repeatability > -1 { + return i, call + } + } + } + } + + return -1, expectedCall +} + +type matchCandidate struct { + call *Call + mismatch string + diffCount int +} + +func (c matchCandidate) isBetterMatchThan(other matchCandidate) bool { + if c.call == nil { + return false + } + if other.call == nil { + return true + } + + if c.diffCount > other.diffCount { + return false + } + if c.diffCount < other.diffCount { + return true + } + + if c.call.Repeatability > 0 && other.call.Repeatability <= 0 { + return true + } + return false +} + +func (m *Mock) findClosestCall(method string, arguments ...interface{}) (*Call, string) { + var bestMatch matchCandidate + + for _, call := range m.expectedCalls() { + if call.Method == method { + + errInfo, tempDiffCount := call.Arguments.Diff(arguments) + tempCandidate := matchCandidate{ + call: call, + mismatch: errInfo, + diffCount: tempDiffCount, + } + if tempCandidate.isBetterMatchThan(bestMatch) { + bestMatch = tempCandidate + } + } + } + + return bestMatch.call, bestMatch.mismatch +} + +func callString(method string, arguments Arguments, includeArgumentValues bool) string { + + var argValsString string + if includeArgumentValues { + var argVals []string + for argIndex, arg := range arguments { + argVals = append(argVals, fmt.Sprintf("%d: %#v", argIndex, arg)) + } + argValsString = fmt.Sprintf("\n\t\t%s", strings.Join(argVals, "\n\t\t")) + } + + return fmt.Sprintf("%s(%s)%s", method, arguments.String(), argValsString) +} + +// Called tells the mock object that a method has been called, and gets an array +// of arguments to return. Panics if the call is unexpected (i.e. not preceded by +// appropriate .On .Return() calls) +// If Call.WaitFor is set, blocks until the channel is closed or receives a message. +func (m *Mock) Called(arguments ...interface{}) Arguments { + // get the calling function's name + pc, _, _, ok := runtime.Caller(1) + if !ok { + panic("Couldn't get the caller information") + } + functionPath := runtime.FuncForPC(pc).Name() + //Next four lines are required to use GCCGO function naming conventions. + //For Ex: github_com_docker_libkv_store_mock.WatchTree.pN39_github_com_docker_libkv_store_mock.Mock + //uses interface information unlike golang github.com/docker/libkv/store/mock.(*Mock).WatchTree + //With GCCGO we need to remove interface information starting from pN
. + re := regexp.MustCompile("\\.pN\\d+_") + if re.MatchString(functionPath) { + functionPath = re.Split(functionPath, -1)[0] + } + parts := strings.Split(functionPath, ".") + functionName := parts[len(parts)-1] + return m.MethodCalled(functionName, arguments...) +} + +// MethodCalled tells the mock object that the given method has been called, and gets +// an array of arguments to return. Panics if the call is unexpected (i.e. not preceded +// by appropriate .On .Return() calls) +// If Call.WaitFor is set, blocks until the channel is closed or receives a message. +func (m *Mock) MethodCalled(methodName string, arguments ...interface{}) Arguments { + m.mutex.Lock() + //TODO: could combine expected and closes in single loop + found, call := m.findExpectedCall(methodName, arguments...) + + if found < 0 { + // expected call found but it has already been called with repeatable times + if call != nil { + m.mutex.Unlock() + m.fail("\nassert: mock: The method has been called over %d times.\n\tEither do one more Mock.On(\"%s\").Return(...), or remove extra call.\n\tThis call was unexpected:\n\t\t%s\n\tat: %s", call.totalCalls, methodName, callString(methodName, arguments, true), assert.CallerInfo()) + } + // we have to fail here - because we don't know what to do + // as the return arguments. This is because: + // + // a) this is a totally unexpected call to this method, + // b) the arguments are not what was expected, or + // c) the developer has forgotten to add an accompanying On...Return pair. + closestCall, mismatch := m.findClosestCall(methodName, arguments...) + m.mutex.Unlock() + + if closestCall != nil { + m.fail("\n\nmock: Unexpected Method Call\n-----------------------------\n\n%s\n\nThe closest call I have is: \n\n%s\n\n%s\nDiff: %s", + callString(methodName, arguments, true), + callString(methodName, closestCall.Arguments, true), + diffArguments(closestCall.Arguments, arguments), + strings.TrimSpace(mismatch), + ) + } else { + m.fail("\nassert: mock: I don't know what to return because the method call was unexpected.\n\tEither do Mock.On(\"%s\").Return(...) first, or remove the %s() call.\n\tThis method was unexpected:\n\t\t%s\n\tat: %s", methodName, methodName, callString(methodName, arguments, true), assert.CallerInfo()) + } + } + + if call.Repeatability == 1 { + call.Repeatability = -1 + } else if call.Repeatability > 1 { + call.Repeatability-- + } + call.totalCalls++ + + // add the call + m.Calls = append(m.Calls, *newCall(m, methodName, assert.CallerInfo(), arguments...)) + m.mutex.Unlock() + + // block if specified + if call.WaitFor != nil { + <-call.WaitFor + } else { + time.Sleep(call.waitTime) + } + + m.mutex.Lock() + panicMsg := call.PanicMsg + m.mutex.Unlock() + if panicMsg != nil { + panic(*panicMsg) + } + + m.mutex.Lock() + runFn := call.RunFn + m.mutex.Unlock() + + if runFn != nil { + runFn(arguments) + } + + m.mutex.Lock() + returnArgs := call.ReturnArguments + m.mutex.Unlock() + + return returnArgs +} + +/* + Assertions +*/ + +type assertExpectationser interface { + AssertExpectations(TestingT) bool +} + +// AssertExpectationsForObjects asserts that everything specified with On and Return +// of the specified objects was in fact called as expected. +// +// Calls may have occurred in any order. +func AssertExpectationsForObjects(t TestingT, testObjects ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + for _, obj := range testObjects { + if m, ok := obj.(Mock); ok { + t.Logf("Deprecated mock.AssertExpectationsForObjects(myMock.Mock) use mock.AssertExpectationsForObjects(myMock)") + obj = &m + } + m := obj.(assertExpectationser) + if !m.AssertExpectations(t) { + t.Logf("Expectations didn't match for Mock: %+v", reflect.TypeOf(m)) + return false + } + } + return true +} + +// AssertExpectations asserts that everything specified with On and Return was +// in fact called as expected. Calls may have occurred in any order. +func (m *Mock) AssertExpectations(t TestingT) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + m.mutex.Lock() + defer m.mutex.Unlock() + var somethingMissing bool + var failedExpectations int + + // iterate through each expectation + expectedCalls := m.expectedCalls() + for _, expectedCall := range expectedCalls { + if !expectedCall.optional && !m.methodWasCalled(expectedCall.Method, expectedCall.Arguments) && expectedCall.totalCalls == 0 { + somethingMissing = true + failedExpectations++ + t.Logf("FAIL:\t%s(%s)\n\t\tat: %s", expectedCall.Method, expectedCall.Arguments.String(), expectedCall.callerInfo) + } else { + if expectedCall.Repeatability > 0 { + somethingMissing = true + failedExpectations++ + t.Logf("FAIL:\t%s(%s)\n\t\tat: %s", expectedCall.Method, expectedCall.Arguments.String(), expectedCall.callerInfo) + } else { + t.Logf("PASS:\t%s(%s)", expectedCall.Method, expectedCall.Arguments.String()) + } + } + } + + if somethingMissing { + t.Errorf("FAIL: %d out of %d expectation(s) were met.\n\tThe code you are testing needs to make %d more call(s).\n\tat: %s", len(expectedCalls)-failedExpectations, len(expectedCalls), failedExpectations, assert.CallerInfo()) + } + + return !somethingMissing +} + +// AssertNumberOfCalls asserts that the method was called expectedCalls times. +func (m *Mock) AssertNumberOfCalls(t TestingT, methodName string, expectedCalls int) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + m.mutex.Lock() + defer m.mutex.Unlock() + var actualCalls int + for _, call := range m.calls() { + if call.Method == methodName { + actualCalls++ + } + } + return assert.Equal(t, expectedCalls, actualCalls, fmt.Sprintf("Expected number of calls (%d) does not match the actual number of calls (%d).", expectedCalls, actualCalls)) +} + +// AssertCalled asserts that the method was called. +// It can produce a false result when an argument is a pointer type and the underlying value changed after calling the mocked method. +func (m *Mock) AssertCalled(t TestingT, methodName string, arguments ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + m.mutex.Lock() + defer m.mutex.Unlock() + if !m.methodWasCalled(methodName, arguments) { + var calledWithArgs []string + for _, call := range m.calls() { + calledWithArgs = append(calledWithArgs, fmt.Sprintf("%v", call.Arguments)) + } + if len(calledWithArgs) == 0 { + return assert.Fail(t, "Should have called with given arguments", + fmt.Sprintf("Expected %q to have been called with:\n%v\nbut no actual calls happened", methodName, arguments)) + } + return assert.Fail(t, "Should have called with given arguments", + fmt.Sprintf("Expected %q to have been called with:\n%v\nbut actual calls were:\n %v", methodName, arguments, strings.Join(calledWithArgs, "\n"))) + } + return true +} + +// AssertNotCalled asserts that the method was not called. +// It can produce a false result when an argument is a pointer type and the underlying value changed after calling the mocked method. +func (m *Mock) AssertNotCalled(t TestingT, methodName string, arguments ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + m.mutex.Lock() + defer m.mutex.Unlock() + if m.methodWasCalled(methodName, arguments) { + return assert.Fail(t, "Should not have called with given arguments", + fmt.Sprintf("Expected %q to not have been called with:\n%v\nbut actually it was.", methodName, arguments)) + } + return true +} + +// IsMethodCallable checking that the method can be called +// If the method was called more than `Repeatability` return false +func (m *Mock) IsMethodCallable(t TestingT, methodName string, arguments ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + m.mutex.Lock() + defer m.mutex.Unlock() + + for _, v := range m.ExpectedCalls { + if v.Method != methodName { + continue + } + if len(arguments) != len(v.Arguments) { + continue + } + if v.Repeatability < v.totalCalls { + continue + } + if isArgsEqual(v.Arguments, arguments) { + return true + } + } + return false +} + +// isArgsEqual compares arguments +func isArgsEqual(expected Arguments, args []interface{}) bool { + if len(expected) != len(args) { + return false + } + for i, v := range args { + if !reflect.DeepEqual(expected[i], v) { + return false + } + } + return true +} + +func (m *Mock) methodWasCalled(methodName string, expected []interface{}) bool { + for _, call := range m.calls() { + if call.Method == methodName { + + _, differences := Arguments(expected).Diff(call.Arguments) + + if differences == 0 { + // found the expected call + return true + } + + } + } + // we didn't find the expected call + return false +} + +func (m *Mock) expectedCalls() []*Call { + return append([]*Call{}, m.ExpectedCalls...) +} + +func (m *Mock) calls() []Call { + return append([]Call{}, m.Calls...) +} + +/* + Arguments +*/ + +// Arguments holds an array of method arguments or return values. +type Arguments []interface{} + +const ( + // Anything is used in Diff and Assert when the argument being tested + // shouldn't be taken into consideration. + Anything = "mock.Anything" +) + +// AnythingOfTypeArgument is a string that contains the type of an argument +// for use when type checking. Used in Diff and Assert. +type AnythingOfTypeArgument string + +// AnythingOfType returns an AnythingOfTypeArgument object containing the +// name of the type to check for. Used in Diff and Assert. +// +// For example: +// Assert(t, AnythingOfType("string"), AnythingOfType("int")) +func AnythingOfType(t string) AnythingOfTypeArgument { + return AnythingOfTypeArgument(t) +} + +// IsTypeArgument is a struct that contains the type of an argument +// for use when type checking. This is an alternative to AnythingOfType. +// Used in Diff and Assert. +type IsTypeArgument struct { + t interface{} +} + +// IsType returns an IsTypeArgument object containing the type to check for. +// You can provide a zero-value of the type to check. This is an +// alternative to AnythingOfType. Used in Diff and Assert. +// +// For example: +// Assert(t, IsType(""), IsType(0)) +func IsType(t interface{}) *IsTypeArgument { + return &IsTypeArgument{t: t} +} + +// argumentMatcher performs custom argument matching, returning whether or +// not the argument is matched by the expectation fixture function. +type argumentMatcher struct { + // fn is a function which accepts one argument, and returns a bool. + fn reflect.Value +} + +func (f argumentMatcher) Matches(argument interface{}) bool { + expectType := f.fn.Type().In(0) + expectTypeNilSupported := false + switch expectType.Kind() { + case reflect.Interface, reflect.Chan, reflect.Func, reflect.Map, reflect.Slice, reflect.Ptr: + expectTypeNilSupported = true + } + + argType := reflect.TypeOf(argument) + var arg reflect.Value + if argType == nil { + arg = reflect.New(expectType).Elem() + } else { + arg = reflect.ValueOf(argument) + } + + if argType == nil && !expectTypeNilSupported { + panic(errors.New("attempting to call matcher with nil for non-nil expected type")) + } + if argType == nil || argType.AssignableTo(expectType) { + result := f.fn.Call([]reflect.Value{arg}) + return result[0].Bool() + } + return false +} + +func (f argumentMatcher) String() string { + return fmt.Sprintf("func(%s) bool", f.fn.Type().In(0).String()) +} + +// MatchedBy can be used to match a mock call based on only certain properties +// from a complex struct or some calculation. It takes a function that will be +// evaluated with the called argument and will return true when there's a match +// and false otherwise. +// +// Example: +// m.On("Do", MatchedBy(func(req *http.Request) bool { return req.Host == "example.com" })) +// +// |fn|, must be a function accepting a single argument (of the expected type) +// which returns a bool. If |fn| doesn't match the required signature, +// MatchedBy() panics. +func MatchedBy(fn interface{}) argumentMatcher { + fnType := reflect.TypeOf(fn) + + if fnType.Kind() != reflect.Func { + panic(fmt.Sprintf("assert: arguments: %s is not a func", fn)) + } + if fnType.NumIn() != 1 { + panic(fmt.Sprintf("assert: arguments: %s does not take exactly one argument", fn)) + } + if fnType.NumOut() != 1 || fnType.Out(0).Kind() != reflect.Bool { + panic(fmt.Sprintf("assert: arguments: %s does not return a bool", fn)) + } + + return argumentMatcher{fn: reflect.ValueOf(fn)} +} + +// Get Returns the argument at the specified index. +func (args Arguments) Get(index int) interface{} { + if index+1 > len(args) { + panic(fmt.Sprintf("assert: arguments: Cannot call Get(%d) because there are %d argument(s).", index, len(args))) + } + return args[index] +} + +// Is gets whether the objects match the arguments specified. +func (args Arguments) Is(objects ...interface{}) bool { + for i, obj := range args { + if obj != objects[i] { + return false + } + } + return true +} + +// Diff gets a string describing the differences between the arguments +// and the specified objects. +// +// Returns the diff string and number of differences found. +func (args Arguments) Diff(objects []interface{}) (string, int) { + //TODO: could return string as error and nil for No difference + + var output = "\n" + var differences int + + var maxArgCount = len(args) + if len(objects) > maxArgCount { + maxArgCount = len(objects) + } + + for i := 0; i < maxArgCount; i++ { + var actual, expected interface{} + var actualFmt, expectedFmt string + + if len(objects) <= i { + actual = "(Missing)" + actualFmt = "(Missing)" + } else { + actual = objects[i] + actualFmt = fmt.Sprintf("(%[1]T=%[1]v)", actual) + } + + if len(args) <= i { + expected = "(Missing)" + expectedFmt = "(Missing)" + } else { + expected = args[i] + expectedFmt = fmt.Sprintf("(%[1]T=%[1]v)", expected) + } + + if matcher, ok := expected.(argumentMatcher); ok { + if matcher.Matches(actual) { + output = fmt.Sprintf("%s\t%d: PASS: %s matched by %s\n", output, i, actualFmt, matcher) + } else { + differences++ + output = fmt.Sprintf("%s\t%d: FAIL: %s not matched by %s\n", output, i, actualFmt, matcher) + } + } else if reflect.TypeOf(expected) == reflect.TypeOf((*AnythingOfTypeArgument)(nil)).Elem() { + + // type checking + if reflect.TypeOf(actual).Name() != string(expected.(AnythingOfTypeArgument)) && reflect.TypeOf(actual).String() != string(expected.(AnythingOfTypeArgument)) { + // not match + differences++ + output = fmt.Sprintf("%s\t%d: FAIL: type %s != type %s - %s\n", output, i, expected, reflect.TypeOf(actual).Name(), actualFmt) + } + + } else if reflect.TypeOf(expected) == reflect.TypeOf((*IsTypeArgument)(nil)) { + t := expected.(*IsTypeArgument).t + if reflect.TypeOf(t) != reflect.TypeOf(actual) { + differences++ + output = fmt.Sprintf("%s\t%d: FAIL: type %s != type %s - %s\n", output, i, reflect.TypeOf(t).Name(), reflect.TypeOf(actual).Name(), actualFmt) + } + } else { + + // normal checking + + if assert.ObjectsAreEqual(expected, Anything) || assert.ObjectsAreEqual(actual, Anything) || assert.ObjectsAreEqual(actual, expected) { + // match + output = fmt.Sprintf("%s\t%d: PASS: %s == %s\n", output, i, actualFmt, expectedFmt) + } else { + // not match + differences++ + output = fmt.Sprintf("%s\t%d: FAIL: %s != %s\n", output, i, actualFmt, expectedFmt) + } + } + + } + + if differences == 0 { + return "No differences.", differences + } + + return output, differences + +} + +// Assert compares the arguments with the specified objects and fails if +// they do not exactly match. +func (args Arguments) Assert(t TestingT, objects ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + // get the differences + diff, diffCount := args.Diff(objects) + + if diffCount == 0 { + return true + } + + // there are differences... report them... + t.Logf(diff) + t.Errorf("%sArguments do not match.", assert.CallerInfo()) + + return false + +} + +// String gets the argument at the specified index. Panics if there is no argument, or +// if the argument is of the wrong type. +// +// If no index is provided, String() returns a complete string representation +// of the arguments. +func (args Arguments) String(indexOrNil ...int) string { + + if len(indexOrNil) == 0 { + // normal String() method - return a string representation of the args + var argsStr []string + for _, arg := range args { + argsStr = append(argsStr, fmt.Sprintf("%T", arg)) // handles nil nicely + } + return strings.Join(argsStr, ",") + } else if len(indexOrNil) == 1 { + // Index has been specified - get the argument at that index + var index = indexOrNil[0] + var s string + var ok bool + if s, ok = args.Get(index).(string); !ok { + panic(fmt.Sprintf("assert: arguments: String(%d) failed because object wasn't correct type: %s", index, args.Get(index))) + } + return s + } + + panic(fmt.Sprintf("assert: arguments: Wrong number of arguments passed to String. Must be 0 or 1, not %d", len(indexOrNil))) + +} + +// Int gets the argument at the specified index. Panics if there is no argument, or +// if the argument is of the wrong type. +func (args Arguments) Int(index int) int { + var s int + var ok bool + if s, ok = args.Get(index).(int); !ok { + panic(fmt.Sprintf("assert: arguments: Int(%d) failed because object wasn't correct type: %v", index, args.Get(index))) + } + return s +} + +// Error gets the argument at the specified index. Panics if there is no argument, or +// if the argument is of the wrong type. +func (args Arguments) Error(index int) error { + obj := args.Get(index) + var s error + var ok bool + if obj == nil { + return nil + } + if s, ok = obj.(error); !ok { + panic(fmt.Sprintf("assert: arguments: Error(%d) failed because object wasn't correct type: %v", index, args.Get(index))) + } + return s +} + +// Bool gets the argument at the specified index. Panics if there is no argument, or +// if the argument is of the wrong type. +func (args Arguments) Bool(index int) bool { + var s bool + var ok bool + if s, ok = args.Get(index).(bool); !ok { + panic(fmt.Sprintf("assert: arguments: Bool(%d) failed because object wasn't correct type: %v", index, args.Get(index))) + } + return s +} + +func typeAndKind(v interface{}) (reflect.Type, reflect.Kind) { + t := reflect.TypeOf(v) + k := t.Kind() + + if k == reflect.Ptr { + t = t.Elem() + k = t.Kind() + } + return t, k +} + +func diffArguments(expected Arguments, actual Arguments) string { + if len(expected) != len(actual) { + return fmt.Sprintf("Provided %v arguments, mocked for %v arguments", len(expected), len(actual)) + } + + for x := range expected { + if diffString := diff(expected[x], actual[x]); diffString != "" { + return fmt.Sprintf("Difference found in argument %v:\n\n%s", x, diffString) + } + } + + return "" +} + +// diff returns a diff of both values as long as both are of the same type and +// are a struct, map, slice or array. Otherwise it returns an empty string. +func diff(expected interface{}, actual interface{}) string { + if expected == nil || actual == nil { + return "" + } + + et, ek := typeAndKind(expected) + at, _ := typeAndKind(actual) + + if et != at { + return "" + } + + if ek != reflect.Struct && ek != reflect.Map && ek != reflect.Slice && ek != reflect.Array { + return "" + } + + e := spewConfig.Sdump(expected) + a := spewConfig.Sdump(actual) + + diff, _ := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{ + A: difflib.SplitLines(e), + B: difflib.SplitLines(a), + FromFile: "Expected", + FromDate: "", + ToFile: "Actual", + ToDate: "", + Context: 1, + }) + + return diff +} + +var spewConfig = spew.ConfigState{ + Indent: " ", + DisablePointerAddresses: true, + DisableCapacities: true, + SortKeys: true, +} + +type tHelper interface { + Helper() +} diff --git a/vendor/github.com/stretchr/testify/require/require.go b/vendor/github.com/stretchr/testify/require/require.go index 51820df..59c4827 100644 --- a/vendor/github.com/stretchr/testify/require/require.go +++ b/vendor/github.com/stretchr/testify/require/require.go @@ -280,6 +280,36 @@ func ErrorAsf(t TestingT, err error, target interface{}, msg string, args ...int t.FailNow() } +// ErrorContains asserts that a function returned an error (i.e. not `nil`) +// and that the error contains the specified substring. +// +// actualObj, err := SomeFunction() +// assert.ErrorContains(t, err, expectedErrorSubString) +func ErrorContains(t TestingT, theError error, contains string, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.ErrorContains(t, theError, contains, msgAndArgs...) { + return + } + t.FailNow() +} + +// ErrorContainsf asserts that a function returned an error (i.e. not `nil`) +// and that the error contains the specified substring. +// +// actualObj, err := SomeFunction() +// assert.ErrorContainsf(t, err, expectedErrorSubString, "error message %s", "formatted") +func ErrorContainsf(t TestingT, theError error, contains string, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.ErrorContainsf(t, theError, contains, msg, args...) { + return + } + t.FailNow() +} + // ErrorIs asserts that at least one of the errors in err's chain matches target. // This is a wrapper for errors.Is. func ErrorIs(t TestingT, err error, target error, msgAndArgs ...interface{}) { diff --git a/vendor/github.com/stretchr/testify/require/require_forward.go b/vendor/github.com/stretchr/testify/require/require_forward.go index ed54a9d..5bb07c8 100644 --- a/vendor/github.com/stretchr/testify/require/require_forward.go +++ b/vendor/github.com/stretchr/testify/require/require_forward.go @@ -223,6 +223,30 @@ func (a *Assertions) ErrorAsf(err error, target interface{}, msg string, args .. ErrorAsf(a.t, err, target, msg, args...) } +// ErrorContains asserts that a function returned an error (i.e. not `nil`) +// and that the error contains the specified substring. +// +// actualObj, err := SomeFunction() +// a.ErrorContains(err, expectedErrorSubString) +func (a *Assertions) ErrorContains(theError error, contains string, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + ErrorContains(a.t, theError, contains, msgAndArgs...) +} + +// ErrorContainsf asserts that a function returned an error (i.e. not `nil`) +// and that the error contains the specified substring. +// +// actualObj, err := SomeFunction() +// a.ErrorContainsf(err, expectedErrorSubString, "error message %s", "formatted") +func (a *Assertions) ErrorContainsf(theError error, contains string, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + ErrorContainsf(a.t, theError, contains, msg, args...) +} + // ErrorIs asserts that at least one of the errors in err's chain matches target. // This is a wrapper for errors.Is. func (a *Assertions) ErrorIs(err error, target error, msgAndArgs ...interface{}) { diff --git a/vendor/gopkg.in/yaml.v3/decode.go b/vendor/gopkg.in/yaml.v3/decode.go index df36e3a..0173b69 100644 --- a/vendor/gopkg.in/yaml.v3/decode.go +++ b/vendor/gopkg.in/yaml.v3/decode.go @@ -100,7 +100,10 @@ func (p *parser) peek() yaml_event_type_t { if p.event.typ != yaml_NO_EVENT { return p.event.typ } - if !yaml_parser_parse(&p.parser, &p.event) { + // It's curious choice from the underlying API to generally return a + // positive result on success, but on this case return true in an error + // scenario. This was the source of bugs in the past (issue #666). + if !yaml_parser_parse(&p.parser, &p.event) || p.parser.error != yaml_NO_ERROR { p.fail() } return p.event.typ @@ -320,6 +323,8 @@ type decoder struct { decodeCount int aliasCount int aliasDepth int + + mergedFields map[interface{}]bool } var ( @@ -808,6 +813,11 @@ func (d *decoder) mapping(n *Node, out reflect.Value) (good bool) { } } + mergedFields := d.mergedFields + d.mergedFields = nil + + var mergeNode *Node + mapIsNew := false if out.IsNil() { out.Set(reflect.MakeMap(outt)) @@ -815,11 +825,18 @@ func (d *decoder) mapping(n *Node, out reflect.Value) (good bool) { } for i := 0; i < l; i += 2 { if isMerge(n.Content[i]) { - d.merge(n.Content[i+1], out) + mergeNode = n.Content[i+1] continue } k := reflect.New(kt).Elem() if d.unmarshal(n.Content[i], k) { + if mergedFields != nil { + ki := k.Interface() + if mergedFields[ki] { + continue + } + mergedFields[ki] = true + } kkind := k.Kind() if kkind == reflect.Interface { kkind = k.Elem().Kind() @@ -833,6 +850,12 @@ func (d *decoder) mapping(n *Node, out reflect.Value) (good bool) { } } } + + d.mergedFields = mergedFields + if mergeNode != nil { + d.merge(n, mergeNode, out) + } + d.stringMapType = stringMapType d.generalMapType = generalMapType return true @@ -844,7 +867,8 @@ func isStringMap(n *Node) bool { } l := len(n.Content) for i := 0; i < l; i += 2 { - if n.Content[i].ShortTag() != strTag { + shortTag := n.Content[i].ShortTag() + if shortTag != strTag && shortTag != mergeTag { return false } } @@ -861,7 +885,6 @@ func (d *decoder) mappingStruct(n *Node, out reflect.Value) (good bool) { var elemType reflect.Type if sinfo.InlineMap != -1 { inlineMap = out.Field(sinfo.InlineMap) - inlineMap.Set(reflect.New(inlineMap.Type()).Elem()) elemType = inlineMap.Type().Elem() } @@ -870,6 +893,9 @@ func (d *decoder) mappingStruct(n *Node, out reflect.Value) (good bool) { d.prepare(n, field) } + mergedFields := d.mergedFields + d.mergedFields = nil + var mergeNode *Node var doneFields []bool if d.uniqueKeys { doneFields = make([]bool, len(sinfo.FieldsList)) @@ -879,13 +905,20 @@ func (d *decoder) mappingStruct(n *Node, out reflect.Value) (good bool) { for i := 0; i < l; i += 2 { ni := n.Content[i] if isMerge(ni) { - d.merge(n.Content[i+1], out) + mergeNode = n.Content[i+1] continue } if !d.unmarshal(ni, name) { continue } - if info, ok := sinfo.FieldsMap[name.String()]; ok { + sname := name.String() + if mergedFields != nil { + if mergedFields[sname] { + continue + } + mergedFields[sname] = true + } + if info, ok := sinfo.FieldsMap[sname]; ok { if d.uniqueKeys { if doneFields[info.Id] { d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s already set in type %s", ni.Line, name.String(), out.Type())) @@ -911,6 +944,11 @@ func (d *decoder) mappingStruct(n *Node, out reflect.Value) (good bool) { d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s not found in type %s", ni.Line, name.String(), out.Type())) } } + + d.mergedFields = mergedFields + if mergeNode != nil { + d.merge(n, mergeNode, out) + } return true } @@ -918,19 +956,29 @@ func failWantMap() { failf("map merge requires map or sequence of maps as the value") } -func (d *decoder) merge(n *Node, out reflect.Value) { - switch n.Kind { +func (d *decoder) merge(parent *Node, merge *Node, out reflect.Value) { + mergedFields := d.mergedFields + if mergedFields == nil { + d.mergedFields = make(map[interface{}]bool) + for i := 0; i < len(parent.Content); i += 2 { + k := reflect.New(ifaceType).Elem() + if d.unmarshal(parent.Content[i], k) { + d.mergedFields[k.Interface()] = true + } + } + } + + switch merge.Kind { case MappingNode: - d.unmarshal(n, out) + d.unmarshal(merge, out) case AliasNode: - if n.Alias != nil && n.Alias.Kind != MappingNode { + if merge.Alias != nil && merge.Alias.Kind != MappingNode { failWantMap() } - d.unmarshal(n, out) + d.unmarshal(merge, out) case SequenceNode: - // Step backwards as earlier nodes take precedence. - for i := len(n.Content) - 1; i >= 0; i-- { - ni := n.Content[i] + for i := 0; i < len(merge.Content); i++ { + ni := merge.Content[i] if ni.Kind == AliasNode { if ni.Alias != nil && ni.Alias.Kind != MappingNode { failWantMap() @@ -943,6 +991,8 @@ func (d *decoder) merge(n *Node, out reflect.Value) { default: failWantMap() } + + d.mergedFields = mergedFields } func isMerge(n *Node) bool { diff --git a/vendor/howett.net/plist/README.md b/vendor/howett.net/plist/README.md index a13e29a..d751c06 100644 --- a/vendor/howett.net/plist/README.md +++ b/vendor/howett.net/plist/README.md @@ -1,4 +1,4 @@ -# plist - A pure Go property list transcoder [![coverage report](https://gitlab.howett.net/go/plist/badges/master/coverage.svg)](https://gitlab.howett.net/go/plist/commits/master) +# plist - A pure Go property list transcoder [![coverage report](https://gitlab.howett.net/go/plist/badges/main/coverage.svg)](https://gitlab.howett.net/go/plist/commits/main) ## INSTALL ``` $ go get howett.net/plist diff --git a/vendor/howett.net/plist/go.mod b/vendor/howett.net/plist/go.mod index ec83441..fb957fa 100644 --- a/vendor/howett.net/plist/go.mod +++ b/vendor/howett.net/plist/go.mod @@ -1,9 +1,10 @@ module howett.net/plist +go 1.12 + require ( // for cmd/ply github.com/jessevdk/go-flags v1.4.0 - github.com/kr/pretty v0.1.0 // indirect - gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect - gopkg.in/yaml.v2 v2.2.1 + gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 // indirect + gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0 ) diff --git a/vendor/howett.net/plist/go.sum b/vendor/howett.net/plist/go.sum new file mode 100644 index 0000000..dd7775d --- /dev/null +++ b/vendor/howett.net/plist/go.sum @@ -0,0 +1,6 @@ +github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0 h1:POO/ycCATvegFmVuPpQzZFJ+pGZeX22Ufu6fibxDVjU= +gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg= diff --git a/vendor/modules.txt b/vendor/modules.txt index 7973e59..d37fd1a 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,41 +1,74 @@ -# github.com/bitrise-io/go-steputils v0.0.0-20210507072936-92fde382fb33 +# github.com/bitrise-io/go-steputils v1.0.2 ## explicit github.com/bitrise-io/go-steputils/input -# github.com/bitrise-io/go-utils v0.0.0-20210506064210-b22e2b7b3ad3 +# github.com/bitrise-io/go-steputils/v2 v2.0.0-alpha.2 +## explicit +github.com/bitrise-io/go-steputils/v2/stepconf +# github.com/bitrise-io/go-utils v1.0.2 ## explicit github.com/bitrise-io/go-utils/colorstring github.com/bitrise-io/go-utils/command +github.com/bitrise-io/go-utils/errorutil +github.com/bitrise-io/go-utils/filedownloader github.com/bitrise-io/go-utils/fileutil +github.com/bitrise-io/go-utils/httputil github.com/bitrise-io/go-utils/log +github.com/bitrise-io/go-utils/parseutil github.com/bitrise-io/go-utils/pathutil -# github.com/bitrise-io/go-xcode v0.0.0-20210507081243-7cf47826c379 +github.com/bitrise-io/go-utils/pointers +github.com/bitrise-io/go-utils/retry +github.com/bitrise-io/go-utils/sliceutil +# github.com/bitrise-io/go-utils/v2 v2.0.0-alpha.11 +## explicit +github.com/bitrise-io/go-utils/v2/command +github.com/bitrise-io/go-utils/v2/env +# github.com/bitrise-io/go-xcode v1.0.10 ## explicit github.com/bitrise-io/go-xcode/certificateutil +github.com/bitrise-io/go-xcode/devportalservice github.com/bitrise-io/go-xcode/exportoptions -github.com/bitrise-io/go-xcode/models github.com/bitrise-io/go-xcode/plistutil github.com/bitrise-io/go-xcode/profileutil -github.com/bitrise-io/go-xcode/utility -# github.com/bitrise-io/pkcs12 v0.0.0-20210430063833-0da06eb56630 +github.com/bitrise-io/go-xcode/xcodeproject/serialized +# github.com/bitrise-io/go-xcode/v2 v2.0.0-alpha.24 +## explicit +github.com/bitrise-io/go-xcode/v2/autocodesign +github.com/bitrise-io/go-xcode/v2/autocodesign/certdownloader +github.com/bitrise-io/go-xcode/v2/autocodesign/codesignasset +github.com/bitrise-io/go-xcode/v2/autocodesign/devportalclient/appstoreconnect +github.com/bitrise-io/go-xcode/v2/autocodesign/devportalclient/time +github.com/bitrise-io/go-xcode/v2/autocodesign/keychain +github.com/bitrise-io/go-xcode/v2/autocodesign/localcodesignasset +github.com/bitrise-io/go-xcode/v2/autocodesign/profiledownloader +# github.com/bitrise-io/pkcs12 v0.0.0-20211108084543-e52728e011c8 github.com/bitrise-io/pkcs12 github.com/bitrise-io/pkcs12/internal/rc2 # github.com/davecgh/go-spew v1.1.1 github.com/davecgh/go-spew/spew # github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa github.com/fullsailor/pkcs7 -# github.com/hashicorp/go-version v1.3.0 -## explicit +# github.com/golang-jwt/jwt/v4 v4.4.1 +github.com/golang-jwt/jwt/v4 +# github.com/google/go-querystring v1.1.0 +github.com/google/go-querystring/query +# github.com/hashicorp/go-cleanhttp v0.5.2 +github.com/hashicorp/go-cleanhttp +# github.com/hashicorp/go-retryablehttp v0.7.1 +github.com/hashicorp/go-retryablehttp +# github.com/hashicorp/go-version v1.5.0 github.com/hashicorp/go-version # github.com/pkg/errors v0.9.1 -## explicit github.com/pkg/errors # github.com/pmezard/go-difflib v1.0.0 github.com/pmezard/go-difflib/difflib -# github.com/stretchr/testify v1.7.0 +# github.com/stretchr/objx v0.4.0 +github.com/stretchr/objx +# github.com/stretchr/testify v1.7.1 ## explicit github.com/stretchr/testify/assert +github.com/stretchr/testify/mock github.com/stretchr/testify/require -# gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b +# gopkg.in/yaml.v3 v3.0.0 gopkg.in/yaml.v3 -# howett.net/plist v0.0.0-20201203080718-1454fab16a06 +# howett.net/plist v1.0.0 howett.net/plist