vendor: update go-actions-cache to 4d48f2ff
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>master
parent
c13f5de2fb
commit
25d5c1f0fe
3
go.mod
3
go.mod
|
@ -50,7 +50,7 @@ require (
|
||||||
github.com/sirupsen/logrus v1.8.1
|
github.com/sirupsen/logrus v1.8.1
|
||||||
github.com/stretchr/testify v1.7.0
|
github.com/stretchr/testify v1.7.0
|
||||||
github.com/tonistiigi/fsutil v0.0.0-20210818161904-4442383b5028
|
github.com/tonistiigi/fsutil v0.0.0-20210818161904-4442383b5028
|
||||||
github.com/tonistiigi/go-actions-cache v0.0.0-20210714033416-b93d7f1b2e70
|
github.com/tonistiigi/go-actions-cache v0.0.0-20211002214948-4d48f2ff622a
|
||||||
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea
|
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea
|
||||||
github.com/tonistiigi/vt100 v0.0.0-20210615222946-8066bb97264f
|
github.com/tonistiigi/vt100 v0.0.0-20210615222946-8066bb97264f
|
||||||
github.com/urfave/cli v1.22.4
|
github.com/urfave/cli v1.22.4
|
||||||
|
@ -88,6 +88,7 @@ require (
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
|
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
|
||||||
|
github.com/dimchansky/utfbom v1.1.1 // indirect
|
||||||
github.com/docker/docker-credential-helpers v0.6.4 // indirect
|
github.com/docker/docker-credential-helpers v0.6.4 // indirect
|
||||||
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect
|
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect
|
||||||
github.com/docker/go-metrics v0.0.1 // indirect
|
github.com/docker/go-metrics v0.0.1 // indirect
|
||||||
|
|
6
go.sum
6
go.sum
|
@ -325,6 +325,8 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumC
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||||
github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8=
|
github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8=
|
||||||
|
github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U=
|
||||||
|
github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE=
|
||||||
github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
|
github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
|
||||||
github.com/docker/cli v0.0.0-20190925022749-754388324470/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
github.com/docker/cli v0.0.0-20190925022749-754388324470/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||||
github.com/docker/cli v0.0.0-20191017083524-a8ff7f821017/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
github.com/docker/cli v0.0.0-20191017083524-a8ff7f821017/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||||
|
@ -1034,8 +1036,8 @@ github.com/tommy-muehle/go-mnd v1.3.1-0.20200224220436-e6f9a994e8fa/go.mod h1:dS
|
||||||
github.com/tonistiigi/fsutil v0.0.0-20201103201449-0834f99b7b85/go.mod h1:a7cilN64dG941IOXfhJhlH0qB92hxJ9A1ewrdUmJ6xo=
|
github.com/tonistiigi/fsutil v0.0.0-20201103201449-0834f99b7b85/go.mod h1:a7cilN64dG941IOXfhJhlH0qB92hxJ9A1ewrdUmJ6xo=
|
||||||
github.com/tonistiigi/fsutil v0.0.0-20210818161904-4442383b5028 h1:uEkkUFMCPtzz1HVOa42u15OHems1ugiRt172tSRTWSk=
|
github.com/tonistiigi/fsutil v0.0.0-20210818161904-4442383b5028 h1:uEkkUFMCPtzz1HVOa42u15OHems1ugiRt172tSRTWSk=
|
||||||
github.com/tonistiigi/fsutil v0.0.0-20210818161904-4442383b5028/go.mod h1:E6osHKls9ix67jofYQ61RQKwlJhqJOZM2hintp+49iI=
|
github.com/tonistiigi/fsutil v0.0.0-20210818161904-4442383b5028/go.mod h1:E6osHKls9ix67jofYQ61RQKwlJhqJOZM2hintp+49iI=
|
||||||
github.com/tonistiigi/go-actions-cache v0.0.0-20210714033416-b93d7f1b2e70 h1:+ZlFs3Tl5qYZJvX2PxfZxGlVXz847LsOJGyNVU5pCHo=
|
github.com/tonistiigi/go-actions-cache v0.0.0-20211002214948-4d48f2ff622a h1:TkwT/jFyObWQRFSUdLPEUIBXXlbqkGzStfOFgu/okCE=
|
||||||
github.com/tonistiigi/go-actions-cache v0.0.0-20210714033416-b93d7f1b2e70/go.mod h1:dNS+PPTqGnSl80x3wEyWWCHeON5xiBGtcM0uD6CgHNU=
|
github.com/tonistiigi/go-actions-cache v0.0.0-20211002214948-4d48f2ff622a/go.mod h1:YiIBjH5gP7mao3t0dBrNNBGuKYdeJmcAJjYLXr43k6A=
|
||||||
github.com/tonistiigi/opentelemetry-go-contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.0.0-20210714055410-d010b05b4939 h1:s6wDMZYNyWt8KvkjhrMpOthFPgI3JB8ipJS+eCV/psg=
|
github.com/tonistiigi/opentelemetry-go-contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.0.0-20210714055410-d010b05b4939 h1:s6wDMZYNyWt8KvkjhrMpOthFPgI3JB8ipJS+eCV/psg=
|
||||||
github.com/tonistiigi/opentelemetry-go-contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.0.0-20210714055410-d010b05b4939/go.mod h1:Vm5u/mtkj1OMhtao0v+BGo2LUoLCgHYXvRmj0jWITlE=
|
github.com/tonistiigi/opentelemetry-go-contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.0.0-20210714055410-d010b05b4939/go.mod h1:Vm5u/mtkj1OMhtao0v+BGo2LUoLCgHYXvRmj0jWITlE=
|
||||||
github.com/tonistiigi/opentelemetry-go-contrib/instrumentation/net/http/httptrace/otelhttptrace v0.0.0-20210714055410-d010b05b4939 h1:ZZ1KHKvs97BcRoblbm6RhrDzs/OejFv7miYSIcZI7Ds=
|
github.com/tonistiigi/opentelemetry-go-contrib/instrumentation/net/http/httptrace/otelhttptrace v0.0.0-20210714055410-d010b05b4939 h1:ZZ1KHKvs97BcRoblbm6RhrDzs/OejFv7miYSIcZI7Ds=
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
# Binaries for programs and plugins
|
||||||
|
*.exe
|
||||||
|
*.dll
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
*.o
|
||||||
|
*.a
|
||||||
|
|
||||||
|
# Folders
|
||||||
|
_obj
|
||||||
|
_test
|
||||||
|
|
||||||
|
# Architecture specific extensions/prefixes
|
||||||
|
*.[568vq]
|
||||||
|
[568vq].out
|
||||||
|
|
||||||
|
*.cgo1.go
|
||||||
|
*.cgo2.c
|
||||||
|
_cgo_defun.c
|
||||||
|
_cgo_gotypes.go
|
||||||
|
_cgo_export.*
|
||||||
|
|
||||||
|
_testmain.go
|
||||||
|
|
||||||
|
*.prof
|
||||||
|
|
||||||
|
# Test binary, build with `go test -c`
|
||||||
|
*.test
|
||||||
|
|
||||||
|
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||||
|
*.out
|
||||||
|
|
||||||
|
# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
|
||||||
|
.glide/
|
||||||
|
|
||||||
|
# Gogland
|
||||||
|
.idea/
|
|
@ -0,0 +1,29 @@
|
||||||
|
language: go
|
||||||
|
sudo: false
|
||||||
|
|
||||||
|
go:
|
||||||
|
- 1.10.x
|
||||||
|
- 1.11.x
|
||||||
|
- 1.12.x
|
||||||
|
- 1.13.x
|
||||||
|
- 1.14.x
|
||||||
|
- 1.15.x
|
||||||
|
|
||||||
|
cache:
|
||||||
|
directories:
|
||||||
|
- $HOME/.cache/go-build
|
||||||
|
- $HOME/gopath/pkg/mod
|
||||||
|
|
||||||
|
env:
|
||||||
|
global:
|
||||||
|
- GO111MODULE=on
|
||||||
|
|
||||||
|
before_install:
|
||||||
|
- go get github.com/mattn/goveralls
|
||||||
|
- go get golang.org/x/tools/cmd/cover
|
||||||
|
- go get golang.org/x/tools/cmd/goimports
|
||||||
|
- go get golang.org/x/lint/golint
|
||||||
|
script:
|
||||||
|
- gofiles=$(find ./ -name '*.go') && [ -z "$gofiles" ] || unformatted=$(goimports -l $gofiles) && [ -z "$unformatted" ] || (echo >&2 "Go files must be formatted with gofmt. Following files has problem:\n $unformatted" && false)
|
||||||
|
- golint ./... # This won't break the build, just show warnings
|
||||||
|
- $HOME/gopath/bin/goveralls -service=travis-ci
|
|
@ -0,0 +1,201 @@
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright (c) 2018-2020, Dmitrij Koniajev (dimchansky@gmail.com)
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
|
@ -0,0 +1,66 @@
|
||||||
|
# utfbom [![Godoc](https://godoc.org/github.com/dimchansky/utfbom?status.png)](https://godoc.org/github.com/dimchansky/utfbom) [![License](https://img.shields.io/:license-apache-blue.svg)](https://opensource.org/licenses/Apache-2.0) [![Build Status](https://travis-ci.org/dimchansky/utfbom.svg?branch=master)](https://travis-ci.org/dimchansky/utfbom) [![Go Report Card](https://goreportcard.com/badge/github.com/dimchansky/utfbom)](https://goreportcard.com/report/github.com/dimchansky/utfbom) [![Coverage Status](https://coveralls.io/repos/github/dimchansky/utfbom/badge.svg?branch=master)](https://coveralls.io/github/dimchansky/utfbom?branch=master)
|
||||||
|
|
||||||
|
The package utfbom implements the detection of the BOM (Unicode Byte Order Mark) and removing as necessary. It can also return the encoding detected by the BOM.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
go get -u github.com/dimchansky/utfbom
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
|
||||||
|
"github.com/dimchansky/utfbom"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
trySkip([]byte("\xEF\xBB\xBFhello"))
|
||||||
|
trySkip([]byte("hello"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func trySkip(byteData []byte) {
|
||||||
|
fmt.Println("Input:", byteData)
|
||||||
|
|
||||||
|
// just skip BOM
|
||||||
|
output, err := ioutil.ReadAll(utfbom.SkipOnly(bytes.NewReader(byteData)))
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Println("ReadAll with BOM skipping", output)
|
||||||
|
|
||||||
|
// skip BOM and detect encoding
|
||||||
|
sr, enc := utfbom.Skip(bytes.NewReader(byteData))
|
||||||
|
fmt.Printf("Detected encoding: %s\n", enc)
|
||||||
|
output, err = ioutil.ReadAll(sr)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Println("ReadAll with BOM detection and skipping", output)
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ go run main.go
|
||||||
|
Input: [239 187 191 104 101 108 108 111]
|
||||||
|
ReadAll with BOM skipping [104 101 108 108 111]
|
||||||
|
Detected encoding: UTF8
|
||||||
|
ReadAll with BOM detection and skipping [104 101 108 108 111]
|
||||||
|
|
||||||
|
Input: [104 101 108 108 111]
|
||||||
|
ReadAll with BOM skipping [104 101 108 108 111]
|
||||||
|
Detected encoding: Unknown
|
||||||
|
ReadAll with BOM detection and skipping [104 101 108 108 111]
|
||||||
|
```
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,192 @@
|
||||||
|
// Package utfbom implements the detection of the BOM (Unicode Byte Order Mark) and removing as necessary.
|
||||||
|
// It wraps an io.Reader object, creating another object (Reader) that also implements the io.Reader
|
||||||
|
// interface but provides automatic BOM checking and removing as necessary.
|
||||||
|
package utfbom
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Encoding is type alias for detected UTF encoding.
|
||||||
|
type Encoding int
|
||||||
|
|
||||||
|
// Constants to identify detected UTF encodings.
|
||||||
|
const (
|
||||||
|
// Unknown encoding, returned when no BOM was detected
|
||||||
|
Unknown Encoding = iota
|
||||||
|
|
||||||
|
// UTF8, BOM bytes: EF BB BF
|
||||||
|
UTF8
|
||||||
|
|
||||||
|
// UTF-16, big-endian, BOM bytes: FE FF
|
||||||
|
UTF16BigEndian
|
||||||
|
|
||||||
|
// UTF-16, little-endian, BOM bytes: FF FE
|
||||||
|
UTF16LittleEndian
|
||||||
|
|
||||||
|
// UTF-32, big-endian, BOM bytes: 00 00 FE FF
|
||||||
|
UTF32BigEndian
|
||||||
|
|
||||||
|
// UTF-32, little-endian, BOM bytes: FF FE 00 00
|
||||||
|
UTF32LittleEndian
|
||||||
|
)
|
||||||
|
|
||||||
|
// String returns a user-friendly string representation of the encoding. Satisfies fmt.Stringer interface.
|
||||||
|
func (e Encoding) String() string {
|
||||||
|
switch e {
|
||||||
|
case UTF8:
|
||||||
|
return "UTF8"
|
||||||
|
case UTF16BigEndian:
|
||||||
|
return "UTF16BigEndian"
|
||||||
|
case UTF16LittleEndian:
|
||||||
|
return "UTF16LittleEndian"
|
||||||
|
case UTF32BigEndian:
|
||||||
|
return "UTF32BigEndian"
|
||||||
|
case UTF32LittleEndian:
|
||||||
|
return "UTF32LittleEndian"
|
||||||
|
default:
|
||||||
|
return "Unknown"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const maxConsecutiveEmptyReads = 100
|
||||||
|
|
||||||
|
// Skip creates Reader which automatically detects BOM (Unicode Byte Order Mark) and removes it as necessary.
|
||||||
|
// It also returns the encoding detected by the BOM.
|
||||||
|
// If the detected encoding is not needed, you can call the SkipOnly function.
|
||||||
|
func Skip(rd io.Reader) (*Reader, Encoding) {
|
||||||
|
// Is it already a Reader?
|
||||||
|
b, ok := rd.(*Reader)
|
||||||
|
if ok {
|
||||||
|
return b, Unknown
|
||||||
|
}
|
||||||
|
|
||||||
|
enc, left, err := detectUtf(rd)
|
||||||
|
return &Reader{
|
||||||
|
rd: rd,
|
||||||
|
buf: left,
|
||||||
|
err: err,
|
||||||
|
}, enc
|
||||||
|
}
|
||||||
|
|
||||||
|
// SkipOnly creates Reader which automatically detects BOM (Unicode Byte Order Mark) and removes it as necessary.
|
||||||
|
func SkipOnly(rd io.Reader) *Reader {
|
||||||
|
r, _ := Skip(rd)
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reader implements automatic BOM (Unicode Byte Order Mark) checking and
|
||||||
|
// removing as necessary for an io.Reader object.
|
||||||
|
type Reader struct {
|
||||||
|
rd io.Reader // reader provided by the client
|
||||||
|
buf []byte // buffered data
|
||||||
|
err error // last error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read is an implementation of io.Reader interface.
|
||||||
|
// The bytes are taken from the underlying Reader, but it checks for BOMs, removing them as necessary.
|
||||||
|
func (r *Reader) Read(p []byte) (n int, err error) {
|
||||||
|
if len(p) == 0 {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.buf == nil {
|
||||||
|
if r.err != nil {
|
||||||
|
return 0, r.readErr()
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.rd.Read(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy as much as we can
|
||||||
|
n = copy(p, r.buf)
|
||||||
|
r.buf = nilIfEmpty(r.buf[n:])
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Reader) readErr() error {
|
||||||
|
err := r.err
|
||||||
|
r.err = nil
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var errNegativeRead = errors.New("utfbom: reader returned negative count from Read")
|
||||||
|
|
||||||
|
func detectUtf(rd io.Reader) (enc Encoding, buf []byte, err error) {
|
||||||
|
buf, err = readBOM(rd)
|
||||||
|
|
||||||
|
if len(buf) >= 4 {
|
||||||
|
if isUTF32BigEndianBOM4(buf) {
|
||||||
|
return UTF32BigEndian, nilIfEmpty(buf[4:]), err
|
||||||
|
}
|
||||||
|
if isUTF32LittleEndianBOM4(buf) {
|
||||||
|
return UTF32LittleEndian, nilIfEmpty(buf[4:]), err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(buf) > 2 && isUTF8BOM3(buf) {
|
||||||
|
return UTF8, nilIfEmpty(buf[3:]), err
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err != nil && err != io.EOF) || (len(buf) < 2) {
|
||||||
|
return Unknown, nilIfEmpty(buf), err
|
||||||
|
}
|
||||||
|
|
||||||
|
if isUTF16BigEndianBOM2(buf) {
|
||||||
|
return UTF16BigEndian, nilIfEmpty(buf[2:]), err
|
||||||
|
}
|
||||||
|
if isUTF16LittleEndianBOM2(buf) {
|
||||||
|
return UTF16LittleEndian, nilIfEmpty(buf[2:]), err
|
||||||
|
}
|
||||||
|
|
||||||
|
return Unknown, nilIfEmpty(buf), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func readBOM(rd io.Reader) (buf []byte, err error) {
|
||||||
|
const maxBOMSize = 4
|
||||||
|
var bom [maxBOMSize]byte // used to read BOM
|
||||||
|
|
||||||
|
// read as many bytes as possible
|
||||||
|
for nEmpty, n := 0, 0; err == nil && len(buf) < maxBOMSize; buf = bom[:len(buf)+n] {
|
||||||
|
if n, err = rd.Read(bom[len(buf):]); n < 0 {
|
||||||
|
panic(errNegativeRead)
|
||||||
|
}
|
||||||
|
if n > 0 {
|
||||||
|
nEmpty = 0
|
||||||
|
} else {
|
||||||
|
nEmpty++
|
||||||
|
if nEmpty >= maxConsecutiveEmptyReads {
|
||||||
|
err = io.ErrNoProgress
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func isUTF32BigEndianBOM4(buf []byte) bool {
|
||||||
|
return buf[0] == 0x00 && buf[1] == 0x00 && buf[2] == 0xFE && buf[3] == 0xFF
|
||||||
|
}
|
||||||
|
|
||||||
|
func isUTF32LittleEndianBOM4(buf []byte) bool {
|
||||||
|
return buf[0] == 0xFF && buf[1] == 0xFE && buf[2] == 0x00 && buf[3] == 0x00
|
||||||
|
}
|
||||||
|
|
||||||
|
func isUTF8BOM3(buf []byte) bool {
|
||||||
|
return buf[0] == 0xEF && buf[1] == 0xBB && buf[2] == 0xBF
|
||||||
|
}
|
||||||
|
|
||||||
|
func isUTF16BigEndianBOM2(buf []byte) bool {
|
||||||
|
return buf[0] == 0xFE && buf[1] == 0xFF
|
||||||
|
}
|
||||||
|
|
||||||
|
func isUTF16LittleEndianBOM2(buf []byte) bool {
|
||||||
|
return buf[0] == 0xFF && buf[1] == 0xFE
|
||||||
|
}
|
||||||
|
|
||||||
|
func nilIfEmpty(buf []byte) (res []byte) {
|
||||||
|
if len(buf) > 0 {
|
||||||
|
res = buf
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2021 Tõnis Tiigi
|
||||||
|
|
||||||
|
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.
|
|
@ -18,6 +18,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
jwt "github.com/dgrijalva/jwt-go"
|
jwt "github.com/dgrijalva/jwt-go"
|
||||||
|
"github.com/dimchansky/utfbom"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
)
|
)
|
||||||
|
@ -79,7 +80,9 @@ func TryEnv(opt Opt) (*Cache, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Opt struct {
|
type Opt struct {
|
||||||
Client *http.Client
|
Client *http.Client
|
||||||
|
Timeout time.Duration
|
||||||
|
BackoffPool *BackoffPool
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(token, url string, opt Opt) (*Cache, error) {
|
func New(token, url string, opt Opt) (*Cache, error) {
|
||||||
|
@ -137,6 +140,13 @@ func New(token, url string, opt Opt) (*Cache, error) {
|
||||||
if opt.Client == nil {
|
if opt.Client == nil {
|
||||||
opt.Client = http.DefaultClient
|
opt.Client = http.DefaultClient
|
||||||
}
|
}
|
||||||
|
if opt.Timeout == 0 {
|
||||||
|
opt.Timeout = 5 * time.Minute
|
||||||
|
}
|
||||||
|
|
||||||
|
if opt.BackoffPool == nil {
|
||||||
|
opt.BackoffPool = defaultBackoffPool
|
||||||
|
}
|
||||||
|
|
||||||
return &Cache{
|
return &Cache{
|
||||||
opt: opt,
|
opt: opt,
|
||||||
|
@ -198,15 +208,11 @@ func (c *Cache) Load(ctx context.Context, keys ...string) (*Entry, error) {
|
||||||
q.Set("keys", strings.Join(keys, ","))
|
q.Set("keys", strings.Join(keys, ","))
|
||||||
q.Set("version", version(keys[0]))
|
q.Set("version", version(keys[0]))
|
||||||
req.URL.RawQuery = q.Encode()
|
req.URL.RawQuery = q.Encode()
|
||||||
req = req.WithContext(ctx)
|
|
||||||
Log("load cache %s", req.URL.String())
|
Log("load cache %s", req.URL.String())
|
||||||
resp, err := c.opt.Client.Do(req)
|
resp, err := c.doWithRetries(ctx, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.WithStack(err)
|
return nil, errors.WithStack(err)
|
||||||
}
|
}
|
||||||
if err := checkResponse(resp); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
var ce Entry
|
var ce Entry
|
||||||
dt, err := ioutil.ReadAll(io.LimitReader(resp.Body, 32*1024))
|
dt, err := ioutil.ReadAll(io.LimitReader(resp.Body, 32*1024))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -237,15 +243,11 @@ func (c *Cache) reserve(ctx context.Context, key string) (int, error) {
|
||||||
c.auth(req)
|
c.auth(req)
|
||||||
c.accept(req)
|
c.accept(req)
|
||||||
req.Header.Set("Content-Type", "application/json")
|
req.Header.Set("Content-Type", "application/json")
|
||||||
req = req.WithContext(ctx)
|
|
||||||
Log("save cache req %s body=%s", req.URL.String(), dt)
|
Log("save cache req %s body=%s", req.URL.String(), dt)
|
||||||
resp, err := c.opt.Client.Do(req)
|
resp, err := c.doWithRetries(ctx, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, errors.WithStack(err)
|
return 0, errors.WithStack(err)
|
||||||
}
|
}
|
||||||
if err := checkResponse(resp); err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
dt, err = ioutil.ReadAll(io.LimitReader(resp.Body, 32*1024))
|
dt, err = ioutil.ReadAll(io.LimitReader(resp.Body, 32*1024))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -275,13 +277,10 @@ func (c *Cache) commit(ctx context.Context, id int, size int64) error {
|
||||||
c.accept(req)
|
c.accept(req)
|
||||||
req.Header.Set("Content-Type", "application/json")
|
req.Header.Set("Content-Type", "application/json")
|
||||||
Log("commit cache %s, size %d", req.URL.String(), size)
|
Log("commit cache %s, size %d", req.URL.String(), size)
|
||||||
resp, err := c.opt.Client.Do(req)
|
resp, err := c.doWithRetries(ctx, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "error committing cache %d", id)
|
return errors.Wrapf(err, "error committing cache %d", id)
|
||||||
}
|
}
|
||||||
if err := checkResponse(resp); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
dt, err = ioutil.ReadAll(io.LimitReader(resp.Body, 32*1024))
|
dt, err = ioutil.ReadAll(io.LimitReader(resp.Body, 32*1024))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -412,13 +411,10 @@ func (c *Cache) uploadChunk(ctx context.Context, id int, ra io.ReaderAt, off, n
|
||||||
req.Header.Set("Content-Range", fmt.Sprintf("bytes %d-%d/*", off, off+n-1))
|
req.Header.Set("Content-Range", fmt.Sprintf("bytes %d-%d/*", off, off+n-1))
|
||||||
|
|
||||||
Log("upload cache chunk %s, range %d-%d", req.URL.String(), off, off+n-1)
|
Log("upload cache chunk %s, range %d-%d", req.URL.String(), off, off+n-1)
|
||||||
resp, err := c.opt.Client.Do(req)
|
resp, err := c.doWithRetries(ctx, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.WithStack(err)
|
return errors.WithStack(err)
|
||||||
}
|
}
|
||||||
if err := checkResponse(resp); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
dt, err := ioutil.ReadAll(io.LimitReader(resp.Body, 32*1024))
|
dt, err := ioutil.ReadAll(io.LimitReader(resp.Body, 32*1024))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.WithStack(err)
|
return errors.WithStack(err)
|
||||||
|
@ -429,6 +425,38 @@ func (c *Cache) uploadChunk(ctx context.Context, id int, ra io.ReaderAt, off, n
|
||||||
return resp.Body.Close()
|
return resp.Body.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Cache) doWithRetries(ctx context.Context, req *http.Request) (*http.Response, error) {
|
||||||
|
req = req.WithContext(ctx)
|
||||||
|
var err error
|
||||||
|
max := time.Now().Add(c.opt.Timeout)
|
||||||
|
for {
|
||||||
|
if err1 := c.opt.BackoffPool.Wait(ctx, time.Until(max)); err1 != nil {
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "%v", err1)
|
||||||
|
}
|
||||||
|
return nil, err1
|
||||||
|
}
|
||||||
|
var resp *http.Response
|
||||||
|
resp, err = c.opt.Client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.WithStack(err)
|
||||||
|
}
|
||||||
|
if err := checkResponse(resp); err != nil {
|
||||||
|
var he HTTPError
|
||||||
|
if errors.As(err, &he) {
|
||||||
|
if he.StatusCode == http.StatusTooManyRequests {
|
||||||
|
c.opt.BackoffPool.Delay()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.opt.BackoffPool.Reset()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
c.opt.BackoffPool.Reset()
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Cache) auth(r *http.Request) {
|
func (c *Cache) auth(r *http.Request) {
|
||||||
r.Header.Add("Authorization", "Bearer "+c.Token.Raw)
|
r.Header.Add("Authorization", "Bearer "+c.Token.Raw)
|
||||||
}
|
}
|
||||||
|
@ -535,29 +563,47 @@ func (e GithubAPIError) Is(err error) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type HTTPError struct {
|
||||||
|
StatusCode int
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e HTTPError) Error() string {
|
||||||
|
return e.Err.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e HTTPError) Unwrap() error {
|
||||||
|
return e.Err
|
||||||
|
}
|
||||||
|
|
||||||
func checkResponse(resp *http.Response) error {
|
func checkResponse(resp *http.Response) error {
|
||||||
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
|
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
dt, err := ioutil.ReadAll(io.LimitReader(resp.Body, 32*1024))
|
dt, err := ioutil.ReadAll(utfbom.SkipOnly(io.LimitReader(resp.Body, 32*1024)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.WithStack(err)
|
return errors.WithStack(err)
|
||||||
}
|
}
|
||||||
var gae GithubAPIError
|
var gae GithubAPIError
|
||||||
if err := json.Unmarshal(dt, &gae); err != nil {
|
if err1 := json.Unmarshal(dt, &gae); err1 != nil {
|
||||||
return errors.Wrapf(err, "failed to parse error response %d: %s", resp.StatusCode, dt)
|
err = errors.Wrapf(err1, "failed to parse error response %d: %s", resp.StatusCode, dt)
|
||||||
|
} else if gae.Message != "" {
|
||||||
|
err = errors.WithStack(gae)
|
||||||
|
} else {
|
||||||
|
err = errors.Errorf("unknown error %s: %s", resp.Status, dt)
|
||||||
}
|
}
|
||||||
if gae.Message != "" {
|
|
||||||
return errors.WithStack(gae)
|
return HTTPError{
|
||||||
|
StatusCode: resp.StatusCode,
|
||||||
|
Err: err,
|
||||||
}
|
}
|
||||||
return errors.Errorf("unknown error %d: %s", resp.StatusCode, dt)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func decryptToken(enc, pass string) (string, string, error) {
|
func decryptToken(enc, pass string) (string, string, error) {
|
||||||
// openssl key derivation uses some non-standard algorithm so exec instead of using go libraries
|
// openssl key derivation uses some non-standard algorithm so exec instead of using go libraries
|
||||||
// this is only used on testing anyway
|
// this is only used on testing anyway
|
||||||
cmd := exec.Command("openssl", "enc", "-d", "-aes-256-cbc", "-a", "-A", "-salt", "-md", "sha256", "-pass", "env:GOCACHE_TOKEN_PW")
|
cmd := exec.Command("openssl", "enc", "-d", "-aes-256-cbc", "-a", "-A", "-salt", "-md", "sha256", "-pass", "env:GHCACHE_TOKEN_PW")
|
||||||
cmd.Env = append(cmd.Env, fmt.Sprintf("GOCACHE_TOKEN_PW=%s", pass))
|
cmd.Env = append(cmd.Env, fmt.Sprintf("GHCACHE_TOKEN_PW=%s", pass))
|
||||||
cmd.Stdin = bytes.NewReader([]byte(enc))
|
cmd.Stdin = bytes.NewReader([]byte(enc))
|
||||||
buf := &bytes.Buffer{}
|
buf := &bytes.Buffer{}
|
||||||
cmd.Stdout = buf
|
cmd.Stdout = buf
|
||||||
|
|
|
@ -0,0 +1,108 @@
|
||||||
|
package actionscache
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
const maxBackoff = time.Second * 90
|
||||||
|
const minBackoff = time.Second * 1
|
||||||
|
|
||||||
|
var defaultBackoffPool = &BackoffPool{}
|
||||||
|
|
||||||
|
type BackoffPool struct {
|
||||||
|
mu sync.Mutex
|
||||||
|
queue []chan struct{}
|
||||||
|
timer *time.Timer
|
||||||
|
backoff time.Duration
|
||||||
|
target time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BackoffPool) Wait(ctx context.Context, timeout time.Duration) error {
|
||||||
|
b.mu.Lock()
|
||||||
|
if b.timer == nil {
|
||||||
|
b.mu.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
done := make(chan struct{})
|
||||||
|
b.queue = append(b.queue, done)
|
||||||
|
|
||||||
|
b.mu.Unlock()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
case <-done:
|
||||||
|
return nil
|
||||||
|
case <-time.After(timeout):
|
||||||
|
return errors.Errorf("maximum timeout reached")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BackoffPool) Reset() {
|
||||||
|
b.mu.Lock()
|
||||||
|
b.reset()
|
||||||
|
b.backoff = 0
|
||||||
|
b.mu.Unlock()
|
||||||
|
}
|
||||||
|
func (b *BackoffPool) reset() {
|
||||||
|
for _, done := range b.queue {
|
||||||
|
close(done)
|
||||||
|
}
|
||||||
|
b.queue = nil
|
||||||
|
if b.timer != nil {
|
||||||
|
b.timer.Stop()
|
||||||
|
b.timer = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BackoffPool) trigger(t *time.Timer) {
|
||||||
|
b.mu.Lock()
|
||||||
|
if b.timer != t {
|
||||||
|
// this timer is not the current one
|
||||||
|
b.mu.Unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
b.reset()
|
||||||
|
b.backoff = b.backoff * 2
|
||||||
|
if b.backoff > maxBackoff {
|
||||||
|
b.backoff = maxBackoff
|
||||||
|
}
|
||||||
|
b.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BackoffPool) Delay() {
|
||||||
|
b.mu.Lock()
|
||||||
|
if b.timer != nil {
|
||||||
|
minTime := time.Now().Add(minBackoff)
|
||||||
|
if b.target.Before(minTime) {
|
||||||
|
b.target = minTime
|
||||||
|
b.timer.Stop()
|
||||||
|
b.setupTimer()
|
||||||
|
}
|
||||||
|
b.mu.Unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.backoff == 0 {
|
||||||
|
b.backoff = minBackoff
|
||||||
|
}
|
||||||
|
|
||||||
|
b.target = time.Now().Add(b.backoff)
|
||||||
|
b.setupTimer()
|
||||||
|
|
||||||
|
b.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BackoffPool) setupTimer() {
|
||||||
|
var t *time.Timer
|
||||||
|
b.timer = time.AfterFunc(time.Until(b.target), func() {
|
||||||
|
b.trigger(t)
|
||||||
|
})
|
||||||
|
t = b.timer
|
||||||
|
}
|
|
@ -201,6 +201,9 @@ github.com/davecgh/go-spew/spew
|
||||||
# github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
# github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||||
## explicit
|
## explicit
|
||||||
github.com/dgrijalva/jwt-go
|
github.com/dgrijalva/jwt-go
|
||||||
|
# github.com/dimchansky/utfbom v1.1.1
|
||||||
|
## explicit
|
||||||
|
github.com/dimchansky/utfbom
|
||||||
# github.com/docker/cli v20.10.8+incompatible
|
# github.com/docker/cli v20.10.8+incompatible
|
||||||
## explicit
|
## explicit
|
||||||
github.com/docker/cli/cli/config
|
github.com/docker/cli/cli/config
|
||||||
|
@ -450,7 +453,7 @@ github.com/tonistiigi/fsutil
|
||||||
github.com/tonistiigi/fsutil/copy
|
github.com/tonistiigi/fsutil/copy
|
||||||
github.com/tonistiigi/fsutil/prefix
|
github.com/tonistiigi/fsutil/prefix
|
||||||
github.com/tonistiigi/fsutil/types
|
github.com/tonistiigi/fsutil/types
|
||||||
# github.com/tonistiigi/go-actions-cache v0.0.0-20210714033416-b93d7f1b2e70
|
# github.com/tonistiigi/go-actions-cache v0.0.0-20211002214948-4d48f2ff622a
|
||||||
## explicit; go 1.16
|
## explicit; go 1.16
|
||||||
github.com/tonistiigi/go-actions-cache
|
github.com/tonistiigi/go-actions-cache
|
||||||
# github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea
|
# github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea
|
||||||
|
|
Loading…
Reference in New Issue