Merge pull request #20411 from hashicorp/vendor-go-mod-aware
vendor: Bump remaining dependencies to go-mod-aware versions
This commit is contained in:
commit
cea81d921a
33
go.mod
33
go.mod
|
@ -13,13 +13,11 @@ require (
|
|||
github.com/antchfx/xpath v0.0.0-20170728053731-b5c552e1acbd // indirect
|
||||
github.com/antchfx/xquery v0.0.0-20170730121040-eb8c3c172607 // indirect
|
||||
github.com/apparentlymart/go-cidr v1.0.0
|
||||
github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e
|
||||
github.com/apparentlymart/go-dump v0.0.0-20190214190832-042adf3cf4a0
|
||||
github.com/armon/circbuf v0.0.0-20190214190532-5111143e8da2
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da // indirect
|
||||
github.com/armon/go-radix v1.0.0 // indirect
|
||||
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/blang/semver v3.5.1+incompatible
|
||||
github.com/boltdb/bolt v1.3.1 // indirect
|
||||
github.com/chzyer/logex v1.1.10 // indirect
|
||||
|
@ -50,21 +48,20 @@ require (
|
|||
github.com/hashicorp/consul v0.0.0-20171026175957-610f3c86a089
|
||||
github.com/hashicorp/errwrap v1.0.0
|
||||
github.com/hashicorp/go-azure-helpers v0.0.0-20190129193224-166dfd221bb2
|
||||
github.com/hashicorp/go-checkpoint v0.0.0-20171009173528-1545e56e46de
|
||||
github.com/hashicorp/go-checkpoint 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-immutable-radix v0.0.0-20180129170900-7f3cd4390caa // indirect
|
||||
github.com/hashicorp/go-msgpack v0.0.0-20150518234257-fa3f63826f7c // indirect
|
||||
github.com/hashicorp/go-multierror v1.0.0
|
||||
github.com/hashicorp/go-plugin v0.0.0-20190220160451-3f118e8ee104
|
||||
github.com/hashicorp/go-retryablehttp v0.5.1
|
||||
github.com/hashicorp/go-rootcerts v0.0.0-20160503143440-6bb64b370b90
|
||||
github.com/hashicorp/go-safetemp v1.0.0 // indirect
|
||||
github.com/hashicorp/go-rootcerts v1.0.0
|
||||
github.com/hashicorp/go-sockaddr v0.0.0-20180320115054-6d291a969b86 // indirect
|
||||
github.com/hashicorp/go-tfe v0.3.8
|
||||
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/hcl v0.0.0-20170504190234-a4b07c25de5f
|
||||
github.com/hashicorp/hcl2 v0.0.0-20190214011454-504b92060753
|
||||
|
@ -84,37 +81,35 @@ require (
|
|||
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/winrm v0.0.0-20180224160350-7e40f93ae939
|
||||
github.com/mattn/go-colorable v0.0.0-20160220075935-9cbef7c35391
|
||||
github.com/mattn/go-isatty v0.0.4 // indirect
|
||||
github.com/mattn/go-colorable v0.0.9
|
||||
github.com/mattn/go-shellwords v1.0.1
|
||||
github.com/miekg/dns v1.0.8 // indirect
|
||||
github.com/mitchellh/cli v0.0.0-20171129193617-33edc47170b5
|
||||
github.com/mitchellh/colorstring v0.0.0-20150917214807-8631ce90f286
|
||||
github.com/mitchellh/cli v1.0.0
|
||||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db
|
||||
github.com/mitchellh/copystructure v1.0.0
|
||||
github.com/mitchellh/go-homedir v1.0.0
|
||||
github.com/mitchellh/go-linereader v0.0.0-20141013185533-07bab5fdd958
|
||||
github.com/mitchellh/go-linereader v0.0.0-20190213213312-1b945b3263eb
|
||||
github.com/mitchellh/go-wordwrap v1.0.0
|
||||
github.com/mitchellh/hashstructure v1.0.0
|
||||
github.com/mitchellh/mapstructure v1.1.2
|
||||
github.com/mitchellh/panicwrap v0.0.0-20161208170302-ba9e1a65e0f7
|
||||
github.com/mitchellh/prefixedio v0.0.0-20151214002211-6e6954073784
|
||||
github.com/mitchellh/panicwrap v0.0.0-20190213213626-17011010aaa4
|
||||
github.com/mitchellh/prefixedio v0.0.0-20190213213902-5733675afd51
|
||||
github.com/mitchellh/reflectwalk v1.0.0
|
||||
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect
|
||||
github.com/packer-community/winrmcp v0.0.0-20180102160824-81144009af58
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c // indirect
|
||||
github.com/pkg/errors v0.0.0-20170505043639-c605e284fe17 // indirect
|
||||
github.com/posener/complete v0.0.0-20171219111128-6bee943216c8
|
||||
github.com/posener/complete v1.2.1
|
||||
github.com/satori/go.uuid v0.0.0-20160927100844-b061729afc07 // indirect
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect
|
||||
github.com/sirupsen/logrus v1.1.1 // indirect
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d // indirect
|
||||
github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a // indirect
|
||||
github.com/soheilhy/cmux v0.1.4 // indirect
|
||||
github.com/spf13/afero v1.0.2
|
||||
github.com/spf13/afero v1.2.1
|
||||
github.com/terraform-providers/terraform-provider-openstack v1.15.0
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6 // 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/xanzy/ssh-agent v0.2.0
|
||||
github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18 // indirect
|
||||
|
|
74
go.sum
74
go.sum
|
@ -35,14 +35,18 @@ github.com/apparentlymart/go-cidr v1.0.0 h1:lGDvXx8Lv9QHjrAVP7jyzleG4F9+FkRhJcEs
|
|||
github.com/apparentlymart/go-cidr v1.0.0/go.mod h1:EBcsNrHc3zQeuaeCeCtQruQm+n9/YjEn/vI25Lg7Gwc=
|
||||
github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3 h1:ZSTrOEhiM5J5RFxEaFvMZVEAM1KvT1YzbEOwB2EAGjA=
|
||||
github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM=
|
||||
github.com/apparentlymart/go-dump v0.0.0-20190214190832-042adf3cf4a0 h1:MzVXffFUye+ZcSR6opIgz9Co7WcDx6ZcY+RjfFHoA0I=
|
||||
github.com/apparentlymart/go-dump v0.0.0-20190214190832-042adf3cf4a0/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM=
|
||||
github.com/apparentlymart/go-textseg v1.0.0 h1:rRmlIsPEEhUTIKQb7T++Nz/A5Q6C9IuX2wFoYVvnCs0=
|
||||
github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk=
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e h1:QEF07wC0T1rKkctt1RINW/+RMTVmiwxETico2l3gxJA=
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
github.com/armon/circbuf v0.0.0-20190214190532-5111143e8da2 h1:7Ip0wMmLHLRJdrloDxZfhMm0xrLXZS8+COSu2bXmEQs=
|
||||
github.com/armon/circbuf v0.0.0-20190214190532-5111143e8da2/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da h1:8GUt8eRujhVEGZFFEjBj46YV4rDjvGrNxb0KMWYkL2I=
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
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/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/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0=
|
||||
|
@ -57,6 +61,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/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/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/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20161106042343-c914be64f07d h1:aG5FcWiZTOhPQzYIxwxSR1zEOxzL32fwr1CsaCfhO6w=
|
||||
|
@ -88,6 +93,8 @@ 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/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/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
|
||||
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/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||
|
@ -150,12 +157,12 @@ github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/U
|
|||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-azure-helpers v0.0.0-20190129193224-166dfd221bb2 h1:VBRx+yPYUZaobnn5ANBcOUf4hhWpTHSQgftG4TcDkhI=
|
||||
github.com/hashicorp/go-azure-helpers v0.0.0-20190129193224-166dfd221bb2/go.mod h1:lu62V//auUow6k0IykxLK2DCNW8qTmpm8KqhYVWattA=
|
||||
github.com/hashicorp/go-checkpoint v0.0.0-20171009173528-1545e56e46de h1:XDCSythtg8aWSRSO29uwhgh7b127fWr+m5SemqjSUL8=
|
||||
github.com/hashicorp/go-checkpoint v0.0.0-20171009173528-1545e56e46de/go.mod h1:xIwEieBHERyEvaeKF/TcHh1Hu+lxPM+n2vT1+g9I4m4=
|
||||
github.com/hashicorp/go-checkpoint v0.5.0 h1:MFYpPZCnQqQTE18jFwSII6eUQrD/oxMFp3mlgcqk5mU=
|
||||
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/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-getter v0.0.0-20180327010114-90bb99a48d86 h1:hLYM35twiyKH44g36g+GFYODcrZQetEAY4+zrJtGea0=
|
||||
github.com/hashicorp/go-getter v0.0.0-20180327010114-90bb99a48d86/go.mod h1:6rdJFnhkXnzGOJbvkrdv4t9nLwKcVA+tmbQeUlkIzrU=
|
||||
github.com/hashicorp/go-getter v1.1.0 h1:iGVeg7L4V5FTFV3D6w+1NAyvth7BIWWSzD60pWloe2Q=
|
||||
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-20181001195459-61d530d6c27f h1:Yv9YzBlAETjy6AOX9eLBZ3nshNVRREgerT/3nvxlGho=
|
||||
github.com/hashicorp/go-hclog v0.0.0-20181001195459-61d530d6c27f/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
|
||||
|
@ -170,8 +177,8 @@ github.com/hashicorp/go-plugin v0.0.0-20190220160451-3f118e8ee104 h1:9iQ/zrTOJqz
|
|||
github.com/hashicorp/go-plugin v0.0.0-20190220160451-3f118e8ee104/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY=
|
||||
github.com/hashicorp/go-retryablehttp v0.5.1 h1:Vsx5XKPqPs3M6sM4U4GWyUqFS8aBiL9U5gkgvpkg4SE=
|
||||
github.com/hashicorp/go-retryablehttp v0.5.1/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
|
||||
github.com/hashicorp/go-rootcerts v0.0.0-20160503143440-6bb64b370b90 h1:VBj0QYQ0u2MCJzBfeYXGexnAl17GsH1yidnoxCqqD9E=
|
||||
github.com/hashicorp/go-rootcerts v0.0.0-20160503143440-6bb64b370b90/go.mod h1:o4zcYY1e0GEZI6eSEr+43QDYmuGglw1qSO6qdHUHCgg=
|
||||
github.com/hashicorp/go-rootcerts v1.0.0 h1:Rqb66Oo1X/eSV1x66xbDccZjhJigjg0+e82kpwzSwCI=
|
||||
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
|
||||
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-slug v0.2.0 h1:MVdZAkTmDsUi1AT+3NQDsn8n3ssnVSIHwiM6RcUHvE8=
|
||||
|
@ -182,8 +189,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-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-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/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/hcl v0.0.0-20170504190234-a4b07c25de5f h1:UdxlrJz4JOnY8W+DbLISwf2B8WXEolNRA8BGCwI9jws=
|
||||
|
@ -208,6 +215,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/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/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/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo=
|
||||
|
@ -241,10 +249,12 @@ 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/winrm v0.0.0-20180224160350-7e40f93ae939 h1:cRFHA33ER97Xy5jmjS519OXCS/yE3AT3zdbQAg0Z53g=
|
||||
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.0-20160220075935-9cbef7c35391/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
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.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
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/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/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||
|
@ -252,18 +262,20 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5
|
|||
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
|
||||
github.com/miekg/dns v1.0.8 h1:Zi8HNpze3NeRWH1PQV6O71YcvJRQ6j0lORO6DAEmAAI=
|
||||
github.com/miekg/dns v1.0.8/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/mitchellh/cli v0.0.0-20171129193617-33edc47170b5 h1:OYr3N2fY3e3kP/x/d81CJXlcZrIV2hH8gPnuRLpiME4=
|
||||
github.com/mitchellh/cli v0.0.0-20171129193617-33edc47170b5/go.mod h1:oGumspjLm2kTyiT1QMGpFqRlmxnKHfCvhZEVnx+5UeE=
|
||||
github.com/mitchellh/colorstring v0.0.0-20150917214807-8631ce90f286 h1:KHyL+3mQOF9sPfs26lsefckcFNDcIZtiACQiECzIUkw=
|
||||
github.com/mitchellh/colorstring v0.0.0-20150917214807-8631ce90f286/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=
|
||||
github.com/mitchellh/cli v1.0.0 h1:iGBIsUe3+HZ/AD/Vd7DErOt5sU9fa8Uj7A2s1aggv1Y=
|
||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ=
|
||||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=
|
||||
github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ=
|
||||
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
|
||||
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-linereader v0.0.0-20141013185533-07bab5fdd958 h1:wN+5lV34eSnVSZgBLWRGHr6L4giR3/wI2B9DLmVnlfI=
|
||||
github.com/mitchellh/go-linereader v0.0.0-20141013185533-07bab5fdd958/go.mod h1:OaY7UOoTkkrX3wRwjpYRKafIkkyeD0UtweSHAWWiqQM=
|
||||
github.com/mitchellh/go-linereader v0.0.0-20190213213312-1b945b3263eb h1:GRiLv4rgyqjqzxbhJke65IYUf4NCOOvrPOJbV/sPxkM=
|
||||
github.com/mitchellh/go-linereader v0.0.0-20190213213312-1b945b3263eb/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/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 v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4=
|
||||
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||
|
@ -271,10 +283,10 @@ github.com/mitchellh/hashstructure v1.0.0 h1:ZkRJX1CyOoTkar7p/mLS5TZU4nJ1Rn/F8u9
|
|||
github.com/mitchellh/hashstructure v1.0.0/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ=
|
||||
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/panicwrap v0.0.0-20161208170302-ba9e1a65e0f7 h1:+PBI9A4rLQJlch3eQI/RkTY2HzX+bl2lPUf3goenBxs=
|
||||
github.com/mitchellh/panicwrap v0.0.0-20161208170302-ba9e1a65e0f7/go.mod h1:QuAqW7/z+iv6aWFJdrA8kCbsF0OOJVKCICqTcYBexuY=
|
||||
github.com/mitchellh/prefixedio v0.0.0-20151214002211-6e6954073784 h1:+DAetXqxv/mSyCkE9KBIYOZs9b68y7SUaDCxQMRjA68=
|
||||
github.com/mitchellh/prefixedio v0.0.0-20151214002211-6e6954073784/go.mod h1:kB1naBgV9ORnkiTVeyJOI1DavaJkG4oNIq0Af6ZVKUo=
|
||||
github.com/mitchellh/panicwrap v0.0.0-20190213213626-17011010aaa4 h1:jw9tsdJ1FQmUkyTXdIF/nByTX+mMnnp16glnvGZMsC4=
|
||||
github.com/mitchellh/panicwrap v0.0.0-20190213213626-17011010aaa4/go.mod h1:YYMf4xtQnR8LRC0vKi3afvQ5QwRPQ17zjcpkBCufb+I=
|
||||
github.com/mitchellh/prefixedio v0.0.0-20190213213902-5733675afd51 h1:eD92Am0Qf3rqhsOeA1zwBHSfRkoHrt4o6uORamdmJP8=
|
||||
github.com/mitchellh/prefixedio v0.0.0-20190213213902-5733675afd51/go.mod h1:kB1naBgV9ORnkiTVeyJOI1DavaJkG4oNIq0Af6ZVKUo=
|
||||
github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY=
|
||||
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
|
||||
|
@ -295,8 +307,10 @@ github.com/pkg/errors v0.0.0-20170505043639-c605e284fe17 h1:chPfVn+gpAM5CTpTyVU9
|
|||
github.com/pkg/errors v0.0.0-20170505043639-c605e284fe17/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/posener/complete v0.0.0-20171219111128-6bee943216c8 h1:lcb1zvdlaZyEbl2OXifN3uOYYyIvllofUbmp9bwbL+0=
|
||||
github.com/posener/complete v0.0.0-20171219111128-6bee943216c8/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||
github.com/posener/complete v1.1.1 h1:ccV59UEOTzVDnDUEFdT95ZzHVZ+5+158q8+SJb2QV5w=
|
||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||
github.com/posener/complete v1.2.1 h1:LrvDIY//XNo65Lq84G/akBuMGlawHvGBABv8f/ZN6DI=
|
||||
github.com/posener/complete v1.2.1/go.mod h1:6gapUrK/U1TAN7ciCoNRIdVC5sbdBTUh1DKN0g6uH7E=
|
||||
github.com/prometheus/client_golang v0.8.0 h1:1921Yw9Gc3iSc4VQh3PIoOqgPCZS7G/4xQNVUp8Mda8=
|
||||
github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8=
|
||||
|
@ -344,8 +358,8 @@ github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E=
|
|||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=
|
||||
github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
|
||||
github.com/spf13/afero v1.0.2 h1:5bRmqmInNmNFkI9NG9O0Xc/Lgl9wOWWUUA/O8XZqTCo=
|
||||
github.com/spf13/afero v1.0.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/afero v1.2.1 h1:qgMbHoJbPbw579P+1zVY+6n4nIFuIchaIjzZ/I/Yq8M=
|
||||
github.com/spf13/afero v1.2.1/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
||||
github.com/spf13/pflag v1.0.2 h1:Fy0orTDgHdbnzHcsOgfCN4LtHf0ec3wwtiwJqwvf3Gc=
|
||||
github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
|
@ -361,8 +375,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/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/ulikunitz/xz v0.5.4 h1:zATC2OoZ8H1TZll3FpbX+ikwmadbO699PE06cIkm9oU=
|
||||
github.com/ulikunitz/xz v0.5.4/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
|
||||
github.com/ulikunitz/xz v0.5.5 h1:pFrO0lVpTBXLpYw+pnLj6TbvHuyjXMfjGeCwSqCVwok=
|
||||
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 v4.0.1+incompatible h1:RMF1enSPeKTlXrXdOcqjFUElywVZjjC6pqse21bKbEU=
|
||||
github.com/vmihailenco/msgpack v4.0.1+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
|
||||
|
@ -402,6 +416,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-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-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/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
|
@ -418,10 +433,12 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJ
|
|||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/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-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/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
|
@ -459,6 +476,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 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/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/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=
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
module github.com/armon/circbuf
|
|
@ -0,0 +1,5 @@
|
|||
language: go
|
||||
go:
|
||||
- 1.8.x
|
||||
- tip
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/mattn/go-colorable"
|
||||
packages = ["."]
|
||||
revision = "167de6bfdfba052fa6b2d3664c8f5272e23c9072"
|
||||
version = "v0.0.9"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/mattn/go-isatty"
|
||||
packages = ["."]
|
||||
revision = "0360b2af4f38e8d38c7fce2a9f4e702702d73a39"
|
||||
version = "v0.0.3"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/sys"
|
||||
packages = ["unix"]
|
||||
revision = "37707fdb30a5b38865cfb95e5aab41707daec7fd"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "e8a50671c3cb93ea935bf210b1cd20702876b9d9226129be581ef646d1565cdc"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
|
@ -0,0 +1,30 @@
|
|||
|
||||
# Gopkg.toml example
|
||||
#
|
||||
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
|
||||
# for detailed Gopkg.toml documentation.
|
||||
#
|
||||
# required = ["github.com/user/thing/cmd/thing"]
|
||||
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project"
|
||||
# version = "1.0.0"
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project2"
|
||||
# branch = "dev"
|
||||
# source = "github.com/myfork/project2"
|
||||
#
|
||||
# [[override]]
|
||||
# name = "github.com/x/y"
|
||||
# version = "2.4.0"
|
||||
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/mattn/go-colorable"
|
||||
version = "0.0.9"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/mattn/go-isatty"
|
||||
version = "0.0.3"
|
|
@ -0,0 +1,20 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013 Fatih Arslan
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -0,0 +1,179 @@
|
|||
# Color [![GoDoc](https://godoc.org/github.com/fatih/color?status.svg)](https://godoc.org/github.com/fatih/color) [![Build Status](https://img.shields.io/travis/fatih/color.svg?style=flat-square)](https://travis-ci.org/fatih/color)
|
||||
|
||||
|
||||
|
||||
Color lets you use colorized outputs in terms of [ANSI Escape
|
||||
Codes](http://en.wikipedia.org/wiki/ANSI_escape_code#Colors) in Go (Golang). It
|
||||
has support for Windows too! The API can be used in several ways, pick one that
|
||||
suits you.
|
||||
|
||||
|
||||
![Color](https://i.imgur.com/c1JI0lA.png)
|
||||
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
go get github.com/fatih/color
|
||||
```
|
||||
|
||||
Note that the `vendor` folder is here for stability. Remove the folder if you
|
||||
already have the dependencies in your GOPATH.
|
||||
|
||||
## Examples
|
||||
|
||||
### Standard colors
|
||||
|
||||
```go
|
||||
// Print with default helper functions
|
||||
color.Cyan("Prints text in cyan.")
|
||||
|
||||
// A newline will be appended automatically
|
||||
color.Blue("Prints %s in blue.", "text")
|
||||
|
||||
// These are using the default foreground colors
|
||||
color.Red("We have red")
|
||||
color.Magenta("And many others ..")
|
||||
|
||||
```
|
||||
|
||||
### Mix and reuse colors
|
||||
|
||||
```go
|
||||
// Create a new color object
|
||||
c := color.New(color.FgCyan).Add(color.Underline)
|
||||
c.Println("Prints cyan text with an underline.")
|
||||
|
||||
// Or just add them to New()
|
||||
d := color.New(color.FgCyan, color.Bold)
|
||||
d.Printf("This prints bold cyan %s\n", "too!.")
|
||||
|
||||
// Mix up foreground and background colors, create new mixes!
|
||||
red := color.New(color.FgRed)
|
||||
|
||||
boldRed := red.Add(color.Bold)
|
||||
boldRed.Println("This will print text in bold red.")
|
||||
|
||||
whiteBackground := red.Add(color.BgWhite)
|
||||
whiteBackground.Println("Red text with white background.")
|
||||
```
|
||||
|
||||
### Use your own output (io.Writer)
|
||||
|
||||
```go
|
||||
// Use your own io.Writer output
|
||||
color.New(color.FgBlue).Fprintln(myWriter, "blue color!")
|
||||
|
||||
blue := color.New(color.FgBlue)
|
||||
blue.Fprint(writer, "This will print text in blue.")
|
||||
```
|
||||
|
||||
### Custom print functions (PrintFunc)
|
||||
|
||||
```go
|
||||
// Create a custom print function for convenience
|
||||
red := color.New(color.FgRed).PrintfFunc()
|
||||
red("Warning")
|
||||
red("Error: %s", err)
|
||||
|
||||
// Mix up multiple attributes
|
||||
notice := color.New(color.Bold, color.FgGreen).PrintlnFunc()
|
||||
notice("Don't forget this...")
|
||||
```
|
||||
|
||||
### Custom fprint functions (FprintFunc)
|
||||
|
||||
```go
|
||||
blue := color.New(FgBlue).FprintfFunc()
|
||||
blue(myWriter, "important notice: %s", stars)
|
||||
|
||||
// Mix up with multiple attributes
|
||||
success := color.New(color.Bold, color.FgGreen).FprintlnFunc()
|
||||
success(myWriter, "Don't forget this...")
|
||||
```
|
||||
|
||||
### Insert into noncolor strings (SprintFunc)
|
||||
|
||||
```go
|
||||
// Create SprintXxx functions to mix strings with other non-colorized strings:
|
||||
yellow := color.New(color.FgYellow).SprintFunc()
|
||||
red := color.New(color.FgRed).SprintFunc()
|
||||
fmt.Printf("This is a %s and this is %s.\n", yellow("warning"), red("error"))
|
||||
|
||||
info := color.New(color.FgWhite, color.BgGreen).SprintFunc()
|
||||
fmt.Printf("This %s rocks!\n", info("package"))
|
||||
|
||||
// Use helper functions
|
||||
fmt.Println("This", color.RedString("warning"), "should be not neglected.")
|
||||
fmt.Printf("%v %v\n", color.GreenString("Info:"), "an important message.")
|
||||
|
||||
// Windows supported too! Just don't forget to change the output to color.Output
|
||||
fmt.Fprintf(color.Output, "Windows support: %s", color.GreenString("PASS"))
|
||||
```
|
||||
|
||||
### Plug into existing code
|
||||
|
||||
```go
|
||||
// Use handy standard colors
|
||||
color.Set(color.FgYellow)
|
||||
|
||||
fmt.Println("Existing text will now be in yellow")
|
||||
fmt.Printf("This one %s\n", "too")
|
||||
|
||||
color.Unset() // Don't forget to unset
|
||||
|
||||
// You can mix up parameters
|
||||
color.Set(color.FgMagenta, color.Bold)
|
||||
defer color.Unset() // Use it in your function
|
||||
|
||||
fmt.Println("All text will now be bold magenta.")
|
||||
```
|
||||
|
||||
### Disable/Enable color
|
||||
|
||||
There might be a case where you want to explicitly disable/enable color output. the
|
||||
`go-isatty` package will automatically disable color output for non-tty output streams
|
||||
(for example if the output were piped directly to `less`)
|
||||
|
||||
`Color` has support to disable/enable colors both globally and for single color
|
||||
definitions. For example suppose you have a CLI app and a `--no-color` bool flag. You
|
||||
can easily disable the color output with:
|
||||
|
||||
```go
|
||||
|
||||
var flagNoColor = flag.Bool("no-color", false, "Disable color output")
|
||||
|
||||
if *flagNoColor {
|
||||
color.NoColor = true // disables colorized output
|
||||
}
|
||||
```
|
||||
|
||||
It also has support for single color definitions (local). You can
|
||||
disable/enable color output on the fly:
|
||||
|
||||
```go
|
||||
c := color.New(color.FgCyan)
|
||||
c.Println("Prints cyan text")
|
||||
|
||||
c.DisableColor()
|
||||
c.Println("This is printed without any color")
|
||||
|
||||
c.EnableColor()
|
||||
c.Println("This prints again cyan...")
|
||||
```
|
||||
|
||||
## Todo
|
||||
|
||||
* Save/Return previous values
|
||||
* Evaluate fmt.Formatter interface
|
||||
|
||||
|
||||
## Credits
|
||||
|
||||
* [Fatih Arslan](https://github.com/fatih)
|
||||
* Windows support via @mattn: [colorable](https://github.com/mattn/go-colorable)
|
||||
|
||||
## License
|
||||
|
||||
The MIT License (MIT) - see [`LICENSE.md`](https://github.com/fatih/color/blob/master/LICENSE.md) for more details
|
||||
|
|
@ -0,0 +1,603 @@
|
|||
package color
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/mattn/go-colorable"
|
||||
"github.com/mattn/go-isatty"
|
||||
)
|
||||
|
||||
var (
|
||||
// NoColor defines if the output is colorized or not. It's dynamically set to
|
||||
// false or true based on the stdout's file descriptor referring to a terminal
|
||||
// or not. This is a global option and affects all colors. For more control
|
||||
// over each color block use the methods DisableColor() individually.
|
||||
NoColor = os.Getenv("TERM") == "dumb" ||
|
||||
(!isatty.IsTerminal(os.Stdout.Fd()) && !isatty.IsCygwinTerminal(os.Stdout.Fd()))
|
||||
|
||||
// Output defines the standard output of the print functions. By default
|
||||
// os.Stdout is used.
|
||||
Output = colorable.NewColorableStdout()
|
||||
|
||||
// Error defines a color supporting writer for os.Stderr.
|
||||
Error = colorable.NewColorableStderr()
|
||||
|
||||
// colorsCache is used to reduce the count of created Color objects and
|
||||
// allows to reuse already created objects with required Attribute.
|
||||
colorsCache = make(map[Attribute]*Color)
|
||||
colorsCacheMu sync.Mutex // protects colorsCache
|
||||
)
|
||||
|
||||
// Color defines a custom color object which is defined by SGR parameters.
|
||||
type Color struct {
|
||||
params []Attribute
|
||||
noColor *bool
|
||||
}
|
||||
|
||||
// Attribute defines a single SGR Code
|
||||
type Attribute int
|
||||
|
||||
const escape = "\x1b"
|
||||
|
||||
// Base attributes
|
||||
const (
|
||||
Reset Attribute = iota
|
||||
Bold
|
||||
Faint
|
||||
Italic
|
||||
Underline
|
||||
BlinkSlow
|
||||
BlinkRapid
|
||||
ReverseVideo
|
||||
Concealed
|
||||
CrossedOut
|
||||
)
|
||||
|
||||
// Foreground text colors
|
||||
const (
|
||||
FgBlack Attribute = iota + 30
|
||||
FgRed
|
||||
FgGreen
|
||||
FgYellow
|
||||
FgBlue
|
||||
FgMagenta
|
||||
FgCyan
|
||||
FgWhite
|
||||
)
|
||||
|
||||
// Foreground Hi-Intensity text colors
|
||||
const (
|
||||
FgHiBlack Attribute = iota + 90
|
||||
FgHiRed
|
||||
FgHiGreen
|
||||
FgHiYellow
|
||||
FgHiBlue
|
||||
FgHiMagenta
|
||||
FgHiCyan
|
||||
FgHiWhite
|
||||
)
|
||||
|
||||
// Background text colors
|
||||
const (
|
||||
BgBlack Attribute = iota + 40
|
||||
BgRed
|
||||
BgGreen
|
||||
BgYellow
|
||||
BgBlue
|
||||
BgMagenta
|
||||
BgCyan
|
||||
BgWhite
|
||||
)
|
||||
|
||||
// Background Hi-Intensity text colors
|
||||
const (
|
||||
BgHiBlack Attribute = iota + 100
|
||||
BgHiRed
|
||||
BgHiGreen
|
||||
BgHiYellow
|
||||
BgHiBlue
|
||||
BgHiMagenta
|
||||
BgHiCyan
|
||||
BgHiWhite
|
||||
)
|
||||
|
||||
// New returns a newly created color object.
|
||||
func New(value ...Attribute) *Color {
|
||||
c := &Color{params: make([]Attribute, 0)}
|
||||
c.Add(value...)
|
||||
return c
|
||||
}
|
||||
|
||||
// Set sets the given parameters immediately. It will change the color of
|
||||
// output with the given SGR parameters until color.Unset() is called.
|
||||
func Set(p ...Attribute) *Color {
|
||||
c := New(p...)
|
||||
c.Set()
|
||||
return c
|
||||
}
|
||||
|
||||
// Unset resets all escape attributes and clears the output. Usually should
|
||||
// be called after Set().
|
||||
func Unset() {
|
||||
if NoColor {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprintf(Output, "%s[%dm", escape, Reset)
|
||||
}
|
||||
|
||||
// Set sets the SGR sequence.
|
||||
func (c *Color) Set() *Color {
|
||||
if c.isNoColorSet() {
|
||||
return c
|
||||
}
|
||||
|
||||
fmt.Fprintf(Output, c.format())
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Color) unset() {
|
||||
if c.isNoColorSet() {
|
||||
return
|
||||
}
|
||||
|
||||
Unset()
|
||||
}
|
||||
|
||||
func (c *Color) setWriter(w io.Writer) *Color {
|
||||
if c.isNoColorSet() {
|
||||
return c
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, c.format())
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Color) unsetWriter(w io.Writer) {
|
||||
if c.isNoColorSet() {
|
||||
return
|
||||
}
|
||||
|
||||
if NoColor {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, "%s[%dm", escape, Reset)
|
||||
}
|
||||
|
||||
// Add is used to chain SGR parameters. Use as many as parameters to combine
|
||||
// and create custom color objects. Example: Add(color.FgRed, color.Underline).
|
||||
func (c *Color) Add(value ...Attribute) *Color {
|
||||
c.params = append(c.params, value...)
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Color) prepend(value Attribute) {
|
||||
c.params = append(c.params, 0)
|
||||
copy(c.params[1:], c.params[0:])
|
||||
c.params[0] = value
|
||||
}
|
||||
|
||||
// Fprint formats using the default formats for its operands and writes to w.
|
||||
// Spaces are added between operands when neither is a string.
|
||||
// It returns the number of bytes written and any write error encountered.
|
||||
// On Windows, users should wrap w with colorable.NewColorable() if w is of
|
||||
// type *os.File.
|
||||
func (c *Color) Fprint(w io.Writer, a ...interface{}) (n int, err error) {
|
||||
c.setWriter(w)
|
||||
defer c.unsetWriter(w)
|
||||
|
||||
return fmt.Fprint(w, a...)
|
||||
}
|
||||
|
||||
// Print formats using the default formats for its operands and writes to
|
||||
// standard output. Spaces are added between operands when neither is a
|
||||
// string. It returns the number of bytes written and any write error
|
||||
// encountered. This is the standard fmt.Print() method wrapped with the given
|
||||
// color.
|
||||
func (c *Color) Print(a ...interface{}) (n int, err error) {
|
||||
c.Set()
|
||||
defer c.unset()
|
||||
|
||||
return fmt.Fprint(Output, a...)
|
||||
}
|
||||
|
||||
// Fprintf formats according to a format specifier and writes to w.
|
||||
// It returns the number of bytes written and any write error encountered.
|
||||
// On Windows, users should wrap w with colorable.NewColorable() if w is of
|
||||
// type *os.File.
|
||||
func (c *Color) Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
|
||||
c.setWriter(w)
|
||||
defer c.unsetWriter(w)
|
||||
|
||||
return fmt.Fprintf(w, format, a...)
|
||||
}
|
||||
|
||||
// Printf formats according to a format specifier and writes to standard output.
|
||||
// It returns the number of bytes written and any write error encountered.
|
||||
// This is the standard fmt.Printf() method wrapped with the given color.
|
||||
func (c *Color) Printf(format string, a ...interface{}) (n int, err error) {
|
||||
c.Set()
|
||||
defer c.unset()
|
||||
|
||||
return fmt.Fprintf(Output, format, a...)
|
||||
}
|
||||
|
||||
// Fprintln formats using the default formats for its operands and writes to w.
|
||||
// Spaces are always added between operands and a newline is appended.
|
||||
// On Windows, users should wrap w with colorable.NewColorable() if w is of
|
||||
// type *os.File.
|
||||
func (c *Color) Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
|
||||
c.setWriter(w)
|
||||
defer c.unsetWriter(w)
|
||||
|
||||
return fmt.Fprintln(w, a...)
|
||||
}
|
||||
|
||||
// Println formats using the default formats for its operands and writes to
|
||||
// standard output. Spaces are always added between operands and a newline is
|
||||
// appended. It returns the number of bytes written and any write error
|
||||
// encountered. This is the standard fmt.Print() method wrapped with the given
|
||||
// color.
|
||||
func (c *Color) Println(a ...interface{}) (n int, err error) {
|
||||
c.Set()
|
||||
defer c.unset()
|
||||
|
||||
return fmt.Fprintln(Output, a...)
|
||||
}
|
||||
|
||||
// Sprint is just like Print, but returns a string instead of printing it.
|
||||
func (c *Color) Sprint(a ...interface{}) string {
|
||||
return c.wrap(fmt.Sprint(a...))
|
||||
}
|
||||
|
||||
// Sprintln is just like Println, but returns a string instead of printing it.
|
||||
func (c *Color) Sprintln(a ...interface{}) string {
|
||||
return c.wrap(fmt.Sprintln(a...))
|
||||
}
|
||||
|
||||
// Sprintf is just like Printf, but returns a string instead of printing it.
|
||||
func (c *Color) Sprintf(format string, a ...interface{}) string {
|
||||
return c.wrap(fmt.Sprintf(format, a...))
|
||||
}
|
||||
|
||||
// FprintFunc returns a new function that prints the passed arguments as
|
||||
// colorized with color.Fprint().
|
||||
func (c *Color) FprintFunc() func(w io.Writer, a ...interface{}) {
|
||||
return func(w io.Writer, a ...interface{}) {
|
||||
c.Fprint(w, a...)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintFunc returns a new function that prints the passed arguments as
|
||||
// colorized with color.Print().
|
||||
func (c *Color) PrintFunc() func(a ...interface{}) {
|
||||
return func(a ...interface{}) {
|
||||
c.Print(a...)
|
||||
}
|
||||
}
|
||||
|
||||
// FprintfFunc returns a new function that prints the passed arguments as
|
||||
// colorized with color.Fprintf().
|
||||
func (c *Color) FprintfFunc() func(w io.Writer, format string, a ...interface{}) {
|
||||
return func(w io.Writer, format string, a ...interface{}) {
|
||||
c.Fprintf(w, format, a...)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintfFunc returns a new function that prints the passed arguments as
|
||||
// colorized with color.Printf().
|
||||
func (c *Color) PrintfFunc() func(format string, a ...interface{}) {
|
||||
return func(format string, a ...interface{}) {
|
||||
c.Printf(format, a...)
|
||||
}
|
||||
}
|
||||
|
||||
// FprintlnFunc returns a new function that prints the passed arguments as
|
||||
// colorized with color.Fprintln().
|
||||
func (c *Color) FprintlnFunc() func(w io.Writer, a ...interface{}) {
|
||||
return func(w io.Writer, a ...interface{}) {
|
||||
c.Fprintln(w, a...)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintlnFunc returns a new function that prints the passed arguments as
|
||||
// colorized with color.Println().
|
||||
func (c *Color) PrintlnFunc() func(a ...interface{}) {
|
||||
return func(a ...interface{}) {
|
||||
c.Println(a...)
|
||||
}
|
||||
}
|
||||
|
||||
// SprintFunc returns a new function that returns colorized strings for the
|
||||
// given arguments with fmt.Sprint(). Useful to put into or mix into other
|
||||
// string. Windows users should use this in conjunction with color.Output, example:
|
||||
//
|
||||
// put := New(FgYellow).SprintFunc()
|
||||
// fmt.Fprintf(color.Output, "This is a %s", put("warning"))
|
||||
func (c *Color) SprintFunc() func(a ...interface{}) string {
|
||||
return func(a ...interface{}) string {
|
||||
return c.wrap(fmt.Sprint(a...))
|
||||
}
|
||||
}
|
||||
|
||||
// SprintfFunc returns a new function that returns colorized strings for the
|
||||
// given arguments with fmt.Sprintf(). Useful to put into or mix into other
|
||||
// string. Windows users should use this in conjunction with color.Output.
|
||||
func (c *Color) SprintfFunc() func(format string, a ...interface{}) string {
|
||||
return func(format string, a ...interface{}) string {
|
||||
return c.wrap(fmt.Sprintf(format, a...))
|
||||
}
|
||||
}
|
||||
|
||||
// SprintlnFunc returns a new function that returns colorized strings for the
|
||||
// given arguments with fmt.Sprintln(). Useful to put into or mix into other
|
||||
// string. Windows users should use this in conjunction with color.Output.
|
||||
func (c *Color) SprintlnFunc() func(a ...interface{}) string {
|
||||
return func(a ...interface{}) string {
|
||||
return c.wrap(fmt.Sprintln(a...))
|
||||
}
|
||||
}
|
||||
|
||||
// sequence returns a formatted SGR sequence to be plugged into a "\x1b[...m"
|
||||
// an example output might be: "1;36" -> bold cyan
|
||||
func (c *Color) sequence() string {
|
||||
format := make([]string, len(c.params))
|
||||
for i, v := range c.params {
|
||||
format[i] = strconv.Itoa(int(v))
|
||||
}
|
||||
|
||||
return strings.Join(format, ";")
|
||||
}
|
||||
|
||||
// wrap wraps the s string with the colors attributes. The string is ready to
|
||||
// be printed.
|
||||
func (c *Color) wrap(s string) string {
|
||||
if c.isNoColorSet() {
|
||||
return s
|
||||
}
|
||||
|
||||
return c.format() + s + c.unformat()
|
||||
}
|
||||
|
||||
func (c *Color) format() string {
|
||||
return fmt.Sprintf("%s[%sm", escape, c.sequence())
|
||||
}
|
||||
|
||||
func (c *Color) unformat() string {
|
||||
return fmt.Sprintf("%s[%dm", escape, Reset)
|
||||
}
|
||||
|
||||
// DisableColor disables the color output. Useful to not change any existing
|
||||
// code and still being able to output. Can be used for flags like
|
||||
// "--no-color". To enable back use EnableColor() method.
|
||||
func (c *Color) DisableColor() {
|
||||
c.noColor = boolPtr(true)
|
||||
}
|
||||
|
||||
// EnableColor enables the color output. Use it in conjunction with
|
||||
// DisableColor(). Otherwise this method has no side effects.
|
||||
func (c *Color) EnableColor() {
|
||||
c.noColor = boolPtr(false)
|
||||
}
|
||||
|
||||
func (c *Color) isNoColorSet() bool {
|
||||
// check first if we have user setted action
|
||||
if c.noColor != nil {
|
||||
return *c.noColor
|
||||
}
|
||||
|
||||
// if not return the global option, which is disabled by default
|
||||
return NoColor
|
||||
}
|
||||
|
||||
// Equals returns a boolean value indicating whether two colors are equal.
|
||||
func (c *Color) Equals(c2 *Color) bool {
|
||||
if len(c.params) != len(c2.params) {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, attr := range c.params {
|
||||
if !c2.attrExists(attr) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *Color) attrExists(a Attribute) bool {
|
||||
for _, attr := range c.params {
|
||||
if attr == a {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func boolPtr(v bool) *bool {
|
||||
return &v
|
||||
}
|
||||
|
||||
func getCachedColor(p Attribute) *Color {
|
||||
colorsCacheMu.Lock()
|
||||
defer colorsCacheMu.Unlock()
|
||||
|
||||
c, ok := colorsCache[p]
|
||||
if !ok {
|
||||
c = New(p)
|
||||
colorsCache[p] = c
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func colorPrint(format string, p Attribute, a ...interface{}) {
|
||||
c := getCachedColor(p)
|
||||
|
||||
if !strings.HasSuffix(format, "\n") {
|
||||
format += "\n"
|
||||
}
|
||||
|
||||
if len(a) == 0 {
|
||||
c.Print(format)
|
||||
} else {
|
||||
c.Printf(format, a...)
|
||||
}
|
||||
}
|
||||
|
||||
func colorString(format string, p Attribute, a ...interface{}) string {
|
||||
c := getCachedColor(p)
|
||||
|
||||
if len(a) == 0 {
|
||||
return c.SprintFunc()(format)
|
||||
}
|
||||
|
||||
return c.SprintfFunc()(format, a...)
|
||||
}
|
||||
|
||||
// Black is a convenient helper function to print with black foreground. A
|
||||
// newline is appended to format by default.
|
||||
func Black(format string, a ...interface{}) { colorPrint(format, FgBlack, a...) }
|
||||
|
||||
// Red is a convenient helper function to print with red foreground. A
|
||||
// newline is appended to format by default.
|
||||
func Red(format string, a ...interface{}) { colorPrint(format, FgRed, a...) }
|
||||
|
||||
// Green is a convenient helper function to print with green foreground. A
|
||||
// newline is appended to format by default.
|
||||
func Green(format string, a ...interface{}) { colorPrint(format, FgGreen, a...) }
|
||||
|
||||
// Yellow is a convenient helper function to print with yellow foreground.
|
||||
// A newline is appended to format by default.
|
||||
func Yellow(format string, a ...interface{}) { colorPrint(format, FgYellow, a...) }
|
||||
|
||||
// Blue is a convenient helper function to print with blue foreground. A
|
||||
// newline is appended to format by default.
|
||||
func Blue(format string, a ...interface{}) { colorPrint(format, FgBlue, a...) }
|
||||
|
||||
// Magenta is a convenient helper function to print with magenta foreground.
|
||||
// A newline is appended to format by default.
|
||||
func Magenta(format string, a ...interface{}) { colorPrint(format, FgMagenta, a...) }
|
||||
|
||||
// Cyan is a convenient helper function to print with cyan foreground. A
|
||||
// newline is appended to format by default.
|
||||
func Cyan(format string, a ...interface{}) { colorPrint(format, FgCyan, a...) }
|
||||
|
||||
// White is a convenient helper function to print with white foreground. A
|
||||
// newline is appended to format by default.
|
||||
func White(format string, a ...interface{}) { colorPrint(format, FgWhite, a...) }
|
||||
|
||||
// BlackString is a convenient helper function to return a string with black
|
||||
// foreground.
|
||||
func BlackString(format string, a ...interface{}) string { return colorString(format, FgBlack, a...) }
|
||||
|
||||
// RedString is a convenient helper function to return a string with red
|
||||
// foreground.
|
||||
func RedString(format string, a ...interface{}) string { return colorString(format, FgRed, a...) }
|
||||
|
||||
// GreenString is a convenient helper function to return a string with green
|
||||
// foreground.
|
||||
func GreenString(format string, a ...interface{}) string { return colorString(format, FgGreen, a...) }
|
||||
|
||||
// YellowString is a convenient helper function to return a string with yellow
|
||||
// foreground.
|
||||
func YellowString(format string, a ...interface{}) string { return colorString(format, FgYellow, a...) }
|
||||
|
||||
// BlueString is a convenient helper function to return a string with blue
|
||||
// foreground.
|
||||
func BlueString(format string, a ...interface{}) string { return colorString(format, FgBlue, a...) }
|
||||
|
||||
// MagentaString is a convenient helper function to return a string with magenta
|
||||
// foreground.
|
||||
func MagentaString(format string, a ...interface{}) string {
|
||||
return colorString(format, FgMagenta, a...)
|
||||
}
|
||||
|
||||
// CyanString is a convenient helper function to return a string with cyan
|
||||
// foreground.
|
||||
func CyanString(format string, a ...interface{}) string { return colorString(format, FgCyan, a...) }
|
||||
|
||||
// WhiteString is a convenient helper function to return a string with white
|
||||
// foreground.
|
||||
func WhiteString(format string, a ...interface{}) string { return colorString(format, FgWhite, a...) }
|
||||
|
||||
// HiBlack is a convenient helper function to print with hi-intensity black foreground. A
|
||||
// newline is appended to format by default.
|
||||
func HiBlack(format string, a ...interface{}) { colorPrint(format, FgHiBlack, a...) }
|
||||
|
||||
// HiRed is a convenient helper function to print with hi-intensity red foreground. A
|
||||
// newline is appended to format by default.
|
||||
func HiRed(format string, a ...interface{}) { colorPrint(format, FgHiRed, a...) }
|
||||
|
||||
// HiGreen is a convenient helper function to print with hi-intensity green foreground. A
|
||||
// newline is appended to format by default.
|
||||
func HiGreen(format string, a ...interface{}) { colorPrint(format, FgHiGreen, a...) }
|
||||
|
||||
// HiYellow is a convenient helper function to print with hi-intensity yellow foreground.
|
||||
// A newline is appended to format by default.
|
||||
func HiYellow(format string, a ...interface{}) { colorPrint(format, FgHiYellow, a...) }
|
||||
|
||||
// HiBlue is a convenient helper function to print with hi-intensity blue foreground. A
|
||||
// newline is appended to format by default.
|
||||
func HiBlue(format string, a ...interface{}) { colorPrint(format, FgHiBlue, a...) }
|
||||
|
||||
// HiMagenta is a convenient helper function to print with hi-intensity magenta foreground.
|
||||
// A newline is appended to format by default.
|
||||
func HiMagenta(format string, a ...interface{}) { colorPrint(format, FgHiMagenta, a...) }
|
||||
|
||||
// HiCyan is a convenient helper function to print with hi-intensity cyan foreground. A
|
||||
// newline is appended to format by default.
|
||||
func HiCyan(format string, a ...interface{}) { colorPrint(format, FgHiCyan, a...) }
|
||||
|
||||
// HiWhite is a convenient helper function to print with hi-intensity white foreground. A
|
||||
// newline is appended to format by default.
|
||||
func HiWhite(format string, a ...interface{}) { colorPrint(format, FgHiWhite, a...) }
|
||||
|
||||
// HiBlackString is a convenient helper function to return a string with hi-intensity black
|
||||
// foreground.
|
||||
func HiBlackString(format string, a ...interface{}) string {
|
||||
return colorString(format, FgHiBlack, a...)
|
||||
}
|
||||
|
||||
// HiRedString is a convenient helper function to return a string with hi-intensity red
|
||||
// foreground.
|
||||
func HiRedString(format string, a ...interface{}) string { return colorString(format, FgHiRed, a...) }
|
||||
|
||||
// HiGreenString is a convenient helper function to return a string with hi-intensity green
|
||||
// foreground.
|
||||
func HiGreenString(format string, a ...interface{}) string {
|
||||
return colorString(format, FgHiGreen, a...)
|
||||
}
|
||||
|
||||
// HiYellowString is a convenient helper function to return a string with hi-intensity yellow
|
||||
// foreground.
|
||||
func HiYellowString(format string, a ...interface{}) string {
|
||||
return colorString(format, FgHiYellow, a...)
|
||||
}
|
||||
|
||||
// HiBlueString is a convenient helper function to return a string with hi-intensity blue
|
||||
// foreground.
|
||||
func HiBlueString(format string, a ...interface{}) string { return colorString(format, FgHiBlue, a...) }
|
||||
|
||||
// HiMagentaString is a convenient helper function to return a string with hi-intensity magenta
|
||||
// foreground.
|
||||
func HiMagentaString(format string, a ...interface{}) string {
|
||||
return colorString(format, FgHiMagenta, a...)
|
||||
}
|
||||
|
||||
// HiCyanString is a convenient helper function to return a string with hi-intensity cyan
|
||||
// foreground.
|
||||
func HiCyanString(format string, a ...interface{}) string { return colorString(format, FgHiCyan, a...) }
|
||||
|
||||
// HiWhiteString is a convenient helper function to return a string with hi-intensity white
|
||||
// foreground.
|
||||
func HiWhiteString(format string, a ...interface{}) string {
|
||||
return colorString(format, FgHiWhite, a...)
|
||||
}
|
|
@ -0,0 +1,133 @@
|
|||
/*
|
||||
Package color is an ANSI color package to output colorized or SGR defined
|
||||
output to the standard output. The API can be used in several way, pick one
|
||||
that suits you.
|
||||
|
||||
Use simple and default helper functions with predefined foreground colors:
|
||||
|
||||
color.Cyan("Prints text in cyan.")
|
||||
|
||||
// a newline will be appended automatically
|
||||
color.Blue("Prints %s in blue.", "text")
|
||||
|
||||
// More default foreground colors..
|
||||
color.Red("We have red")
|
||||
color.Yellow("Yellow color too!")
|
||||
color.Magenta("And many others ..")
|
||||
|
||||
// Hi-intensity colors
|
||||
color.HiGreen("Bright green color.")
|
||||
color.HiBlack("Bright black means gray..")
|
||||
color.HiWhite("Shiny white color!")
|
||||
|
||||
However there are times where custom color mixes are required. Below are some
|
||||
examples to create custom color objects and use the print functions of each
|
||||
separate color object.
|
||||
|
||||
// Create a new color object
|
||||
c := color.New(color.FgCyan).Add(color.Underline)
|
||||
c.Println("Prints cyan text with an underline.")
|
||||
|
||||
// Or just add them to New()
|
||||
d := color.New(color.FgCyan, color.Bold)
|
||||
d.Printf("This prints bold cyan %s\n", "too!.")
|
||||
|
||||
|
||||
// Mix up foreground and background colors, create new mixes!
|
||||
red := color.New(color.FgRed)
|
||||
|
||||
boldRed := red.Add(color.Bold)
|
||||
boldRed.Println("This will print text in bold red.")
|
||||
|
||||
whiteBackground := red.Add(color.BgWhite)
|
||||
whiteBackground.Println("Red text with White background.")
|
||||
|
||||
// Use your own io.Writer output
|
||||
color.New(color.FgBlue).Fprintln(myWriter, "blue color!")
|
||||
|
||||
blue := color.New(color.FgBlue)
|
||||
blue.Fprint(myWriter, "This will print text in blue.")
|
||||
|
||||
You can create PrintXxx functions to simplify even more:
|
||||
|
||||
// Create a custom print function for convenient
|
||||
red := color.New(color.FgRed).PrintfFunc()
|
||||
red("warning")
|
||||
red("error: %s", err)
|
||||
|
||||
// Mix up multiple attributes
|
||||
notice := color.New(color.Bold, color.FgGreen).PrintlnFunc()
|
||||
notice("don't forget this...")
|
||||
|
||||
You can also FprintXxx functions to pass your own io.Writer:
|
||||
|
||||
blue := color.New(FgBlue).FprintfFunc()
|
||||
blue(myWriter, "important notice: %s", stars)
|
||||
|
||||
// Mix up with multiple attributes
|
||||
success := color.New(color.Bold, color.FgGreen).FprintlnFunc()
|
||||
success(myWriter, don't forget this...")
|
||||
|
||||
|
||||
Or create SprintXxx functions to mix strings with other non-colorized strings:
|
||||
|
||||
yellow := New(FgYellow).SprintFunc()
|
||||
red := New(FgRed).SprintFunc()
|
||||
|
||||
fmt.Printf("this is a %s and this is %s.\n", yellow("warning"), red("error"))
|
||||
|
||||
info := New(FgWhite, BgGreen).SprintFunc()
|
||||
fmt.Printf("this %s rocks!\n", info("package"))
|
||||
|
||||
Windows support is enabled by default. All Print functions work as intended.
|
||||
However only for color.SprintXXX functions, user should use fmt.FprintXXX and
|
||||
set the output to color.Output:
|
||||
|
||||
fmt.Fprintf(color.Output, "Windows support: %s", color.GreenString("PASS"))
|
||||
|
||||
info := New(FgWhite, BgGreen).SprintFunc()
|
||||
fmt.Fprintf(color.Output, "this %s rocks!\n", info("package"))
|
||||
|
||||
Using with existing code is possible. Just use the Set() method to set the
|
||||
standard output to the given parameters. That way a rewrite of an existing
|
||||
code is not required.
|
||||
|
||||
// Use handy standard colors.
|
||||
color.Set(color.FgYellow)
|
||||
|
||||
fmt.Println("Existing text will be now in Yellow")
|
||||
fmt.Printf("This one %s\n", "too")
|
||||
|
||||
color.Unset() // don't forget to unset
|
||||
|
||||
// You can mix up parameters
|
||||
color.Set(color.FgMagenta, color.Bold)
|
||||
defer color.Unset() // use it in your function
|
||||
|
||||
fmt.Println("All text will be now bold magenta.")
|
||||
|
||||
There might be a case where you want to disable color output (for example to
|
||||
pipe the standard output of your app to somewhere else). `Color` has support to
|
||||
disable colors both globally and for single color definition. For example
|
||||
suppose you have a CLI app and a `--no-color` bool flag. You can easily disable
|
||||
the color output with:
|
||||
|
||||
var flagNoColor = flag.Bool("no-color", false, "Disable color output")
|
||||
|
||||
if *flagNoColor {
|
||||
color.NoColor = true // disables colorized output
|
||||
}
|
||||
|
||||
It also has support for single color definitions (local). You can
|
||||
disable/enable color output on the fly:
|
||||
|
||||
c := color.New(color.FgCyan)
|
||||
c.Println("Prints cyan text")
|
||||
|
||||
c.DisableColor()
|
||||
c.Println("This is printed without any color")
|
||||
|
||||
c.EnableColor()
|
||||
c.Println("This prints again cyan...")
|
||||
*/
|
||||
package color
|
|
@ -1,11 +1,7 @@
|
|||
// checkpoint is a package for checking version information and alerts
|
||||
// for a HashiCorp product.
|
||||
package checkpoint
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/rand"
|
||||
crand "crypto/rand"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
@ -23,112 +19,9 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/hashicorp/go-cleanhttp"
|
||||
uuid "github.com/hashicorp/go-uuid"
|
||||
)
|
||||
|
||||
var magicBytes [4]byte = [4]byte{0x35, 0x77, 0x69, 0xFB}
|
||||
|
||||
// ReportParams are the parameters for configuring a telemetry report.
|
||||
type ReportParams struct {
|
||||
// Signature is some random signature that should be stored and used
|
||||
// as a cookie-like value. This ensures that alerts aren't repeated.
|
||||
// If the signature is changed, repeat alerts may be sent down. The
|
||||
// signature should NOT be anything identifiable to a user (such as
|
||||
// a MAC address). It should be random.
|
||||
//
|
||||
// If SignatureFile is given, then the signature will be read from this
|
||||
// file. If the file doesn't exist, then a random signature will
|
||||
// automatically be generated and stored here. SignatureFile will be
|
||||
// ignored if Signature is given.
|
||||
Signature string `json:"signature"`
|
||||
SignatureFile string `json:"-"`
|
||||
|
||||
StartTime time.Time `json:"start_time"`
|
||||
EndTime time.Time `json:"end_time"`
|
||||
Arch string `json:"arch"`
|
||||
OS string `json:"os"`
|
||||
Payload interface{} `json:"payload,omitempty"`
|
||||
Product string `json:"product"`
|
||||
RunID string `json:"run_id"`
|
||||
SchemaVersion string `json:"schema_version"`
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
func (i *ReportParams) signature() string {
|
||||
signature := i.Signature
|
||||
if i.Signature == "" && i.SignatureFile != "" {
|
||||
var err error
|
||||
signature, err = checkSignature(i.SignatureFile)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
return signature
|
||||
}
|
||||
|
||||
// Report sends telemetry information to checkpoint
|
||||
func Report(ctx context.Context, r *ReportParams) error {
|
||||
if disabled := os.Getenv("CHECKPOINT_DISABLE"); disabled != "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
req, err := ReportRequest(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client := cleanhttp.DefaultClient()
|
||||
resp, err := client.Do(req.WithContext(ctx))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if resp.StatusCode != 201 {
|
||||
return fmt.Errorf("Unknown status: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReportRequest creates a request object for making a report
|
||||
func ReportRequest(r *ReportParams) (*http.Request, error) {
|
||||
// Populate some fields automatically if we can
|
||||
if r.RunID == "" {
|
||||
uuid, err := uuid.GenerateUUID()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.RunID = uuid
|
||||
}
|
||||
if r.Arch == "" {
|
||||
r.Arch = runtime.GOARCH
|
||||
}
|
||||
if r.OS == "" {
|
||||
r.OS = runtime.GOOS
|
||||
}
|
||||
if r.Signature == "" {
|
||||
r.Signature = r.signature()
|
||||
}
|
||||
|
||||
b, err := json.Marshal(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
u := &url.URL{
|
||||
Scheme: "https",
|
||||
Host: "checkpoint-api.hashicorp.com",
|
||||
Path: fmt.Sprintf("/v1/telemetry/%s", r.Product),
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("POST", u.String(), bytes.NewReader(b))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Add("Accept", "application/json")
|
||||
req.Header.Add("User-Agent", "HashiCorp/go-checkpoint")
|
||||
|
||||
return req, nil
|
||||
}
|
||||
var magicBytes = [4]byte{0x35, 0x77, 0x69, 0xFB}
|
||||
|
||||
// CheckParams are the parameters for configuring a check request.
|
||||
type CheckParams struct {
|
||||
|
@ -177,14 +70,14 @@ type CheckParams struct {
|
|||
|
||||
// CheckResponse is the response for a check request.
|
||||
type CheckResponse struct {
|
||||
Product string
|
||||
CurrentVersion string `json:"current_version"`
|
||||
CurrentReleaseDate int `json:"current_release_date"`
|
||||
CurrentDownloadURL string `json:"current_download_url"`
|
||||
CurrentChangelogURL string `json:"current_changelog_url"`
|
||||
ProjectWebsite string `json:"project_website"`
|
||||
Outdated bool `json:"outdated"`
|
||||
Alerts []*CheckAlert
|
||||
Product string `json:"product"`
|
||||
CurrentVersion string `json:"current_version"`
|
||||
CurrentReleaseDate int `json:"current_release_date"`
|
||||
CurrentDownloadURL string `json:"current_download_url"`
|
||||
CurrentChangelogURL string `json:"current_changelog_url"`
|
||||
ProjectWebsite string `json:"project_website"`
|
||||
Outdated bool `json:"outdated"`
|
||||
Alerts []*CheckAlert `json:"alerts"`
|
||||
}
|
||||
|
||||
// CheckAlert is a single alert message from a check request.
|
||||
|
@ -192,11 +85,11 @@ type CheckResponse struct {
|
|||
// These never have to be manually constructed, and are typically populated
|
||||
// into a CheckResponse as a result of the Check request.
|
||||
type CheckAlert struct {
|
||||
ID int
|
||||
Date int
|
||||
Message string
|
||||
URL string
|
||||
Level string
|
||||
ID int `json:"id"`
|
||||
Date int `json:"date"`
|
||||
Message string `json:"message"`
|
||||
URL string `json:"url"`
|
||||
Level string `json:"level"`
|
||||
}
|
||||
|
||||
// Check checks for alerts and new version information.
|
||||
|
@ -205,7 +98,7 @@ func Check(p *CheckParams) (*CheckResponse, error) {
|
|||
return &CheckResponse{}, nil
|
||||
}
|
||||
|
||||
// set a default timeout of 3 sec for the check request (in milliseconds)
|
||||
// Set a default timeout of 3 sec for the check request (in milliseconds)
|
||||
timeout := 3000
|
||||
if _, err := strconv.Atoi(os.Getenv("CHECKPOINT_TIMEOUT")); err == nil {
|
||||
timeout, _ = strconv.Atoi(os.Getenv("CHECKPOINT_TIMEOUT"))
|
||||
|
@ -253,8 +146,8 @@ func Check(p *CheckParams) (*CheckResponse, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Add("Accept", "application/json")
|
||||
req.Header.Add("User-Agent", "HashiCorp/go-checkpoint")
|
||||
req.Header.Set("Accept", "application/json")
|
||||
req.Header.Set("User-Agent", "HashiCorp/go-checkpoint")
|
||||
|
||||
client := cleanhttp.DefaultClient()
|
||||
|
||||
|
@ -266,6 +159,8 @@ func Check(p *CheckParams) (*CheckResponse, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
return nil, fmt.Errorf("Unknown status: %d", resp.StatusCode)
|
||||
}
|
||||
|
@ -390,14 +285,11 @@ func checkCache(current string, path string, d time.Duration) (io.ReadCloser, er
|
|||
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func checkResult(r io.Reader) (*CheckResponse, error) {
|
||||
var result CheckResponse
|
||||
dec := json.NewDecoder(r)
|
||||
if err := dec.Decode(&result); err != nil {
|
||||
if err := json.NewDecoder(r).Decode(&result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
|
@ -426,7 +318,7 @@ func checkSignature(path string) (string, error) {
|
|||
var b [16]byte
|
||||
n := 0
|
||||
for n < 16 {
|
||||
n2, err := rand.Read(b[n:])
|
||||
n2, err := crand.Read(b[n:])
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
@ -456,7 +348,7 @@ func writeCacheHeader(f io.Writer, v string) error {
|
|||
}
|
||||
|
||||
// Write out our current version length
|
||||
var length uint32 = uint32(len(v))
|
||||
length := uint32(len(v))
|
||||
if err := binary.Write(f, binary.LittleEndian, length); err != nil {
|
||||
return err
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
module github.com/hashicorp/go-checkpoint
|
||||
|
||||
require (
|
||||
github.com/hashicorp/go-cleanhttp v0.5.0
|
||||
github.com/hashicorp/go-uuid v1.0.0
|
||||
)
|
|
@ -0,0 +1,4 @@
|
|||
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-uuid v1.0.0 h1:RS8zrF7PhGwyNPOtxSClXXj9HA8feRnJzgnI1RJCSnM=
|
||||
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
|
@ -0,0 +1,118 @@
|
|||
package checkpoint
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/go-cleanhttp"
|
||||
uuid "github.com/hashicorp/go-uuid"
|
||||
)
|
||||
|
||||
// ReportParams are the parameters for configuring a telemetry report.
|
||||
type ReportParams struct {
|
||||
// Signature is some random signature that should be stored and used
|
||||
// as a cookie-like value. This ensures that alerts aren't repeated.
|
||||
// If the signature is changed, repeat alerts may be sent down. The
|
||||
// signature should NOT be anything identifiable to a user (such as
|
||||
// a MAC address). It should be random.
|
||||
//
|
||||
// If SignatureFile is given, then the signature will be read from this
|
||||
// file. If the file doesn't exist, then a random signature will
|
||||
// automatically be generated and stored here. SignatureFile will be
|
||||
// ignored if Signature is given.
|
||||
Signature string `json:"signature"`
|
||||
SignatureFile string `json:"-"`
|
||||
|
||||
StartTime time.Time `json:"start_time"`
|
||||
EndTime time.Time `json:"end_time"`
|
||||
Arch string `json:"arch"`
|
||||
OS string `json:"os"`
|
||||
Payload interface{} `json:"payload,omitempty"`
|
||||
Product string `json:"product"`
|
||||
RunID string `json:"run_id"`
|
||||
SchemaVersion string `json:"schema_version"`
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
func (i *ReportParams) signature() string {
|
||||
signature := i.Signature
|
||||
if i.Signature == "" && i.SignatureFile != "" {
|
||||
var err error
|
||||
signature, err = checkSignature(i.SignatureFile)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
return signature
|
||||
}
|
||||
|
||||
// Report sends telemetry information to checkpoint
|
||||
func Report(ctx context.Context, r *ReportParams) error {
|
||||
if disabled := os.Getenv("CHECKPOINT_DISABLE"); disabled != "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
req, err := ReportRequest(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client := cleanhttp.DefaultClient()
|
||||
resp, err := client.Do(req.WithContext(ctx))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if resp.StatusCode != 201 {
|
||||
return fmt.Errorf("Unknown status: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReportRequest creates a request object for making a report
|
||||
func ReportRequest(r *ReportParams) (*http.Request, error) {
|
||||
// Populate some fields automatically if we can
|
||||
if r.RunID == "" {
|
||||
uuid, err := uuid.GenerateUUID()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.RunID = uuid
|
||||
}
|
||||
if r.Arch == "" {
|
||||
r.Arch = runtime.GOARCH
|
||||
}
|
||||
if r.OS == "" {
|
||||
r.OS = runtime.GOOS
|
||||
}
|
||||
if r.Signature == "" {
|
||||
r.Signature = r.signature()
|
||||
}
|
||||
|
||||
b, err := json.Marshal(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
u := &url.URL{
|
||||
Scheme: "https",
|
||||
Host: "checkpoint-api.hashicorp.com",
|
||||
Path: fmt.Sprintf("/v1/telemetry/%s", r.Product),
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("POST", u.String(), bytes.NewReader(b))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("Accept", "application/json")
|
||||
req.Header.Set("User-Agent", "HashiCorp/go-checkpoint")
|
||||
|
||||
return req, nil
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
package checkpoint
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/go-cleanhttp"
|
||||
)
|
||||
|
||||
// VersionsParams are the parameters for a versions request.
|
||||
type VersionsParams struct {
|
||||
// Service is used to lookup the correct service.
|
||||
Service string
|
||||
|
||||
// Product is used to filter the version contraints.
|
||||
Product string
|
||||
|
||||
// Force, if true, will force the check even if CHECKPOINT_DISABLE
|
||||
// is set. Within HashiCorp products, this is ONLY USED when the user
|
||||
// specifically requests it. This is never automatically done without
|
||||
// the user's consent.
|
||||
Force bool
|
||||
}
|
||||
|
||||
// VersionsResponse is the response for a versions request.
|
||||
type VersionsResponse struct {
|
||||
Service string `json:"service"`
|
||||
Product string `json:"product"`
|
||||
Minimum string `json:"minimum"`
|
||||
Maximum string `json:"maximum"`
|
||||
Excluding []string `json:"excluding"`
|
||||
}
|
||||
|
||||
// Versions returns the version constrains for a given service and product.
|
||||
func Versions(p *VersionsParams) (*VersionsResponse, error) {
|
||||
if disabled := os.Getenv("CHECKPOINT_DISABLE"); disabled != "" && !p.Force {
|
||||
return &VersionsResponse{}, nil
|
||||
}
|
||||
|
||||
// Set a default timeout of 1 sec for the versions request (in milliseconds)
|
||||
timeout := 1000
|
||||
if _, err := strconv.Atoi(os.Getenv("CHECKPOINT_TIMEOUT")); err == nil {
|
||||
timeout, _ = strconv.Atoi(os.Getenv("CHECKPOINT_TIMEOUT"))
|
||||
}
|
||||
|
||||
v := url.Values{}
|
||||
v.Set("product", p.Product)
|
||||
|
||||
u := &url.URL{
|
||||
Scheme: "https",
|
||||
Host: "checkpoint-api.hashicorp.com",
|
||||
Path: fmt.Sprintf("/v1/versions/%s", p.Service),
|
||||
RawQuery: v.Encode(),
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("GET", u.String(), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("Accept", "application/json")
|
||||
req.Header.Set("User-Agent", "HashiCorp/go-checkpoint")
|
||||
|
||||
client := cleanhttp.DefaultClient()
|
||||
|
||||
// We use a short timeout since checking for new versions is not critical
|
||||
// enough to block on if checkpoint is broken/slow.
|
||||
client.Timeout = time.Duration(timeout) * time.Millisecond
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
return nil, fmt.Errorf("Unknown status: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
result := &VersionsResponse{}
|
||||
if err := json.NewDecoder(resp.Body).Decode(result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
|
@ -9,15 +9,16 @@ addons:
|
|||
|
||||
language: go
|
||||
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
|
||||
go:
|
||||
- 1.8.x
|
||||
- 1.9.x
|
||||
- master
|
||||
- "1.11.x"
|
||||
|
||||
before_script:
|
||||
- go build ./cmd/go-getter
|
||||
|
||||
branches:
|
||||
only:
|
||||
- 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.
|
||||
|
||||
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
|
||||
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,
|
||||
not directories, but checksumming will work for any protocol.
|
||||
|
||||
To checksum a file, append a `checksum` query parameter to the URL.
|
||||
The paramter value should be in the format of `type:value`, where
|
||||
type is "md5", "sha1", "sha256", or "sha512". The "value" should be
|
||||
the actual checksum value. go-getter will parse out this query parameter
|
||||
automatically and use it to verify the checksum. An example URL
|
||||
is shown below:
|
||||
To checksum a file, append a `checksum` query parameter to the URL. go-getter
|
||||
will parse out this query parameter automatically and use it to verify the
|
||||
checksum. The parameter value can be in the format of `type:value` or just
|
||||
`value`, where type is "md5", "sha1", "sha256", "sha512" or "file" . The
|
||||
"value" should be the actual checksum value or download URL for "file". When
|
||||
`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=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
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
This section documents the protocol-specific options that can be specified
|
||||
for go-getter. These options should be appended to the input as normal query
|
||||
parameters. Depending on the usage of go-getter, applications may provide
|
||||
alternate ways of inputting options. For example, [Nomad](https://www.nomadproject.io)
|
||||
provides a nice options block for specifying options rather than in the URL.
|
||||
This section documents the protocol-specific options that can be specified for
|
||||
go-getter. These options should be appended to the input as normal query
|
||||
parameters ([HTTP headers](#headers) are an exception to this, however).
|
||||
Depending on the usage of go-getter, applications may provide alternate ways of
|
||||
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)
|
||||
|
||||
|
@ -250,6 +275,9 @@ None
|
|||
from a private key file on disk, you would run `base64 -w0 <file>`.
|
||||
|
||||
**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`)
|
||||
|
||||
|
@ -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
|
||||
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 takes various access configurations in the URL. Note that it will also
|
||||
|
|
|
@ -13,4 +13,4 @@ install:
|
|||
|
||||
go get -d -v -t ./...
|
||||
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
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/md5"
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"encoding/hex"
|
||||
"context"
|
||||
"fmt"
|
||||
"hash"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
@ -17,7 +10,7 @@ import (
|
|||
"strings"
|
||||
|
||||
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.
|
||||
|
@ -26,6 +19,9 @@ import (
|
|||
// Using a client directly allows more fine-grained control over how downloading
|
||||
// is done, as well as customizing the protocols supported.
|
||||
type Client struct {
|
||||
// Ctx for cancellation
|
||||
Ctx context.Context
|
||||
|
||||
// Src is the source URL to get.
|
||||
//
|
||||
// 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.
|
||||
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.
|
||||
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
|
||||
mode := c.Mode
|
||||
if mode == ClientModeInvalid {
|
||||
|
@ -76,18 +82,7 @@ func (c *Client) Get() error {
|
|||
}
|
||||
}
|
||||
|
||||
// Default decompressor value
|
||||
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)
|
||||
src, err := Detect(c.Src, c.Pwd, c.Detectors)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -119,12 +114,7 @@ func (c *Client) Get() error {
|
|||
force = u.Scheme
|
||||
}
|
||||
|
||||
getters := c.Getters
|
||||
if getters == nil {
|
||||
getters = Getters
|
||||
}
|
||||
|
||||
g, ok := getters[force]
|
||||
g, ok := c.Getters[force]
|
||||
if !ok {
|
||||
return fmt.Errorf(
|
||||
"download not supported for scheme '%s'", force)
|
||||
|
@ -150,7 +140,7 @@ func (c *Client) Get() error {
|
|||
if archiveV == "" {
|
||||
// We don't appear to... but is it part of the filename?
|
||||
matchingLen := 0
|
||||
for k, _ := range decompressors {
|
||||
for k := range c.Decompressors {
|
||||
if strings.HasSuffix(u.Path, "."+k) && len(k) > matchingLen {
|
||||
archiveV = k
|
||||
matchingLen = len(k)
|
||||
|
@ -163,7 +153,7 @@ func (c *Client) Get() error {
|
|||
// real path.
|
||||
var decompressDst string
|
||||
var decompressDir bool
|
||||
decompressor := decompressors[archiveV]
|
||||
decompressor := c.Decompressors[archiveV]
|
||||
if decompressor != nil {
|
||||
// Create a temporary directory to store our archive. We delete
|
||||
// this at the end of everything.
|
||||
|
@ -182,44 +172,16 @@ func (c *Client) Get() error {
|
|||
mode = ClientModeFile
|
||||
}
|
||||
|
||||
// Determine if we have a checksum
|
||||
var checksumHash hash.Hash
|
||||
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 {
|
||||
return fmt.Errorf("invalid checksum: %s", err)
|
||||
}
|
||||
|
||||
// Set our value
|
||||
checksumValue = b
|
||||
// Determine checksum if we have one
|
||||
checksum, err := c.extractChecksum(u)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid checksum: %s", err)
|
||||
}
|
||||
|
||||
// Delete the query parameter if we have it.
|
||||
q.Del("checksum")
|
||||
u.RawQuery = q.Encode()
|
||||
|
||||
if mode == ClientModeAny {
|
||||
// Ask the getter which client mode to use
|
||||
mode, err = g.ClientMode(u)
|
||||
|
@ -248,15 +210,24 @@ func (c *Client) Get() error {
|
|||
// If we're not downloading a directory, then just download the file
|
||||
// and return.
|
||||
if mode == ClientModeFile {
|
||||
err := g.GetFile(dst, u)
|
||||
if err != nil {
|
||||
return err
|
||||
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 checksumHash != nil {
|
||||
if err := checksum(dst, checksumHash, checksumValue); err != nil {
|
||||
if getFile {
|
||||
err := g.GetFile(dst, u)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if checksum != nil {
|
||||
if err := checksum.checksum(dst); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if decompressor != nil {
|
||||
|
@ -291,7 +262,7 @@ func (c *Client) Get() error {
|
|||
if decompressor == nil {
|
||||
// If we're getting a directory, then this is an error. You cannot
|
||||
// checksum a directory. TODO: test
|
||||
if checksumHash != nil {
|
||||
if checksum != nil {
|
||||
return fmt.Errorf(
|
||||
"checksum cannot be specified for directory download")
|
||||
}
|
||||
|
@ -320,30 +291,7 @@ func (c *Client) Get() error {
|
|||
return err
|
||||
}
|
||||
|
||||
return copyDir(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 copyDir(c.Ctx, realDst, subDir, false)
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
import (
|
||||
"io"
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
@ -11,7 +11,7 @@ import (
|
|||
// should already exist.
|
||||
//
|
||||
// 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)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -66,7 +66,7 @@ func copyDir(dst string, src string, ignoreDot bool) error {
|
|||
}
|
||||
defer dstF.Close()
|
||||
|
||||
if _, err := io.Copy(dstF, srcF); err != nil {
|
||||
if _, err := Copy(ctx, dstF, srcF); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 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)
|
||||
done := false
|
||||
dirHdrs := []*tar.Header{}
|
||||
now := time.Now()
|
||||
for {
|
||||
hdr, err := tarR.Next()
|
||||
if err == io.EOF {
|
||||
|
@ -95,17 +97,37 @@ func untar(input io.Reader, dst, src string, dir bool) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// Set the access and modification time
|
||||
if err := os.Chtimes(path, hdr.AccessTime, hdr.ModTime); err != nil {
|
||||
// Set the access and modification time if valid, otherwise default to current time
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
// Adding a file or subdirectory changes the mtime of a directory
|
||||
// We therefore wait until we've extracted everything and then set the mtime and atime attributes
|
||||
// Perform a final pass over extracted directories to update metadata
|
||||
for _, dirHdr := range dirHdrs {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,16 +18,18 @@ import (
|
|||
|
||||
// TestDecompressCase is a single test case for testing decompressors
|
||||
type TestDecompressCase struct {
|
||||
Input string // Input is the complete path to the input file
|
||||
Dir bool // Dir is whether or not we're testing directory mode
|
||||
Err bool // Err is whether we expect an error or not
|
||||
DirList []string // DirList is the list of files for Dir mode
|
||||
FileMD5 string // FileMD5 is the expected MD5 for a single file
|
||||
Mtime *time.Time // Mtime is the optionally expected mtime for a single file (or all files if in Dir mode)
|
||||
Input string // Input is the complete path to the input file
|
||||
Dir bool // Dir is whether or not we're testing directory mode
|
||||
Err bool // Err is whether we expect an error or not
|
||||
DirList []string // DirList is the list of files for Dir mode
|
||||
FileMD5 string // FileMD5 is the expected MD5 for a single file
|
||||
Mtime *time.Time // Mtime is the optionally expected mtime for a single file (or all files if in Dir mode)
|
||||
}
|
||||
|
||||
// TestDecompressor is a helper function for testing generic decompressors.
|
||||
func TestDecompressor(t testing.T, d Decompressor, cases []TestDecompressCase) {
|
||||
t.Helper()
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Logf("Testing: %s", tc.Input)
|
||||
|
||||
|
@ -72,9 +74,13 @@ func TestDecompressor(t testing.T, d Decompressor, cases []TestDecompressCase) {
|
|||
|
||||
if tc.Mtime != nil {
|
||||
actual := fi.ModTime()
|
||||
expected := *tc.Mtime
|
||||
if actual != expected {
|
||||
t.Fatalf("err %s: expected mtime '%s' for %s, got '%s'", tc.Input, expected.String(), dst, actual.String())
|
||||
if tc.Mtime.Unix() > 0 {
|
||||
expected := *tc.Mtime
|
||||
if actual != expected {
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -103,10 +109,15 @@ func TestDecompressor(t testing.T, d Decompressor, cases []TestDecompressCase) {
|
|||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
actual := fi.ModTime()
|
||||
expected := *tc.Mtime
|
||||
if actual != expected {
|
||||
t.Fatalf("err %s: expected mtime '%s' for %s, got '%s'", tc.Input, expected.String(), path, actual.String())
|
||||
if tc.Mtime.Unix() > 0 {
|
||||
expected := *tc.Mtime
|
||||
if actual != expected {
|
||||
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() {
|
||||
Detectors = []Detector{
|
||||
new(GitHubDetector),
|
||||
new(GitDetector),
|
||||
new(BitBucketDetector),
|
||||
new(S3Detector),
|
||||
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/") {
|
||||
return d.detectHTTP(src)
|
||||
} else if strings.HasPrefix(src, "git@github.com:") {
|
||||
return d.detectSSH(src)
|
||||
}
|
||||
|
||||
return "", false, nil
|
||||
|
@ -47,27 +45,3 @@ func (d *GitHubDetector) detectHTTP(src string) (string, bool, error) {
|
|||
|
||||
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
|
||||
// allow clients to let the getters decide which mode to use.
|
||||
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
|
||||
|
@ -74,12 +79,12 @@ func init() {
|
|||
//
|
||||
// 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.
|
||||
func Get(dst, src string) error {
|
||||
func Get(dst, src string, opts ...ClientOption) error {
|
||||
return (&Client{
|
||||
Src: src,
|
||||
Dst: dst,
|
||||
Dir: true,
|
||||
Getters: Getters,
|
||||
Options: opts,
|
||||
}).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
|
||||
// into dst with the basename of the URL. If src is a directory or
|
||||
// archive, it will be unpacked directly into dst.
|
||||
func GetAny(dst, src string) error {
|
||||
func GetAny(dst, src string, opts ...ClientOption) error {
|
||||
return (&Client{
|
||||
Src: src,
|
||||
Dst: dst,
|
||||
Mode: ClientModeAny,
|
||||
Getters: Getters,
|
||||
Options: opts,
|
||||
}).Get()
|
||||
}
|
||||
|
||||
// GetFile downloads the file specified by src into the path specified by
|
||||
// dst.
|
||||
func GetFile(dst, src string) error {
|
||||
func GetFile(dst, src string, opts ...ClientOption) error {
|
||||
return (&Client{
|
||||
Src: src,
|
||||
Dst: dst,
|
||||
Dir: false,
|
||||
Getters: Getters,
|
||||
Options: opts,
|
||||
}).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
|
||||
// a file scheme.
|
||||
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
|
||||
}
|
||||
|
||||
|
|
|
@ -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 (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
"os"
|
||||
"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 {
|
||||
ctx := g.Context()
|
||||
path := u.Path
|
||||
if u.RawPath != "" {
|
||||
path = u.RawPath
|
||||
|
@ -98,6 +98,6 @@ func (g *FileGetter) GetFile(dst string, u *url.URL) error {
|
|||
}
|
||||
defer dstF.Close()
|
||||
|
||||
_, err = io.Copy(dstF, srcF)
|
||||
_, err = Copy(ctx, dstF, srcF)
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -4,15 +4,16 @@ package getter
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func (g *FileGetter) Get(dst string, u *url.URL) error {
|
||||
ctx := g.Context()
|
||||
path := u.Path
|
||||
if u.RawPath != "" {
|
||||
path = u.RawPath
|
||||
|
@ -51,7 +52,7 @@ func (g *FileGetter) Get(dst string, u *url.URL) error {
|
|||
sourcePath := toBackslash(path)
|
||||
|
||||
// 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 {
|
||||
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 {
|
||||
ctx := g.Context()
|
||||
path := u.Path
|
||||
if 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 !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
|
||||
|
@ -108,7 +124,7 @@ func (g *FileGetter) GetFile(dst string, u *url.URL) error {
|
|||
}
|
||||
defer dstF.Close()
|
||||
|
||||
_, err = io.Copy(dstF, srcF)
|
||||
_, err = Copy(ctx, dstF, srcF)
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package getter
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
|
@ -8,28 +9,34 @@ import (
|
|||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
urlhelper "github.com/hashicorp/go-getter/helper/url"
|
||||
"github.com/hashicorp/go-safetemp"
|
||||
"github.com/hashicorp/go-version"
|
||||
safetemp "github.com/hashicorp/go-safetemp"
|
||||
version "github.com/hashicorp/go-version"
|
||||
)
|
||||
|
||||
// GitGetter is a Getter implementation that will download a module from
|
||||
// a git repository.
|
||||
type GitGetter struct{}
|
||||
type GitGetter struct {
|
||||
getter
|
||||
}
|
||||
|
||||
func (g *GitGetter) ClientMode(_ *url.URL) (ClientMode, error) {
|
||||
return ClientModeDir, nil
|
||||
}
|
||||
|
||||
func (g *GitGetter) Get(dst string, u *url.URL) error {
|
||||
ctx := g.Context()
|
||||
if _, err := exec.LookPath("git"); err != nil {
|
||||
return fmt.Errorf("git must be available and on the PATH")
|
||||
}
|
||||
|
||||
// Extract some query parameters we use
|
||||
var ref, sshKey string
|
||||
var depth int
|
||||
q := u.Query()
|
||||
if len(q) > 0 {
|
||||
ref = q.Get("ref")
|
||||
|
@ -38,6 +45,11 @@ func (g *GitGetter) Get(dst string, u *url.URL) error {
|
|||
sshKey = q.Get("sshkey")
|
||||
q.Del("sshkey")
|
||||
|
||||
if n, err := strconv.Atoi(q.Get("depth")); err == nil {
|
||||
depth = n
|
||||
}
|
||||
q.Del("depth")
|
||||
|
||||
// Copy the URL
|
||||
var newU url.URL = *u
|
||||
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
|
||||
_, err := os.Stat(dst)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
if err == nil {
|
||||
err = g.update(dst, sshKeyFile, ref)
|
||||
err = g.update(ctx, dst, sshKeyFile, ref, depth)
|
||||
} else {
|
||||
err = g.clone(dst, sshKeyFile, u)
|
||||
err = g.clone(ctx, dst, sshKeyFile, u, depth)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -100,7 +132,7 @@ func (g *GitGetter) Get(dst string, u *url.URL) error {
|
|||
}
|
||||
|
||||
// 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
|
||||
|
@ -138,16 +170,23 @@ func (g *GitGetter) checkout(dst string, ref string) error {
|
|||
return getRunCommand(cmd)
|
||||
}
|
||||
|
||||
func (g *GitGetter) clone(dst, sshKeyFile string, u *url.URL) error {
|
||||
cmd := exec.Command("git", "clone", u.String(), dst)
|
||||
func (g *GitGetter) clone(ctx context.Context, dst, sshKeyFile string, u *url.URL, depth int) error {
|
||||
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)
|
||||
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
|
||||
// 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
|
||||
|
||||
if getRunCommand(cmd) != nil {
|
||||
|
@ -162,15 +201,24 @@ func (g *GitGetter) update(dst, sshKeyFile, ref string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
cmd = exec.Command("git", "pull", "--ff-only")
|
||||
if depth > 0 {
|
||||
cmd = exec.Command("git", "pull", "--depth", strconv.Itoa(depth), "--ff-only")
|
||||
} else {
|
||||
cmd = exec.Command("git", "pull", "--ff-only")
|
||||
}
|
||||
|
||||
cmd.Dir = dst
|
||||
setupGitEnv(cmd, sshKeyFile)
|
||||
return getRunCommand(cmd)
|
||||
}
|
||||
|
||||
// fetchSubmodules downloads any configured submodules recursively.
|
||||
func (g *GitGetter) fetchSubmodules(dst, sshKeyFile string) error {
|
||||
cmd := exec.Command("git", "submodule", "update", "--init", "--recursive")
|
||||
func (g *GitGetter) fetchSubmodules(ctx context.Context, dst, sshKeyFile string, depth int) error {
|
||||
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
|
||||
setupGitEnv(cmd, sshKeyFile)
|
||||
return getRunCommand(cmd)
|
||||
|
@ -187,7 +235,7 @@ func setupGitEnv(cmd *exec.Cmd, sshKeyFile string) {
|
|||
// with versions of Go < 1.9.
|
||||
env := os.Environ()
|
||||
for i, v := range env {
|
||||
if strings.HasPrefix(v, gitSSHCommand) {
|
||||
if strings.HasPrefix(v, gitSSHCommand) && len(v) > len(gitSSHCommand) {
|
||||
sshCmd = []string{v}
|
||||
|
||||
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 != "" {
|
||||
// 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)
|
||||
}
|
||||
|
||||
|
@ -224,11 +275,20 @@ func checkGitVersion(min string) error {
|
|||
}
|
||||
|
||||
fields := strings.Fields(string(out))
|
||||
if len(fields) != 3 {
|
||||
if len(fields) < 3 {
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package getter
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
|
@ -9,18 +10,21 @@ import (
|
|||
"runtime"
|
||||
|
||||
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
|
||||
// a Mercurial repository.
|
||||
type HgGetter struct{}
|
||||
type HgGetter struct {
|
||||
getter
|
||||
}
|
||||
|
||||
func (g *HgGetter) ClientMode(_ *url.URL) (ClientMode, error) {
|
||||
return ClientModeDir, nil
|
||||
}
|
||||
|
||||
func (g *HgGetter) Get(dst string, u *url.URL) error {
|
||||
ctx := g.Context()
|
||||
if _, err := exec.LookPath("hg"); err != nil {
|
||||
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 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
|
||||
|
@ -93,7 +97,7 @@ func (g *HgGetter) GetFile(dst string, u *url.URL) error {
|
|||
return err
|
||||
}
|
||||
|
||||
fg := &FileGetter{Copy: true}
|
||||
fg := &FileGetter{Copy: true, getter: g.getter}
|
||||
return fg.GetFile(dst, u)
|
||||
}
|
||||
|
||||
|
@ -108,13 +112,13 @@ func (g *HgGetter) pull(dst string, u *url.URL) error {
|
|||
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"}
|
||||
if rev != "" {
|
||||
args = append(args, rev)
|
||||
}
|
||||
|
||||
cmd := exec.Command("hg", args...)
|
||||
cmd := exec.CommandContext(ctx, "hg", args...)
|
||||
cmd.Dir = dst
|
||||
return getRunCommand(cmd)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package getter
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io"
|
||||
|
@ -8,9 +9,10 @@ import (
|
|||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/go-safetemp"
|
||||
safetemp "github.com/hashicorp/go-safetemp"
|
||||
)
|
||||
|
||||
// HttpGetter is a Getter implementation that will download from an HTTP
|
||||
|
@ -18,7 +20,7 @@ import (
|
|||
//
|
||||
// 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
|
||||
// "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
|
||||
// paths are not allowed.
|
||||
type HttpGetter struct {
|
||||
getter
|
||||
|
||||
// Netrc, if true, will lookup and use auth information found
|
||||
// in the user's netrc file if available.
|
||||
Netrc bool
|
||||
|
@ -41,6 +45,12 @@ type HttpGetter struct {
|
|||
// Client is the http.Client to use for Get requests.
|
||||
// This defaults to a cleanhttp.DefaultClient if left unset.
|
||||
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) {
|
||||
|
@ -51,6 +61,7 @@ func (g *HttpGetter) ClientMode(u *url.URL) (ClientMode, error) {
|
|||
}
|
||||
|
||||
func (g *HttpGetter) Get(dst string, u *url.URL) error {
|
||||
ctx := g.Context()
|
||||
// Copy the URL so we can modify it
|
||||
var newU url.URL = *u
|
||||
u = &newU
|
||||
|
@ -72,10 +83,17 @@ func (g *HttpGetter) Get(dst string, u *url.URL) error {
|
|||
u.RawQuery = q.Encode()
|
||||
|
||||
// Get the URL
|
||||
resp, err := g.Client.Get(u.String())
|
||||
req, err := http.NewRequest("GET", u.String(), nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req.Header = g.Header
|
||||
resp, err := g.Client.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||
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.
|
||||
source, subDir := SourceDirSubdir(source)
|
||||
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
|
||||
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 {
|
||||
// Add auth from netrc if we can
|
||||
if err := addAuthFromNetrc(u); err != nil {
|
||||
if err := addAuthFromNetrc(src); err != nil {
|
||||
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 {
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != 200 {
|
||||
if g.Header != nil {
|
||||
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)
|
||||
}
|
||||
|
||||
// Create all the parent directories
|
||||
if err := os.MkdirAll(filepath.Dir(dst), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
body := resp.Body
|
||||
|
||||
f, err := os.Create(dst)
|
||||
if err != nil {
|
||||
return err
|
||||
if g.client != nil && g.client.ProgressListener != nil {
|
||||
// track download
|
||||
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 {
|
||||
err = io.ErrShortWrite
|
||||
}
|
||||
if err1 := f.Close(); err == nil {
|
||||
err = err1
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// getSubdir downloads the source into the destination, but with
|
||||
// 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
|
||||
// a non-existent directory.
|
||||
td, tdcloser, err := safetemp.Dir("", "getter")
|
||||
|
@ -158,8 +226,12 @@ func (g *HttpGetter) getSubdir(dst, source, subDir string) error {
|
|||
}
|
||||
defer tdcloser.Close()
|
||||
|
||||
var opts []ClientOption
|
||||
if g.client != nil {
|
||||
opts = g.client.Options
|
||||
}
|
||||
// Download that into the given directory
|
||||
if err := Get(td, source); err != nil {
|
||||
if err := Get(td, source, opts...); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -185,7 +257,7 @@ func (g *HttpGetter) getSubdir(dst, source, subDir string) error {
|
|||
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
|
||||
|
|
|
@ -6,6 +6,8 @@ import (
|
|||
|
||||
// MockGetter is an implementation of Getter that can be used for tests.
|
||||
type MockGetter struct {
|
||||
getter
|
||||
|
||||
// Proxy, if set, will be called after recording the calls below.
|
||||
// If it isn't set, then the *Err values will be returned.
|
||||
Proxy Getter
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package getter
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
@ -18,7 +18,9 @@ import (
|
|||
|
||||
// S3Getter is a Getter implementation that will download a module from
|
||||
// a S3 bucket.
|
||||
type S3Getter struct{}
|
||||
type S3Getter struct {
|
||||
getter
|
||||
}
|
||||
|
||||
func (g *S3Getter) ClientMode(u *url.URL) (ClientMode, error) {
|
||||
// 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 {
|
||||
ctx := g.Context()
|
||||
|
||||
// Parse URL
|
||||
region, bucket, path, _, creds, err := g.parseUrl(u)
|
||||
if err != nil {
|
||||
|
@ -124,7 +128,7 @@ func (g *S3Getter) Get(dst string, u *url.URL) error {
|
|||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -134,6 +138,7 @@ func (g *S3Getter) Get(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)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -142,10 +147,10 @@ func (g *S3Getter) GetFile(dst string, u *url.URL) error {
|
|||
config := g.getAWSConfig(region, u, creds)
|
||||
sess := session.New(config)
|
||||
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{
|
||||
Bucket: aws.String(bucket),
|
||||
Key: aws.String(key),
|
||||
|
@ -170,7 +175,7 @@ func (g *S3Getter) getObject(client *s3.S3, dst, bucket, key, version string) er
|
|||
}
|
||||
defer f.Close()
|
||||
|
||||
_, err = io.Copy(f, resp.Body)
|
||||
_, err = Copy(ctx, f, resp.Body)
|
||||
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.
|
||||
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)
|
||||
if err != nil {
|
||||
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://") {
|
||||
// Assume we're dealing with a drive letter file path where the drive
|
||||
// letter has been parsed into the URL Host.
|
||||
|
|
|
@ -6,18 +6,31 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
// SourceDirSubdir takes a source and returns a tuple of the URL without
|
||||
// the subdir and the URL with the subdir.
|
||||
// SourceDirSubdir takes a source URL and returns a tuple of the URL without
|
||||
// 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) {
|
||||
// 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.
|
||||
var offset int
|
||||
if idx := strings.Index(src, "://"); idx > -1 {
|
||||
if idx := strings.Index(src[:stop], "://"); idx > -1 {
|
||||
offset = idx + 3
|
||||
}
|
||||
|
||||
// First see if we even have an explicit subdir
|
||||
idx := strings.Index(src[offset:], "//")
|
||||
idx := strings.Index(src[offset:stop], "//")
|
||||
if idx == -1 {
|
||||
return src, ""
|
||||
}
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
module github.com/hashicorp/go-rootcerts
|
||||
|
||||
require github.com/mitchellh/go-homedir v1.0.0
|
|
@ -0,0 +1,2 @@
|
|||
github.com/mitchellh/go-homedir v1.0.0 h1:vKb8ShqSby24Yrqr/yDYkuFz8d0WUjys40rvnGC8aR0=
|
||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
|
@ -10,14 +10,25 @@ import (
|
|||
)
|
||||
|
||||
// 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
|
||||
// of a version.
|
||||
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-9A-Za-z\-~]+(\.[0-9A-Za-z\-~]+)*))?` +
|
||||
`?`
|
||||
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-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.
|
||||
type Version struct {
|
||||
|
@ -30,12 +41,24 @@ type Version struct {
|
|||
|
||||
func init() {
|
||||
versionRegexp = regexp.MustCompile("^" + VersionRegexpRaw + "$")
|
||||
semverRegexp = regexp.MustCompile("^" + SemverRegexpRaw + "$")
|
||||
}
|
||||
|
||||
// NewVersion parses the given version and returns a new
|
||||
// Version.
|
||||
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 {
|
||||
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
|
||||
|
||||
[![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.
|
||||
|
||||
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 !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()")
|
||||
|
@ -15,10 +19,12 @@ func NewColorable(file *os.File) io.Writer {
|
|||
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,8 +1,10 @@
|
|||
// +build windows
|
||||
// +build !appengine
|
||||
|
||||
package colorable
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"os"
|
||||
|
@ -52,6 +54,11 @@ type consoleScreenBufferInfo struct {
|
|||
maximumWindowSize coord
|
||||
}
|
||||
|
||||
type consoleCursorInfo struct {
|
||||
size dword
|
||||
visible int32
|
||||
}
|
||||
|
||||
var (
|
||||
kernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||
procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo")
|
||||
|
@ -59,15 +66,20 @@ var (
|
|||
procSetConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition")
|
||||
procFillConsoleOutputCharacter = kernel32.NewProc("FillConsoleOutputCharacterW")
|
||||
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 {
|
||||
out io.Writer
|
||||
handle syscall.Handle
|
||||
lastbuf bytes.Buffer
|
||||
oldattr word
|
||||
oldpos coord
|
||||
}
|
||||
|
||||
// NewColorable return new instance of Writer which handle escape sequence from File.
|
||||
func NewColorable(file *os.File) io.Writer {
|
||||
if file == nil {
|
||||
panic("nil passed instead of *os.File to NewColorable()")
|
||||
|
@ -77,16 +89,17 @@ func NewColorable(file *os.File) io.Writer {
|
|||
var csbi consoleScreenBufferInfo
|
||||
handle := syscall.Handle(file.Fd())
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
return &Writer{out: file, handle: handle, oldattr: csbi.attributes}
|
||||
} else {
|
||||
return file
|
||||
return &Writer{out: file, handle: handle, oldattr: csbi.attributes, oldpos: coord{0, 0}}
|
||||
}
|
||||
return file
|
||||
}
|
||||
|
||||
// NewColorableStdout return new instance of Writer which handle escape sequence for stdout.
|
||||
func NewColorableStdout() io.Writer {
|
||||
return NewColorable(os.Stdout)
|
||||
}
|
||||
|
||||
// NewColorableStderr return new instance of Writer which handle escape sequence for stderr.
|
||||
func NewColorableStderr() io.Writer {
|
||||
return NewColorable(os.Stderr)
|
||||
}
|
||||
|
@ -350,45 +363,83 @@ var color256 = map[int]int{
|
|||
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) {
|
||||
var csbi consoleScreenBufferInfo
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
|
||||
er := bytes.NewBuffer(data)
|
||||
er := bytes.NewReader(data)
|
||||
var bw [1]byte
|
||||
loop:
|
||||
for {
|
||||
r1, _, err := procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
if r1 == 0 {
|
||||
break loop
|
||||
}
|
||||
|
||||
c1, _, err := er.ReadRune()
|
||||
c1, err := er.ReadByte()
|
||||
if err != nil {
|
||||
break loop
|
||||
}
|
||||
if c1 != 0x1b {
|
||||
fmt.Fprint(w.out, string(c1))
|
||||
bw[0] = c1
|
||||
w.out.Write(bw[:])
|
||||
continue
|
||||
}
|
||||
c2, _, err := er.ReadRune()
|
||||
c2, err := er.ReadByte()
|
||||
if err != nil {
|
||||
w.lastbuf.WriteRune(c1)
|
||||
break loop
|
||||
}
|
||||
|
||||
if c2 == ']' {
|
||||
if err := doTitleSequence(er); err != nil {
|
||||
break loop
|
||||
}
|
||||
continue
|
||||
}
|
||||
if c2 != 0x5b {
|
||||
w.lastbuf.WriteRune(c1)
|
||||
w.lastbuf.WriteRune(c2)
|
||||
continue
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
var m rune
|
||||
var m byte
|
||||
for {
|
||||
c, _, err := er.ReadRune()
|
||||
c, err := er.ReadByte()
|
||||
if err != nil {
|
||||
w.lastbuf.WriteRune(c1)
|
||||
w.lastbuf.WriteRune(c2)
|
||||
w.lastbuf.Write(buf.Bytes())
|
||||
break loop
|
||||
}
|
||||
if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '@' {
|
||||
|
@ -398,7 +449,6 @@ loop:
|
|||
buf.Write([]byte(string(c)))
|
||||
}
|
||||
|
||||
var csbi consoleScreenBufferInfo
|
||||
switch m {
|
||||
case 'A':
|
||||
n, err = strconv.Atoi(buf.String())
|
||||
|
@ -422,19 +472,16 @@ loop:
|
|||
continue
|
||||
}
|
||||
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)))
|
||||
case 'D':
|
||||
n, err = strconv.Atoi(buf.String())
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if n, err = strconv.Atoi(buf.String()); err == nil {
|
||||
var csbi consoleScreenBufferInfo
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
csbi.cursorPosition.x += short(n)
|
||||
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
||||
}
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
csbi.cursorPosition.x -= short(n)
|
||||
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
||||
case 'E':
|
||||
n, err = strconv.Atoi(buf.String())
|
||||
if err != nil {
|
||||
|
@ -459,61 +506,85 @@ loop:
|
|||
continue
|
||||
}
|
||||
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)))
|
||||
case 'H':
|
||||
token := strings.Split(buf.String(), ";")
|
||||
if len(token) != 2 {
|
||||
continue
|
||||
case 'H', 'f':
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
if buf.Len() > 0 {
|
||||
token := strings.Split(buf.String(), ";")
|
||||
switch len(token) {
|
||||
case 1:
|
||||
n1, err := strconv.Atoi(token[0])
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
csbi.cursorPosition.y = short(n1 - 1)
|
||||
case 2:
|
||||
n1, err := strconv.Atoi(token[0])
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
n2, err := strconv.Atoi(token[1])
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
csbi.cursorPosition.x = short(n2 - 1)
|
||||
csbi.cursorPosition.y = short(n1 - 1)
|
||||
}
|
||||
} else {
|
||||
csbi.cursorPosition.y = 0
|
||||
}
|
||||
n1, err := strconv.Atoi(token[0])
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
n2, err := strconv.Atoi(token[1])
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
csbi.cursorPosition.x = short(n2)
|
||||
csbi.cursorPosition.x = short(n1)
|
||||
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
||||
case 'J':
|
||||
n, err := strconv.Atoi(buf.String())
|
||||
if err != nil {
|
||||
continue
|
||||
n := 0
|
||||
if buf.Len() > 0 {
|
||||
n, err = strconv.Atoi(buf.String())
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
}
|
||||
var count, written dword
|
||||
var cursor coord
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
switch n {
|
||||
case 0:
|
||||
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:
|
||||
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:
|
||||
cursor = coord{x: csbi.window.left, y: csbi.window.top}
|
||||
count = dword(csbi.size.x - csbi.cursorPosition.x + (csbi.size.y-csbi.cursorPosition.y)*csbi.size.x)
|
||||
}
|
||||
var count, written dword
|
||||
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)))
|
||||
procFillConsoleOutputAttribute.Call(uintptr(w.handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
|
||||
case 'K':
|
||||
n, err := strconv.Atoi(buf.String())
|
||||
if err != nil {
|
||||
continue
|
||||
n := 0
|
||||
if buf.Len() > 0 {
|
||||
n, err = strconv.Atoi(buf.String())
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
}
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
var cursor coord
|
||||
var count, written dword
|
||||
switch n {
|
||||
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:
|
||||
cursor = coord{x: csbi.window.left, y: csbi.window.top + csbi.cursorPosition.y}
|
||||
count = dword(csbi.size.x - csbi.cursorPosition.x)
|
||||
case 2:
|
||||
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)))
|
||||
procFillConsoleOutputAttribute.Call(uintptr(w.handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
|
||||
case 'm':
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
attr := csbi.attributes
|
||||
cs := buf.String()
|
||||
if cs == "" {
|
||||
|
@ -521,7 +592,7 @@ loop:
|
|||
continue
|
||||
}
|
||||
token := strings.Split(cs, ";")
|
||||
for i := 0; i < len(token); i += 1 {
|
||||
for i := 0; i < len(token); i++ {
|
||||
ns := token[i]
|
||||
if n, err = strconv.Atoi(ns); err == nil {
|
||||
switch {
|
||||
|
@ -531,12 +602,12 @@ loop:
|
|||
attr |= foregroundIntensity
|
||||
case n == 7:
|
||||
attr = ((attr & foregroundMask) << 4) | ((attr & backgroundMask) >> 4)
|
||||
case 22 == n || n == 25 || n == 25:
|
||||
case n == 22 || n == 25:
|
||||
attr |= foregroundIntensity
|
||||
case n == 27:
|
||||
attr = ((attr & foregroundMask) << 4) | ((attr & backgroundMask) >> 4)
|
||||
case 30 <= n && n <= 37:
|
||||
attr = (attr & backgroundMask)
|
||||
attr &= backgroundMask
|
||||
if (n-30)&1 != 0 {
|
||||
attr |= foregroundRed
|
||||
}
|
||||
|
@ -563,7 +634,7 @@ loop:
|
|||
attr &= backgroundMask
|
||||
attr |= w.oldattr & foregroundMask
|
||||
case 40 <= n && n <= 47:
|
||||
attr = (attr & foregroundMask)
|
||||
attr &= foregroundMask
|
||||
if (n-40)&1 != 0 {
|
||||
attr |= backgroundRed
|
||||
}
|
||||
|
@ -617,9 +688,39 @@ loop:
|
|||
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 {
|
||||
|
@ -663,22 +764,22 @@ func (c consoleColor) backgroundAttr() (attr word) {
|
|||
}
|
||||
|
||||
var color16 = []consoleColor{
|
||||
consoleColor{0x000000, false, false, false, false},
|
||||
consoleColor{0x000080, false, false, true, false},
|
||||
consoleColor{0x008000, false, true, false, false},
|
||||
consoleColor{0x008080, false, true, true, false},
|
||||
consoleColor{0x800000, true, false, false, false},
|
||||
consoleColor{0x800080, true, false, true, false},
|
||||
consoleColor{0x808000, true, true, false, false},
|
||||
consoleColor{0xc0c0c0, true, true, true, false},
|
||||
consoleColor{0x808080, false, false, false, true},
|
||||
consoleColor{0x0000ff, false, false, true, true},
|
||||
consoleColor{0x00ff00, false, true, false, true},
|
||||
consoleColor{0x00ffff, false, true, true, true},
|
||||
consoleColor{0xff0000, true, false, false, true},
|
||||
consoleColor{0xff00ff, true, false, true, true},
|
||||
consoleColor{0xffff00, true, true, false, true},
|
||||
consoleColor{0xffffff, true, true, true, true},
|
||||
{0x000000, false, false, false, false},
|
||||
{0x000080, false, false, true, false},
|
||||
{0x008000, false, true, false, false},
|
||||
{0x008080, false, true, true, false},
|
||||
{0x800000, true, false, false, false},
|
||||
{0x800080, true, false, true, false},
|
||||
{0x808000, true, true, false, false},
|
||||
{0xc0c0c0, true, true, true, false},
|
||||
{0x808080, false, false, false, true},
|
||||
{0x0000ff, false, false, true, true},
|
||||
{0x00ff00, false, true, false, true},
|
||||
{0x00ffff, false, true, true, true},
|
||||
{0xff0000, true, false, false, true},
|
||||
{0xff00ff, true, false, true, true},
|
||||
{0xffff00, true, true, false, true},
|
||||
{0xffffff, true, true, true, true},
|
||||
}
|
||||
|
||||
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
|
||||
}
|
|
@ -3,8 +3,9 @@ sudo: false
|
|||
language: go
|
||||
|
||||
go:
|
||||
- 1.8
|
||||
- 1.9
|
||||
- "1.8"
|
||||
- "1.9"
|
||||
- "1.10"
|
||||
|
||||
branches:
|
||||
only:
|
||||
|
|
|
@ -87,7 +87,7 @@ type CLI struct {
|
|||
// should be set exactly to the binary name that is autocompleted.
|
||||
//
|
||||
// Autocompletion is supported via the github.com/posener/complete
|
||||
// library. This library supports both bash and zsh. To add support
|
||||
// library. This library supports bash, zsh and fish. To add support
|
||||
// for other shells, please see that library.
|
||||
//
|
||||
// AutocompleteInstall and AutocompleteUninstall are the global flag
|
||||
|
@ -419,6 +419,11 @@ func (c *CLI) initAutocomplete() {
|
|||
func (c *CLI) initAutocompleteSub(prefix string) complete.Command {
|
||||
var cmd complete.Command
|
||||
walkFn := func(k string, raw interface{}) bool {
|
||||
// Ignore the empty key which can be present for default commands.
|
||||
if k == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
// Keep track of the full key so that we can nest further if necessary
|
||||
fullKey := k
|
||||
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
module github.com/mitchellh/cli
|
||||
|
||||
require (
|
||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310
|
||||
github.com/bgentry/speakeasy v0.1.0
|
||||
github.com/fatih/color v1.7.0
|
||||
github.com/hashicorp/go-multierror v1.0.0 // indirect
|
||||
github.com/mattn/go-colorable v0.0.9 // indirect
|
||||
github.com/mattn/go-isatty v0.0.3
|
||||
github.com/posener/complete v1.1.1
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc // indirect
|
||||
)
|
|
@ -0,0 +1,22 @@
|
|||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310 h1:BUAU3CGlLvorLI26FmByPp2eC2qla6E1Tw+scpcg/to=
|
||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/hashicorp/errwrap v0.0.0-20180715044906-d6c0cd880357 h1:Rem2+U35z1QtPQc6r+WolF7yXiefXqDKyk+lN2pE164=
|
||||
github.com/hashicorp/errwrap v0.0.0-20180715044906-d6c0cd880357/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-multierror v0.0.0-20180717150148-3d5d8f294aa0 h1:j30noezaCfvNLcdMYSvHLv81DxYRSt1grlpseG67vhU=
|
||||
github.com/hashicorp/go-multierror v0.0.0-20180717150148-3d5d8f294aa0/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I=
|
||||
github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
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.3 h1:ns/ykhmWi7G9O+8a448SecJU3nSMBXJfqQkl0upE1jI=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/posener/complete v1.1.1 h1:ccV59UEOTzVDnDUEFdT95ZzHVZ+5+158q8+SJb2QV5w=
|
||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc h1:MeuS1UDyZyFH++6vVy44PuufTeFF0d0nfI6XB87YGSk=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
@ -1,7 +1,11 @@
|
|||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/fatih/color"
|
||||
)
|
||||
|
||||
const (
|
||||
noColor = -1
|
||||
)
|
||||
|
||||
// UiColor is a posix shell color code to use.
|
||||
|
@ -12,13 +16,13 @@ type UiColor struct {
|
|||
|
||||
// A list of colors that are useful. These are all non-bolded by default.
|
||||
var (
|
||||
UiColorNone UiColor = UiColor{-1, false}
|
||||
UiColorRed = UiColor{31, false}
|
||||
UiColorGreen = UiColor{32, false}
|
||||
UiColorYellow = UiColor{33, false}
|
||||
UiColorBlue = UiColor{34, false}
|
||||
UiColorMagenta = UiColor{35, false}
|
||||
UiColorCyan = UiColor{36, false}
|
||||
UiColorNone UiColor = UiColor{noColor, false}
|
||||
UiColorRed = UiColor{int(color.FgHiRed), false}
|
||||
UiColorGreen = UiColor{int(color.FgHiGreen), false}
|
||||
UiColorYellow = UiColor{int(color.FgHiYellow), false}
|
||||
UiColorBlue = UiColor{int(color.FgHiBlue), false}
|
||||
UiColorMagenta = UiColor{int(color.FgHiMagenta), false}
|
||||
UiColorCyan = UiColor{int(color.FgHiCyan), false}
|
||||
)
|
||||
|
||||
// ColoredUi is a Ui implementation that colors its output according
|
||||
|
@ -55,15 +59,15 @@ func (u *ColoredUi) Warn(message string) {
|
|||
u.Ui.Warn(u.colorize(message, u.WarnColor))
|
||||
}
|
||||
|
||||
func (u *ColoredUi) colorize(message string, color UiColor) string {
|
||||
if color.Code == -1 {
|
||||
func (u *ColoredUi) colorize(message string, uc UiColor) string {
|
||||
if uc.Code == noColor {
|
||||
return message
|
||||
}
|
||||
|
||||
attr := 0
|
||||
if color.Bold {
|
||||
attr = 1
|
||||
attr := []color.Attribute{color.Attribute(uc.Code)}
|
||||
if uc.Bold {
|
||||
attr = append(attr, color.Bold)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("\033[%d;%dm%s\033[0m", attr, color.Code, message)
|
||||
return color.New(attr...).SprintFunc()(message)
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
module github.com/mitchellh/colorstring
|
|
@ -0,0 +1 @@
|
|||
module github.com/mitchellh/go-linereader
|
|
@ -0,0 +1 @@
|
|||
module github.com/mitchellh/go-testing-interface
|
|
@ -0,0 +1,3 @@
|
|||
module github.com/mitchellh/panicwrap
|
||||
|
||||
require github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1
|
|
@ -0,0 +1,2 @@
|
|||
github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1 h1:PJPDf8OUfOK1bb/NeTKd4f1QXZItOX389VN3B6qC8ro=
|
||||
github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
|
|
@ -178,7 +178,7 @@ func Wrap(c *WrapConfig) (int, error) {
|
|||
c.IgnoreSignals = []os.Signal{os.Interrupt}
|
||||
}
|
||||
signal.Notify(sigCh, c.IgnoreSignals...)
|
||||
signal.Notify(sigCh, c.ForwardSignals...)
|
||||
signal.Notify(fwdSigCh, c.ForwardSignals...)
|
||||
go func() {
|
||||
defer signal.Stop(sigCh)
|
||||
defer signal.Stop(fwdSigCh)
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
module github.com/mitchellh/prefixedio
|
|
@ -1,2 +1,4 @@
|
|||
.idea
|
||||
coverage.txt
|
||||
gocomplete/gocomplete
|
||||
example/self/self
|
||||
|
|
|
@ -1,17 +1,16 @@
|
|||
language: go
|
||||
sudo: false
|
||||
go:
|
||||
- 1.11
|
||||
- 1.10.x
|
||||
- 1.9
|
||||
- 1.8
|
||||
|
||||
before_install:
|
||||
- go get -u -t ./...
|
||||
- go get -u gopkg.in/alecthomas/gometalinter.v1
|
||||
- gometalinter.v1 --install
|
||||
|
||||
script:
|
||||
- gometalinter.v1 --config metalinter.json ./...
|
||||
- ./test.sh
|
||||
- GO111MODULE=on ./test.sh
|
||||
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
|
|
|
@ -57,11 +57,20 @@ func newArgs(line string) Args {
|
|||
}
|
||||
}
|
||||
|
||||
// splitFields returns a list of fields from the given command line.
|
||||
// If the last character is space, it appends an empty field in the end
|
||||
// indicating that the field before it was completed.
|
||||
// If the last field is of the form "a=b", it splits it to two fields: "a", "b",
|
||||
// So it can be completed.
|
||||
func splitFields(line string) []string {
|
||||
parts := strings.Fields(line)
|
||||
|
||||
// Add empty field if the last field was completed.
|
||||
if len(line) > 0 && unicode.IsSpace(rune(line[len(line)-1])) {
|
||||
parts = append(parts, "")
|
||||
}
|
||||
|
||||
// Treat the last field if it is of the form "a=b"
|
||||
parts = splitLastEqual(parts)
|
||||
return parts
|
||||
}
|
||||
|
|
|
@ -103,7 +103,7 @@ func (f *CLI) AddFlags(flags *flag.FlagSet) {
|
|||
fmt.Sprintf("Uninstall completion for %s command", f.Name))
|
||||
}
|
||||
if flags.Lookup("y") == nil {
|
||||
flags.BoolVar(&f.yes, "y", false, "Don't prompt user for typing 'yes'")
|
||||
flags.BoolVar(&f.yes, "y", false, "Don't prompt user for typing 'yes' when installing completion")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
package install
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
// (un)install in fish
|
||||
|
||||
type fish struct {
|
||||
configDir string
|
||||
}
|
||||
|
||||
func (f fish) Install(cmd, bin string) error {
|
||||
completionFile := filepath.Join(f.configDir, "completions", fmt.Sprintf("%s.fish", cmd))
|
||||
completeCmd, err := f.cmd(cmd, bin)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := os.Stat(completionFile); err == nil {
|
||||
return fmt.Errorf("already installed at %s", completionFile)
|
||||
}
|
||||
|
||||
return createFile(completionFile, completeCmd)
|
||||
}
|
||||
|
||||
func (f fish) Uninstall(cmd, bin string) error {
|
||||
completionFile := filepath.Join(f.configDir, "completions", fmt.Sprintf("%s.fish", cmd))
|
||||
if _, err := os.Stat(completionFile); err != nil {
|
||||
return fmt.Errorf("does not installed in %s", f.configDir)
|
||||
}
|
||||
|
||||
return os.Remove(completionFile)
|
||||
}
|
||||
|
||||
func (f fish) cmd(cmd, bin string) (string, error) {
|
||||
var buf bytes.Buffer
|
||||
params := struct{ Cmd, Bin string }{cmd, bin}
|
||||
tmpl := template.Must(template.New("cmd").Parse(`
|
||||
function __complete_{{.Cmd}}
|
||||
set -lx COMP_LINE (string join ' ' (commandline -o))
|
||||
test (commandline -ct) = ""
|
||||
and set COMP_LINE "$COMP_LINE "
|
||||
{{.Bin}}
|
||||
end
|
||||
complete -c {{.Cmd}} -a "(__complete_{{.Cmd}})"
|
||||
`))
|
||||
err := tmpl.Execute(&buf, params)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return buf.String(), nil
|
||||
}
|
|
@ -51,7 +51,7 @@ func Uninstall(cmd string) error {
|
|||
for _, i := range is {
|
||||
errI := i.Uninstall(cmd, bin)
|
||||
if errI != nil {
|
||||
multierror.Append(err, errI)
|
||||
err = multierror.Append(err, errI)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -68,9 +68,36 @@ func installers() (i []installer) {
|
|||
if f := rcFile(".zshrc"); f != "" {
|
||||
i = append(i, zsh{f})
|
||||
}
|
||||
if d := fishConfigDir(); d != "" {
|
||||
i = append(i, fish{d})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func fishConfigDir() string {
|
||||
configDir := filepath.Join(getConfigHomePath(), "fish")
|
||||
if configDir == "" {
|
||||
return ""
|
||||
}
|
||||
if info, err := os.Stat(configDir); err != nil || !info.IsDir() {
|
||||
return ""
|
||||
}
|
||||
return configDir
|
||||
}
|
||||
|
||||
func getConfigHomePath() string {
|
||||
u, err := user.Current()
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
configHome := os.Getenv("XDG_CONFIG_HOME")
|
||||
if configHome == "" {
|
||||
return filepath.Join(u.HomeDir, ".config")
|
||||
}
|
||||
return configHome
|
||||
}
|
||||
|
||||
func getBinaryPath() (string, error) {
|
||||
bin, err := os.Executable()
|
||||
if err != nil {
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
func lineInFile(name string, lookFor string) bool {
|
||||
|
@ -36,6 +37,24 @@ func lineInFile(name string, lookFor string) bool {
|
|||
}
|
||||
}
|
||||
|
||||
func createFile(name string, content string) error {
|
||||
// make sure file directory exists
|
||||
if err := os.MkdirAll(filepath.Dir(name), 0775); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// create the file
|
||||
f, err := os.Create(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
// write file content
|
||||
_, err = f.WriteString(fmt.Sprintf("%s\n", content))
|
||||
return err
|
||||
}
|
||||
|
||||
func appendToFile(name string, content string) error {
|
||||
f, err := os.OpenFile(name, os.O_RDWR|os.O_APPEND, 0)
|
||||
if err != nil {
|
||||
|
@ -96,7 +115,10 @@ func removeContentToTempFile(name, content string) (string, error) {
|
|||
if str == content {
|
||||
continue
|
||||
}
|
||||
wf.WriteString(str + "\n")
|
||||
_, err = wf.WriteString(str + "\n")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
prefix = prefix[:0]
|
||||
}
|
||||
return wf.Name(), nil
|
||||
|
|
|
@ -10,14 +10,16 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/posener/complete/cmd"
|
||||
"github.com/posener/complete/match"
|
||||
)
|
||||
|
||||
const (
|
||||
envComplete = "COMP_LINE"
|
||||
envDebug = "COMP_DEBUG"
|
||||
envLine = "COMP_LINE"
|
||||
envPoint = "COMP_POINT"
|
||||
envDebug = "COMP_DEBUG"
|
||||
)
|
||||
|
||||
// Complete structs define completion for a command with CLI options
|
||||
|
@ -55,13 +57,18 @@ func (c *Complete) Run() bool {
|
|||
// For installation: it assumes that flags were added and parsed before
|
||||
// it was called.
|
||||
func (c *Complete) Complete() bool {
|
||||
line, ok := getLine()
|
||||
line, point, ok := getEnv()
|
||||
if !ok {
|
||||
// make sure flags parsed,
|
||||
// in case they were not added in the main program
|
||||
return c.CLI.Run()
|
||||
}
|
||||
Log("Completing line: %s", line)
|
||||
|
||||
if point >= 0 && point < len(line) {
|
||||
line = line[:point]
|
||||
}
|
||||
|
||||
Log("Completing phrase: %s", line)
|
||||
a := newArgs(line)
|
||||
Log("Completing last field: %s", a.Last)
|
||||
options := c.Command.Predict(a)
|
||||
|
@ -79,12 +86,19 @@ func (c *Complete) Complete() bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func getLine() (string, bool) {
|
||||
line := os.Getenv(envComplete)
|
||||
func getEnv() (line string, point int, ok bool) {
|
||||
line = os.Getenv(envLine)
|
||||
if line == "" {
|
||||
return "", false
|
||||
return
|
||||
}
|
||||
return line, true
|
||||
point, err := strconv.Atoi(os.Getenv(envPoint))
|
||||
if err != nil {
|
||||
// If failed parsing point for some reason, set it to point
|
||||
// on the end of the line.
|
||||
Log("Failed parsing point %s: %v", os.Getenv(envPoint), err)
|
||||
point = len(line)
|
||||
}
|
||||
return line, point, true
|
||||
}
|
||||
|
||||
func (c *Complete) output(options []string) {
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
module github.com/posener/complete
|
||||
|
||||
require github.com/hashicorp/go-multierror v1.0.0
|
|
@ -0,0 +1,4 @@
|
|||
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
|
@ -1,7 +1,6 @@
|
|||
package complete
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
|
@ -15,7 +14,7 @@ import (
|
|||
var Log = getLogger()
|
||||
|
||||
func getLogger() func(format string, args ...interface{}) {
|
||||
var logfile io.Writer = ioutil.Discard
|
||||
var logfile = ioutil.Discard
|
||||
if os.Getenv(envDebug) != "" {
|
||||
logfile = os.Stderr
|
||||
}
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
{
|
||||
"Vendor": true,
|
||||
"DisableAll": true,
|
||||
"Enable": [
|
||||
"gofmt",
|
||||
"goimports",
|
||||
"interfacer",
|
||||
"goconst",
|
||||
"misspell",
|
||||
"unconvert",
|
||||
"gosimple",
|
||||
"golint",
|
||||
"structcheck",
|
||||
"deadcode",
|
||||
"vet"
|
||||
],
|
||||
"Exclude": [
|
||||
"initTests is unused"
|
||||
],
|
||||
"Deadline": "2m"
|
||||
}
|
|
@ -1,12 +1,13 @@
|
|||
# complete
|
||||
|
||||
A tool for bash writing bash completion in go, and bash completion for the go command line.
|
||||
|
||||
[![Build Status](https://travis-ci.org/posener/complete.svg?branch=master)](https://travis-ci.org/posener/complete)
|
||||
[![codecov](https://codecov.io/gh/posener/complete/branch/master/graph/badge.svg)](https://codecov.io/gh/posener/complete)
|
||||
[![golangci](https://golangci.com/badges/github.com/posener/complete.svg)](https://golangci.com/r/github.com/posener/complete)
|
||||
[![GoDoc](https://godoc.org/github.com/posener/complete?status.svg)](http://godoc.org/github.com/posener/complete)
|
||||
[![Go Report Card](https://goreportcard.com/badge/github.com/posener/complete)](https://goreportcard.com/report/github.com/posener/complete)
|
||||
|
||||
A tool for bash writing bash completion in go.
|
||||
|
||||
Writing bash completion scripts is a hard work. This package provides an easy way
|
||||
to create bash completion scripts for any command, and also an easy way to install/uninstall
|
||||
the completion of the command.
|
||||
|
@ -42,6 +43,7 @@ Supported shells:
|
|||
|
||||
- [x] bash
|
||||
- [x] zsh
|
||||
- [x] fish
|
||||
|
||||
### Usage
|
||||
|
||||
|
|
|
@ -2,8 +2,8 @@ sudo: false
|
|||
language: go
|
||||
|
||||
go:
|
||||
- 1.7.5
|
||||
- 1.8
|
||||
- 1.9
|
||||
- "1.10"
|
||||
- tip
|
||||
|
||||
os:
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package afero
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
|
@ -9,6 +8,8 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
var _ Lstater = (*BasePathFs)(nil)
|
||||
|
||||
// The BasePathFs restricts all operations to a given path within an Fs.
|
||||
// The given file name to the operations on this Fs will be prepended with
|
||||
// the base path before calling the base Fs.
|
||||
|
@ -22,6 +23,16 @@ type BasePathFs struct {
|
|||
path string
|
||||
}
|
||||
|
||||
type BasePathFile struct {
|
||||
File
|
||||
path string
|
||||
}
|
||||
|
||||
func (f *BasePathFile) Name() string {
|
||||
sourcename := f.File.Name()
|
||||
return strings.TrimPrefix(sourcename, filepath.Clean(f.path))
|
||||
}
|
||||
|
||||
func NewBasePathFs(source Fs, path string) Fs {
|
||||
return &BasePathFs{source: source, path: path}
|
||||
}
|
||||
|
@ -30,7 +41,7 @@ func NewBasePathFs(source Fs, path string) Fs {
|
|||
// else the given file with the base path prepended
|
||||
func (b *BasePathFs) RealPath(name string) (path string, err error) {
|
||||
if err := validateBasePathName(name); err != nil {
|
||||
return "", err
|
||||
return name, err
|
||||
}
|
||||
|
||||
bpath := filepath.Clean(b.path)
|
||||
|
@ -52,7 +63,7 @@ func validateBasePathName(name string) error {
|
|||
// On Windows a common mistake would be to provide an absolute OS path
|
||||
// We could strip out the base part, but that would not be very portable.
|
||||
if filepath.IsAbs(name) {
|
||||
return &os.PathError{Op: "realPath", Path: name, Err: errors.New("got a real OS path instead of a virtual")}
|
||||
return os.ErrNotExist
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -111,14 +122,22 @@ func (b *BasePathFs) OpenFile(name string, flag int, mode os.FileMode) (f File,
|
|||
if name, err = b.RealPath(name); err != nil {
|
||||
return nil, &os.PathError{Op: "openfile", Path: name, Err: err}
|
||||
}
|
||||
return b.source.OpenFile(name, flag, mode)
|
||||
sourcef, err := b.source.OpenFile(name, flag, mode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &BasePathFile{sourcef, b.path}, nil
|
||||
}
|
||||
|
||||
func (b *BasePathFs) Open(name string) (f File, err error) {
|
||||
if name, err = b.RealPath(name); err != nil {
|
||||
return nil, &os.PathError{Op: "open", Path: name, Err: err}
|
||||
}
|
||||
return b.source.Open(name)
|
||||
sourcef, err := b.source.Open(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &BasePathFile{File: sourcef, path: b.path}, nil
|
||||
}
|
||||
|
||||
func (b *BasePathFs) Mkdir(name string, mode os.FileMode) (err error) {
|
||||
|
@ -139,7 +158,23 @@ func (b *BasePathFs) Create(name string) (f File, err error) {
|
|||
if name, err = b.RealPath(name); err != nil {
|
||||
return nil, &os.PathError{Op: "create", Path: name, Err: err}
|
||||
}
|
||||
return b.source.Create(name)
|
||||
sourcef, err := b.source.Create(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &BasePathFile{File: sourcef, path: b.path}, nil
|
||||
}
|
||||
|
||||
func (b *BasePathFs) LstatIfPossible(name string) (os.FileInfo, bool, error) {
|
||||
name, err := b.RealPath(name)
|
||||
if err != nil {
|
||||
return nil, false, &os.PathError{Op: "lstat", Path: name, Err: err}
|
||||
}
|
||||
if lstater, ok := b.source.(Lstater); ok {
|
||||
return lstater.LstatIfPossible(name)
|
||||
}
|
||||
fi, err := b.source.Stat(name)
|
||||
return fi, false, err
|
||||
}
|
||||
|
||||
// vim: ts=4 sw=4 noexpandtab nolist syn=go
|
||||
|
|
|
@ -205,7 +205,7 @@ func (u *CacheOnReadFs) OpenFile(name string, flag int, perm os.FileMode) (File,
|
|||
bfi.Close() // oops, what if O_TRUNC was set and file opening in the layer failed...?
|
||||
return nil, err
|
||||
}
|
||||
return &UnionFile{base: bfi, layer: lfi}, nil
|
||||
return &UnionFile{Base: bfi, Layer: lfi}, nil
|
||||
}
|
||||
return u.layer.OpenFile(name, flag, perm)
|
||||
}
|
||||
|
@ -251,7 +251,7 @@ func (u *CacheOnReadFs) Open(name string) (File, error) {
|
|||
if err != nil && bfile == nil {
|
||||
return nil, err
|
||||
}
|
||||
return &UnionFile{base: bfile, layer: lfile}, nil
|
||||
return &UnionFile{Base: bfile, Layer: lfile}, nil
|
||||
}
|
||||
|
||||
func (u *CacheOnReadFs) Mkdir(name string, perm os.FileMode) error {
|
||||
|
@ -286,5 +286,5 @@ func (u *CacheOnReadFs) Create(name string) (File, error) {
|
|||
bfh.Close()
|
||||
return nil, err
|
||||
}
|
||||
return &UnionFile{base: bfh, layer: lfh}, nil
|
||||
return &UnionFile{Base: bfh, Layer: lfh}, nil
|
||||
}
|
||||
|
|
|
@ -8,6 +8,8 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
var _ Lstater = (*CopyOnWriteFs)(nil)
|
||||
|
||||
// The CopyOnWriteFs is a union filesystem: a read only base file system with
|
||||
// a possibly writeable layer on top. Changes to the file system will only
|
||||
// be made in the overlay: Changing an existing file in the base layer which
|
||||
|
@ -76,18 +78,55 @@ func (u *CopyOnWriteFs) Chmod(name string, mode os.FileMode) error {
|
|||
func (u *CopyOnWriteFs) Stat(name string) (os.FileInfo, error) {
|
||||
fi, err := u.layer.Stat(name)
|
||||
if err != nil {
|
||||
origErr := err
|
||||
if e, ok := err.(*os.PathError); ok {
|
||||
err = e.Err
|
||||
}
|
||||
if err == os.ErrNotExist || err == syscall.ENOENT || err == syscall.ENOTDIR {
|
||||
isNotExist := u.isNotExist(err)
|
||||
if isNotExist {
|
||||
return u.base.Stat(name)
|
||||
}
|
||||
return nil, origErr
|
||||
return nil, err
|
||||
}
|
||||
return fi, nil
|
||||
}
|
||||
|
||||
func (u *CopyOnWriteFs) LstatIfPossible(name string) (os.FileInfo, bool, error) {
|
||||
llayer, ok1 := u.layer.(Lstater)
|
||||
lbase, ok2 := u.base.(Lstater)
|
||||
|
||||
if ok1 {
|
||||
fi, b, err := llayer.LstatIfPossible(name)
|
||||
if err == nil {
|
||||
return fi, b, nil
|
||||
}
|
||||
|
||||
if !u.isNotExist(err) {
|
||||
return nil, b, err
|
||||
}
|
||||
}
|
||||
|
||||
if ok2 {
|
||||
fi, b, err := lbase.LstatIfPossible(name)
|
||||
if err == nil {
|
||||
return fi, b, nil
|
||||
}
|
||||
if !u.isNotExist(err) {
|
||||
return nil, b, err
|
||||
}
|
||||
}
|
||||
|
||||
fi, err := u.Stat(name)
|
||||
|
||||
return fi, false, err
|
||||
}
|
||||
|
||||
func (u *CopyOnWriteFs) isNotExist(err error) bool {
|
||||
if e, ok := err.(*os.PathError); ok {
|
||||
err = e.Err
|
||||
}
|
||||
if err == os.ErrNotExist || err == syscall.ENOENT || err == syscall.ENOTDIR {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Renaming files present only in the base layer is not permitted
|
||||
func (u *CopyOnWriteFs) Rename(oldname, newname string) error {
|
||||
b, err := u.isBaseFile(oldname)
|
||||
|
@ -219,7 +258,7 @@ func (u *CopyOnWriteFs) Open(name string) (File, error) {
|
|||
return nil, fmt.Errorf("BaseErr: %v\nOverlayErr: %v", bErr, lErr)
|
||||
}
|
||||
|
||||
return &UnionFile{base: bfile, layer: lfile}, nil
|
||||
return &UnionFile{Base: bfile, Layer: lfile}, nil
|
||||
}
|
||||
|
||||
func (u *CopyOnWriteFs) Mkdir(name string, perm os.FileMode) error {
|
||||
|
@ -228,7 +267,7 @@ func (u *CopyOnWriteFs) Mkdir(name string, perm os.FileMode) error {
|
|||
return u.layer.MkdirAll(name, perm)
|
||||
}
|
||||
if dir {
|
||||
return syscall.EEXIST
|
||||
return ErrFileExists
|
||||
}
|
||||
return u.layer.MkdirAll(name, perm)
|
||||
}
|
||||
|
@ -243,7 +282,8 @@ func (u *CopyOnWriteFs) MkdirAll(name string, perm os.FileMode) error {
|
|||
return u.layer.MkdirAll(name, perm)
|
||||
}
|
||||
if dir {
|
||||
return syscall.EEXIST
|
||||
// This is in line with how os.MkdirAll behaves.
|
||||
return nil
|
||||
}
|
||||
return u.layer.MkdirAll(name, perm)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
module github.com/spf13/afero
|
||||
|
||||
require golang.org/x/text v0.3.0
|
|
@ -0,0 +1,2 @@
|
|||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
|
@ -0,0 +1,27 @@
|
|||
// Copyright © 2018 Steve Francia <spf@spf13.com>.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package afero
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
// Lstater is an optional interface in Afero. It is only implemented by the
|
||||
// filesystems saying so.
|
||||
// It will call Lstat if the filesystem iself is, or it delegates to, the os filesystem.
|
||||
// Else it will call Stat.
|
||||
// In addtion to the FileInfo, it will return a boolean telling whether Lstat was called or not.
|
||||
type Lstater interface {
|
||||
LstatIfPossible(name string) (os.FileInfo, bool, error)
|
||||
}
|
|
@ -33,8 +33,8 @@ import (
|
|||
// built-ins from that package.
|
||||
func Glob(fs Fs, pattern string) (matches []string, err error) {
|
||||
if !hasMeta(pattern) {
|
||||
// afero does not support Lstat directly.
|
||||
if _, err = lstatIfOs(fs, pattern); err != nil {
|
||||
// Lstat not supported by a ll filesystems.
|
||||
if _, err = lstatIfPossible(fs, pattern); err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
return []string{pattern}, nil
|
||||
|
|
|
@ -131,6 +131,9 @@ func (f *File) Sync() error {
|
|||
}
|
||||
|
||||
func (f *File) Readdir(count int) (res []os.FileInfo, err error) {
|
||||
if !f.fileData.dir {
|
||||
return nil, &os.PathError{Op: "readdir", Path: f.fileData.name, Err: errors.New("not a dir")}
|
||||
}
|
||||
var outLength int64
|
||||
|
||||
f.fileData.Lock()
|
||||
|
|
|
@ -19,6 +19,8 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
var _ Lstater = (*OsFs)(nil)
|
||||
|
||||
// OsFs is a Fs implementation that uses functions provided by the os package.
|
||||
//
|
||||
// For details in any method, check the documentation of the os package
|
||||
|
@ -92,3 +94,8 @@ func (OsFs) Chmod(name string, mode os.FileMode) error {
|
|||
func (OsFs) Chtimes(name string, atime time.Time, mtime time.Time) error {
|
||||
return os.Chtimes(name, atime, mtime)
|
||||
}
|
||||
|
||||
func (OsFs) LstatIfPossible(name string) (os.FileInfo, bool, error) {
|
||||
fi, err := os.Lstat(name)
|
||||
return fi, true, err
|
||||
}
|
||||
|
|
|
@ -60,7 +60,7 @@ func walk(fs Fs, path string, info os.FileInfo, walkFn filepath.WalkFunc) error
|
|||
|
||||
for _, name := range names {
|
||||
filename := filepath.Join(path, name)
|
||||
fileInfo, err := lstatIfOs(fs, filename)
|
||||
fileInfo, err := lstatIfPossible(fs, filename)
|
||||
if err != nil {
|
||||
if err := walkFn(filename, fileInfo, err); err != nil && err != filepath.SkipDir {
|
||||
return err
|
||||
|
@ -77,15 +77,13 @@ func walk(fs Fs, path string, info os.FileInfo, walkFn filepath.WalkFunc) error
|
|||
return nil
|
||||
}
|
||||
|
||||
// if the filesystem is OsFs use Lstat, else use fs.Stat
|
||||
func lstatIfOs(fs Fs, path string) (info os.FileInfo, err error) {
|
||||
_, ok := fs.(*OsFs)
|
||||
if ok {
|
||||
info, err = os.Lstat(path)
|
||||
} else {
|
||||
info, err = fs.Stat(path)
|
||||
// if the filesystem supports it, use Lstat, else use fs.Stat
|
||||
func lstatIfPossible(fs Fs, path string) (os.FileInfo, error) {
|
||||
if lfs, ok := fs.(Lstater); ok {
|
||||
fi, _, err := lfs.LstatIfPossible(path)
|
||||
return fi, err
|
||||
}
|
||||
return
|
||||
return fs.Stat(path)
|
||||
}
|
||||
|
||||
// Walk walks the file tree rooted at root, calling walkFn for each file or
|
||||
|
@ -100,7 +98,7 @@ func (a Afero) Walk(root string, walkFn filepath.WalkFunc) error {
|
|||
}
|
||||
|
||||
func Walk(fs Fs, root string, walkFn filepath.WalkFunc) error {
|
||||
info, err := lstatIfOs(fs, root)
|
||||
info, err := lstatIfPossible(fs, root)
|
||||
if err != nil {
|
||||
return walkFn(root, nil, err)
|
||||
}
|
||||
|
|
|
@ -6,6 +6,8 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
var _ Lstater = (*ReadOnlyFs)(nil)
|
||||
|
||||
type ReadOnlyFs struct {
|
||||
source Fs
|
||||
}
|
||||
|
@ -34,6 +36,14 @@ func (r *ReadOnlyFs) Stat(name string) (os.FileInfo, error) {
|
|||
return r.source.Stat(name)
|
||||
}
|
||||
|
||||
func (r *ReadOnlyFs) LstatIfPossible(name string) (os.FileInfo, bool, error) {
|
||||
if lsf, ok := r.source.(Lstater); ok {
|
||||
return lsf.LstatIfPossible(name)
|
||||
}
|
||||
fi, err := r.Stat(name)
|
||||
return fi, false, err
|
||||
}
|
||||
|
||||
func (r *ReadOnlyFs) Rename(o, n string) error {
|
||||
return syscall.EPERM
|
||||
}
|
||||
|
|
|
@ -21,32 +21,33 @@ import (
|
|||
// successful read in the overlay will move the cursor position in the base layer
|
||||
// by the number of bytes read.
|
||||
type UnionFile struct {
|
||||
base File
|
||||
layer File
|
||||
off int
|
||||
files []os.FileInfo
|
||||
Base File
|
||||
Layer File
|
||||
Merger DirsMerger
|
||||
off int
|
||||
files []os.FileInfo
|
||||
}
|
||||
|
||||
func (f *UnionFile) Close() error {
|
||||
// first close base, so we have a newer timestamp in the overlay. If we'd close
|
||||
// the overlay first, we'd get a cacheStale the next time we access this file
|
||||
// -> cache would be useless ;-)
|
||||
if f.base != nil {
|
||||
f.base.Close()
|
||||
if f.Base != nil {
|
||||
f.Base.Close()
|
||||
}
|
||||
if f.layer != nil {
|
||||
return f.layer.Close()
|
||||
if f.Layer != nil {
|
||||
return f.Layer.Close()
|
||||
}
|
||||
return BADFD
|
||||
}
|
||||
|
||||
func (f *UnionFile) Read(s []byte) (int, error) {
|
||||
if f.layer != nil {
|
||||
n, err := f.layer.Read(s)
|
||||
if (err == nil || err == io.EOF) && f.base != nil {
|
||||
if f.Layer != nil {
|
||||
n, err := f.Layer.Read(s)
|
||||
if (err == nil || err == io.EOF) && f.Base != nil {
|
||||
// advance the file position also in the base file, the next
|
||||
// call may be a write at this position (or a seek with SEEK_CUR)
|
||||
if _, seekErr := f.base.Seek(int64(n), os.SEEK_CUR); seekErr != nil {
|
||||
if _, seekErr := f.Base.Seek(int64(n), os.SEEK_CUR); seekErr != nil {
|
||||
// only overwrite err in case the seek fails: we need to
|
||||
// report an eventual io.EOF to the caller
|
||||
err = seekErr
|
||||
|
@ -54,109 +55,150 @@ func (f *UnionFile) Read(s []byte) (int, error) {
|
|||
}
|
||||
return n, err
|
||||
}
|
||||
if f.base != nil {
|
||||
return f.base.Read(s)
|
||||
if f.Base != nil {
|
||||
return f.Base.Read(s)
|
||||
}
|
||||
return 0, BADFD
|
||||
}
|
||||
|
||||
func (f *UnionFile) ReadAt(s []byte, o int64) (int, error) {
|
||||
if f.layer != nil {
|
||||
n, err := f.layer.ReadAt(s, o)
|
||||
if (err == nil || err == io.EOF) && f.base != nil {
|
||||
_, err = f.base.Seek(o+int64(n), os.SEEK_SET)
|
||||
if f.Layer != nil {
|
||||
n, err := f.Layer.ReadAt(s, o)
|
||||
if (err == nil || err == io.EOF) && f.Base != nil {
|
||||
_, err = f.Base.Seek(o+int64(n), os.SEEK_SET)
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
if f.base != nil {
|
||||
return f.base.ReadAt(s, o)
|
||||
if f.Base != nil {
|
||||
return f.Base.ReadAt(s, o)
|
||||
}
|
||||
return 0, BADFD
|
||||
}
|
||||
|
||||
func (f *UnionFile) Seek(o int64, w int) (pos int64, err error) {
|
||||
if f.layer != nil {
|
||||
pos, err = f.layer.Seek(o, w)
|
||||
if (err == nil || err == io.EOF) && f.base != nil {
|
||||
_, err = f.base.Seek(o, w)
|
||||
if f.Layer != nil {
|
||||
pos, err = f.Layer.Seek(o, w)
|
||||
if (err == nil || err == io.EOF) && f.Base != nil {
|
||||
_, err = f.Base.Seek(o, w)
|
||||
}
|
||||
return pos, err
|
||||
}
|
||||
if f.base != nil {
|
||||
return f.base.Seek(o, w)
|
||||
if f.Base != nil {
|
||||
return f.Base.Seek(o, w)
|
||||
}
|
||||
return 0, BADFD
|
||||
}
|
||||
|
||||
func (f *UnionFile) Write(s []byte) (n int, err error) {
|
||||
if f.layer != nil {
|
||||
n, err = f.layer.Write(s)
|
||||
if err == nil && f.base != nil { // hmm, do we have fixed size files where a write may hit the EOF mark?
|
||||
_, err = f.base.Write(s)
|
||||
if f.Layer != nil {
|
||||
n, err = f.Layer.Write(s)
|
||||
if err == nil && f.Base != nil { // hmm, do we have fixed size files where a write may hit the EOF mark?
|
||||
_, err = f.Base.Write(s)
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
if f.base != nil {
|
||||
return f.base.Write(s)
|
||||
if f.Base != nil {
|
||||
return f.Base.Write(s)
|
||||
}
|
||||
return 0, BADFD
|
||||
}
|
||||
|
||||
func (f *UnionFile) WriteAt(s []byte, o int64) (n int, err error) {
|
||||
if f.layer != nil {
|
||||
n, err = f.layer.WriteAt(s, o)
|
||||
if err == nil && f.base != nil {
|
||||
_, err = f.base.WriteAt(s, o)
|
||||
if f.Layer != nil {
|
||||
n, err = f.Layer.WriteAt(s, o)
|
||||
if err == nil && f.Base != nil {
|
||||
_, err = f.Base.WriteAt(s, o)
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
if f.base != nil {
|
||||
return f.base.WriteAt(s, o)
|
||||
if f.Base != nil {
|
||||
return f.Base.WriteAt(s, o)
|
||||
}
|
||||
return 0, BADFD
|
||||
}
|
||||
|
||||
func (f *UnionFile) Name() string {
|
||||
if f.layer != nil {
|
||||
return f.layer.Name()
|
||||
if f.Layer != nil {
|
||||
return f.Layer.Name()
|
||||
}
|
||||
return f.base.Name()
|
||||
return f.Base.Name()
|
||||
}
|
||||
|
||||
// DirsMerger is how UnionFile weaves two directories together.
|
||||
// It takes the FileInfo slices from the layer and the base and returns a
|
||||
// single view.
|
||||
type DirsMerger func(lofi, bofi []os.FileInfo) ([]os.FileInfo, error)
|
||||
|
||||
var defaultUnionMergeDirsFn = func(lofi, bofi []os.FileInfo) ([]os.FileInfo, error) {
|
||||
var files = make(map[string]os.FileInfo)
|
||||
|
||||
for _, fi := range lofi {
|
||||
files[fi.Name()] = fi
|
||||
}
|
||||
|
||||
for _, fi := range bofi {
|
||||
if _, exists := files[fi.Name()]; !exists {
|
||||
files[fi.Name()] = fi
|
||||
}
|
||||
}
|
||||
|
||||
rfi := make([]os.FileInfo, len(files))
|
||||
|
||||
i := 0
|
||||
for _, fi := range files {
|
||||
rfi[i] = fi
|
||||
i++
|
||||
}
|
||||
|
||||
return rfi, nil
|
||||
|
||||
}
|
||||
|
||||
// Readdir will weave the two directories together and
|
||||
// return a single view of the overlayed directories
|
||||
// At the end of the directory view, the error is io.EOF.
|
||||
func (f *UnionFile) Readdir(c int) (ofi []os.FileInfo, err error) {
|
||||
var merge DirsMerger = f.Merger
|
||||
if merge == nil {
|
||||
merge = defaultUnionMergeDirsFn
|
||||
}
|
||||
|
||||
if f.off == 0 {
|
||||
var files = make(map[string]os.FileInfo)
|
||||
var rfi []os.FileInfo
|
||||
if f.layer != nil {
|
||||
rfi, err = f.layer.Readdir(-1)
|
||||
var lfi []os.FileInfo
|
||||
if f.Layer != nil {
|
||||
lfi, err = f.Layer.Readdir(-1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, fi := range rfi {
|
||||
files[fi.Name()] = fi
|
||||
}
|
||||
}
|
||||
|
||||
if f.base != nil {
|
||||
rfi, err = f.base.Readdir(-1)
|
||||
var bfi []os.FileInfo
|
||||
if f.Base != nil {
|
||||
bfi, err = f.Base.Readdir(-1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, fi := range rfi {
|
||||
if _, exists := files[fi.Name()]; !exists {
|
||||
files[fi.Name()] = fi
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
for _, fi := range files {
|
||||
f.files = append(f.files, fi)
|
||||
merged, err := merge(lfi, bfi)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f.files = append(f.files, merged...)
|
||||
}
|
||||
|
||||
if f.off >= len(f.files) {
|
||||
return nil, io.EOF
|
||||
}
|
||||
|
||||
if c == -1 {
|
||||
return f.files[f.off:], nil
|
||||
}
|
||||
|
||||
if c > len(f.files) {
|
||||
c = len(f.files)
|
||||
}
|
||||
|
||||
defer func() { f.off += c }()
|
||||
return f.files[f.off:c], nil
|
||||
}
|
||||
|
@ -174,53 +216,53 @@ func (f *UnionFile) Readdirnames(c int) ([]string, error) {
|
|||
}
|
||||
|
||||
func (f *UnionFile) Stat() (os.FileInfo, error) {
|
||||
if f.layer != nil {
|
||||
return f.layer.Stat()
|
||||
if f.Layer != nil {
|
||||
return f.Layer.Stat()
|
||||
}
|
||||
if f.base != nil {
|
||||
return f.base.Stat()
|
||||
if f.Base != nil {
|
||||
return f.Base.Stat()
|
||||
}
|
||||
return nil, BADFD
|
||||
}
|
||||
|
||||
func (f *UnionFile) Sync() (err error) {
|
||||
if f.layer != nil {
|
||||
err = f.layer.Sync()
|
||||
if err == nil && f.base != nil {
|
||||
err = f.base.Sync()
|
||||
if f.Layer != nil {
|
||||
err = f.Layer.Sync()
|
||||
if err == nil && f.Base != nil {
|
||||
err = f.Base.Sync()
|
||||
}
|
||||
return err
|
||||
}
|
||||
if f.base != nil {
|
||||
return f.base.Sync()
|
||||
if f.Base != nil {
|
||||
return f.Base.Sync()
|
||||
}
|
||||
return BADFD
|
||||
}
|
||||
|
||||
func (f *UnionFile) Truncate(s int64) (err error) {
|
||||
if f.layer != nil {
|
||||
err = f.layer.Truncate(s)
|
||||
if err == nil && f.base != nil {
|
||||
err = f.base.Truncate(s)
|
||||
if f.Layer != nil {
|
||||
err = f.Layer.Truncate(s)
|
||||
if err == nil && f.Base != nil {
|
||||
err = f.Base.Truncate(s)
|
||||
}
|
||||
return err
|
||||
}
|
||||
if f.base != nil {
|
||||
return f.base.Truncate(s)
|
||||
if f.Base != nil {
|
||||
return f.Base.Truncate(s)
|
||||
}
|
||||
return BADFD
|
||||
}
|
||||
|
||||
func (f *UnionFile) WriteString(s string) (n int, err error) {
|
||||
if f.layer != nil {
|
||||
n, err = f.layer.WriteString(s)
|
||||
if err == nil && f.base != nil {
|
||||
_, err = f.base.WriteString(s)
|
||||
if f.Layer != nil {
|
||||
n, err = f.Layer.WriteString(s)
|
||||
if err == nil && f.Base != nil {
|
||||
_, err = f.Base.WriteString(s)
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
if f.base != nil {
|
||||
return f.base.WriteString(s)
|
||||
if f.Base != nil {
|
||||
return f.Base.WriteString(s)
|
||||
}
|
||||
return 0, BADFD
|
||||
}
|
||||
|
|
|
@ -12,46 +12,48 @@ 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
|
||||
parallel compression and decompression. Stay tuned!
|
||||
|
||||
# Using the API
|
||||
## Using the API
|
||||
|
||||
The following example program shows how to use the API.
|
||||
|
||||
package main
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/ulikunitz/xz"
|
||||
)
|
||||
"github.com/ulikunitz/xz"
|
||||
)
|
||||
|
||||
func main() {
|
||||
const text = "The quick brown fox jumps over the lazy dog.\n"
|
||||
var buf bytes.Buffer
|
||||
// compress text
|
||||
w, err := xz.NewWriter(&buf)
|
||||
if err != nil {
|
||||
log.Fatalf("xz.NewWriter error %s", err)
|
||||
}
|
||||
if _, err := io.WriteString(w, text); err != nil {
|
||||
log.Fatalf("WriteString error %s", err)
|
||||
}
|
||||
if err := w.Close(); err != nil {
|
||||
log.Fatalf("w.Close error %s", err)
|
||||
}
|
||||
// decompress buffer and write output to stdout
|
||||
r, err := xz.NewReader(&buf)
|
||||
if err != nil {
|
||||
log.Fatalf("NewReader error %s", err)
|
||||
}
|
||||
if _, err = io.Copy(os.Stdout, r); err != nil {
|
||||
log.Fatalf("io.Copy error %s", err)
|
||||
}
|
||||
func main() {
|
||||
const text = "The quick brown fox jumps over the lazy dog.\n"
|
||||
var buf bytes.Buffer
|
||||
// compress text
|
||||
w, err := xz.NewWriter(&buf)
|
||||
if err != nil {
|
||||
log.Fatalf("xz.NewWriter error %s", err)
|
||||
}
|
||||
if _, err := io.WriteString(w, text); err != nil {
|
||||
log.Fatalf("WriteString error %s", err)
|
||||
}
|
||||
if err := w.Close(); err != nil {
|
||||
log.Fatalf("w.Close error %s", err)
|
||||
}
|
||||
// decompress buffer and write output to stdout
|
||||
r, err := xz.NewReader(&buf)
|
||||
if err != nil {
|
||||
log.Fatalf("NewReader error %s", err)
|
||||
}
|
||||
if _, err = io.Copy(os.Stdout, r); err != nil {
|
||||
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
|
||||
decompression.
|
||||
|
|
|
@ -86,6 +86,10 @@
|
|||
|
||||
## Log
|
||||
|
||||
### 2018-10-28
|
||||
|
||||
Release v0.5.5 fixes issues #19 observing ErrLimit outputs.
|
||||
|
||||
### 2017-06-05
|
||||
|
||||
Release v0.5.4 fixes issues #15 of another problem with the padding size
|
||||
|
@ -102,7 +106,7 @@ Release v0.5.2 became necessary to allow the decoding of xz files with
|
|||
4-byte padding in the block header. Many thanks to Greg, who reported
|
||||
the issue.
|
||||
|
||||
### 2016-07-23
|
||||
### 2016-07-23
|
||||
|
||||
Release v0.5.1 became necessary to fix problems with 32-bit platforms.
|
||||
Many thanks to Bruno Brigas, who reported the issue.
|
||||
|
@ -194,7 +198,7 @@ and lzma.Writer and fixed the error handling.
|
|||
By computing the bit length of the LZMA operations I was able to
|
||||
improve the greedy algorithm implementation. By using an 8 MByte buffer
|
||||
the compression rate was not as good as for xz but already better then
|
||||
gzip default.
|
||||
gzip default.
|
||||
|
||||
Compression is currently slow, but this is something we will be able to
|
||||
improve over time.
|
||||
|
@ -213,7 +217,7 @@ The package lzb contains now the basic implementation for creating or
|
|||
reading LZMA byte streams. It allows the support for the implementation
|
||||
of the DAG-shortest-path algorithm for the compression function.
|
||||
|
||||
### 2015-04-23
|
||||
### 2015-04-23
|
||||
|
||||
Completed yesterday the lzbase classes. I'm a little bit concerned that
|
||||
using the components may require too much code, but on the other hand
|
||||
|
@ -242,7 +246,7 @@ right lzbase.Reader and lzbase.Writer.
|
|||
|
||||
As a start I have implemented ReaderState and WriterState to ensure
|
||||
that the state for reading is only used by readers and WriterState only
|
||||
used by Writers.
|
||||
used by Writers.
|
||||
|
||||
### 2015-04-20
|
||||
|
||||
|
@ -274,7 +278,7 @@ almost all files from lzma.
|
|||
|
||||
### 2015-03-31
|
||||
|
||||
Removed only a TODO item.
|
||||
Removed only a TODO item.
|
||||
|
||||
However in Francesco Campoy's presentation "Go for Javaneros
|
||||
(Javaïstes?)" is the the idea that using an embedded field E, all the
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
|
||||
// opLenMargin provides the upper limit of the number of bytes required
|
||||
// to encode a single operation.
|
||||
const opLenMargin = 10
|
||||
const opLenMargin = 16
|
||||
|
||||
// compressFlags control the compression process.
|
||||
type compressFlags uint32
|
||||
|
|
|
@ -53,11 +53,11 @@ github.com/antchfx/xpath
|
|||
github.com/antchfx/xquery/xml
|
||||
# github.com/apparentlymart/go-cidr v1.0.0
|
||||
github.com/apparentlymart/go-cidr/cidr
|
||||
# github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3
|
||||
# github.com/apparentlymart/go-dump v0.0.0-20190214190832-042adf3cf4a0
|
||||
github.com/apparentlymart/go-dump/dump
|
||||
# github.com/apparentlymart/go-textseg v1.0.0
|
||||
github.com/apparentlymart/go-textseg/textseg
|
||||
# github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e
|
||||
# github.com/armon/circbuf v0.0.0-20190214190532-5111143e8da2
|
||||
github.com/armon/circbuf
|
||||
# github.com/armon/go-radix v1.0.0
|
||||
github.com/armon/go-radix
|
||||
|
@ -136,6 +136,8 @@ github.com/dimchansky/utfbom
|
|||
github.com/dylanmei/iso8601
|
||||
# github.com/dylanmei/winrmtest v0.0.0-20170819153634-c2fbb09e6c08
|
||||
github.com/dylanmei/winrmtest
|
||||
# github.com/fatih/color v1.7.0
|
||||
github.com/fatih/color
|
||||
# github.com/go-test/deep v1.0.1
|
||||
github.com/go-test/deep
|
||||
# github.com/gogo/protobuf v1.2.0
|
||||
|
@ -266,11 +268,11 @@ github.com/hashicorp/errwrap
|
|||
# github.com/hashicorp/go-azure-helpers v0.0.0-20190129193224-166dfd221bb2
|
||||
github.com/hashicorp/go-azure-helpers/authentication
|
||||
github.com/hashicorp/go-azure-helpers/storage
|
||||
# github.com/hashicorp/go-checkpoint v0.0.0-20171009173528-1545e56e46de
|
||||
# github.com/hashicorp/go-checkpoint v0.5.0
|
||||
github.com/hashicorp/go-checkpoint
|
||||
# github.com/hashicorp/go-cleanhttp v0.5.0
|
||||
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/helper/url
|
||||
# github.com/hashicorp/go-hclog v0.0.0-20181001195459-61d530d6c27f
|
||||
|
@ -282,7 +284,7 @@ github.com/hashicorp/go-plugin
|
|||
github.com/hashicorp/go-plugin/internal/plugin
|
||||
# github.com/hashicorp/go-retryablehttp v0.5.1
|
||||
github.com/hashicorp/go-retryablehttp
|
||||
# github.com/hashicorp/go-rootcerts v0.0.0-20160503143440-6bb64b370b90
|
||||
# github.com/hashicorp/go-rootcerts v1.0.0
|
||||
github.com/hashicorp/go-rootcerts
|
||||
# github.com/hashicorp/go-safetemp v1.0.0
|
||||
github.com/hashicorp/go-safetemp
|
||||
|
@ -292,7 +294,7 @@ github.com/hashicorp/go-slug
|
|||
github.com/hashicorp/go-tfe
|
||||
# github.com/hashicorp/go-uuid v1.0.0
|
||||
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/hcl v0.0.0-20170504190234-a4b07c25de5f
|
||||
github.com/hashicorp/hcl
|
||||
|
@ -366,23 +368,23 @@ github.com/masterzen/simplexml/dom
|
|||
# github.com/masterzen/winrm v0.0.0-20180224160350-7e40f93ae939
|
||||
github.com/masterzen/winrm
|
||||
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-isatty v0.0.4
|
||||
github.com/mattn/go-isatty
|
||||
# github.com/mattn/go-shellwords v1.0.1
|
||||
github.com/mattn/go-shellwords
|
||||
# github.com/mitchellh/cli v0.0.0-20171129193617-33edc47170b5
|
||||
# github.com/mitchellh/cli v1.0.0
|
||||
github.com/mitchellh/cli
|
||||
# github.com/mitchellh/colorstring v0.0.0-20150917214807-8631ce90f286
|
||||
# github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db
|
||||
github.com/mitchellh/colorstring
|
||||
# github.com/mitchellh/copystructure v1.0.0
|
||||
github.com/mitchellh/copystructure
|
||||
# github.com/mitchellh/go-homedir v1.0.0
|
||||
github.com/mitchellh/go-homedir
|
||||
# github.com/mitchellh/go-linereader v0.0.0-20141013185533-07bab5fdd958
|
||||
# github.com/mitchellh/go-linereader v0.0.0-20190213213312-1b945b3263eb
|
||||
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-wordwrap v1.0.0
|
||||
github.com/mitchellh/go-wordwrap
|
||||
|
@ -390,9 +392,9 @@ github.com/mitchellh/go-wordwrap
|
|||
github.com/mitchellh/hashstructure
|
||||
# github.com/mitchellh/mapstructure v1.1.2
|
||||
github.com/mitchellh/mapstructure
|
||||
# github.com/mitchellh/panicwrap v0.0.0-20161208170302-ba9e1a65e0f7
|
||||
# github.com/mitchellh/panicwrap v0.0.0-20190213213626-17011010aaa4
|
||||
github.com/mitchellh/panicwrap
|
||||
# github.com/mitchellh/prefixedio v0.0.0-20151214002211-6e6954073784
|
||||
# github.com/mitchellh/prefixedio v0.0.0-20190213213902-5733675afd51
|
||||
github.com/mitchellh/prefixedio
|
||||
# github.com/mitchellh/reflectwalk v1.0.0
|
||||
github.com/mitchellh/reflectwalk
|
||||
|
@ -404,14 +406,14 @@ github.com/oklog/run
|
|||
github.com/packer-community/winrmcp/winrmcp
|
||||
# github.com/pkg/errors v0.0.0-20170505043639-c605e284fe17
|
||||
github.com/pkg/errors
|
||||
# github.com/posener/complete v0.0.0-20171219111128-6bee943216c8
|
||||
# github.com/posener/complete v1.2.1
|
||||
github.com/posener/complete
|
||||
github.com/posener/complete/cmd/install
|
||||
github.com/posener/complete/cmd
|
||||
github.com/posener/complete/match
|
||||
# github.com/satori/go.uuid v0.0.0-20160927100844-b061729afc07
|
||||
github.com/satori/go.uuid
|
||||
# github.com/spf13/afero v1.0.2
|
||||
# github.com/spf13/afero v1.2.1
|
||||
github.com/spf13/afero
|
||||
github.com/spf13/afero/mem
|
||||
# github.com/svanharmelen/jsonapi v0.0.0-20180618144545-0c0828c3f16d
|
||||
|
@ -420,7 +422,7 @@ github.com/svanharmelen/jsonapi
|
|||
github.com/terraform-providers/terraform-provider-openstack/openstack
|
||||
# github.com/ugorji/go v0.0.0-20180813092308-00b869d2f4a5
|
||||
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/internal/xlog
|
||||
github.com/ulikunitz/xz/lzma
|
||||
|
|
Loading…
Reference in New Issue