vendor: github.com/hashicorp/go-getter@v1.1.0
go get github.com/hashicorp/go-getter@v1.1.0 go mod tidy go mod vendor
This commit is contained in:
parent
725dfe8a54
commit
b43e5e83f5
10
go.mod
10
go.mod
|
@ -18,7 +18,6 @@ require (
|
||||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da // indirect
|
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da // indirect
|
||||||
github.com/armon/go-radix v1.0.0 // indirect
|
github.com/armon/go-radix v1.0.0 // indirect
|
||||||
github.com/aws/aws-sdk-go v1.16.36
|
github.com/aws/aws-sdk-go v1.16.36
|
||||||
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect
|
|
||||||
github.com/bgentry/speakeasy v0.1.0 // indirect
|
github.com/bgentry/speakeasy v0.1.0 // indirect
|
||||||
github.com/blang/semver v3.5.1+incompatible
|
github.com/blang/semver v3.5.1+incompatible
|
||||||
github.com/boltdb/bolt v1.3.1 // indirect
|
github.com/boltdb/bolt v1.3.1 // indirect
|
||||||
|
@ -52,7 +51,7 @@ require (
|
||||||
github.com/hashicorp/go-azure-helpers v0.0.0-20190129193224-166dfd221bb2
|
github.com/hashicorp/go-azure-helpers v0.0.0-20190129193224-166dfd221bb2
|
||||||
github.com/hashicorp/go-checkpoint v0.5.0
|
github.com/hashicorp/go-checkpoint v0.5.0
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.0
|
github.com/hashicorp/go-cleanhttp v0.5.0
|
||||||
github.com/hashicorp/go-getter v0.0.0-20180327010114-90bb99a48d86
|
github.com/hashicorp/go-getter v1.1.0
|
||||||
github.com/hashicorp/go-hclog v0.0.0-20181001195459-61d530d6c27f
|
github.com/hashicorp/go-hclog v0.0.0-20181001195459-61d530d6c27f
|
||||||
github.com/hashicorp/go-immutable-radix v0.0.0-20180129170900-7f3cd4390caa // indirect
|
github.com/hashicorp/go-immutable-radix v0.0.0-20180129170900-7f3cd4390caa // indirect
|
||||||
github.com/hashicorp/go-msgpack v0.0.0-20150518234257-fa3f63826f7c // indirect
|
github.com/hashicorp/go-msgpack v0.0.0-20150518234257-fa3f63826f7c // indirect
|
||||||
|
@ -60,11 +59,10 @@ require (
|
||||||
github.com/hashicorp/go-plugin v0.0.0-20190220160451-3f118e8ee104
|
github.com/hashicorp/go-plugin v0.0.0-20190220160451-3f118e8ee104
|
||||||
github.com/hashicorp/go-retryablehttp v0.5.1
|
github.com/hashicorp/go-retryablehttp v0.5.1
|
||||||
github.com/hashicorp/go-rootcerts v0.0.0-20160503143440-6bb64b370b90
|
github.com/hashicorp/go-rootcerts v0.0.0-20160503143440-6bb64b370b90
|
||||||
github.com/hashicorp/go-safetemp v1.0.0 // indirect
|
|
||||||
github.com/hashicorp/go-sockaddr v0.0.0-20180320115054-6d291a969b86 // indirect
|
github.com/hashicorp/go-sockaddr v0.0.0-20180320115054-6d291a969b86 // indirect
|
||||||
github.com/hashicorp/go-tfe v0.3.8
|
github.com/hashicorp/go-tfe v0.3.8
|
||||||
github.com/hashicorp/go-uuid v1.0.0
|
github.com/hashicorp/go-uuid v1.0.0
|
||||||
github.com/hashicorp/go-version v1.0.0
|
github.com/hashicorp/go-version v1.1.0
|
||||||
github.com/hashicorp/golang-lru v0.5.0 // indirect
|
github.com/hashicorp/golang-lru v0.5.0 // indirect
|
||||||
github.com/hashicorp/hcl v0.0.0-20170504190234-a4b07c25de5f
|
github.com/hashicorp/hcl v0.0.0-20170504190234-a4b07c25de5f
|
||||||
github.com/hashicorp/hcl2 v0.0.0-20190214011454-504b92060753
|
github.com/hashicorp/hcl2 v0.0.0-20190214011454-504b92060753
|
||||||
|
@ -84,8 +82,7 @@ require (
|
||||||
github.com/masterzen/azure-sdk-for-go v0.0.0-20161014135628-ee4f0065d00c // indirect
|
github.com/masterzen/azure-sdk-for-go v0.0.0-20161014135628-ee4f0065d00c // indirect
|
||||||
github.com/masterzen/simplexml v0.0.0-20160608183007-4572e39b1ab9 // indirect
|
github.com/masterzen/simplexml v0.0.0-20160608183007-4572e39b1ab9 // indirect
|
||||||
github.com/masterzen/winrm v0.0.0-20180224160350-7e40f93ae939
|
github.com/masterzen/winrm v0.0.0-20180224160350-7e40f93ae939
|
||||||
github.com/mattn/go-colorable v0.0.0-20160220075935-9cbef7c35391
|
github.com/mattn/go-colorable v0.0.9
|
||||||
github.com/mattn/go-isatty v0.0.4 // indirect
|
|
||||||
github.com/mattn/go-shellwords v1.0.1
|
github.com/mattn/go-shellwords v1.0.1
|
||||||
github.com/miekg/dns v1.0.8 // indirect
|
github.com/miekg/dns v1.0.8 // indirect
|
||||||
github.com/mitchellh/cli v0.0.0-20171129193617-33edc47170b5
|
github.com/mitchellh/cli v0.0.0-20171129193617-33edc47170b5
|
||||||
|
@ -114,7 +111,6 @@ require (
|
||||||
github.com/terraform-providers/terraform-provider-openstack v1.15.0
|
github.com/terraform-providers/terraform-provider-openstack v1.15.0
|
||||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6 // indirect
|
github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6 // indirect
|
||||||
github.com/ugorji/go v0.0.0-20180813092308-00b869d2f4a5 // indirect
|
github.com/ugorji/go v0.0.0-20180813092308-00b869d2f4a5 // indirect
|
||||||
github.com/ulikunitz/xz v0.5.4 // indirect
|
|
||||||
github.com/vmihailenco/msgpack v4.0.1+incompatible // indirect
|
github.com/vmihailenco/msgpack v4.0.1+incompatible // indirect
|
||||||
github.com/xanzy/ssh-agent v0.2.0
|
github.com/xanzy/ssh-agent v0.2.0
|
||||||
github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18 // indirect
|
github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18 // indirect
|
||||||
|
|
26
go.sum
26
go.sum
|
@ -45,6 +45,7 @@ github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da h1:8GUt8eRujhVEGZ
|
||||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||||
github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI=
|
github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI=
|
||||||
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||||
|
github.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM=
|
||||||
github.com/aws/aws-sdk-go v1.16.36 h1:POeH34ZME++pr7GBGh+ZO6Y5kOwSMQpqp5BGUgooJ6k=
|
github.com/aws/aws-sdk-go v1.16.36 h1:POeH34ZME++pr7GBGh+ZO6Y5kOwSMQpqp5BGUgooJ6k=
|
||||||
github.com/aws/aws-sdk-go v1.16.36/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
github.com/aws/aws-sdk-go v1.16.36/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0=
|
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0=
|
||||||
|
@ -59,6 +60,7 @@ github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4=
|
||||||
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
|
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
|
||||||
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
|
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
|
||||||
github.com/bsm/go-vlq v0.0.0-20150828105119-ec6e8d4f5f4e/go.mod h1:N+BjUcTjSxc2mtRGSCPsat1kze3CUtvJN3/jTXlp29k=
|
github.com/bsm/go-vlq v0.0.0-20150828105119-ec6e8d4f5f4e/go.mod h1:N+BjUcTjSxc2mtRGSCPsat1kze3CUtvJN3/jTXlp29k=
|
||||||
|
github.com/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s=
|
||||||
github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE=
|
github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE=
|
||||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||||
github.com/chzyer/readline v0.0.0-20161106042343-c914be64f07d h1:aG5FcWiZTOhPQzYIxwxSR1zEOxzL32fwr1CsaCfhO6w=
|
github.com/chzyer/readline v0.0.0-20161106042343-c914be64f07d h1:aG5FcWiZTOhPQzYIxwxSR1zEOxzL32fwr1CsaCfhO6w=
|
||||||
|
@ -90,6 +92,7 @@ github.com/dylanmei/iso8601 v0.1.0 h1:812NGQDBcqquTfH5Yeo7lwR0nzx/cKdsmf3qMjPURU
|
||||||
github.com/dylanmei/iso8601 v0.1.0/go.mod h1:w9KhXSgIyROl1DefbMYIE7UVSIvELTbMrCfx+QkYnoQ=
|
github.com/dylanmei/iso8601 v0.1.0/go.mod h1:w9KhXSgIyROl1DefbMYIE7UVSIvELTbMrCfx+QkYnoQ=
|
||||||
github.com/dylanmei/winrmtest v0.0.0-20170819153634-c2fbb09e6c08 h1:0bp6/GrNOrTDtSXe9YYGCwf8jp5Fb/b+4a6MTRm4qzY=
|
github.com/dylanmei/winrmtest v0.0.0-20170819153634-c2fbb09e6c08 h1:0bp6/GrNOrTDtSXe9YYGCwf8jp5Fb/b+4a6MTRm4qzY=
|
||||||
github.com/dylanmei/winrmtest v0.0.0-20170819153634-c2fbb09e6c08/go.mod h1:VBVDFSBXCIW8JaHQpI8lldSKfYaLMzP9oyq6IJ4fhzY=
|
github.com/dylanmei/winrmtest v0.0.0-20170819153634-c2fbb09e6c08/go.mod h1:VBVDFSBXCIW8JaHQpI8lldSKfYaLMzP9oyq6IJ4fhzY=
|
||||||
|
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||||
|
@ -156,8 +159,8 @@ github.com/hashicorp/go-checkpoint v0.5.0 h1:MFYpPZCnQqQTE18jFwSII6eUQrD/oxMFp3m
|
||||||
github.com/hashicorp/go-checkpoint v0.5.0/go.mod h1:7nfLNL10NsxqO4iWuW6tWW0HjZuDrwkBuEQsVcpCOgg=
|
github.com/hashicorp/go-checkpoint v0.5.0/go.mod h1:7nfLNL10NsxqO4iWuW6tWW0HjZuDrwkBuEQsVcpCOgg=
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.0 h1:wvCrVc9TjDls6+YGAF2hAifE1E5U1+b4tH6KdvN3Gig=
|
github.com/hashicorp/go-cleanhttp v0.5.0 h1:wvCrVc9TjDls6+YGAF2hAifE1E5U1+b4tH6KdvN3Gig=
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||||
github.com/hashicorp/go-getter v0.0.0-20180327010114-90bb99a48d86 h1:hLYM35twiyKH44g36g+GFYODcrZQetEAY4+zrJtGea0=
|
github.com/hashicorp/go-getter v1.1.0 h1:iGVeg7L4V5FTFV3D6w+1NAyvth7BIWWSzD60pWloe2Q=
|
||||||
github.com/hashicorp/go-getter v0.0.0-20180327010114-90bb99a48d86/go.mod h1:6rdJFnhkXnzGOJbvkrdv4t9nLwKcVA+tmbQeUlkIzrU=
|
github.com/hashicorp/go-getter v1.1.0/go.mod h1:q+PoBhh16brIKwJS9kt18jEtXHTg2EGkmrA9P7HVS+U=
|
||||||
github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI=
|
github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI=
|
||||||
github.com/hashicorp/go-hclog v0.0.0-20181001195459-61d530d6c27f h1:Yv9YzBlAETjy6AOX9eLBZ3nshNVRREgerT/3nvxlGho=
|
github.com/hashicorp/go-hclog v0.0.0-20181001195459-61d530d6c27f h1:Yv9YzBlAETjy6AOX9eLBZ3nshNVRREgerT/3nvxlGho=
|
||||||
github.com/hashicorp/go-hclog v0.0.0-20181001195459-61d530d6c27f/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
|
github.com/hashicorp/go-hclog v0.0.0-20181001195459-61d530d6c27f/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
|
||||||
|
@ -184,8 +187,8 @@ github.com/hashicorp/go-tfe v0.3.8 h1:pUqxmnhZ7Dj3biugEEo2oGZ758zVQy70lx8p7p4JRE
|
||||||
github.com/hashicorp/go-tfe v0.3.8/go.mod h1:LHLchj07PCYgQqcyE5Sz+g4zrMNW+nALKbiSNTZedEs=
|
github.com/hashicorp/go-tfe v0.3.8/go.mod h1:LHLchj07PCYgQqcyE5Sz+g4zrMNW+nALKbiSNTZedEs=
|
||||||
github.com/hashicorp/go-uuid v1.0.0 h1:RS8zrF7PhGwyNPOtxSClXXj9HA8feRnJzgnI1RJCSnM=
|
github.com/hashicorp/go-uuid v1.0.0 h1:RS8zrF7PhGwyNPOtxSClXXj9HA8feRnJzgnI1RJCSnM=
|
||||||
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||||
github.com/hashicorp/go-version v1.0.0 h1:21MVWPKDphxa7ineQQTrCU5brh7OuVVAzGOCnnCPtE8=
|
github.com/hashicorp/go-version v1.1.0 h1:bPIoEKD27tNdebFGGxxYwcL4nepeY4j1QP23PFRGzg0=
|
||||||
github.com/hashicorp/go-version v1.0.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||||
github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo=
|
github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo=
|
||||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
github.com/hashicorp/hcl v0.0.0-20170504190234-a4b07c25de5f h1:UdxlrJz4JOnY8W+DbLISwf2B8WXEolNRA8BGCwI9jws=
|
github.com/hashicorp/hcl v0.0.0-20170504190234-a4b07c25de5f h1:UdxlrJz4JOnY8W+DbLISwf2B8WXEolNRA8BGCwI9jws=
|
||||||
|
@ -210,6 +213,7 @@ github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKe
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
|
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
|
||||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||||
|
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
|
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
|
||||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||||
github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo=
|
github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo=
|
||||||
|
@ -243,10 +247,11 @@ github.com/masterzen/simplexml v0.0.0-20160608183007-4572e39b1ab9 h1:SmVbOZFWAly
|
||||||
github.com/masterzen/simplexml v0.0.0-20160608183007-4572e39b1ab9/go.mod h1:kCEbxUJlNDEBNbdQMkPSp6yaKcRXVI6f4ddk8Riv4bc=
|
github.com/masterzen/simplexml v0.0.0-20160608183007-4572e39b1ab9/go.mod h1:kCEbxUJlNDEBNbdQMkPSp6yaKcRXVI6f4ddk8Riv4bc=
|
||||||
github.com/masterzen/winrm v0.0.0-20180224160350-7e40f93ae939 h1:cRFHA33ER97Xy5jmjS519OXCS/yE3AT3zdbQAg0Z53g=
|
github.com/masterzen/winrm v0.0.0-20180224160350-7e40f93ae939 h1:cRFHA33ER97Xy5jmjS519OXCS/yE3AT3zdbQAg0Z53g=
|
||||||
github.com/masterzen/winrm v0.0.0-20180224160350-7e40f93ae939/go.mod h1:CfZSN7zwz5gJiFhZJz49Uzk7mEBHIceWmbFmYx7Hf7E=
|
github.com/masterzen/winrm v0.0.0-20180224160350-7e40f93ae939/go.mod h1:CfZSN7zwz5gJiFhZJz49Uzk7mEBHIceWmbFmYx7Hf7E=
|
||||||
github.com/mattn/go-colorable v0.0.0-20160220075935-9cbef7c35391 h1:x4vT4RoTH2BNkPx0LgrBKeFuPQPviK1aSAIWG6ruc+Y=
|
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
|
||||||
github.com/mattn/go-colorable v0.0.0-20160220075935-9cbef7c35391/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||||
github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
|
github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
|
||||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||||
|
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||||
github.com/mattn/go-shellwords v1.0.1 h1:2/mQs/EosKUge1MHnAavnrNwa0wLnWDjG4dTYMGf/kI=
|
github.com/mattn/go-shellwords v1.0.1 h1:2/mQs/EosKUge1MHnAavnrNwa0wLnWDjG4dTYMGf/kI=
|
||||||
github.com/mattn/go-shellwords v1.0.1/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
|
github.com/mattn/go-shellwords v1.0.1/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||||
|
@ -266,6 +271,8 @@ github.com/mitchellh/go-linereader v0.0.0-20141013185533-07bab5fdd958 h1:wN+5lV3
|
||||||
github.com/mitchellh/go-linereader v0.0.0-20141013185533-07bab5fdd958/go.mod h1:OaY7UOoTkkrX3wRwjpYRKafIkkyeD0UtweSHAWWiqQM=
|
github.com/mitchellh/go-linereader v0.0.0-20141013185533-07bab5fdd958/go.mod h1:OaY7UOoTkkrX3wRwjpYRKafIkkyeD0UtweSHAWWiqQM=
|
||||||
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77 h1:7GoSOOW2jpsfkntVKaS2rAr1TJqfcxotyaUcuxoZSzg=
|
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77 h1:7GoSOOW2jpsfkntVKaS2rAr1TJqfcxotyaUcuxoZSzg=
|
||||||
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||||
|
github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0=
|
||||||
|
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||||
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||||
github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4=
|
github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4=
|
||||||
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||||
|
@ -363,8 +370,8 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6 h1:lYIiVD
|
||||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||||
github.com/ugorji/go v0.0.0-20180813092308-00b869d2f4a5 h1:cMjKdf4PxEBN9K5HaD9UMW8gkTbM0kMzkTa9SJe0WNQ=
|
github.com/ugorji/go v0.0.0-20180813092308-00b869d2f4a5 h1:cMjKdf4PxEBN9K5HaD9UMW8gkTbM0kMzkTa9SJe0WNQ=
|
||||||
github.com/ugorji/go v0.0.0-20180813092308-00b869d2f4a5/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ=
|
github.com/ugorji/go v0.0.0-20180813092308-00b869d2f4a5/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ=
|
||||||
github.com/ulikunitz/xz v0.5.4 h1:zATC2OoZ8H1TZll3FpbX+ikwmadbO699PE06cIkm9oU=
|
github.com/ulikunitz/xz v0.5.5 h1:pFrO0lVpTBXLpYw+pnLj6TbvHuyjXMfjGeCwSqCVwok=
|
||||||
github.com/ulikunitz/xz v0.5.4/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
|
github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
|
||||||
github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
|
github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
|
||||||
github.com/vmihailenco/msgpack v4.0.1+incompatible h1:RMF1enSPeKTlXrXdOcqjFUElywVZjjC6pqse21bKbEU=
|
github.com/vmihailenco/msgpack v4.0.1+incompatible h1:RMF1enSPeKTlXrXdOcqjFUElywVZjjC6pqse21bKbEU=
|
||||||
github.com/vmihailenco/msgpack v4.0.1+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
|
github.com/vmihailenco/msgpack v4.0.1+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
|
||||||
|
@ -404,6 +411,7 @@ golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73r
|
||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20181129055619-fae4c4e3ad76 h1:xx5MUFyRQRbPk6VjWjIE1epE/K5AoDD8QUN116NCy8k=
|
golang.org/x/net v0.0.0-20181129055619-fae4c4e3ad76 h1:xx5MUFyRQRbPk6VjWjIE1epE/K5AoDD8QUN116NCy8k=
|
||||||
golang.org/x/net v0.0.0-20181129055619-fae4c4e3ad76/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20181129055619-fae4c4e3ad76/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
@ -424,6 +432,7 @@ golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5h
|
||||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20181213200352-4d1cda033e06/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc h1:WiYx1rIFmx8c0mXAFtv5D/mHyKe1+jmuP7PViuwqwuQ=
|
golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc h1:WiYx1rIFmx8c0mXAFtv5D/mHyKe1+jmuP7PViuwqwuQ=
|
||||||
golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||||
|
@ -461,6 +470,7 @@ google.golang.org/grpc v1.18.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
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 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/cheggaaa/pb.v1 v1.0.27/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
|
||||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||||
|
|
|
@ -9,15 +9,16 @@ addons:
|
||||||
|
|
||||||
language: go
|
language: go
|
||||||
|
|
||||||
|
os:
|
||||||
|
- linux
|
||||||
|
- osx
|
||||||
|
|
||||||
go:
|
go:
|
||||||
- 1.8.x
|
- "1.11.x"
|
||||||
- 1.9.x
|
|
||||||
- master
|
before_script:
|
||||||
|
- go build ./cmd/go-getter
|
||||||
|
|
||||||
branches:
|
branches:
|
||||||
only:
|
only:
|
||||||
- master
|
- master
|
||||||
|
|
||||||
matrix:
|
|
||||||
allow_failures:
|
|
||||||
- go: master
|
|
||||||
|
|
|
@ -97,7 +97,7 @@ would download the given HTTP URL using the Git protocol.
|
||||||
|
|
||||||
Forced protocols will also override any detectors.
|
Forced protocols will also override any detectors.
|
||||||
|
|
||||||
In the absense of a forced protocol, detectors may be run on the URL, transforming
|
In the absence of a forced protocol, detectors may be run on the URL, transforming
|
||||||
the protocol anyways. The above example would've used the Git protocol either
|
the protocol anyways. The above example would've used the Git protocol either
|
||||||
way since the Git detector would've detected it was a GitHub URL.
|
way since the Git detector would've detected it was a GitHub URL.
|
||||||
|
|
||||||
|
@ -155,20 +155,44 @@ For file downloads of any protocol, go-getter can automatically verify
|
||||||
a checksum for you. Note that checksumming only works for downloading files,
|
a checksum for you. Note that checksumming only works for downloading files,
|
||||||
not directories, but checksumming will work for any protocol.
|
not directories, but checksumming will work for any protocol.
|
||||||
|
|
||||||
To checksum a file, append a `checksum` query parameter to the URL.
|
To checksum a file, append a `checksum` query parameter to the URL. go-getter
|
||||||
The paramter value should be in the format of `type:value`, where
|
will parse out this query parameter automatically and use it to verify the
|
||||||
type is "md5", "sha1", "sha256", or "sha512". The "value" should be
|
checksum. The parameter value can be in the format of `type:value` or just
|
||||||
the actual checksum value. go-getter will parse out this query parameter
|
`value`, where type is "md5", "sha1", "sha256", "sha512" or "file" . The
|
||||||
automatically and use it to verify the checksum. An example URL
|
"value" should be the actual checksum value or download URL for "file". When
|
||||||
is shown below:
|
`type` part is omitted, type will be guessed based on the length of the
|
||||||
|
checksum string. Examples:
|
||||||
|
|
||||||
```
|
```
|
||||||
./foo.txt?checksum=md5:b7d96c89d09d9e204f5fedc4d5d55b21
|
./foo.txt?checksum=md5:b7d96c89d09d9e204f5fedc4d5d55b21
|
||||||
```
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
./foo.txt?checksum=b7d96c89d09d9e204f5fedc4d5d55b21
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
./foo.txt?checksum=file:./foo.txt.sha256sum
|
||||||
|
```
|
||||||
|
|
||||||
|
When checksumming from a file - ex: with `checksum=file:url` - go-getter will
|
||||||
|
get the file linked in the URL after `file:` using the same configuration. For
|
||||||
|
example, in `file:http://releases.ubuntu.com/cosmic/MD5SUMS` go-getter will
|
||||||
|
download a checksum file under the aforementioned url using the http protocol.
|
||||||
|
All protocols supported by go-getter can be used. The checksum file will be
|
||||||
|
downloaded in a temporary file then parsed. The destination of the temporary
|
||||||
|
file can be changed by setting system specific environment variables: `TMPDIR`
|
||||||
|
for unix; `TMP`, `TEMP` or `USERPROFILE` on windows. Read godoc of
|
||||||
|
[os.TempDir](https://golang.org/pkg/os/#TempDir) for more information on the
|
||||||
|
temporary directory selection. Content of files are expected to be BSD or GNU
|
||||||
|
style. Once go-getter is done with the checksum file; it is deleted.
|
||||||
|
|
||||||
The checksum query parameter is never sent to the backend protocol
|
The checksum query parameter is never sent to the backend protocol
|
||||||
implementation. It is used at a higher level by go-getter itself.
|
implementation. It is used at a higher level by go-getter itself.
|
||||||
|
|
||||||
|
If the destination file exists and the checksums match: download
|
||||||
|
will be skipped.
|
||||||
|
|
||||||
### Unarchiving
|
### Unarchiving
|
||||||
|
|
||||||
go-getter will automatically unarchive files into a file or directory
|
go-getter will automatically unarchive files into a file or directory
|
||||||
|
@ -215,11 +239,12 @@ from the URL before going to the final protocol downloader.
|
||||||
|
|
||||||
## Protocol-Specific Options
|
## Protocol-Specific Options
|
||||||
|
|
||||||
This section documents the protocol-specific options that can be specified
|
This section documents the protocol-specific options that can be specified for
|
||||||
for go-getter. These options should be appended to the input as normal query
|
go-getter. These options should be appended to the input as normal query
|
||||||
parameters. Depending on the usage of go-getter, applications may provide
|
parameters ([HTTP headers](#headers) are an exception to this, however).
|
||||||
alternate ways of inputting options. For example, [Nomad](https://www.nomadproject.io)
|
Depending on the usage of go-getter, applications may provide alternate ways of
|
||||||
provides a nice options block for specifying options rather than in the URL.
|
inputting options. For example, [Nomad](https://www.nomadproject.io) provides a
|
||||||
|
nice options block for specifying options rather than in the URL.
|
||||||
|
|
||||||
## General (All Protocols)
|
## General (All Protocols)
|
||||||
|
|
||||||
|
@ -251,6 +276,9 @@ None
|
||||||
|
|
||||||
**Note**: Git 2.3+ is required to use this feature.
|
**Note**: Git 2.3+ is required to use this feature.
|
||||||
|
|
||||||
|
* `depth` - The Git clone depth. The provided number specifies the last `n`
|
||||||
|
revisions to clone from the repository.
|
||||||
|
|
||||||
### Mercurial (`hg`)
|
### Mercurial (`hg`)
|
||||||
|
|
||||||
* `rev` - The Mercurial revision to checkout.
|
* `rev` - The Mercurial revision to checkout.
|
||||||
|
@ -263,6 +291,13 @@ To use HTTP basic authentication with go-getter, simply prepend `username:passwo
|
||||||
hostname in the URL such as `https://Aladdin:OpenSesame@www.example.com/index.html`. All special
|
hostname in the URL such as `https://Aladdin:OpenSesame@www.example.com/index.html`. All special
|
||||||
characters, including the username and password, must be URL encoded.
|
characters, including the username and password, must be URL encoded.
|
||||||
|
|
||||||
|
#### Headers
|
||||||
|
|
||||||
|
Optional request headers can be added by supplying them in a custom
|
||||||
|
[`HttpGetter`](https://godoc.org/github.com/hashicorp/go-getter#HttpGetter)
|
||||||
|
(_not_ as query parameters like most other options). These headers will be sent
|
||||||
|
out on every request the getter in question makes.
|
||||||
|
|
||||||
### S3 (`s3`)
|
### S3 (`s3`)
|
||||||
|
|
||||||
S3 takes various access configurations in the URL. Note that it will also
|
S3 takes various access configurations in the URL. Note that it will also
|
||||||
|
|
|
@ -13,4 +13,4 @@ install:
|
||||||
|
|
||||||
go get -d -v -t ./...
|
go get -d -v -t ./...
|
||||||
build_script:
|
build_script:
|
||||||
- cmd: go test -v ./...
|
- cmd: go test ./...
|
||||||
|
|
|
@ -0,0 +1,291 @@
|
||||||
|
package getter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"crypto/md5"
|
||||||
|
"crypto/sha1"
|
||||||
|
"crypto/sha256"
|
||||||
|
"crypto/sha512"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"hash"
|
||||||
|
"io"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
urlhelper "github.com/hashicorp/go-getter/helper/url"
|
||||||
|
)
|
||||||
|
|
||||||
|
// fileChecksum helps verifying the checksum for a file.
|
||||||
|
type fileChecksum struct {
|
||||||
|
Type string
|
||||||
|
Hash hash.Hash
|
||||||
|
Value []byte
|
||||||
|
Filename string
|
||||||
|
}
|
||||||
|
|
||||||
|
// checksum is a simple method to compute the checksum of a source file
|
||||||
|
// and compare it to the given expected value.
|
||||||
|
func (c *fileChecksum) checksum(source string) error {
|
||||||
|
f, err := os.Open(source)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Failed to open file for checksum: %s", err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
c.Hash.Reset()
|
||||||
|
if _, err := io.Copy(c.Hash, f); err != nil {
|
||||||
|
return fmt.Errorf("Failed to hash: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if actual := c.Hash.Sum(nil); !bytes.Equal(actual, c.Value) {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"Checksums did not match.\nExpected: %s\nGot: %s",
|
||||||
|
hex.EncodeToString(c.Value),
|
||||||
|
hex.EncodeToString(actual))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// extractChecksum will return a fileChecksum based on the 'checksum'
|
||||||
|
// parameter of u.
|
||||||
|
// ex:
|
||||||
|
// http://hashicorp.com/terraform?checksum=<checksumValue>
|
||||||
|
// http://hashicorp.com/terraform?checksum=<checksumType>:<checksumValue>
|
||||||
|
// http://hashicorp.com/terraform?checksum=file:<checksum_url>
|
||||||
|
// when checksumming from a file, extractChecksum will go get checksum_url
|
||||||
|
// in a temporary directory, parse the content of the file then delete it.
|
||||||
|
// Content of files are expected to be BSD style or GNU style.
|
||||||
|
//
|
||||||
|
// BSD-style checksum:
|
||||||
|
// MD5 (file1) = <checksum>
|
||||||
|
// MD5 (file2) = <checksum>
|
||||||
|
//
|
||||||
|
// GNU-style:
|
||||||
|
// <checksum> file1
|
||||||
|
// <checksum> *file2
|
||||||
|
//
|
||||||
|
// see parseChecksumLine for more detail on checksum file parsing
|
||||||
|
func (c *Client) extractChecksum(u *url.URL) (*fileChecksum, error) {
|
||||||
|
q := u.Query()
|
||||||
|
v := q.Get("checksum")
|
||||||
|
|
||||||
|
if v == "" {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
vs := strings.SplitN(v, ":", 2)
|
||||||
|
switch len(vs) {
|
||||||
|
case 2:
|
||||||
|
break // good
|
||||||
|
default:
|
||||||
|
// here, we try to guess the checksum from it's length
|
||||||
|
// if the type was not passed
|
||||||
|
return newChecksumFromValue(v, filepath.Base(u.EscapedPath()))
|
||||||
|
}
|
||||||
|
|
||||||
|
checksumType, checksumValue := vs[0], vs[1]
|
||||||
|
|
||||||
|
switch checksumType {
|
||||||
|
case "file":
|
||||||
|
return c.checksumFromFile(checksumValue, u)
|
||||||
|
default:
|
||||||
|
return newChecksumFromType(checksumType, checksumValue, filepath.Base(u.EscapedPath()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newChecksum(checksumValue, filename string) (*fileChecksum, error) {
|
||||||
|
c := &fileChecksum{
|
||||||
|
Filename: filename,
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
c.Value, err = hex.DecodeString(checksumValue)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid checksum: %s", err)
|
||||||
|
}
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newChecksumFromType(checksumType, checksumValue, filename string) (*fileChecksum, error) {
|
||||||
|
c, err := newChecksum(checksumValue, filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Type = strings.ToLower(checksumType)
|
||||||
|
switch c.Type {
|
||||||
|
case "md5":
|
||||||
|
c.Hash = md5.New()
|
||||||
|
case "sha1":
|
||||||
|
c.Hash = sha1.New()
|
||||||
|
case "sha256":
|
||||||
|
c.Hash = sha256.New()
|
||||||
|
case "sha512":
|
||||||
|
c.Hash = sha512.New()
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf(
|
||||||
|
"unsupported checksum type: %s", checksumType)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newChecksumFromValue(checksumValue, filename string) (*fileChecksum, error) {
|
||||||
|
c, err := newChecksum(checksumValue, filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch len(c.Value) {
|
||||||
|
case md5.Size:
|
||||||
|
c.Hash = md5.New()
|
||||||
|
c.Type = "md5"
|
||||||
|
case sha1.Size:
|
||||||
|
c.Hash = sha1.New()
|
||||||
|
c.Type = "sha1"
|
||||||
|
case sha256.Size:
|
||||||
|
c.Hash = sha256.New()
|
||||||
|
c.Type = "sha256"
|
||||||
|
case sha512.Size:
|
||||||
|
c.Hash = sha512.New()
|
||||||
|
c.Type = "sha512"
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("Unknown type for checksum %s", checksumValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// checksumsFromFile will return all the fileChecksums found in file
|
||||||
|
//
|
||||||
|
// checksumsFromFile will try to guess the hashing algorithm based on content
|
||||||
|
// of checksum file
|
||||||
|
//
|
||||||
|
// checksumsFromFile will only return checksums for files that match file
|
||||||
|
// behind src
|
||||||
|
func (c *Client) checksumFromFile(checksumFile string, src *url.URL) (*fileChecksum, error) {
|
||||||
|
checksumFileURL, err := urlhelper.Parse(checksumFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tempfile, err := tmpFile("", filepath.Base(checksumFileURL.Path))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer os.Remove(tempfile)
|
||||||
|
|
||||||
|
c2 := &Client{
|
||||||
|
Ctx: c.Ctx,
|
||||||
|
Getters: c.Getters,
|
||||||
|
Decompressors: c.Decompressors,
|
||||||
|
Detectors: c.Detectors,
|
||||||
|
Pwd: c.Pwd,
|
||||||
|
Dir: false,
|
||||||
|
Src: checksumFile,
|
||||||
|
Dst: tempfile,
|
||||||
|
ProgressListener: c.ProgressListener,
|
||||||
|
}
|
||||||
|
if err = c2.Get(); err != nil {
|
||||||
|
return nil, fmt.Errorf(
|
||||||
|
"Error downloading checksum file: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
filename := filepath.Base(src.Path)
|
||||||
|
absPath, err := filepath.Abs(src.Path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
checksumFileDir := filepath.Dir(checksumFileURL.Path)
|
||||||
|
relpath, err := filepath.Rel(checksumFileDir, absPath)
|
||||||
|
switch {
|
||||||
|
case err == nil ||
|
||||||
|
err.Error() == "Rel: can't make "+absPath+" relative to "+checksumFileDir:
|
||||||
|
// ex: on windows C:\gopath\...\content.txt cannot be relative to \
|
||||||
|
// which is okay, may be another expected path will work.
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// possible file identifiers:
|
||||||
|
options := []string{
|
||||||
|
filename, // ubuntu-14.04.1-server-amd64.iso
|
||||||
|
"*" + filename, // *ubuntu-14.04.1-server-amd64.iso Standard checksum
|
||||||
|
"?" + filename, // ?ubuntu-14.04.1-server-amd64.iso shasum -p
|
||||||
|
relpath, // dir/ubuntu-14.04.1-server-amd64.iso
|
||||||
|
"./" + relpath, // ./dir/ubuntu-14.04.1-server-amd64.iso
|
||||||
|
absPath, // fullpath; set if local
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := os.Open(tempfile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf(
|
||||||
|
"Error opening downloaded file: %s", err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
rd := bufio.NewReader(f)
|
||||||
|
for {
|
||||||
|
line, err := rd.ReadString('\n')
|
||||||
|
if err != nil {
|
||||||
|
if err != io.EOF {
|
||||||
|
return nil, fmt.Errorf(
|
||||||
|
"Error reading checksum file: %s", err)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
checksum, err := parseChecksumLine(line)
|
||||||
|
if err != nil || checksum == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if checksum.Filename == "" {
|
||||||
|
// filename not sure, let's try
|
||||||
|
return checksum, nil
|
||||||
|
}
|
||||||
|
// make sure the checksum is for the right file
|
||||||
|
for _, option := range options {
|
||||||
|
if option != "" && checksum.Filename == option {
|
||||||
|
// any checksum will work so we return the first one
|
||||||
|
return checksum, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("no checksum found in: %s", checksumFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseChecksumLine takes a line from a checksum file and returns
|
||||||
|
// checksumType, checksumValue and filename parseChecksumLine guesses the style
|
||||||
|
// of the checksum BSD vs GNU by splitting the line and by counting the parts.
|
||||||
|
// of a line.
|
||||||
|
// for BSD type sums parseChecksumLine guesses the hashing algorithm
|
||||||
|
// by checking the length of the checksum.
|
||||||
|
func parseChecksumLine(line string) (*fileChecksum, error) {
|
||||||
|
parts := strings.Fields(line)
|
||||||
|
|
||||||
|
switch len(parts) {
|
||||||
|
case 4:
|
||||||
|
// BSD-style checksum:
|
||||||
|
// MD5 (file1) = <checksum>
|
||||||
|
// MD5 (file2) = <checksum>
|
||||||
|
if len(parts[1]) <= 2 ||
|
||||||
|
parts[1][0] != '(' || parts[1][len(parts[1])-1] != ')' {
|
||||||
|
return nil, fmt.Errorf(
|
||||||
|
"Unexpected BSD-style-checksum filename format: %s", line)
|
||||||
|
}
|
||||||
|
filename := parts[1][1 : len(parts[1])-1]
|
||||||
|
return newChecksumFromType(parts[0], parts[3], filename)
|
||||||
|
case 2:
|
||||||
|
// GNU-style:
|
||||||
|
// <checksum> file1
|
||||||
|
// <checksum> *file2
|
||||||
|
return newChecksumFromValue(parts[0], parts[1])
|
||||||
|
case 0:
|
||||||
|
return nil, nil // empty line
|
||||||
|
default:
|
||||||
|
return newChecksumFromValue(parts[0], "")
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,15 +1,8 @@
|
||||||
package getter
|
package getter
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"context"
|
||||||
"crypto/md5"
|
|
||||||
"crypto/sha1"
|
|
||||||
"crypto/sha256"
|
|
||||||
"crypto/sha512"
|
|
||||||
"encoding/hex"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"hash"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
@ -17,7 +10,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
urlhelper "github.com/hashicorp/go-getter/helper/url"
|
urlhelper "github.com/hashicorp/go-getter/helper/url"
|
||||||
"github.com/hashicorp/go-safetemp"
|
safetemp "github.com/hashicorp/go-safetemp"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Client is a client for downloading things.
|
// Client is a client for downloading things.
|
||||||
|
@ -26,6 +19,9 @@ import (
|
||||||
// Using a client directly allows more fine-grained control over how downloading
|
// Using a client directly allows more fine-grained control over how downloading
|
||||||
// is done, as well as customizing the protocols supported.
|
// is done, as well as customizing the protocols supported.
|
||||||
type Client struct {
|
type Client struct {
|
||||||
|
// Ctx for cancellation
|
||||||
|
Ctx context.Context
|
||||||
|
|
||||||
// Src is the source URL to get.
|
// Src is the source URL to get.
|
||||||
//
|
//
|
||||||
// Dst is the path to save the downloaded thing as. If Dir is set to
|
// Dst is the path to save the downloaded thing as. If Dir is set to
|
||||||
|
@ -62,10 +58,20 @@ type Client struct {
|
||||||
//
|
//
|
||||||
// WARNING: deprecated. If Mode is set, that will take precedence.
|
// WARNING: deprecated. If Mode is set, that will take precedence.
|
||||||
Dir bool
|
Dir bool
|
||||||
|
|
||||||
|
// ProgressListener allows to track file downloads.
|
||||||
|
// By default a no op progress listener is used.
|
||||||
|
ProgressListener ProgressTracker
|
||||||
|
|
||||||
|
Options []ClientOption
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get downloads the configured source to the destination.
|
// Get downloads the configured source to the destination.
|
||||||
func (c *Client) Get() error {
|
func (c *Client) Get() error {
|
||||||
|
if err := c.Configure(c.Options...); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// Store this locally since there are cases we swap this
|
// Store this locally since there are cases we swap this
|
||||||
mode := c.Mode
|
mode := c.Mode
|
||||||
if mode == ClientModeInvalid {
|
if mode == ClientModeInvalid {
|
||||||
|
@ -76,18 +82,7 @@ func (c *Client) Get() error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default decompressor value
|
src, err := Detect(c.Src, c.Pwd, c.Detectors)
|
||||||
decompressors := c.Decompressors
|
|
||||||
if decompressors == nil {
|
|
||||||
decompressors = Decompressors
|
|
||||||
}
|
|
||||||
|
|
||||||
// Detect the URL. This is safe if it is already detected.
|
|
||||||
detectors := c.Detectors
|
|
||||||
if detectors == nil {
|
|
||||||
detectors = Detectors
|
|
||||||
}
|
|
||||||
src, err := Detect(c.Src, c.Pwd, detectors)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -119,12 +114,7 @@ func (c *Client) Get() error {
|
||||||
force = u.Scheme
|
force = u.Scheme
|
||||||
}
|
}
|
||||||
|
|
||||||
getters := c.Getters
|
g, ok := c.Getters[force]
|
||||||
if getters == nil {
|
|
||||||
getters = Getters
|
|
||||||
}
|
|
||||||
|
|
||||||
g, ok := getters[force]
|
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf(
|
return fmt.Errorf(
|
||||||
"download not supported for scheme '%s'", force)
|
"download not supported for scheme '%s'", force)
|
||||||
|
@ -150,7 +140,7 @@ func (c *Client) Get() error {
|
||||||
if archiveV == "" {
|
if archiveV == "" {
|
||||||
// We don't appear to... but is it part of the filename?
|
// We don't appear to... but is it part of the filename?
|
||||||
matchingLen := 0
|
matchingLen := 0
|
||||||
for k, _ := range decompressors {
|
for k := range c.Decompressors {
|
||||||
if strings.HasSuffix(u.Path, "."+k) && len(k) > matchingLen {
|
if strings.HasSuffix(u.Path, "."+k) && len(k) > matchingLen {
|
||||||
archiveV = k
|
archiveV = k
|
||||||
matchingLen = len(k)
|
matchingLen = len(k)
|
||||||
|
@ -163,7 +153,7 @@ func (c *Client) Get() error {
|
||||||
// real path.
|
// real path.
|
||||||
var decompressDst string
|
var decompressDst string
|
||||||
var decompressDir bool
|
var decompressDir bool
|
||||||
decompressor := decompressors[archiveV]
|
decompressor := c.Decompressors[archiveV]
|
||||||
if decompressor != nil {
|
if decompressor != nil {
|
||||||
// Create a temporary directory to store our archive. We delete
|
// Create a temporary directory to store our archive. We delete
|
||||||
// this at the end of everything.
|
// this at the end of everything.
|
||||||
|
@ -182,43 +172,15 @@ func (c *Client) Get() error {
|
||||||
mode = ClientModeFile
|
mode = ClientModeFile
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine if we have a checksum
|
// Determine checksum if we have one
|
||||||
var checksumHash hash.Hash
|
checksum, err := c.extractChecksum(u)
|
||||||
var checksumValue []byte
|
|
||||||
if v := q.Get("checksum"); v != "" {
|
|
||||||
// Delete the query parameter if we have it.
|
|
||||||
q.Del("checksum")
|
|
||||||
u.RawQuery = q.Encode()
|
|
||||||
|
|
||||||
// Determine the checksum hash type
|
|
||||||
checksumType := ""
|
|
||||||
idx := strings.Index(v, ":")
|
|
||||||
if idx > -1 {
|
|
||||||
checksumType = v[:idx]
|
|
||||||
}
|
|
||||||
switch checksumType {
|
|
||||||
case "md5":
|
|
||||||
checksumHash = md5.New()
|
|
||||||
case "sha1":
|
|
||||||
checksumHash = sha1.New()
|
|
||||||
case "sha256":
|
|
||||||
checksumHash = sha256.New()
|
|
||||||
case "sha512":
|
|
||||||
checksumHash = sha512.New()
|
|
||||||
default:
|
|
||||||
return fmt.Errorf(
|
|
||||||
"unsupported checksum type: %s", checksumType)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the remainder of the value and parse it into bytes
|
|
||||||
b, err := hex.DecodeString(v[idx+1:])
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("invalid checksum: %s", err)
|
return fmt.Errorf("invalid checksum: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set our value
|
// Delete the query parameter if we have it.
|
||||||
checksumValue = b
|
q.Del("checksum")
|
||||||
}
|
u.RawQuery = q.Encode()
|
||||||
|
|
||||||
if mode == ClientModeAny {
|
if mode == ClientModeAny {
|
||||||
// Ask the getter which client mode to use
|
// Ask the getter which client mode to use
|
||||||
|
@ -248,16 +210,25 @@ func (c *Client) Get() error {
|
||||||
// If we're not downloading a directory, then just download the file
|
// If we're not downloading a directory, then just download the file
|
||||||
// and return.
|
// and return.
|
||||||
if mode == ClientModeFile {
|
if mode == ClientModeFile {
|
||||||
|
getFile := true
|
||||||
|
if checksum != nil {
|
||||||
|
if err := checksum.checksum(dst); err == nil {
|
||||||
|
// don't get the file if the checksum of dst is correct
|
||||||
|
getFile = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if getFile {
|
||||||
err := g.GetFile(dst, u)
|
err := g.GetFile(dst, u)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if checksumHash != nil {
|
if checksum != nil {
|
||||||
if err := checksum(dst, checksumHash, checksumValue); err != nil {
|
if err := checksum.checksum(dst); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if decompressor != nil {
|
if decompressor != nil {
|
||||||
// We have a decompressor, so decompress the current destination
|
// We have a decompressor, so decompress the current destination
|
||||||
|
@ -291,7 +262,7 @@ func (c *Client) Get() error {
|
||||||
if decompressor == nil {
|
if decompressor == nil {
|
||||||
// If we're getting a directory, then this is an error. You cannot
|
// If we're getting a directory, then this is an error. You cannot
|
||||||
// checksum a directory. TODO: test
|
// checksum a directory. TODO: test
|
||||||
if checksumHash != nil {
|
if checksum != nil {
|
||||||
return fmt.Errorf(
|
return fmt.Errorf(
|
||||||
"checksum cannot be specified for directory download")
|
"checksum cannot be specified for directory download")
|
||||||
}
|
}
|
||||||
|
@ -320,30 +291,7 @@ func (c *Client) Get() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return copyDir(realDst, subDir, false)
|
return copyDir(c.Ctx, realDst, subDir, false)
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// checksum is a simple method to compute the checksum of a source file
|
|
||||||
// and compare it to the given expected value.
|
|
||||||
func checksum(source string, h hash.Hash, v []byte) error {
|
|
||||||
f, err := os.Open(source)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Failed to open file for checksum: %s", err)
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
if _, err := io.Copy(h, f); err != nil {
|
|
||||||
return fmt.Errorf("Failed to hash: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if actual := h.Sum(nil); !bytes.Equal(actual, v) {
|
|
||||||
return fmt.Errorf(
|
|
||||||
"Checksums did not match.\nExpected: %s\nGot: %s",
|
|
||||||
hex.EncodeToString(v),
|
|
||||||
hex.EncodeToString(actual))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
package getter
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
|
// A ClientOption allows to configure a client
|
||||||
|
type ClientOption func(*Client) error
|
||||||
|
|
||||||
|
// Configure configures a client with options.
|
||||||
|
func (c *Client) Configure(opts ...ClientOption) error {
|
||||||
|
if c.Ctx == nil {
|
||||||
|
c.Ctx = context.Background()
|
||||||
|
}
|
||||||
|
c.Options = opts
|
||||||
|
for _, opt := range opts {
|
||||||
|
err := opt(c)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Default decompressor values
|
||||||
|
if c.Decompressors == nil {
|
||||||
|
c.Decompressors = Decompressors
|
||||||
|
}
|
||||||
|
// Default detector values
|
||||||
|
if c.Detectors == nil {
|
||||||
|
c.Detectors = Detectors
|
||||||
|
}
|
||||||
|
// Default getter values
|
||||||
|
if c.Getters == nil {
|
||||||
|
c.Getters = Getters
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, getter := range c.Getters {
|
||||||
|
getter.SetClient(c)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithContext allows to pass a context to operation
|
||||||
|
// in order to be able to cancel a download in progress.
|
||||||
|
func WithContext(ctx context.Context) func(*Client) error {
|
||||||
|
return func(c *Client) error {
|
||||||
|
c.Ctx = ctx
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
package getter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// WithProgress allows for a user to track
|
||||||
|
// the progress of a download.
|
||||||
|
// For example by displaying a progress bar with
|
||||||
|
// current download.
|
||||||
|
// Not all getters have progress support yet.
|
||||||
|
func WithProgress(pl ProgressTracker) func(*Client) error {
|
||||||
|
return func(c *Client) error {
|
||||||
|
c.ProgressListener = pl
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProgressTracker allows to track the progress of downloads.
|
||||||
|
type ProgressTracker interface {
|
||||||
|
// TrackProgress should be called when
|
||||||
|
// a new object is being downloaded.
|
||||||
|
// src is the location the file is
|
||||||
|
// downloaded from.
|
||||||
|
// currentSize is the current size of
|
||||||
|
// the file in case it is a partial
|
||||||
|
// download.
|
||||||
|
// totalSize is the total size in bytes,
|
||||||
|
// size can be zero if the file size
|
||||||
|
// is not known.
|
||||||
|
// stream is the file being downloaded, every
|
||||||
|
// written byte will add up to processed size.
|
||||||
|
//
|
||||||
|
// TrackProgress returns a ReadCloser that wraps the
|
||||||
|
// download in progress ( stream ).
|
||||||
|
// When the download is finished, body shall be closed.
|
||||||
|
TrackProgress(src string, currentSize, totalSize int64, stream io.ReadCloser) (body io.ReadCloser)
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package getter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func tmpFile(dir, pattern string) (string, error) {
|
||||||
|
f, err := ioutil.TempFile(dir, pattern)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
f.Close()
|
||||||
|
return f.Name(), nil
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
package getter
|
package getter
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
"context"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -11,7 +11,7 @@ import (
|
||||||
// should already exist.
|
// should already exist.
|
||||||
//
|
//
|
||||||
// If ignoreDot is set to true, then dot-prefixed files/folders are ignored.
|
// If ignoreDot is set to true, then dot-prefixed files/folders are ignored.
|
||||||
func copyDir(dst string, src string, ignoreDot bool) error {
|
func copyDir(ctx context.Context, dst string, src string, ignoreDot bool) error {
|
||||||
src, err := filepath.EvalSymlinks(src)
|
src, err := filepath.EvalSymlinks(src)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -66,7 +66,7 @@ func copyDir(dst string, src string, ignoreDot bool) error {
|
||||||
}
|
}
|
||||||
defer dstF.Close()
|
defer dstF.Close()
|
||||||
|
|
||||||
if _, err := io.Copy(dstF, srcF); err != nil {
|
if _, err := Copy(ctx, dstF, srcF); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// untar is a shared helper for untarring an archive. The reader should provide
|
// untar is a shared helper for untarring an archive. The reader should provide
|
||||||
|
@ -14,6 +15,7 @@ func untar(input io.Reader, dst, src string, dir bool) error {
|
||||||
tarR := tar.NewReader(input)
|
tarR := tar.NewReader(input)
|
||||||
done := false
|
done := false
|
||||||
dirHdrs := []*tar.Header{}
|
dirHdrs := []*tar.Header{}
|
||||||
|
now := time.Now()
|
||||||
for {
|
for {
|
||||||
hdr, err := tarR.Next()
|
hdr, err := tarR.Next()
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
|
@ -95,17 +97,37 @@ func untar(input io.Reader, dst, src string, dir bool) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the access and modification time
|
// Set the access and modification time if valid, otherwise default to current time
|
||||||
if err := os.Chtimes(path, hdr.AccessTime, hdr.ModTime); err != nil {
|
aTime := now
|
||||||
|
mTime := now
|
||||||
|
if hdr.AccessTime.Unix() > 0 {
|
||||||
|
aTime = hdr.AccessTime
|
||||||
|
}
|
||||||
|
if hdr.ModTime.Unix() > 0 {
|
||||||
|
mTime = hdr.ModTime
|
||||||
|
}
|
||||||
|
if err := os.Chtimes(path, aTime, mTime); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adding a file or subdirectory changes the mtime of a directory
|
// Perform a final pass over extracted directories to update metadata
|
||||||
// We therefore wait until we've extracted everything and then set the mtime and atime attributes
|
|
||||||
for _, dirHdr := range dirHdrs {
|
for _, dirHdr := range dirHdrs {
|
||||||
path := filepath.Join(dst, dirHdr.Name)
|
path := filepath.Join(dst, dirHdr.Name)
|
||||||
if err := os.Chtimes(path, dirHdr.AccessTime, dirHdr.ModTime); err != nil {
|
// Chmod the directory since they might be created before we know the mode flags
|
||||||
|
if err := os.Chmod(path, dirHdr.FileInfo().Mode()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Set the mtime/atime attributes since they would have been changed during extraction
|
||||||
|
aTime := now
|
||||||
|
mTime := now
|
||||||
|
if dirHdr.AccessTime.Unix() > 0 {
|
||||||
|
aTime = dirHdr.AccessTime
|
||||||
|
}
|
||||||
|
if dirHdr.ModTime.Unix() > 0 {
|
||||||
|
mTime = dirHdr.ModTime
|
||||||
|
}
|
||||||
|
if err := os.Chtimes(path, aTime, mTime); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,8 @@ type TestDecompressCase struct {
|
||||||
|
|
||||||
// TestDecompressor is a helper function for testing generic decompressors.
|
// TestDecompressor is a helper function for testing generic decompressors.
|
||||||
func TestDecompressor(t testing.T, d Decompressor, cases []TestDecompressCase) {
|
func TestDecompressor(t testing.T, d Decompressor, cases []TestDecompressCase) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
for _, tc := range cases {
|
for _, tc := range cases {
|
||||||
t.Logf("Testing: %s", tc.Input)
|
t.Logf("Testing: %s", tc.Input)
|
||||||
|
|
||||||
|
@ -72,10 +74,14 @@ func TestDecompressor(t testing.T, d Decompressor, cases []TestDecompressCase) {
|
||||||
|
|
||||||
if tc.Mtime != nil {
|
if tc.Mtime != nil {
|
||||||
actual := fi.ModTime()
|
actual := fi.ModTime()
|
||||||
|
if tc.Mtime.Unix() > 0 {
|
||||||
expected := *tc.Mtime
|
expected := *tc.Mtime
|
||||||
if actual != expected {
|
if actual != expected {
|
||||||
t.Fatalf("err %s: expected mtime '%s' for %s, got '%s'", tc.Input, expected.String(), dst, actual.String())
|
t.Fatalf("err %s: expected mtime '%s' for %s, got '%s'", tc.Input, expected.String(), dst, actual.String())
|
||||||
}
|
}
|
||||||
|
} else if actual.Unix() <= 0 {
|
||||||
|
t.Fatalf("err %s: expected mtime to be > 0, got '%s'", actual.String())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
@ -103,10 +109,15 @@ func TestDecompressor(t testing.T, d Decompressor, cases []TestDecompressCase) {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
actual := fi.ModTime()
|
actual := fi.ModTime()
|
||||||
|
if tc.Mtime.Unix() > 0 {
|
||||||
expected := *tc.Mtime
|
expected := *tc.Mtime
|
||||||
if actual != expected {
|
if actual != expected {
|
||||||
t.Fatalf("err %s: expected mtime '%s' for %s, got '%s'", tc.Input, expected.String(), path, actual.String())
|
t.Fatalf("err %s: expected mtime '%s' for %s, got '%s'", tc.Input, expected.String(), path, actual.String())
|
||||||
}
|
}
|
||||||
|
} else if actual.Unix() < 0 {
|
||||||
|
t.Fatalf("err %s: expected mtime to be > 0, got '%s'", actual.String())
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
|
@ -23,6 +23,7 @@ var Detectors []Detector
|
||||||
func init() {
|
func init() {
|
||||||
Detectors = []Detector{
|
Detectors = []Detector{
|
||||||
new(GitHubDetector),
|
new(GitHubDetector),
|
||||||
|
new(GitDetector),
|
||||||
new(BitBucketDetector),
|
new(BitBucketDetector),
|
||||||
new(S3Detector),
|
new(S3Detector),
|
||||||
new(FileDetector),
|
new(FileDetector),
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
package getter
|
||||||
|
|
||||||
|
// GitDetector implements Detector to detect Git SSH URLs such as
|
||||||
|
// git@host.com:dir1/dir2 and converts them to proper URLs.
|
||||||
|
type GitDetector struct{}
|
||||||
|
|
||||||
|
func (d *GitDetector) Detect(src, _ string) (string, bool, error) {
|
||||||
|
if len(src) == 0 {
|
||||||
|
return "", false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
u, err := detectSSH(src)
|
||||||
|
if err != nil {
|
||||||
|
return "", true, err
|
||||||
|
}
|
||||||
|
if u == nil {
|
||||||
|
return "", false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// We require the username to be "git" to assume that this is a Git URL
|
||||||
|
if u.User.Username() != "git" {
|
||||||
|
return "", false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return "git::" + u.String(), true, nil
|
||||||
|
}
|
|
@ -17,8 +17,6 @@ func (d *GitHubDetector) Detect(src, _ string) (string, bool, error) {
|
||||||
|
|
||||||
if strings.HasPrefix(src, "github.com/") {
|
if strings.HasPrefix(src, "github.com/") {
|
||||||
return d.detectHTTP(src)
|
return d.detectHTTP(src)
|
||||||
} else if strings.HasPrefix(src, "git@github.com:") {
|
|
||||||
return d.detectSSH(src)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return "", false, nil
|
return "", false, nil
|
||||||
|
@ -47,27 +45,3 @@ func (d *GitHubDetector) detectHTTP(src string) (string, bool, error) {
|
||||||
|
|
||||||
return "git::" + url.String(), true, nil
|
return "git::" + url.String(), true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *GitHubDetector) detectSSH(src string) (string, bool, error) {
|
|
||||||
idx := strings.Index(src, ":")
|
|
||||||
qidx := strings.Index(src, "?")
|
|
||||||
if qidx == -1 {
|
|
||||||
qidx = len(src)
|
|
||||||
}
|
|
||||||
|
|
||||||
var u url.URL
|
|
||||||
u.Scheme = "ssh"
|
|
||||||
u.User = url.User("git")
|
|
||||||
u.Host = "github.com"
|
|
||||||
u.Path = src[idx+1 : qidx]
|
|
||||||
if qidx < len(src) {
|
|
||||||
q, err := url.ParseQuery(src[qidx+1:])
|
|
||||||
if err != nil {
|
|
||||||
return "", true, fmt.Errorf("error parsing GitHub SSH URL: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
u.RawQuery = q.Encode()
|
|
||||||
}
|
|
||||||
|
|
||||||
return "git::" + u.String(), true, nil
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
package getter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Note that we do not have an SSH-getter currently so this file serves
|
||||||
|
// only to hold the detectSSH helper that is used by other detectors.
|
||||||
|
|
||||||
|
// sshPattern matches SCP-like SSH patterns (user@host:path)
|
||||||
|
var sshPattern = regexp.MustCompile("^(?:([^@]+)@)?([^:]+):/?(.+)$")
|
||||||
|
|
||||||
|
// detectSSH determines if the src string matches an SSH-like URL and
|
||||||
|
// converts it into a net.URL compatible string. This returns nil if the
|
||||||
|
// string doesn't match the SSH pattern.
|
||||||
|
//
|
||||||
|
// This function is tested indirectly via detect_git_test.go
|
||||||
|
func detectSSH(src string) (*url.URL, error) {
|
||||||
|
matched := sshPattern.FindStringSubmatch(src)
|
||||||
|
if matched == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
user := matched[1]
|
||||||
|
host := matched[2]
|
||||||
|
path := matched[3]
|
||||||
|
qidx := strings.Index(path, "?")
|
||||||
|
if qidx == -1 {
|
||||||
|
qidx = len(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
var u url.URL
|
||||||
|
u.Scheme = "ssh"
|
||||||
|
u.User = url.User(user)
|
||||||
|
u.Host = host
|
||||||
|
u.Path = path[0:qidx]
|
||||||
|
if qidx < len(path) {
|
||||||
|
q, err := url.ParseQuery(path[qidx+1:])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error parsing GitHub SSH URL: %s", err)
|
||||||
|
}
|
||||||
|
u.RawQuery = q.Encode()
|
||||||
|
}
|
||||||
|
|
||||||
|
return &u, nil
|
||||||
|
}
|
|
@ -41,6 +41,11 @@ type Getter interface {
|
||||||
// ClientMode returns the mode based on the given URL. This is used to
|
// ClientMode returns the mode based on the given URL. This is used to
|
||||||
// allow clients to let the getters decide which mode to use.
|
// allow clients to let the getters decide which mode to use.
|
||||||
ClientMode(*url.URL) (ClientMode, error)
|
ClientMode(*url.URL) (ClientMode, error)
|
||||||
|
|
||||||
|
// SetClient allows a getter to know it's client
|
||||||
|
// in order to access client's Get functions or
|
||||||
|
// progress tracking.
|
||||||
|
SetClient(*Client)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Getters is the mapping of scheme to the Getter implementation that will
|
// Getters is the mapping of scheme to the Getter implementation that will
|
||||||
|
@ -74,12 +79,12 @@ func init() {
|
||||||
//
|
//
|
||||||
// src is a URL, whereas dst is always just a file path to a folder. This
|
// src is a URL, whereas dst is always just a file path to a folder. This
|
||||||
// folder doesn't need to exist. It will be created if it doesn't exist.
|
// folder doesn't need to exist. It will be created if it doesn't exist.
|
||||||
func Get(dst, src string) error {
|
func Get(dst, src string, opts ...ClientOption) error {
|
||||||
return (&Client{
|
return (&Client{
|
||||||
Src: src,
|
Src: src,
|
||||||
Dst: dst,
|
Dst: dst,
|
||||||
Dir: true,
|
Dir: true,
|
||||||
Getters: Getters,
|
Options: opts,
|
||||||
}).Get()
|
}).Get()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,23 +94,23 @@ func Get(dst, src string) error {
|
||||||
// dst must be a directory. If src is a file, it will be downloaded
|
// dst must be a directory. If src is a file, it will be downloaded
|
||||||
// into dst with the basename of the URL. If src is a directory or
|
// into dst with the basename of the URL. If src is a directory or
|
||||||
// archive, it will be unpacked directly into dst.
|
// archive, it will be unpacked directly into dst.
|
||||||
func GetAny(dst, src string) error {
|
func GetAny(dst, src string, opts ...ClientOption) error {
|
||||||
return (&Client{
|
return (&Client{
|
||||||
Src: src,
|
Src: src,
|
||||||
Dst: dst,
|
Dst: dst,
|
||||||
Mode: ClientModeAny,
|
Mode: ClientModeAny,
|
||||||
Getters: Getters,
|
Options: opts,
|
||||||
}).Get()
|
}).Get()
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetFile downloads the file specified by src into the path specified by
|
// GetFile downloads the file specified by src into the path specified by
|
||||||
// dst.
|
// dst.
|
||||||
func GetFile(dst, src string) error {
|
func GetFile(dst, src string, opts ...ClientOption) error {
|
||||||
return (&Client{
|
return (&Client{
|
||||||
Src: src,
|
Src: src,
|
||||||
Dst: dst,
|
Dst: dst,
|
||||||
Dir: false,
|
Dir: false,
|
||||||
Getters: Getters,
|
Options: opts,
|
||||||
}).Get()
|
}).Get()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
package getter
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
|
// getter is our base getter; it regroups
|
||||||
|
// fields all getters have in common.
|
||||||
|
type getter struct {
|
||||||
|
client *Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *getter) SetClient(c *Client) { g.client = c }
|
||||||
|
|
||||||
|
// Context tries to returns the Contex from the getter's
|
||||||
|
// client. otherwise context.Background() is returned.
|
||||||
|
func (g *getter) Context() context.Context {
|
||||||
|
if g == nil || g.client == nil {
|
||||||
|
return context.Background()
|
||||||
|
}
|
||||||
|
return g.client.Ctx
|
||||||
|
}
|
|
@ -8,7 +8,11 @@ import (
|
||||||
// FileGetter is a Getter implementation that will download a module from
|
// FileGetter is a Getter implementation that will download a module from
|
||||||
// a file scheme.
|
// a file scheme.
|
||||||
type FileGetter struct {
|
type FileGetter struct {
|
||||||
// Copy, if set to true, will copy data instead of using a symlink
|
getter
|
||||||
|
|
||||||
|
// Copy, if set to true, will copy data instead of using a symlink. If
|
||||||
|
// false, attempts to symlink to speed up the operation and to lower the
|
||||||
|
// disk space usage. If the symlink fails, may attempt to copy on windows.
|
||||||
Copy bool
|
Copy bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
package getter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// readerFunc is syntactic sugar for read interface.
|
||||||
|
type readerFunc func(p []byte) (n int, err error)
|
||||||
|
|
||||||
|
func (rf readerFunc) Read(p []byte) (n int, err error) { return rf(p) }
|
||||||
|
|
||||||
|
// Copy is a io.Copy cancellable by context
|
||||||
|
func Copy(ctx context.Context, dst io.Writer, src io.Reader) (int64, error) {
|
||||||
|
// Copy will call the Reader and Writer interface multiple time, in order
|
||||||
|
// to copy by chunk (avoiding loading the whole file in memory).
|
||||||
|
return io.Copy(dst, readerFunc(func(p []byte) (int, error) {
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
// context has been canceled
|
||||||
|
// stop process and propagate "context canceled" error
|
||||||
|
return 0, ctx.Err()
|
||||||
|
default:
|
||||||
|
// otherwise just run default io.Reader implementation
|
||||||
|
return src.Read(p)
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
}
|
|
@ -4,7 +4,6 @@ package getter
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
@ -50,6 +49,7 @@ func (g *FileGetter) Get(dst string, u *url.URL) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *FileGetter) GetFile(dst string, u *url.URL) error {
|
func (g *FileGetter) GetFile(dst string, u *url.URL) error {
|
||||||
|
ctx := g.Context()
|
||||||
path := u.Path
|
path := u.Path
|
||||||
if u.RawPath != "" {
|
if u.RawPath != "" {
|
||||||
path = u.RawPath
|
path = u.RawPath
|
||||||
|
@ -98,6 +98,6 @@ func (g *FileGetter) GetFile(dst string, u *url.URL) error {
|
||||||
}
|
}
|
||||||
defer dstF.Close()
|
defer dstF.Close()
|
||||||
|
|
||||||
_, err = io.Copy(dstF, srcF)
|
_, err = Copy(ctx, dstF, srcF)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,15 +4,16 @@ package getter
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
"syscall"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (g *FileGetter) Get(dst string, u *url.URL) error {
|
func (g *FileGetter) Get(dst string, u *url.URL) error {
|
||||||
|
ctx := g.Context()
|
||||||
path := u.Path
|
path := u.Path
|
||||||
if u.RawPath != "" {
|
if u.RawPath != "" {
|
||||||
path = u.RawPath
|
path = u.RawPath
|
||||||
|
@ -51,7 +52,7 @@ func (g *FileGetter) Get(dst string, u *url.URL) error {
|
||||||
sourcePath := toBackslash(path)
|
sourcePath := toBackslash(path)
|
||||||
|
|
||||||
// Use mklink to create a junction point
|
// Use mklink to create a junction point
|
||||||
output, err := exec.Command("cmd", "/c", "mklink", "/J", dst, sourcePath).CombinedOutput()
|
output, err := exec.CommandContext(ctx, "cmd", "/c", "mklink", "/J", dst, sourcePath).CombinedOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to run mklink %v %v: %v %q", dst, sourcePath, err, output)
|
return fmt.Errorf("failed to run mklink %v %v: %v %q", dst, sourcePath, err, output)
|
||||||
}
|
}
|
||||||
|
@ -60,6 +61,7 @@ func (g *FileGetter) Get(dst string, u *url.URL) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *FileGetter) GetFile(dst string, u *url.URL) error {
|
func (g *FileGetter) GetFile(dst string, u *url.URL) error {
|
||||||
|
ctx := g.Context()
|
||||||
path := u.Path
|
path := u.Path
|
||||||
if u.RawPath != "" {
|
if u.RawPath != "" {
|
||||||
path = u.RawPath
|
path = u.RawPath
|
||||||
|
@ -92,7 +94,21 @@ func (g *FileGetter) GetFile(dst string, u *url.URL) error {
|
||||||
|
|
||||||
// If we're not copying, just symlink and we're done
|
// If we're not copying, just symlink and we're done
|
||||||
if !g.Copy {
|
if !g.Copy {
|
||||||
return os.Symlink(path, dst)
|
if err = os.Symlink(path, dst); err == nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
lerr, ok := err.(*os.LinkError)
|
||||||
|
if !ok {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
switch lerr.Err {
|
||||||
|
case syscall.ERROR_PRIVILEGE_NOT_HELD:
|
||||||
|
// no symlink privilege, let's
|
||||||
|
// fallback to a copy to avoid an error.
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy
|
// Copy
|
||||||
|
@ -108,7 +124,7 @@ func (g *FileGetter) GetFile(dst string, u *url.URL) error {
|
||||||
}
|
}
|
||||||
defer dstF.Close()
|
defer dstF.Close()
|
||||||
|
|
||||||
_, err = io.Copy(dstF, srcF)
|
_, err = Copy(ctx, dstF, srcF)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package getter
|
package getter
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
@ -8,28 +9,34 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
urlhelper "github.com/hashicorp/go-getter/helper/url"
|
urlhelper "github.com/hashicorp/go-getter/helper/url"
|
||||||
"github.com/hashicorp/go-safetemp"
|
safetemp "github.com/hashicorp/go-safetemp"
|
||||||
"github.com/hashicorp/go-version"
|
version "github.com/hashicorp/go-version"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GitGetter is a Getter implementation that will download a module from
|
// GitGetter is a Getter implementation that will download a module from
|
||||||
// a git repository.
|
// a git repository.
|
||||||
type GitGetter struct{}
|
type GitGetter struct {
|
||||||
|
getter
|
||||||
|
}
|
||||||
|
|
||||||
func (g *GitGetter) ClientMode(_ *url.URL) (ClientMode, error) {
|
func (g *GitGetter) ClientMode(_ *url.URL) (ClientMode, error) {
|
||||||
return ClientModeDir, nil
|
return ClientModeDir, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GitGetter) Get(dst string, u *url.URL) error {
|
func (g *GitGetter) Get(dst string, u *url.URL) error {
|
||||||
|
ctx := g.Context()
|
||||||
if _, err := exec.LookPath("git"); err != nil {
|
if _, err := exec.LookPath("git"); err != nil {
|
||||||
return fmt.Errorf("git must be available and on the PATH")
|
return fmt.Errorf("git must be available and on the PATH")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract some query parameters we use
|
// Extract some query parameters we use
|
||||||
var ref, sshKey string
|
var ref, sshKey string
|
||||||
|
var depth int
|
||||||
q := u.Query()
|
q := u.Query()
|
||||||
if len(q) > 0 {
|
if len(q) > 0 {
|
||||||
ref = q.Get("ref")
|
ref = q.Get("ref")
|
||||||
|
@ -38,6 +45,11 @@ func (g *GitGetter) Get(dst string, u *url.URL) error {
|
||||||
sshKey = q.Get("sshkey")
|
sshKey = q.Get("sshkey")
|
||||||
q.Del("sshkey")
|
q.Del("sshkey")
|
||||||
|
|
||||||
|
if n, err := strconv.Atoi(q.Get("depth")); err == nil {
|
||||||
|
depth = n
|
||||||
|
}
|
||||||
|
q.Del("depth")
|
||||||
|
|
||||||
// Copy the URL
|
// Copy the URL
|
||||||
var newU url.URL = *u
|
var newU url.URL = *u
|
||||||
u = &newU
|
u = &newU
|
||||||
|
@ -78,15 +90,35 @@ func (g *GitGetter) Get(dst string, u *url.URL) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For SSH-style URLs, if they use the SCP syntax of host:path, then
|
||||||
|
// the URL will be mangled. We detect that here and correct the path.
|
||||||
|
// Example: host:path/bar will turn into host/path/bar
|
||||||
|
if u.Scheme == "ssh" {
|
||||||
|
if idx := strings.Index(u.Host, ":"); idx > -1 {
|
||||||
|
// Copy the URL so we don't modify the input
|
||||||
|
var newU url.URL = *u
|
||||||
|
u = &newU
|
||||||
|
|
||||||
|
// Path includes the part after the ':'.
|
||||||
|
u.Path = u.Host[idx+1:] + u.Path
|
||||||
|
if u.Path[0] != '/' {
|
||||||
|
u.Path = "/" + u.Path
|
||||||
|
}
|
||||||
|
|
||||||
|
// Host trims up to the :
|
||||||
|
u.Host = u.Host[:idx]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Clone or update the repository
|
// Clone or update the repository
|
||||||
_, err := os.Stat(dst)
|
_, err := os.Stat(dst)
|
||||||
if err != nil && !os.IsNotExist(err) {
|
if err != nil && !os.IsNotExist(err) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = g.update(dst, sshKeyFile, ref)
|
err = g.update(ctx, dst, sshKeyFile, ref, depth)
|
||||||
} else {
|
} else {
|
||||||
err = g.clone(dst, sshKeyFile, u)
|
err = g.clone(ctx, dst, sshKeyFile, u, depth)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -100,7 +132,7 @@ func (g *GitGetter) Get(dst string, u *url.URL) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lastly, download any/all submodules.
|
// Lastly, download any/all submodules.
|
||||||
return g.fetchSubmodules(dst, sshKeyFile)
|
return g.fetchSubmodules(ctx, dst, sshKeyFile, depth)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetFile for Git doesn't support updating at this time. It will download
|
// GetFile for Git doesn't support updating at this time. It will download
|
||||||
|
@ -138,16 +170,23 @@ func (g *GitGetter) checkout(dst string, ref string) error {
|
||||||
return getRunCommand(cmd)
|
return getRunCommand(cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GitGetter) clone(dst, sshKeyFile string, u *url.URL) error {
|
func (g *GitGetter) clone(ctx context.Context, dst, sshKeyFile string, u *url.URL, depth int) error {
|
||||||
cmd := exec.Command("git", "clone", u.String(), dst)
|
args := []string{"clone"}
|
||||||
|
|
||||||
|
if depth > 0 {
|
||||||
|
args = append(args, "--depth", strconv.Itoa(depth))
|
||||||
|
}
|
||||||
|
|
||||||
|
args = append(args, u.String(), dst)
|
||||||
|
cmd := exec.CommandContext(ctx, "git", args...)
|
||||||
setupGitEnv(cmd, sshKeyFile)
|
setupGitEnv(cmd, sshKeyFile)
|
||||||
return getRunCommand(cmd)
|
return getRunCommand(cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GitGetter) update(dst, sshKeyFile, ref string) error {
|
func (g *GitGetter) update(ctx context.Context, dst, sshKeyFile, ref string, depth int) error {
|
||||||
// Determine if we're a branch. If we're NOT a branch, then we just
|
// Determine if we're a branch. If we're NOT a branch, then we just
|
||||||
// switch to master prior to checking out
|
// switch to master prior to checking out
|
||||||
cmd := exec.Command("git", "show-ref", "-q", "--verify", "refs/heads/"+ref)
|
cmd := exec.CommandContext(ctx, "git", "show-ref", "-q", "--verify", "refs/heads/"+ref)
|
||||||
cmd.Dir = dst
|
cmd.Dir = dst
|
||||||
|
|
||||||
if getRunCommand(cmd) != nil {
|
if getRunCommand(cmd) != nil {
|
||||||
|
@ -162,15 +201,24 @@ func (g *GitGetter) update(dst, sshKeyFile, ref string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if depth > 0 {
|
||||||
|
cmd = exec.Command("git", "pull", "--depth", strconv.Itoa(depth), "--ff-only")
|
||||||
|
} else {
|
||||||
cmd = exec.Command("git", "pull", "--ff-only")
|
cmd = exec.Command("git", "pull", "--ff-only")
|
||||||
|
}
|
||||||
|
|
||||||
cmd.Dir = dst
|
cmd.Dir = dst
|
||||||
setupGitEnv(cmd, sshKeyFile)
|
setupGitEnv(cmd, sshKeyFile)
|
||||||
return getRunCommand(cmd)
|
return getRunCommand(cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
// fetchSubmodules downloads any configured submodules recursively.
|
// fetchSubmodules downloads any configured submodules recursively.
|
||||||
func (g *GitGetter) fetchSubmodules(dst, sshKeyFile string) error {
|
func (g *GitGetter) fetchSubmodules(ctx context.Context, dst, sshKeyFile string, depth int) error {
|
||||||
cmd := exec.Command("git", "submodule", "update", "--init", "--recursive")
|
args := []string{"submodule", "update", "--init", "--recursive"}
|
||||||
|
if depth > 0 {
|
||||||
|
args = append(args, "--depth", strconv.Itoa(depth))
|
||||||
|
}
|
||||||
|
cmd := exec.CommandContext(ctx, "git", args...)
|
||||||
cmd.Dir = dst
|
cmd.Dir = dst
|
||||||
setupGitEnv(cmd, sshKeyFile)
|
setupGitEnv(cmd, sshKeyFile)
|
||||||
return getRunCommand(cmd)
|
return getRunCommand(cmd)
|
||||||
|
@ -187,7 +235,7 @@ func setupGitEnv(cmd *exec.Cmd, sshKeyFile string) {
|
||||||
// with versions of Go < 1.9.
|
// with versions of Go < 1.9.
|
||||||
env := os.Environ()
|
env := os.Environ()
|
||||||
for i, v := range env {
|
for i, v := range env {
|
||||||
if strings.HasPrefix(v, gitSSHCommand) {
|
if strings.HasPrefix(v, gitSSHCommand) && len(v) > len(gitSSHCommand) {
|
||||||
sshCmd = []string{v}
|
sshCmd = []string{v}
|
||||||
|
|
||||||
env[i], env[len(env)-1] = env[len(env)-1], env[i]
|
env[i], env[len(env)-1] = env[len(env)-1], env[i]
|
||||||
|
@ -202,6 +250,9 @@ func setupGitEnv(cmd *exec.Cmd, sshKeyFile string) {
|
||||||
|
|
||||||
if sshKeyFile != "" {
|
if sshKeyFile != "" {
|
||||||
// We have an SSH key temp file configured, tell ssh about this.
|
// We have an SSH key temp file configured, tell ssh about this.
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
sshKeyFile = strings.Replace(sshKeyFile, `\`, `/`, -1)
|
||||||
|
}
|
||||||
sshCmd = append(sshCmd, "-i", sshKeyFile)
|
sshCmd = append(sshCmd, "-i", sshKeyFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,11 +275,20 @@ func checkGitVersion(min string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
fields := strings.Fields(string(out))
|
fields := strings.Fields(string(out))
|
||||||
if len(fields) != 3 {
|
if len(fields) < 3 {
|
||||||
return fmt.Errorf("Unexpected 'git version' output: %q", string(out))
|
return fmt.Errorf("Unexpected 'git version' output: %q", string(out))
|
||||||
}
|
}
|
||||||
|
v := fields[2]
|
||||||
|
if runtime.GOOS == "windows" && strings.Contains(v, ".windows.") {
|
||||||
|
// on windows, git version will return for example:
|
||||||
|
// git version 2.20.1.windows.1
|
||||||
|
// Which does not follow the semantic versionning specs
|
||||||
|
// https://semver.org. We remove that part in order for
|
||||||
|
// go-version to not error.
|
||||||
|
v = v[:strings.Index(v, ".windows.")]
|
||||||
|
}
|
||||||
|
|
||||||
have, err := version.NewVersion(fields[2])
|
have, err := version.NewVersion(v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package getter
|
package getter
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
@ -9,18 +10,21 @@ import (
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
urlhelper "github.com/hashicorp/go-getter/helper/url"
|
urlhelper "github.com/hashicorp/go-getter/helper/url"
|
||||||
"github.com/hashicorp/go-safetemp"
|
safetemp "github.com/hashicorp/go-safetemp"
|
||||||
)
|
)
|
||||||
|
|
||||||
// HgGetter is a Getter implementation that will download a module from
|
// HgGetter is a Getter implementation that will download a module from
|
||||||
// a Mercurial repository.
|
// a Mercurial repository.
|
||||||
type HgGetter struct{}
|
type HgGetter struct {
|
||||||
|
getter
|
||||||
|
}
|
||||||
|
|
||||||
func (g *HgGetter) ClientMode(_ *url.URL) (ClientMode, error) {
|
func (g *HgGetter) ClientMode(_ *url.URL) (ClientMode, error) {
|
||||||
return ClientModeDir, nil
|
return ClientModeDir, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *HgGetter) Get(dst string, u *url.URL) error {
|
func (g *HgGetter) Get(dst string, u *url.URL) error {
|
||||||
|
ctx := g.Context()
|
||||||
if _, err := exec.LookPath("hg"); err != nil {
|
if _, err := exec.LookPath("hg"); err != nil {
|
||||||
return fmt.Errorf("hg must be available and on the PATH")
|
return fmt.Errorf("hg must be available and on the PATH")
|
||||||
}
|
}
|
||||||
|
@ -58,7 +62,7 @@ func (g *HgGetter) Get(dst string, u *url.URL) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return g.update(dst, newURL, rev)
|
return g.update(ctx, dst, newURL, rev)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetFile for Hg doesn't support updating at this time. It will download
|
// GetFile for Hg doesn't support updating at this time. It will download
|
||||||
|
@ -93,7 +97,7 @@ func (g *HgGetter) GetFile(dst string, u *url.URL) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
fg := &FileGetter{Copy: true}
|
fg := &FileGetter{Copy: true, getter: g.getter}
|
||||||
return fg.GetFile(dst, u)
|
return fg.GetFile(dst, u)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,13 +112,13 @@ func (g *HgGetter) pull(dst string, u *url.URL) error {
|
||||||
return getRunCommand(cmd)
|
return getRunCommand(cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *HgGetter) update(dst string, u *url.URL, rev string) error {
|
func (g *HgGetter) update(ctx context.Context, dst string, u *url.URL, rev string) error {
|
||||||
args := []string{"update"}
|
args := []string{"update"}
|
||||||
if rev != "" {
|
if rev != "" {
|
||||||
args = append(args, rev)
|
args = append(args, rev)
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := exec.Command("hg", args...)
|
cmd := exec.CommandContext(ctx, "hg", args...)
|
||||||
cmd.Dir = dst
|
cmd.Dir = dst
|
||||||
return getRunCommand(cmd)
|
return getRunCommand(cmd)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package getter
|
package getter
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
@ -8,9 +9,10 @@ import (
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/hashicorp/go-safetemp"
|
safetemp "github.com/hashicorp/go-safetemp"
|
||||||
)
|
)
|
||||||
|
|
||||||
// HttpGetter is a Getter implementation that will download from an HTTP
|
// HttpGetter is a Getter implementation that will download from an HTTP
|
||||||
|
@ -18,7 +20,7 @@ import (
|
||||||
//
|
//
|
||||||
// For file downloads, HTTP is used directly.
|
// For file downloads, HTTP is used directly.
|
||||||
//
|
//
|
||||||
// The protocol for downloading a directory from an HTTP endpoing is as follows:
|
// The protocol for downloading a directory from an HTTP endpoint is as follows:
|
||||||
//
|
//
|
||||||
// An HTTP GET request is made to the URL with the additional GET parameter
|
// An HTTP GET request is made to the URL with the additional GET parameter
|
||||||
// "terraform-get=1". This lets you handle that scenario specially if you
|
// "terraform-get=1". This lets you handle that scenario specially if you
|
||||||
|
@ -34,6 +36,8 @@ import (
|
||||||
// formed URL. The shorthand syntax of "github.com/foo/bar" or relative
|
// formed URL. The shorthand syntax of "github.com/foo/bar" or relative
|
||||||
// paths are not allowed.
|
// paths are not allowed.
|
||||||
type HttpGetter struct {
|
type HttpGetter struct {
|
||||||
|
getter
|
||||||
|
|
||||||
// Netrc, if true, will lookup and use auth information found
|
// Netrc, if true, will lookup and use auth information found
|
||||||
// in the user's netrc file if available.
|
// in the user's netrc file if available.
|
||||||
Netrc bool
|
Netrc bool
|
||||||
|
@ -41,6 +45,12 @@ type HttpGetter struct {
|
||||||
// Client is the http.Client to use for Get requests.
|
// Client is the http.Client to use for Get requests.
|
||||||
// This defaults to a cleanhttp.DefaultClient if left unset.
|
// This defaults to a cleanhttp.DefaultClient if left unset.
|
||||||
Client *http.Client
|
Client *http.Client
|
||||||
|
|
||||||
|
// Header contains optional request header fields that should be included
|
||||||
|
// with every HTTP request. Note that the zero value of this field is nil,
|
||||||
|
// and as such it needs to be initialized before use, via something like
|
||||||
|
// make(http.Header).
|
||||||
|
Header http.Header
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *HttpGetter) ClientMode(u *url.URL) (ClientMode, error) {
|
func (g *HttpGetter) ClientMode(u *url.URL) (ClientMode, error) {
|
||||||
|
@ -51,6 +61,7 @@ func (g *HttpGetter) ClientMode(u *url.URL) (ClientMode, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *HttpGetter) Get(dst string, u *url.URL) error {
|
func (g *HttpGetter) Get(dst string, u *url.URL) error {
|
||||||
|
ctx := g.Context()
|
||||||
// Copy the URL so we can modify it
|
// Copy the URL so we can modify it
|
||||||
var newU url.URL = *u
|
var newU url.URL = *u
|
||||||
u = &newU
|
u = &newU
|
||||||
|
@ -72,10 +83,17 @@ func (g *HttpGetter) Get(dst string, u *url.URL) error {
|
||||||
u.RawQuery = q.Encode()
|
u.RawQuery = q.Encode()
|
||||||
|
|
||||||
// Get the URL
|
// Get the URL
|
||||||
resp, err := g.Client.Get(u.String())
|
req, err := http.NewRequest("GET", u.String(), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
req.Header = g.Header
|
||||||
|
resp, err := g.Client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||||
return fmt.Errorf("bad response code: %d", resp.StatusCode)
|
return fmt.Errorf("bad response code: %d", resp.StatusCode)
|
||||||
|
@ -99,57 +117,107 @@ func (g *HttpGetter) Get(dst string, u *url.URL) error {
|
||||||
// into a temporary directory, then copy over the proper subdir.
|
// into a temporary directory, then copy over the proper subdir.
|
||||||
source, subDir := SourceDirSubdir(source)
|
source, subDir := SourceDirSubdir(source)
|
||||||
if subDir == "" {
|
if subDir == "" {
|
||||||
return Get(dst, source)
|
var opts []ClientOption
|
||||||
|
if g.client != nil {
|
||||||
|
opts = g.client.Options
|
||||||
|
}
|
||||||
|
return Get(dst, source, opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// We have a subdir, time to jump some hoops
|
// We have a subdir, time to jump some hoops
|
||||||
return g.getSubdir(dst, source, subDir)
|
return g.getSubdir(ctx, dst, source, subDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *HttpGetter) GetFile(dst string, u *url.URL) error {
|
func (g *HttpGetter) GetFile(dst string, src *url.URL) error {
|
||||||
|
ctx := g.Context()
|
||||||
if g.Netrc {
|
if g.Netrc {
|
||||||
// Add auth from netrc if we can
|
// Add auth from netrc if we can
|
||||||
if err := addAuthFromNetrc(u); err != nil {
|
if err := addAuthFromNetrc(src); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create all the parent directories if needed
|
||||||
|
if err := os.MkdirAll(filepath.Dir(dst), 0755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := os.OpenFile(dst, os.O_RDWR|os.O_CREATE, os.FileMode(0666))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
if g.Client == nil {
|
if g.Client == nil {
|
||||||
g.Client = httpClient
|
g.Client = httpClient
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := g.Client.Get(u.String())
|
var currentFileSize int64
|
||||||
|
|
||||||
|
// We first make a HEAD request so we can check
|
||||||
|
// if the server supports range queries. If the server/URL doesn't
|
||||||
|
// support HEAD requests, we just fall back to GET.
|
||||||
|
req, err := http.NewRequest("HEAD", src.String(), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
if g.Header != nil {
|
||||||
if resp.StatusCode != 200 {
|
req.Header = g.Header
|
||||||
|
}
|
||||||
|
headResp, err := g.Client.Do(req)
|
||||||
|
if err == nil && headResp != nil {
|
||||||
|
headResp.Body.Close()
|
||||||
|
if headResp.StatusCode == 200 {
|
||||||
|
// If the HEAD request succeeded, then attempt to set the range
|
||||||
|
// query if we can.
|
||||||
|
if headResp.Header.Get("Accept-Ranges") == "bytes" {
|
||||||
|
if fi, err := f.Stat(); err == nil {
|
||||||
|
if _, err = f.Seek(0, os.SEEK_END); err == nil {
|
||||||
|
req.Header.Set("Range", fmt.Sprintf("bytes=%d-", fi.Size()))
|
||||||
|
currentFileSize = fi.Size()
|
||||||
|
totalFileSize, _ := strconv.ParseInt(headResp.Header.Get("Content-Length"), 10, 64)
|
||||||
|
if currentFileSize >= totalFileSize {
|
||||||
|
// file already present
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
req.Method = "GET"
|
||||||
|
|
||||||
|
resp, err := g.Client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
switch resp.StatusCode {
|
||||||
|
case http.StatusOK, http.StatusPartialContent:
|
||||||
|
// all good
|
||||||
|
default:
|
||||||
|
resp.Body.Close()
|
||||||
return fmt.Errorf("bad response code: %d", resp.StatusCode)
|
return fmt.Errorf("bad response code: %d", resp.StatusCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create all the parent directories
|
body := resp.Body
|
||||||
if err := os.MkdirAll(filepath.Dir(dst), 0755); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
f, err := os.Create(dst)
|
if g.client != nil && g.client.ProgressListener != nil {
|
||||||
if err != nil {
|
// track download
|
||||||
return err
|
fn := filepath.Base(src.EscapedPath())
|
||||||
|
body = g.client.ProgressListener.TrackProgress(fn, currentFileSize, currentFileSize+resp.ContentLength, resp.Body)
|
||||||
}
|
}
|
||||||
|
defer body.Close()
|
||||||
|
|
||||||
n, err := io.Copy(f, resp.Body)
|
n, err := Copy(ctx, f, body)
|
||||||
if err == nil && n < resp.ContentLength {
|
if err == nil && n < resp.ContentLength {
|
||||||
err = io.ErrShortWrite
|
err = io.ErrShortWrite
|
||||||
}
|
}
|
||||||
if err1 := f.Close(); err == nil {
|
|
||||||
err = err1
|
|
||||||
}
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// getSubdir downloads the source into the destination, but with
|
// getSubdir downloads the source into the destination, but with
|
||||||
// the proper subdir.
|
// the proper subdir.
|
||||||
func (g *HttpGetter) getSubdir(dst, source, subDir string) error {
|
func (g *HttpGetter) getSubdir(ctx context.Context, dst, source, subDir string) error {
|
||||||
// Create a temporary directory to store the full source. This has to be
|
// Create a temporary directory to store the full source. This has to be
|
||||||
// a non-existent directory.
|
// a non-existent directory.
|
||||||
td, tdcloser, err := safetemp.Dir("", "getter")
|
td, tdcloser, err := safetemp.Dir("", "getter")
|
||||||
|
@ -158,8 +226,12 @@ func (g *HttpGetter) getSubdir(dst, source, subDir string) error {
|
||||||
}
|
}
|
||||||
defer tdcloser.Close()
|
defer tdcloser.Close()
|
||||||
|
|
||||||
|
var opts []ClientOption
|
||||||
|
if g.client != nil {
|
||||||
|
opts = g.client.Options
|
||||||
|
}
|
||||||
// Download that into the given directory
|
// Download that into the given directory
|
||||||
if err := Get(td, source); err != nil {
|
if err := Get(td, source, opts...); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,7 +257,7 @@ func (g *HttpGetter) getSubdir(dst, source, subDir string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return copyDir(dst, sourcePath, false)
|
return copyDir(ctx, dst, sourcePath, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseMeta looks for the first meta tag in the given reader that
|
// parseMeta looks for the first meta tag in the given reader that
|
||||||
|
|
|
@ -6,6 +6,8 @@ import (
|
||||||
|
|
||||||
// MockGetter is an implementation of Getter that can be used for tests.
|
// MockGetter is an implementation of Getter that can be used for tests.
|
||||||
type MockGetter struct {
|
type MockGetter struct {
|
||||||
|
getter
|
||||||
|
|
||||||
// Proxy, if set, will be called after recording the calls below.
|
// Proxy, if set, will be called after recording the calls below.
|
||||||
// If it isn't set, then the *Err values will be returned.
|
// If it isn't set, then the *Err values will be returned.
|
||||||
Proxy Getter
|
Proxy Getter
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
package getter
|
package getter
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
@ -18,7 +18,9 @@ import (
|
||||||
|
|
||||||
// S3Getter is a Getter implementation that will download a module from
|
// S3Getter is a Getter implementation that will download a module from
|
||||||
// a S3 bucket.
|
// a S3 bucket.
|
||||||
type S3Getter struct{}
|
type S3Getter struct {
|
||||||
|
getter
|
||||||
|
}
|
||||||
|
|
||||||
func (g *S3Getter) ClientMode(u *url.URL) (ClientMode, error) {
|
func (g *S3Getter) ClientMode(u *url.URL) (ClientMode, error) {
|
||||||
// Parse URL
|
// Parse URL
|
||||||
|
@ -60,6 +62,8 @@ func (g *S3Getter) ClientMode(u *url.URL) (ClientMode, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *S3Getter) Get(dst string, u *url.URL) error {
|
func (g *S3Getter) Get(dst string, u *url.URL) error {
|
||||||
|
ctx := g.Context()
|
||||||
|
|
||||||
// Parse URL
|
// Parse URL
|
||||||
region, bucket, path, _, creds, err := g.parseUrl(u)
|
region, bucket, path, _, creds, err := g.parseUrl(u)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -124,7 +128,7 @@ func (g *S3Getter) Get(dst string, u *url.URL) error {
|
||||||
}
|
}
|
||||||
objDst = filepath.Join(dst, objDst)
|
objDst = filepath.Join(dst, objDst)
|
||||||
|
|
||||||
if err := g.getObject(client, objDst, bucket, objPath, ""); err != nil {
|
if err := g.getObject(ctx, client, objDst, bucket, objPath, ""); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -134,6 +138,7 @@ func (g *S3Getter) Get(dst string, u *url.URL) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *S3Getter) GetFile(dst string, u *url.URL) error {
|
func (g *S3Getter) GetFile(dst string, u *url.URL) error {
|
||||||
|
ctx := g.Context()
|
||||||
region, bucket, path, version, creds, err := g.parseUrl(u)
|
region, bucket, path, version, creds, err := g.parseUrl(u)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -142,10 +147,10 @@ func (g *S3Getter) GetFile(dst string, u *url.URL) error {
|
||||||
config := g.getAWSConfig(region, u, creds)
|
config := g.getAWSConfig(region, u, creds)
|
||||||
sess := session.New(config)
|
sess := session.New(config)
|
||||||
client := s3.New(sess)
|
client := s3.New(sess)
|
||||||
return g.getObject(client, dst, bucket, path, version)
|
return g.getObject(ctx, client, dst, bucket, path, version)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *S3Getter) getObject(client *s3.S3, dst, bucket, key, version string) error {
|
func (g *S3Getter) getObject(ctx context.Context, client *s3.S3, dst, bucket, key, version string) error {
|
||||||
req := &s3.GetObjectInput{
|
req := &s3.GetObjectInput{
|
||||||
Bucket: aws.String(bucket),
|
Bucket: aws.String(bucket),
|
||||||
Key: aws.String(key),
|
Key: aws.String(key),
|
||||||
|
@ -170,7 +175,7 @@ func (g *S3Getter) getObject(client *s3.S3, dst, bucket, key, version string) er
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
|
||||||
_, err = io.Copy(f, resp.Body)
|
_, err = Copy(ctx, f, resp.Body)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
module github.com/hashicorp/go-getter
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/aws/aws-sdk-go v1.15.78
|
||||||
|
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d
|
||||||
|
github.com/cheggaaa/pb v1.0.27
|
||||||
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
github.com/fatih/color v1.7.0 // indirect
|
||||||
|
github.com/hashicorp/go-cleanhttp v0.5.0
|
||||||
|
github.com/hashicorp/go-safetemp v1.0.0
|
||||||
|
github.com/hashicorp/go-version v1.1.0
|
||||||
|
github.com/mattn/go-colorable v0.0.9 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.4 // indirect
|
||||||
|
github.com/mattn/go-runewidth v0.0.4 // indirect
|
||||||
|
github.com/mitchellh/go-homedir v1.0.0
|
||||||
|
github.com/mitchellh/go-testing-interface v1.0.0
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
github.com/stretchr/testify v1.2.2 // indirect
|
||||||
|
github.com/ulikunitz/xz v0.5.5
|
||||||
|
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a // indirect
|
||||||
|
golang.org/x/sys v0.0.0-20181213200352-4d1cda033e06 // indirect
|
||||||
|
golang.org/x/text v0.3.0 // indirect
|
||||||
|
gopkg.in/cheggaaa/pb.v1 v1.0.27 // indirect
|
||||||
|
)
|
|
@ -0,0 +1,44 @@
|
||||||
|
github.com/aws/aws-sdk-go v1.15.78 h1:LaXy6lWR0YK7LKyuU0QWy2ws/LWTPfYV/UgfiBu4tvY=
|
||||||
|
github.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM=
|
||||||
|
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas=
|
||||||
|
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4=
|
||||||
|
github.com/cheggaaa/pb v1.0.27 h1:wIkZHkNfC7R6GI5w7l/PdAdzXzlrbcI3p8OAlnkTsnc=
|
||||||
|
github.com/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s=
|
||||||
|
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/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
|
||||||
|
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||||
|
github.com/hashicorp/go-cleanhttp v0.5.0 h1:wvCrVc9TjDls6+YGAF2hAifE1E5U1+b4tH6KdvN3Gig=
|
||||||
|
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||||
|
github.com/hashicorp/go-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhEyExpmo=
|
||||||
|
github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I=
|
||||||
|
github.com/hashicorp/go-version v1.0.0 h1:21MVWPKDphxa7ineQQTrCU5brh7OuVVAzGOCnnCPtE8=
|
||||||
|
github.com/hashicorp/go-version v1.0.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||||
|
github.com/hashicorp/go-version v1.1.0 h1:bPIoEKD27tNdebFGGxxYwcL4nepeY4j1QP23PFRGzg0=
|
||||||
|
github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||||
|
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8 h1:12VvqtR6Aowv3l/EQUlocDHW2Cp4G9WJVH7uyH8QFJE=
|
||||||
|
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||||
|
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
|
||||||
|
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||||
|
github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
|
||||||
|
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||||
|
github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
|
||||||
|
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||||
|
github.com/mitchellh/go-homedir v1.0.0 h1:vKb8ShqSby24Yrqr/yDYkuFz8d0WUjys40rvnGC8aR0=
|
||||||
|
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
|
github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0=
|
||||||
|
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||||
|
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=
|
||||||
|
github.com/ulikunitz/xz v0.5.5 h1:pFrO0lVpTBXLpYw+pnLj6TbvHuyjXMfjGeCwSqCVwok=
|
||||||
|
github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
|
||||||
|
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a h1:gOpx8G595UYyvj8UK4+OFyY4rx037g3fmfhe5SasG3U=
|
||||||
|
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/sys v0.0.0-20181213200352-4d1cda033e06 h1:0oC8rFnE+74kEmuHZ46F6KHsMr5Gx2gUQPuNz28iQZM=
|
||||||
|
golang.org/x/sys v0.0.0-20181213200352-4d1cda033e06/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
gopkg.in/cheggaaa/pb.v1 v1.0.27 h1:kJdccidYzt3CaHD1crCFTS1hxyhSi059NhOFUf03YFo=
|
||||||
|
gopkg.in/cheggaaa/pb.v1 v1.0.27/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
|
|
@ -11,19 +11,18 @@ func parse(rawURL string) (*url.URL, error) {
|
||||||
// Make sure we're using "/" since URLs are "/"-based.
|
// Make sure we're using "/" since URLs are "/"-based.
|
||||||
rawURL = filepath.ToSlash(rawURL)
|
rawURL = filepath.ToSlash(rawURL)
|
||||||
|
|
||||||
|
if len(rawURL) > 1 && rawURL[1] == ':' {
|
||||||
|
// Assume we're dealing with a drive letter. In which case we
|
||||||
|
// force the 'file' scheme to avoid "net/url" URL.String() prepending
|
||||||
|
// our url with "./".
|
||||||
|
rawURL = "file://" + rawURL
|
||||||
|
}
|
||||||
|
|
||||||
u, err := url.Parse(rawURL)
|
u, err := url.Parse(rawURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(rawURL) > 1 && rawURL[1] == ':' {
|
|
||||||
// Assume we're dealing with a drive letter file path where the drive
|
|
||||||
// letter has been parsed into the URL Scheme, and the rest of the path
|
|
||||||
// has been parsed into the URL Path without the leading ':' character.
|
|
||||||
u.Path = fmt.Sprintf("%s:%s", string(rawURL[0]), u.Path)
|
|
||||||
u.Scheme = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(u.Host) > 1 && u.Host[1] == ':' && strings.HasPrefix(rawURL, "file://") {
|
if len(u.Host) > 1 && u.Host[1] == ':' && strings.HasPrefix(rawURL, "file://") {
|
||||||
// Assume we're dealing with a drive letter file path where the drive
|
// Assume we're dealing with a drive letter file path where the drive
|
||||||
// letter has been parsed into the URL Host.
|
// letter has been parsed into the URL Host.
|
||||||
|
|
|
@ -6,18 +6,31 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SourceDirSubdir takes a source and returns a tuple of the URL without
|
// SourceDirSubdir takes a source URL and returns a tuple of the URL without
|
||||||
// the subdir and the URL with the subdir.
|
// the subdir and the subdir.
|
||||||
|
//
|
||||||
|
// ex:
|
||||||
|
// dom.com/path/?q=p => dom.com/path/?q=p, ""
|
||||||
|
// proto://dom.com/path//*?q=p => proto://dom.com/path?q=p, "*"
|
||||||
|
// proto://dom.com/path//path2?q=p => proto://dom.com/path?q=p, "path2"
|
||||||
|
//
|
||||||
func SourceDirSubdir(src string) (string, string) {
|
func SourceDirSubdir(src string) (string, string) {
|
||||||
// Calcaulate an offset to avoid accidentally marking the scheme
|
|
||||||
|
// URL might contains another url in query parameters
|
||||||
|
stop := len(src)
|
||||||
|
if idx := strings.Index(src, "?"); idx > -1 {
|
||||||
|
stop = idx
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate an offset to avoid accidentally marking the scheme
|
||||||
// as the dir.
|
// as the dir.
|
||||||
var offset int
|
var offset int
|
||||||
if idx := strings.Index(src, "://"); idx > -1 {
|
if idx := strings.Index(src[:stop], "://"); idx > -1 {
|
||||||
offset = idx + 3
|
offset = idx + 3
|
||||||
}
|
}
|
||||||
|
|
||||||
// First see if we even have an explicit subdir
|
// First see if we even have an explicit subdir
|
||||||
idx := strings.Index(src[offset:], "//")
|
idx := strings.Index(src[offset:stop], "//")
|
||||||
if idx == -1 {
|
if idx == -1 {
|
||||||
return src, ""
|
return src, ""
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,15 +10,26 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// The compiled regular expression used to test the validity of a version.
|
// The compiled regular expression used to test the validity of a version.
|
||||||
var versionRegexp *regexp.Regexp
|
var (
|
||||||
|
versionRegexp *regexp.Regexp
|
||||||
|
semverRegexp *regexp.Regexp
|
||||||
|
)
|
||||||
|
|
||||||
// The raw regular expression string used for testing the validity
|
// The raw regular expression string used for testing the validity
|
||||||
// of a version.
|
// of a version.
|
||||||
const VersionRegexpRaw string = `v?([0-9]+(\.[0-9]+)*?)` +
|
const (
|
||||||
|
VersionRegexpRaw string = `v?([0-9]+(\.[0-9]+)*?)` +
|
||||||
`(-([0-9]+[0-9A-Za-z\-~]*(\.[0-9A-Za-z\-~]+)*)|(-?([A-Za-z\-~]+[0-9A-Za-z\-~]*(\.[0-9A-Za-z\-~]+)*)))?` +
|
`(-([0-9]+[0-9A-Za-z\-~]*(\.[0-9A-Za-z\-~]+)*)|(-?([A-Za-z\-~]+[0-9A-Za-z\-~]*(\.[0-9A-Za-z\-~]+)*)))?` +
|
||||||
`(\+([0-9A-Za-z\-~]+(\.[0-9A-Za-z\-~]+)*))?` +
|
`(\+([0-9A-Za-z\-~]+(\.[0-9A-Za-z\-~]+)*))?` +
|
||||||
`?`
|
`?`
|
||||||
|
|
||||||
|
// SemverRegexpRaw requires a separator between version and prerelease
|
||||||
|
SemverRegexpRaw string = `v?([0-9]+(\.[0-9]+)*?)` +
|
||||||
|
`(-([0-9]+[0-9A-Za-z\-~]*(\.[0-9A-Za-z\-~]+)*)|(-([A-Za-z\-~]+[0-9A-Za-z\-~]*(\.[0-9A-Za-z\-~]+)*)))?` +
|
||||||
|
`(\+([0-9A-Za-z\-~]+(\.[0-9A-Za-z\-~]+)*))?` +
|
||||||
|
`?`
|
||||||
|
)
|
||||||
|
|
||||||
// Version represents a single version.
|
// Version represents a single version.
|
||||||
type Version struct {
|
type Version struct {
|
||||||
metadata string
|
metadata string
|
||||||
|
@ -30,12 +41,24 @@ type Version struct {
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
versionRegexp = regexp.MustCompile("^" + VersionRegexpRaw + "$")
|
versionRegexp = regexp.MustCompile("^" + VersionRegexpRaw + "$")
|
||||||
|
semverRegexp = regexp.MustCompile("^" + SemverRegexpRaw + "$")
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewVersion parses the given version and returns a new
|
// NewVersion parses the given version and returns a new
|
||||||
// Version.
|
// Version.
|
||||||
func NewVersion(v string) (*Version, error) {
|
func NewVersion(v string) (*Version, error) {
|
||||||
matches := versionRegexp.FindStringSubmatch(v)
|
return newVersion(v, versionRegexp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSemver parses the given version and returns a new
|
||||||
|
// Version that adheres strictly to SemVer specs
|
||||||
|
// https://semver.org/
|
||||||
|
func NewSemver(v string) (*Version, error) {
|
||||||
|
return newVersion(v, semverRegexp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newVersion(v string, pattern *regexp.Regexp) (*Version, error) {
|
||||||
|
matches := pattern.FindStringSubmatch(v)
|
||||||
if matches == nil {
|
if matches == nil {
|
||||||
return nil, fmt.Errorf("Malformed version: %s", v)
|
return nil, fmt.Errorf("Malformed version: %s", v)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
language: go
|
||||||
|
go:
|
||||||
|
- tip
|
||||||
|
|
||||||
|
before_install:
|
||||||
|
- go get github.com/mattn/goveralls
|
||||||
|
- go get golang.org/x/tools/cmd/cover
|
||||||
|
script:
|
||||||
|
- $HOME/gopath/bin/goveralls -repotoken xnXqRGwgW3SXIguzxf90ZSK1GPYZPaGrw
|
|
@ -1,5 +1,10 @@
|
||||||
# go-colorable
|
# go-colorable
|
||||||
|
|
||||||
|
[![Godoc Reference](https://godoc.org/github.com/mattn/go-colorable?status.svg)](http://godoc.org/github.com/mattn/go-colorable)
|
||||||
|
[![Build Status](https://travis-ci.org/mattn/go-colorable.svg?branch=master)](https://travis-ci.org/mattn/go-colorable)
|
||||||
|
[![Coverage Status](https://coveralls.io/repos/github/mattn/go-colorable/badge.svg?branch=master)](https://coveralls.io/github/mattn/go-colorable?branch=master)
|
||||||
|
[![Go Report Card](https://goreportcard.com/badge/mattn/go-colorable)](https://goreportcard.com/report/mattn/go-colorable)
|
||||||
|
|
||||||
Colorable writer for windows.
|
Colorable writer for windows.
|
||||||
|
|
||||||
For example, most of logger packages doesn't show colors on windows. (I know we can do it with ansicon. But I don't want.)
|
For example, most of logger packages doesn't show colors on windows. (I know we can do it with ansicon. But I don't want.)
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
// +build appengine
|
||||||
|
|
||||||
|
package colorable
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
_ "github.com/mattn/go-isatty"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewColorable return new instance of Writer which handle escape sequence.
|
||||||
|
func NewColorable(file *os.File) io.Writer {
|
||||||
|
if file == nil {
|
||||||
|
panic("nil passed instead of *os.File to NewColorable()")
|
||||||
|
}
|
||||||
|
|
||||||
|
return file
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewColorableStdout return new instance of Writer which handle escape sequence for stdout.
|
||||||
|
func NewColorableStdout() io.Writer {
|
||||||
|
return os.Stdout
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewColorableStderr return new instance of Writer which handle escape sequence for stderr.
|
||||||
|
func NewColorableStderr() io.Writer {
|
||||||
|
return os.Stderr
|
||||||
|
}
|
|
@ -1,12 +1,16 @@
|
||||||
// +build !windows
|
// +build !windows
|
||||||
|
// +build !appengine
|
||||||
|
|
||||||
package colorable
|
package colorable
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
_ "github.com/mattn/go-isatty"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// NewColorable return new instance of Writer which handle escape sequence.
|
||||||
func NewColorable(file *os.File) io.Writer {
|
func NewColorable(file *os.File) io.Writer {
|
||||||
if file == nil {
|
if file == nil {
|
||||||
panic("nil passed instead of *os.File to NewColorable()")
|
panic("nil passed instead of *os.File to NewColorable()")
|
||||||
|
@ -15,10 +19,12 @@ func NewColorable(file *os.File) io.Writer {
|
||||||
return file
|
return file
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewColorableStdout return new instance of Writer which handle escape sequence for stdout.
|
||||||
func NewColorableStdout() io.Writer {
|
func NewColorableStdout() io.Writer {
|
||||||
return os.Stdout
|
return os.Stdout
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewColorableStderr return new instance of Writer which handle escape sequence for stderr.
|
||||||
func NewColorableStderr() io.Writer {
|
func NewColorableStderr() io.Writer {
|
||||||
return os.Stderr
|
return os.Stderr
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
|
// +build windows
|
||||||
|
// +build !appengine
|
||||||
|
|
||||||
package colorable
|
package colorable
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"math"
|
"math"
|
||||||
"os"
|
"os"
|
||||||
|
@ -52,6 +54,11 @@ type consoleScreenBufferInfo struct {
|
||||||
maximumWindowSize coord
|
maximumWindowSize coord
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type consoleCursorInfo struct {
|
||||||
|
size dword
|
||||||
|
visible int32
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
kernel32 = syscall.NewLazyDLL("kernel32.dll")
|
kernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||||
procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo")
|
procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo")
|
||||||
|
@ -59,15 +66,20 @@ var (
|
||||||
procSetConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition")
|
procSetConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition")
|
||||||
procFillConsoleOutputCharacter = kernel32.NewProc("FillConsoleOutputCharacterW")
|
procFillConsoleOutputCharacter = kernel32.NewProc("FillConsoleOutputCharacterW")
|
||||||
procFillConsoleOutputAttribute = kernel32.NewProc("FillConsoleOutputAttribute")
|
procFillConsoleOutputAttribute = kernel32.NewProc("FillConsoleOutputAttribute")
|
||||||
|
procGetConsoleCursorInfo = kernel32.NewProc("GetConsoleCursorInfo")
|
||||||
|
procSetConsoleCursorInfo = kernel32.NewProc("SetConsoleCursorInfo")
|
||||||
|
procSetConsoleTitle = kernel32.NewProc("SetConsoleTitleW")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Writer provide colorable Writer to the console
|
||||||
type Writer struct {
|
type Writer struct {
|
||||||
out io.Writer
|
out io.Writer
|
||||||
handle syscall.Handle
|
handle syscall.Handle
|
||||||
lastbuf bytes.Buffer
|
|
||||||
oldattr word
|
oldattr word
|
||||||
|
oldpos coord
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewColorable return new instance of Writer which handle escape sequence from File.
|
||||||
func NewColorable(file *os.File) io.Writer {
|
func NewColorable(file *os.File) io.Writer {
|
||||||
if file == nil {
|
if file == nil {
|
||||||
panic("nil passed instead of *os.File to NewColorable()")
|
panic("nil passed instead of *os.File to NewColorable()")
|
||||||
|
@ -77,16 +89,17 @@ func NewColorable(file *os.File) io.Writer {
|
||||||
var csbi consoleScreenBufferInfo
|
var csbi consoleScreenBufferInfo
|
||||||
handle := syscall.Handle(file.Fd())
|
handle := syscall.Handle(file.Fd())
|
||||||
procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
|
procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
|
||||||
return &Writer{out: file, handle: handle, oldattr: csbi.attributes}
|
return &Writer{out: file, handle: handle, oldattr: csbi.attributes, oldpos: coord{0, 0}}
|
||||||
} else {
|
|
||||||
return file
|
|
||||||
}
|
}
|
||||||
|
return file
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewColorableStdout return new instance of Writer which handle escape sequence for stdout.
|
||||||
func NewColorableStdout() io.Writer {
|
func NewColorableStdout() io.Writer {
|
||||||
return NewColorable(os.Stdout)
|
return NewColorable(os.Stdout)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewColorableStderr return new instance of Writer which handle escape sequence for stderr.
|
||||||
func NewColorableStderr() io.Writer {
|
func NewColorableStderr() io.Writer {
|
||||||
return NewColorable(os.Stderr)
|
return NewColorable(os.Stderr)
|
||||||
}
|
}
|
||||||
|
@ -350,45 +363,83 @@ var color256 = map[int]int{
|
||||||
255: 0xeeeeee,
|
255: 0xeeeeee,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// `\033]0;TITLESTR\007`
|
||||||
|
func doTitleSequence(er *bytes.Reader) error {
|
||||||
|
var c byte
|
||||||
|
var err error
|
||||||
|
|
||||||
|
c, err = er.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if c != '0' && c != '2' {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
c, err = er.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if c != ';' {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
title := make([]byte, 0, 80)
|
||||||
|
for {
|
||||||
|
c, err = er.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if c == 0x07 || c == '\n' {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
title = append(title, c)
|
||||||
|
}
|
||||||
|
if len(title) > 0 {
|
||||||
|
title8, err := syscall.UTF16PtrFromString(string(title))
|
||||||
|
if err == nil {
|
||||||
|
procSetConsoleTitle.Call(uintptr(unsafe.Pointer(title8)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write write data on console
|
||||||
func (w *Writer) Write(data []byte) (n int, err error) {
|
func (w *Writer) Write(data []byte) (n int, err error) {
|
||||||
var csbi consoleScreenBufferInfo
|
var csbi consoleScreenBufferInfo
|
||||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||||
|
|
||||||
er := bytes.NewBuffer(data)
|
er := bytes.NewReader(data)
|
||||||
|
var bw [1]byte
|
||||||
loop:
|
loop:
|
||||||
for {
|
for {
|
||||||
r1, _, err := procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
c1, err := er.ReadByte()
|
||||||
if r1 == 0 {
|
|
||||||
break loop
|
|
||||||
}
|
|
||||||
|
|
||||||
c1, _, err := er.ReadRune()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
break loop
|
break loop
|
||||||
}
|
}
|
||||||
if c1 != 0x1b {
|
if c1 != 0x1b {
|
||||||
fmt.Fprint(w.out, string(c1))
|
bw[0] = c1
|
||||||
|
w.out.Write(bw[:])
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
c2, _, err := er.ReadRune()
|
c2, err := er.ReadByte()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.lastbuf.WriteRune(c1)
|
|
||||||
break loop
|
break loop
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if c2 == ']' {
|
||||||
|
if err := doTitleSequence(er); err != nil {
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
if c2 != 0x5b {
|
if c2 != 0x5b {
|
||||||
w.lastbuf.WriteRune(c1)
|
|
||||||
w.lastbuf.WriteRune(c2)
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
var m rune
|
var m byte
|
||||||
for {
|
for {
|
||||||
c, _, err := er.ReadRune()
|
c, err := er.ReadByte()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.lastbuf.WriteRune(c1)
|
|
||||||
w.lastbuf.WriteRune(c2)
|
|
||||||
w.lastbuf.Write(buf.Bytes())
|
|
||||||
break loop
|
break loop
|
||||||
}
|
}
|
||||||
if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '@' {
|
if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '@' {
|
||||||
|
@ -398,7 +449,6 @@ loop:
|
||||||
buf.Write([]byte(string(c)))
|
buf.Write([]byte(string(c)))
|
||||||
}
|
}
|
||||||
|
|
||||||
var csbi consoleScreenBufferInfo
|
|
||||||
switch m {
|
switch m {
|
||||||
case 'A':
|
case 'A':
|
||||||
n, err = strconv.Atoi(buf.String())
|
n, err = strconv.Atoi(buf.String())
|
||||||
|
@ -422,19 +472,16 @@ loop:
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||||
csbi.cursorPosition.x -= short(n)
|
csbi.cursorPosition.x += short(n)
|
||||||
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
||||||
case 'D':
|
case 'D':
|
||||||
n, err = strconv.Atoi(buf.String())
|
n, err = strconv.Atoi(buf.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if n, err = strconv.Atoi(buf.String()); err == nil {
|
|
||||||
var csbi consoleScreenBufferInfo
|
|
||||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||||
csbi.cursorPosition.x += short(n)
|
csbi.cursorPosition.x -= short(n)
|
||||||
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
||||||
}
|
|
||||||
case 'E':
|
case 'E':
|
||||||
n, err = strconv.Atoi(buf.String())
|
n, err = strconv.Atoi(buf.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -459,13 +506,20 @@ loop:
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||||
csbi.cursorPosition.x = short(n)
|
csbi.cursorPosition.x = short(n - 1)
|
||||||
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
||||||
case 'H':
|
case 'H', 'f':
|
||||||
|
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||||
|
if buf.Len() > 0 {
|
||||||
token := strings.Split(buf.String(), ";")
|
token := strings.Split(buf.String(), ";")
|
||||||
if len(token) != 2 {
|
switch len(token) {
|
||||||
|
case 1:
|
||||||
|
n1, err := strconv.Atoi(token[0])
|
||||||
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
csbi.cursorPosition.y = short(n1 - 1)
|
||||||
|
case 2:
|
||||||
n1, err := strconv.Atoi(token[0])
|
n1, err := strconv.Atoi(token[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
|
@ -474,46 +528,63 @@ loop:
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
csbi.cursorPosition.x = short(n2)
|
csbi.cursorPosition.x = short(n2 - 1)
|
||||||
csbi.cursorPosition.x = short(n1)
|
csbi.cursorPosition.y = short(n1 - 1)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
csbi.cursorPosition.y = 0
|
||||||
|
}
|
||||||
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
||||||
case 'J':
|
case 'J':
|
||||||
n, err := strconv.Atoi(buf.String())
|
n := 0
|
||||||
|
if buf.Len() > 0 {
|
||||||
|
n, err = strconv.Atoi(buf.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
var count, written dword
|
||||||
var cursor coord
|
var cursor coord
|
||||||
|
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||||
switch n {
|
switch n {
|
||||||
case 0:
|
case 0:
|
||||||
cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y}
|
cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y}
|
||||||
|
count = dword(csbi.size.x - csbi.cursorPosition.x + (csbi.size.y-csbi.cursorPosition.y)*csbi.size.x)
|
||||||
case 1:
|
case 1:
|
||||||
cursor = coord{x: csbi.window.left, y: csbi.window.top}
|
cursor = coord{x: csbi.window.left, y: csbi.window.top}
|
||||||
|
count = dword(csbi.size.x - csbi.cursorPosition.x + (csbi.window.top-csbi.cursorPosition.y)*csbi.size.x)
|
||||||
case 2:
|
case 2:
|
||||||
cursor = coord{x: csbi.window.left, y: csbi.window.top}
|
cursor = coord{x: csbi.window.left, y: csbi.window.top}
|
||||||
}
|
|
||||||
var count, written dword
|
|
||||||
count = dword(csbi.size.x - csbi.cursorPosition.x + (csbi.size.y-csbi.cursorPosition.y)*csbi.size.x)
|
count = dword(csbi.size.x - csbi.cursorPosition.x + (csbi.size.y-csbi.cursorPosition.y)*csbi.size.x)
|
||||||
|
}
|
||||||
procFillConsoleOutputCharacter.Call(uintptr(w.handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
|
procFillConsoleOutputCharacter.Call(uintptr(w.handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
|
||||||
procFillConsoleOutputAttribute.Call(uintptr(w.handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
|
procFillConsoleOutputAttribute.Call(uintptr(w.handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
|
||||||
case 'K':
|
case 'K':
|
||||||
n, err := strconv.Atoi(buf.String())
|
n := 0
|
||||||
|
if buf.Len() > 0 {
|
||||||
|
n, err = strconv.Atoi(buf.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||||
var cursor coord
|
var cursor coord
|
||||||
|
var count, written dword
|
||||||
switch n {
|
switch n {
|
||||||
case 0:
|
case 0:
|
||||||
cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y}
|
cursor = coord{x: csbi.cursorPosition.x + 1, y: csbi.cursorPosition.y}
|
||||||
|
count = dword(csbi.size.x - csbi.cursorPosition.x - 1)
|
||||||
case 1:
|
case 1:
|
||||||
cursor = coord{x: csbi.window.left, y: csbi.window.top + csbi.cursorPosition.y}
|
cursor = coord{x: csbi.window.left, y: csbi.window.top + csbi.cursorPosition.y}
|
||||||
|
count = dword(csbi.size.x - csbi.cursorPosition.x)
|
||||||
case 2:
|
case 2:
|
||||||
cursor = coord{x: csbi.window.left, y: csbi.window.top + csbi.cursorPosition.y}
|
cursor = coord{x: csbi.window.left, y: csbi.window.top + csbi.cursorPosition.y}
|
||||||
|
count = dword(csbi.size.x)
|
||||||
}
|
}
|
||||||
var count, written dword
|
|
||||||
count = dword(csbi.size.x - csbi.cursorPosition.x)
|
|
||||||
procFillConsoleOutputCharacter.Call(uintptr(w.handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
|
procFillConsoleOutputCharacter.Call(uintptr(w.handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
|
||||||
procFillConsoleOutputAttribute.Call(uintptr(w.handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
|
procFillConsoleOutputAttribute.Call(uintptr(w.handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
|
||||||
case 'm':
|
case 'm':
|
||||||
|
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||||
attr := csbi.attributes
|
attr := csbi.attributes
|
||||||
cs := buf.String()
|
cs := buf.String()
|
||||||
if cs == "" {
|
if cs == "" {
|
||||||
|
@ -521,7 +592,7 @@ loop:
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
token := strings.Split(cs, ";")
|
token := strings.Split(cs, ";")
|
||||||
for i := 0; i < len(token); i += 1 {
|
for i := 0; i < len(token); i++ {
|
||||||
ns := token[i]
|
ns := token[i]
|
||||||
if n, err = strconv.Atoi(ns); err == nil {
|
if n, err = strconv.Atoi(ns); err == nil {
|
||||||
switch {
|
switch {
|
||||||
|
@ -531,12 +602,12 @@ loop:
|
||||||
attr |= foregroundIntensity
|
attr |= foregroundIntensity
|
||||||
case n == 7:
|
case n == 7:
|
||||||
attr = ((attr & foregroundMask) << 4) | ((attr & backgroundMask) >> 4)
|
attr = ((attr & foregroundMask) << 4) | ((attr & backgroundMask) >> 4)
|
||||||
case 22 == n || n == 25 || n == 25:
|
case n == 22 || n == 25:
|
||||||
attr |= foregroundIntensity
|
attr |= foregroundIntensity
|
||||||
case n == 27:
|
case n == 27:
|
||||||
attr = ((attr & foregroundMask) << 4) | ((attr & backgroundMask) >> 4)
|
attr = ((attr & foregroundMask) << 4) | ((attr & backgroundMask) >> 4)
|
||||||
case 30 <= n && n <= 37:
|
case 30 <= n && n <= 37:
|
||||||
attr = (attr & backgroundMask)
|
attr &= backgroundMask
|
||||||
if (n-30)&1 != 0 {
|
if (n-30)&1 != 0 {
|
||||||
attr |= foregroundRed
|
attr |= foregroundRed
|
||||||
}
|
}
|
||||||
|
@ -563,7 +634,7 @@ loop:
|
||||||
attr &= backgroundMask
|
attr &= backgroundMask
|
||||||
attr |= w.oldattr & foregroundMask
|
attr |= w.oldattr & foregroundMask
|
||||||
case 40 <= n && n <= 47:
|
case 40 <= n && n <= 47:
|
||||||
attr = (attr & foregroundMask)
|
attr &= foregroundMask
|
||||||
if (n-40)&1 != 0 {
|
if (n-40)&1 != 0 {
|
||||||
attr |= backgroundRed
|
attr |= backgroundRed
|
||||||
}
|
}
|
||||||
|
@ -617,9 +688,39 @@ loop:
|
||||||
procSetConsoleTextAttribute.Call(uintptr(w.handle), uintptr(attr))
|
procSetConsoleTextAttribute.Call(uintptr(w.handle), uintptr(attr))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case 'h':
|
||||||
|
var ci consoleCursorInfo
|
||||||
|
cs := buf.String()
|
||||||
|
if cs == "5>" {
|
||||||
|
procGetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci)))
|
||||||
|
ci.visible = 0
|
||||||
|
procSetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci)))
|
||||||
|
} else if cs == "?25" {
|
||||||
|
procGetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci)))
|
||||||
|
ci.visible = 1
|
||||||
|
procSetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci)))
|
||||||
|
}
|
||||||
|
case 'l':
|
||||||
|
var ci consoleCursorInfo
|
||||||
|
cs := buf.String()
|
||||||
|
if cs == "5>" {
|
||||||
|
procGetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci)))
|
||||||
|
ci.visible = 1
|
||||||
|
procSetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci)))
|
||||||
|
} else if cs == "?25" {
|
||||||
|
procGetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci)))
|
||||||
|
ci.visible = 0
|
||||||
|
procSetConsoleCursorInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&ci)))
|
||||||
|
}
|
||||||
|
case 's':
|
||||||
|
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||||
|
w.oldpos = csbi.cursorPosition
|
||||||
|
case 'u':
|
||||||
|
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&w.oldpos)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return len(data) - w.lastbuf.Len(), nil
|
|
||||||
|
return len(data), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type consoleColor struct {
|
type consoleColor struct {
|
||||||
|
@ -663,22 +764,22 @@ func (c consoleColor) backgroundAttr() (attr word) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var color16 = []consoleColor{
|
var color16 = []consoleColor{
|
||||||
consoleColor{0x000000, false, false, false, false},
|
{0x000000, false, false, false, false},
|
||||||
consoleColor{0x000080, false, false, true, false},
|
{0x000080, false, false, true, false},
|
||||||
consoleColor{0x008000, false, true, false, false},
|
{0x008000, false, true, false, false},
|
||||||
consoleColor{0x008080, false, true, true, false},
|
{0x008080, false, true, true, false},
|
||||||
consoleColor{0x800000, true, false, false, false},
|
{0x800000, true, false, false, false},
|
||||||
consoleColor{0x800080, true, false, true, false},
|
{0x800080, true, false, true, false},
|
||||||
consoleColor{0x808000, true, true, false, false},
|
{0x808000, true, true, false, false},
|
||||||
consoleColor{0xc0c0c0, true, true, true, false},
|
{0xc0c0c0, true, true, true, false},
|
||||||
consoleColor{0x808080, false, false, false, true},
|
{0x808080, false, false, false, true},
|
||||||
consoleColor{0x0000ff, false, false, true, true},
|
{0x0000ff, false, false, true, true},
|
||||||
consoleColor{0x00ff00, false, true, false, true},
|
{0x00ff00, false, true, false, true},
|
||||||
consoleColor{0x00ffff, false, true, true, true},
|
{0x00ffff, false, true, true, true},
|
||||||
consoleColor{0xff0000, true, false, false, true},
|
{0xff0000, true, false, false, true},
|
||||||
consoleColor{0xff00ff, true, false, true, true},
|
{0xff00ff, true, false, true, true},
|
||||||
consoleColor{0xffff00, true, true, false, true},
|
{0xffff00, true, true, false, true},
|
||||||
consoleColor{0xffffff, true, true, true, true},
|
{0xffffff, true, true, true, true},
|
||||||
}
|
}
|
||||||
|
|
||||||
type hsv struct {
|
type hsv struct {
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
package colorable
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NonColorable hold writer but remove escape sequence.
|
||||||
|
type NonColorable struct {
|
||||||
|
out io.Writer
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewNonColorable return new instance of Writer which remove escape sequence from Writer.
|
||||||
|
func NewNonColorable(w io.Writer) io.Writer {
|
||||||
|
return &NonColorable{out: w}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write write data on console
|
||||||
|
func (w *NonColorable) Write(data []byte) (n int, err error) {
|
||||||
|
er := bytes.NewReader(data)
|
||||||
|
var bw [1]byte
|
||||||
|
loop:
|
||||||
|
for {
|
||||||
|
c1, err := er.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
if c1 != 0x1b {
|
||||||
|
bw[0] = c1
|
||||||
|
w.out.Write(bw[:])
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
c2, err := er.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
if c2 != 0x5b {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
for {
|
||||||
|
c, err := er.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '@' {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
buf.Write([]byte(string(c)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return len(data), nil
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
module github.com/mitchellh/go-testing-interface
|
|
@ -12,22 +12,23 @@ have been developed over a long time and are highly optimized. However
|
||||||
there are a number of improvements planned and I'm very optimistic about
|
there are a number of improvements planned and I'm very optimistic about
|
||||||
parallel compression and decompression. Stay tuned!
|
parallel compression and decompression. Stay tuned!
|
||||||
|
|
||||||
# Using the API
|
## Using the API
|
||||||
|
|
||||||
The following example program shows how to use the API.
|
The following example program shows how to use the API.
|
||||||
|
|
||||||
package main
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/ulikunitz/xz"
|
"github.com/ulikunitz/xz"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
const text = "The quick brown fox jumps over the lazy dog.\n"
|
const text = "The quick brown fox jumps over the lazy dog.\n"
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
// compress text
|
// compress text
|
||||||
|
@ -49,9 +50,10 @@ The following example program shows how to use the API.
|
||||||
if _, err = io.Copy(os.Stdout, r); err != nil {
|
if _, err = io.Copy(os.Stdout, r); err != nil {
|
||||||
log.Fatalf("io.Copy error %s", err)
|
log.Fatalf("io.Copy error %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
# Using the gxz compression tool
|
## Using the gxz compression tool
|
||||||
|
|
||||||
The package includes a gxz command line utility for compression and
|
The package includes a gxz command line utility for compression and
|
||||||
decompression.
|
decompression.
|
||||||
|
|
|
@ -86,6 +86,10 @@
|
||||||
|
|
||||||
## Log
|
## Log
|
||||||
|
|
||||||
|
### 2018-10-28
|
||||||
|
|
||||||
|
Release v0.5.5 fixes issues #19 observing ErrLimit outputs.
|
||||||
|
|
||||||
### 2017-06-05
|
### 2017-06-05
|
||||||
|
|
||||||
Release v0.5.4 fixes issues #15 of another problem with the padding size
|
Release v0.5.4 fixes issues #15 of another problem with the padding size
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
|
|
||||||
// opLenMargin provides the upper limit of the number of bytes required
|
// opLenMargin provides the upper limit of the number of bytes required
|
||||||
// to encode a single operation.
|
// to encode a single operation.
|
||||||
const opLenMargin = 10
|
const opLenMargin = 16
|
||||||
|
|
||||||
// compressFlags control the compression process.
|
// compressFlags control the compression process.
|
||||||
type compressFlags uint32
|
type compressFlags uint32
|
||||||
|
|
|
@ -270,7 +270,7 @@ github.com/hashicorp/go-azure-helpers/storage
|
||||||
github.com/hashicorp/go-checkpoint
|
github.com/hashicorp/go-checkpoint
|
||||||
# github.com/hashicorp/go-cleanhttp v0.5.0
|
# github.com/hashicorp/go-cleanhttp v0.5.0
|
||||||
github.com/hashicorp/go-cleanhttp
|
github.com/hashicorp/go-cleanhttp
|
||||||
# github.com/hashicorp/go-getter v0.0.0-20180327010114-90bb99a48d86
|
# github.com/hashicorp/go-getter v1.1.0
|
||||||
github.com/hashicorp/go-getter
|
github.com/hashicorp/go-getter
|
||||||
github.com/hashicorp/go-getter/helper/url
|
github.com/hashicorp/go-getter/helper/url
|
||||||
# github.com/hashicorp/go-hclog v0.0.0-20181001195459-61d530d6c27f
|
# github.com/hashicorp/go-hclog v0.0.0-20181001195459-61d530d6c27f
|
||||||
|
@ -292,7 +292,7 @@ github.com/hashicorp/go-slug
|
||||||
github.com/hashicorp/go-tfe
|
github.com/hashicorp/go-tfe
|
||||||
# github.com/hashicorp/go-uuid v1.0.0
|
# github.com/hashicorp/go-uuid v1.0.0
|
||||||
github.com/hashicorp/go-uuid
|
github.com/hashicorp/go-uuid
|
||||||
# github.com/hashicorp/go-version v1.0.0
|
# github.com/hashicorp/go-version v1.1.0
|
||||||
github.com/hashicorp/go-version
|
github.com/hashicorp/go-version
|
||||||
# github.com/hashicorp/hcl v0.0.0-20170504190234-a4b07c25de5f
|
# github.com/hashicorp/hcl v0.0.0-20170504190234-a4b07c25de5f
|
||||||
github.com/hashicorp/hcl
|
github.com/hashicorp/hcl
|
||||||
|
@ -366,7 +366,7 @@ github.com/masterzen/simplexml/dom
|
||||||
# github.com/masterzen/winrm v0.0.0-20180224160350-7e40f93ae939
|
# github.com/masterzen/winrm v0.0.0-20180224160350-7e40f93ae939
|
||||||
github.com/masterzen/winrm
|
github.com/masterzen/winrm
|
||||||
github.com/masterzen/winrm/soap
|
github.com/masterzen/winrm/soap
|
||||||
# github.com/mattn/go-colorable v0.0.0-20160220075935-9cbef7c35391
|
# github.com/mattn/go-colorable v0.0.9
|
||||||
github.com/mattn/go-colorable
|
github.com/mattn/go-colorable
|
||||||
# github.com/mattn/go-isatty v0.0.4
|
# github.com/mattn/go-isatty v0.0.4
|
||||||
github.com/mattn/go-isatty
|
github.com/mattn/go-isatty
|
||||||
|
@ -382,7 +382,7 @@ github.com/mitchellh/copystructure
|
||||||
github.com/mitchellh/go-homedir
|
github.com/mitchellh/go-homedir
|
||||||
# github.com/mitchellh/go-linereader v0.0.0-20141013185533-07bab5fdd958
|
# github.com/mitchellh/go-linereader v0.0.0-20141013185533-07bab5fdd958
|
||||||
github.com/mitchellh/go-linereader
|
github.com/mitchellh/go-linereader
|
||||||
# github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77
|
# github.com/mitchellh/go-testing-interface v1.0.0
|
||||||
github.com/mitchellh/go-testing-interface
|
github.com/mitchellh/go-testing-interface
|
||||||
# github.com/mitchellh/go-wordwrap v1.0.0
|
# github.com/mitchellh/go-wordwrap v1.0.0
|
||||||
github.com/mitchellh/go-wordwrap
|
github.com/mitchellh/go-wordwrap
|
||||||
|
@ -420,7 +420,7 @@ github.com/svanharmelen/jsonapi
|
||||||
github.com/terraform-providers/terraform-provider-openstack/openstack
|
github.com/terraform-providers/terraform-provider-openstack/openstack
|
||||||
# github.com/ugorji/go v0.0.0-20180813092308-00b869d2f4a5
|
# github.com/ugorji/go v0.0.0-20180813092308-00b869d2f4a5
|
||||||
github.com/ugorji/go/codec
|
github.com/ugorji/go/codec
|
||||||
# github.com/ulikunitz/xz v0.5.4
|
# github.com/ulikunitz/xz v0.5.5
|
||||||
github.com/ulikunitz/xz
|
github.com/ulikunitz/xz
|
||||||
github.com/ulikunitz/xz/internal/xlog
|
github.com/ulikunitz/xz/internal/xlog
|
||||||
github.com/ulikunitz/xz/lzma
|
github.com/ulikunitz/xz/lzma
|
||||||
|
|
Loading…
Reference in New Issue