From 111e8dbcf29d8121c941868b0798e979a3698666 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mislav=20Marohni=C4=87?= Date: Fri, 19 Mar 2021 21:19:04 +0100 Subject: [PATCH 1/2] Pass web browser to each individual command This removes sensitivity to the BROWSER environment variable in tests and makes it easier to verify the URL that the browser was invoked with without having to stub sub-processes. --- go.mod | 1 + go.sum | 140 +++++++++++++++++++++++++- internal/authflow/flow.go | 10 +- pkg/browser/browser.go | 80 --------------- pkg/browser/browser_test.go | 75 -------------- pkg/cmd/factory/default.go | 1 + pkg/cmd/gist/create/create.go | 8 +- pkg/cmd/gist/create/create_test.go | 11 +- pkg/cmd/gist/view/view.go | 8 +- pkg/cmd/issue/comment/comment.go | 3 +- pkg/cmd/issue/comment/comment_test.go | 1 + pkg/cmd/issue/create/create.go | 10 +- pkg/cmd/issue/create/create_test.go | 49 +++------ pkg/cmd/issue/list/list.go | 8 +- pkg/cmd/issue/list/list_test.go | 39 +++++-- pkg/cmd/issue/view/view.go | 8 +- pkg/cmd/issue/view/view_test.go | 99 +++++------------- pkg/cmd/pr/checks/checks.go | 8 +- pkg/cmd/pr/checks/checks_test.go | 39 +++---- pkg/cmd/pr/comment/comment.go | 3 +- pkg/cmd/pr/comment/comment_test.go | 1 + pkg/cmd/pr/create/create.go | 8 +- pkg/cmd/pr/create/create_test.go | 23 ++--- pkg/cmd/pr/list/list.go | 8 +- pkg/cmd/pr/list/list_test.go | 15 ++- pkg/cmd/pr/view/view.go | 8 +- pkg/cmd/pr/view/view_test.go | 138 ++----------------------- pkg/cmd/release/view/view.go | 8 +- pkg/cmd/repo/view/view.go | 8 +- pkg/cmd/repo/view/view_test.go | 10 +- pkg/cmdutil/factory.go | 8 +- pkg/cmdutil/web_browser.go | 83 +++++++++++++++ test/helpers.go | 4 +- utils/utils.go | 19 ---- 34 files changed, 446 insertions(+), 496 deletions(-) delete mode 100644 pkg/browser/browser.go delete mode 100644 pkg/browser/browser_test.go create mode 100644 pkg/cmdutil/web_browser.go diff --git a/go.mod b/go.mod index 85606124c..0739022ca 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/MakeNowJust/heredoc v1.0.0 github.com/briandowns/spinner v1.11.1 github.com/charmbracelet/glamour v0.2.1-0.20200724174618-1246d13c1684 + github.com/cli/browser v1.1.0 github.com/cli/oauth v0.8.0 github.com/cli/safeexec v1.0.0 github.com/cpuguy83/go-md2man/v2 v2.0.0 diff --git a/go.sum b/go.sum index b7808129f..6bc761e13 100644 --- a/go.sum +++ b/go.sum @@ -4,22 +4,31 @@ cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSR cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3 h1:AVXDdKsrtX33oR9fbCMu/+c1o8Ofjq6Ku/MInaLVg5Y= cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go/bigquery v1.0.1 h1:hL+ycaJpVE9M7nLoiXb/Pn10ENE2u+oddxbD8uu0ZVU= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/datastore v1.0.0 h1:Kt+gOPPp2LEPWp8CSfxhsM8ik9CcyE/gYu+0r+RnZvM= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/firestore v1.1.0 h1:9x7Bx0A9R5/M9jibeJeZWqjeVEIxYW9fZYqB9a70/bY= cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +cloud.google.com/go/pubsub v1.0.1 h1:W9tAK3E57P75u0XLLR82LZyw8VpAnhmyTOxW9qzmyj8= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/storage v1.0.0 h1:VV2nUM3wwLLGh9lSABFgZMjInyUbJeaRSE64WuAIQ+4= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9 h1:VpgP7xuJadIUuKccphEpTJnWhS2jkQyMt6Y7pJCD7fY= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/AlecAivazis/survey/v2 v2.2.7 h1:5NbxkF4RSKmpywYdcRgUmos1o+roJY8duCLZXbVjoig= github.com/AlecAivazis/survey/v2 v2.2.7/go.mod h1:9DYvHgXtiXm6nCn+jXnOXLKbH+Yo9u8fAS/SduGdoPk= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOCMdNH+CiXJVYJh5UQNCOBG7jbELc= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nBpB11F9br+3HUrpgb+fcm5iADzXXYEw= github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc= +github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38 h1:smF2tmSOzy2Mm+0dGI2AIUHY+w0BUc+4tn40djz7+6U= github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38/go.mod h1:r7bzyVFMNntcxPZXK3/+KdruV1H5KSlyVY0gc+NgInI= @@ -27,36 +36,53 @@ github.com/alecthomas/chroma v0.7.3 h1:NfdAERMy+esYQs8OXk0I868/qDxxCEo7FMz1WIqMA github.com/alecthomas/chroma v0.7.3/go.mod h1:sko8vR34/90zvl5QdcUdvzL3J8NKjAUx9va9jPuFNoM= github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721 h1:JHZL0hZKJ1VENNfmXvHbgYlbUOvpzYzvy2aZU5gXVeo= github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721/go.mod h1:QO9JBoKquHd+jz9nshCh40fOfO+JzsoXy8qTHF68zU0= +github.com/alecthomas/kong v0.2.4 h1:Y0ZBCHAvHhTHw7FFJ2FzCAAG4pkbTgA45nc7BpMhDNk= github.com/alecthomas/kong v0.2.4/go.mod h1:kQOmtJgV+Lb4aj+I2LEn40cbtawdWJ9Y8QLq+lElKxE= github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897 h1:p9Sln00KOTlrYkxI1zYWl1QLnEqAqEARBEYa8FQnQcY= github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +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/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 h1:BUAU3CGlLvorLI26FmByPp2eC2qla6E1Tw+scpcg/to= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c h1:+0HFd5KSZ/mm3JmhmrDukiId5iR6w4+BdFtfSy4yWIc= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/briandowns/spinner v1.11.1 h1:OixPqDEcX3juo5AjQZAnFPbeUA0jvkp2qzB5gOZJ/L0= github.com/briandowns/spinner v1.11.1/go.mod h1:QOuQk7x+EaDASo80FEXwlwiA+j/PPIcX3FScO+3/ZPQ= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/charmbracelet/glamour v0.2.1-0.20200724174618-1246d13c1684 h1:YMyvXRstOQc7n6eneHfudVMbARSCmZ7EZGjtTkkeB3A= github.com/charmbracelet/glamour v0.2.1-0.20200724174618-1246d13c1684/go.mod h1:UA27Kwj3QHialP74iU6C+Gpc8Y7IOAKupeKMLLBURWM= -github.com/cli/browser v1.0.0 h1:RIleZgXrhdiCVgFBSjtWwkLPUCWyhhhN5k5HGSBt1js= github.com/cli/browser v1.0.0/go.mod h1:IEWkHYbLjkhtjwwWlwTHW2lGxeS5gezEQBMLTwDHf5Q= +github.com/cli/browser v1.1.0 h1:xOZBfkfY9L9vMBgqb1YwRirGu6QFaQ5dP/vXt5ENSOY= +github.com/cli/browser v1.1.0/go.mod h1:HKMQAt9t12kov91Mn7RfZxyJQQgWgyS/3SZswlZ5iTI= github.com/cli/oauth v0.8.0 h1:YTFgPXSTvvDUFti3tR4o6q7Oll2SnQ9ztLwCAn4/IOA= github.com/cli/oauth v0.8.0/go.mod h1:qd/FX8ZBD6n1sVNQO3aIdRxeu5LGw9WhKnYhIIoC2A4= github.com/cli/safeexec v1.0.0 h1:0VngyaIyqACHdcMNWfo6+KdUYnqEr2Sg+bSP1pdF+dI= github.com/cli/safeexec v1.0.0/go.mod h1:Z/D4tTN8Vs5gXYHDCbaM1S/anmEDnJb1iW0+EJ5zx3Q= github.com/cli/shurcooL-graphql v0.0.0-20200707151639-0f7232a2bf7e h1:aq/1jlmtZoS6nlSp3yLOTZQ50G+dzHdeRNENgE/iBew= github.com/cli/shurcooL-graphql v0.0.0-20200707151639-0f7232a2bf7e/go.mod h1:it23pLwxmz6OyM6I5O0ATIXQS1S190Nas26L5Kahp4U= +github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/coreos/bbolt v1.3.2 h1:wZwiHHUieZCquLkDL0B8UhzreNWsPHooDAG3q34zk0s= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/etcd v3.3.13+incompatible h1:8F3hqu9fGYLBifCmRCJsicFqDx/D68Rt3q1JMazcgBQ= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= @@ -65,7 +91,9 @@ github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964/go.mod h1:Xd9 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954 h1:RMLoZVzv4GliuWafOuPuQDKSm1SJph7uCRnnS61JAn4= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dlclark/regexp2 v1.2.0 h1:8sAhBGEM0dRWogWqWyQeIJnxjWO6oIjl8FKqREDsGfk= github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= @@ -73,21 +101,31 @@ github.com/enescakir/emoji v1.0.0 h1:W+HsNql8swfCQFtioDGDHCHri8nudlK1n5p2rHCJoog github.com/enescakir/emoji v1.0.0/go.mod h1:Bt1EKuLnKDTYpLALApstIkAjdDrS/8IAgTkKp+WKFD0= github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/gabriel-vasile/mimetype v1.1.2 h1:gaPnPcNor5aZSVCJVSGipcpbgMWiAAj9z182ocSGbHU= github.com/gabriel-vasile/mimetype v1.1.2/go.mod h1:6CDPel/o/3/s4+bp6kIbsWATq8pmgOisOPG40CJa6To= +github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1 h1:QbL/5oDUmRBzO9/Z7Seo6zf912W/a6Sr4Eu0G/3Jho0= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-kit/kit v0.8.0 h1:Wz+5lgoB0kkuqLEc6NVmwRknTKP6dTGbSqvhZtBI/j0= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef h1:veQD95Isof8w9/WXiA+pa3tz3fJXkt5B7QaRBrM62gk= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1 h1:qGJ6qTW+x6xX/my+8YUVl4WNpX9B7+/l2tRsHGZ7f2s= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= @@ -95,6 +133,7 @@ github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -103,41 +142,69 @@ github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/goterm v0.0.0-20190703233501-fc88cf888a3f h1:5CjVwnuUcp5adK4gmY6i72gpVFVnZDP2h5TmPScB6u4= github.com/google/goterm v0.0.0-20190703233501-fc88cf888a3f/go.mod h1:nOFQdrUlIlx6M6ODdSpBj1NVA+VgLC6kmw60mkw34H4= +github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f h1:Jnx61latede7zDD3DiiP4gmNz33uK0U5HDUaF0a/HVQ= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/graph-gophers/graphql-go v0.0.0-20200622220639-c1d9693c95a6 h1:s0NiTDKy3CsD/GX4MoCaEgDFTxVV4dqlOHn/5pSrNIk= github.com/graph-gophers/graphql-go v0.0.0-20200622220639-c1d9693c95a6/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0 h1:Iju5GlWwrvL6UBg4zJJt3btmonfrMlCDdsejg4CZE7c= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.0 h1:bM6ZAFZmc/wPFaRDi0d5L7hGEZEx/2u+Tmr2evNHDiI= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/hashicorp/consul/api v1.1.0 h1:BNQPM9ytxj6jbjjdRPioQ94T6YXriSopn0i8COv6SRA= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/sdk v0.1.1 h1:LnuDWGNsoajlhGyHJvuWW6FVqRl8JOTPqS6CPTsYjhY= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +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-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3 h1:zKjpN5BK/P5lMYrLmBHdBULWbJ0XpYR+7NGzqkZzoD4= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +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/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-sockaddr v1.0.0 h1:GeH6tui99pF4NJgfnhp+L6+FfobzVW3Ah46sLo0ICXs= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.2.1 h1:zEfKbn2+PDgroKdiOzqiE8rsmLqU2uwi5PB5pBJ3TkI= github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go.net v0.0.1 h1:sNCoNyDEvN1xa+X0baata4RdcpKwcMS6DH+xwfqPgjw= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0 h1:WhIgCr5a7AaVH6jPUwjtRuuE7/RDufnUvzIr48smyxs= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3 h1:EmmoJme1matNzb+hMpDuR/0sbJSUisxyqBGG676r31M= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2 h1:YZ7UKsJv+hKjqGVUUbtE3HNj79Eln2oQ75tniF6iPt0= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/henvic/httpretty v0.0.6 h1:JdzGzKZBajBfnvlMALXXMVQWxWMF/ofTy8C3/OSUTxs= github.com/henvic/httpretty v0.0.6/go.mod h1:X38wLjWXHkXT7r2+uK8LjCMne9rsuNaBLJ+5cU2/Pmo= @@ -147,21 +214,31 @@ github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NH github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/itchyny/astgen-go v0.0.0-20210113000433-0da0671862a3 h1:l7vogWrq+zj8v5t/G69/eT13nAGs2H7cq+CI2nlnKdk= github.com/itchyny/astgen-go v0.0.0-20210113000433-0da0671862a3/go.mod h1:296z3W7Xsrp2mlIY88ruDKscuvrkL6zXCNRtaYVshzw= +github.com/itchyny/go-flags v1.5.0 h1:Z5q2ist2sfDjDlExVPBrMqlsEDxDR2h4zuOElB0OEYI= github.com/itchyny/go-flags v1.5.0/go.mod h1:lenkYuCobuxLBAd/HGFE4LRoW8D3B6iXRQfWYJ+MNbA= github.com/itchyny/gojq v0.12.1 h1:pQJrG8LXgEbZe9hvpfjKg7UlBfieQQydIw3YQq+7WIA= github.com/itchyny/gojq v0.12.1/go.mod h1:Y5Lz0qoT54ii+ucY/K3yNDy19qzxZvWNBMBpKUDQR/4= github.com/itchyny/timefmt-go v0.1.1 h1:rLpnm9xxb39PEEVzO0n4IRp0q6/RmBc7Dy/rE4HrA0U= github.com/itchyny/timefmt-go v0.1.1/go.mod h1:0osSSCQSASBJMsIZnhAaF1C2fCBTJZXrnj37mG8/c+A= +github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024 h1:rBMNdlhTLzJjJSDIjNEXX1Pz3Hmwmz91v+zycvx9PJc= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/julienschmidt/httprouter v1.2.0 h1:TDTW5Yz1mjftljbcKqRcrYhd4XeOoI98t+9HbQbYf7g= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= +github.com/kisielk/errcheck v1.1.0 h1:ZqfnKyx9KGpRcW04j5nnPDgRgoXUeLh2YFBeFzphcA0= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -172,6 +249,7 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac= github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= +github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU= @@ -188,6 +266,7 @@ github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+tw github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= @@ -195,17 +274,25 @@ github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQ github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/microcosm-cc/bluemonday v1.0.2 h1:5lPfLTTAvAbtS0VqT+94yOtFnGfUWYyx0+iToC3Os3s= github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc= +github.com/miekg/dns v1.0.14 h1:9jZdLNd/P4+SfEJ0TNyxYpsK8N4GtfylBLqtbYN1sbA= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +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/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.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/mitchellh/gox v0.4.0 h1:lfGJxY7ToLJQjHHwi0EX6uYBdK78egf954SQl13PQJc= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0 h1:C+X3KsSTLFVBr/tK1eYN/vs4rJcvsiLU338UhYPJWeY= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/muesli/reflow v0.1.0 h1:oQdpLfO56lr5pgLvqD0TcjW85rDjSYSBVdiG1Ch1ddM= github.com/muesli/reflow v0.1.0/go.mod h1:I9bWAt7QTg/que/qmUCJBGlj7wEq8OAFBjPNjc6xK4I= @@ -213,36 +300,52 @@ github.com/muesli/termenv v0.6.0 h1:zxvzTBmo4ZcxhNGGWeMz+Tttm51eF5bmPjfy4MCRYlk= github.com/muesli/termenv v0.6.0/go.mod h1:SohX91w6swWA4AYU+QmPx+aSgXhWO0juiyID9UZmbpA= github.com/muesli/termenv v0.7.4 h1:/pBqvU5CpkY53tU0vVn+xgs2ZTX63aH5nY+SSps5Xa8= github.com/muesli/termenv v0.7.4/go.mod h1:pZ7qY9l3F7e5xsAOS0zCew2tME+p7bWeBkotCEcIIcc= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223 h1:F9x/1yl3T2AeKLr2AMdilSD8+f9bvMnNN8VS5iDtovc= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8= github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA= +github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c h1:Lgl0gzECD8GnQ5QCWA8o6BtfL6mDH5rQgM4/fX3avOs= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1 h1:ccV59UEOTzVDnDUEFdT95ZzHVZ+5+158q8+SJb2QV5w= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3 h1:9iH4JKXLzFbOAdtqv/a+j8aewx2Y8lAjAydhbaScPF8= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.4.0 h1:7etb9YClo3a6HjLzfl6rIQaU+FDfi0VSX39io3aQ+DM= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084 h1:sofwID9zm4tzrgykg80hfFph1mryUeLRsUfoocVVmRY= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/tsdb v0.7.1 h1:YZcsG11NqnK4czYLrWd9mpEuAJIHVQLwdrleYfszMAA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af h1:gu+uRPtBe88sKxUCEXRoeCvVG90TJmwhiqRpvdhQFng= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f h1:UFr9zpz4xgTnIE5yIMtWAMngCdZ9p/+q6lTbgelo80M= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= @@ -250,20 +353,29 @@ github.com/shurcooL/githubv4 v0.0.0-20200928013246-d292edc3691b h1:0/ecDXh/HTHRt github.com/shurcooL/githubv4 v0.0.0-20200928013246-d292edc3691b/go.mod h1:hAF0iLZy4td2EX+/8Tw+4nodhlMrwN3HupfaXj3zkGo= github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v1.1.1 h1:KfztREH0tPxJJ+geloSLaAkaPkr4ki2Er5quFV1TDo4= github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= +github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM= github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= @@ -274,16 +386,24 @@ github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 h1:LnC5Kc/wtumK+WB441p7ynQJzVuNRJiqddSIE3IlSEQ= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/yuin/goldmark v1.2.0 h1:WOOcyaJPlzb8fZ8TloxFe8QZkhOOJx87leDa9MIT9dc= github.com/yuin/goldmark v1.2.0/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.etcd.io/bbolt v1.3.2 h1:Z/90sZLPOeCy2PwprqkFa25PdkusRzaj9P8zm/KNyvk= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -300,8 +420,10 @@ golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136 h1:A1gGSx58LAGVHUUsOf7IiR0u8Xb6W51gRwfDBhkdcaw= golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= @@ -309,10 +431,13 @@ golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTk golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028 h1:4+4C/Iv2U4fMZBiMCc98MG1In4gJY5YRhtpDNeDeHWs= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0 h1:sfUMP1Gu8qASkorDVjnMuvgJzwFbTZSeXFiGBYAVdl4= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -367,6 +492,8 @@ golang.org/x/sys v0.0.0-20200413165638-669c56c373c4 h1:opSr2sbRXk5X5/givKrrKj9HX golang.org/x/sys v0.0.0-20200413165638-669c56c373c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78 h1:nVuTkr9L6Bq62qpUqKo/RnZCFfzDBL0bYo6w9OJUqZY= golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210319071255-635bc2c9138d h1:jbzgAvDZn8aEnytae+4ou0J0GwFZoHR0hOrTg4qH8GA= +golang.org/x/sys v0.0.0-20210319071255-635bc2c9138d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -374,6 +501,7 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -391,6 +519,7 @@ golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc h1:NCy3Ohtk6Iny5V/reW2Ktypo4zIpWBdRJ1uFMjBxdg8= golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= @@ -399,6 +528,7 @@ google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEt google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0 h1:Q3Ui3V3/CVinFWFiW39Iw0kMuVrRzYX0wN6OPFp0lTA= google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/appengine v1.1.0 h1:igQkv0AAhEIvTEpD5LIpAfav2eeVO9HBTjvKHVJPRSs= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= @@ -414,17 +544,23 @@ google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRn google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a h1:Ob5/580gVHBJZgXnff1cZDbG+xLtMVE5mDRTe+nIsX4= google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1 h1:j6XxA85m/6txkUCHvzlV5f+HBNl/1r5cZ2A/3IEFOO8= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/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/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/resty.v1 v1.12.0 h1:CuXP0Pjfw9rOuY6EP+UvtNvt5DSqHpIxILZKT/quCZI= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -437,5 +573,7 @@ gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +rsc.io/binaryregexp v0.2.0 h1:HfqmD5MEmC0zvwBuF187nq9mdnXjXsSivRiXN7SmRkE= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= diff --git a/internal/authflow/flow.go b/internal/authflow/flow.go index 41e06bc21..aa878bd32 100644 --- a/internal/authflow/flow.go +++ b/internal/authflow/flow.go @@ -10,7 +10,7 @@ import ( "github.com/cli/cli/api" "github.com/cli/cli/internal/ghinstance" - "github.com/cli/cli/pkg/browser" + "github.com/cli/cli/pkg/cmdutil" "github.com/cli/cli/pkg/iostreams" "github.com/cli/oauth" ) @@ -93,11 +93,9 @@ func authFlow(oauthHost string, IO *iostreams.IOStreams, notice string, addition fmt.Fprintf(w, "- %s to open %s in your browser... ", cs.Bold("Press Enter"), oauthHost) _ = waitForEnter(IO.In) - browseCmd, err := browser.Command(url) - if err != nil { - return err - } - if err := browseCmd.Run(); err != nil { + // FIXME: read the browser from cmd Factory rather than recreating it + browser := cmdutil.NewBrowser(os.Getenv("BROWSER"), IO.Out, IO.ErrOut) + if err := browser.Browse(url); err != nil { fmt.Fprintf(w, "%s Failed opening a web browser at %s\n", cs.Red("!"), url) fmt.Fprintf(w, " %s\n", err) fmt.Fprint(w, " Please try entering the URL in your browser manually\n") diff --git a/pkg/browser/browser.go b/pkg/browser/browser.go deleted file mode 100644 index ec4c59e6c..000000000 --- a/pkg/browser/browser.go +++ /dev/null @@ -1,80 +0,0 @@ -package browser - -import ( - "os" - "os/exec" - "runtime" - "strings" - - "github.com/cli/safeexec" - "github.com/google/shlex" -) - -// BrowserEnv simply returns the $BROWSER environment variable -func FromEnv() string { - return os.Getenv("BROWSER") -} - -// Command produces an exec.Cmd respecting runtime.GOOS and $BROWSER environment variable -func Command(url string) (*exec.Cmd, error) { - launcher := FromEnv() - if launcher != "" { - return FromLauncher(launcher, url) - } - return ForOS(runtime.GOOS, url), nil -} - -// ForOS produces an exec.Cmd to open the web browser for different OS -func ForOS(goos, url string) *exec.Cmd { - exe := "open" - var args []string - switch goos { - case "darwin": - args = append(args, url) - case "windows": - exe, _ = lookPath("cmd") - r := strings.NewReplacer("&", "^&") - args = append(args, "/c", "start", r.Replace(url)) - default: - exe = linuxExe() - args = append(args, url) - } - - cmd := exec.Command(exe, args...) - cmd.Stderr = os.Stderr - return cmd -} - -// FromLauncher parses the launcher string based on shell splitting rules -func FromLauncher(launcher, url string) (*exec.Cmd, error) { - args, err := shlex.Split(launcher) - if err != nil { - return nil, err - } - - exe, err := lookPath(args[0]) - if err != nil { - return nil, err - } - - args = append(args, url) - cmd := exec.Command(exe, args[1:]...) - cmd.Stderr = os.Stderr - return cmd, nil -} - -func linuxExe() string { - exe := "xdg-open" - - _, err := lookPath(exe) - if err != nil { - _, err := lookPath("wslview") - if err == nil { - exe = "wslview" - } - } - - return exe -} - -var lookPath = safeexec.LookPath diff --git a/pkg/browser/browser_test.go b/pkg/browser/browser_test.go deleted file mode 100644 index 6a24327da..000000000 --- a/pkg/browser/browser_test.go +++ /dev/null @@ -1,75 +0,0 @@ -package browser - -import ( - "errors" - "reflect" - "testing" -) - -func TestForOS(t *testing.T) { - type args struct { - goos string - url string - } - tests := []struct { - name string - args args - exe string - want []string - }{ - { - name: "macOS", - args: args{ - goos: "darwin", - url: "https://example.com/path?a=1&b=2", - }, - want: []string{"open", "https://example.com/path?a=1&b=2"}, - }, - { - name: "Linux", - args: args{ - goos: "linux", - url: "https://example.com/path?a=1&b=2", - }, - exe: "xdg-open", - want: []string{"xdg-open", "https://example.com/path?a=1&b=2"}, - }, - { - name: "WSL", - args: args{ - goos: "linux", - url: "https://example.com/path?a=1&b=2", - }, - exe: "wslview", - want: []string{"wslview", "https://example.com/path?a=1&b=2"}, - }, - { - name: "Windows", - args: args{ - goos: "windows", - url: "https://example.com/path?a=1&b=2&c=3", - }, - exe: "cmd", - want: []string{"cmd", "/c", "start", "https://example.com/path?a=1^&b=2^&c=3"}, - }, - } - for _, tt := range tests { - origLookPath := lookPath - lookPath = func(file string) (string, error) { - if file == tt.exe { - return file, nil - } else { - return "", errors.New("not found") - } - } - defer func() { - lookPath = origLookPath - }() - - t.Run(tt.name, func(t *testing.T) { - if cmd := ForOS(tt.args.goos, tt.args.url); !reflect.DeepEqual(cmd.Args, tt.want) { - t.Errorf("ForOS() = %v, want %v", cmd.Args, tt.want) - } - }) - } -} diff --git a/pkg/cmd/factory/default.go b/pkg/cmd/factory/default.go index ff8ca8ac9..b87ef13ed 100644 --- a/pkg/cmd/factory/default.go +++ b/pkg/cmd/factory/default.go @@ -76,5 +76,6 @@ func New(appVersion string) *cmdutil.Factory { return currentBranch, nil }, Executable: ghExecutable, + Browser: cmdutil.NewBrowser(os.Getenv("BROWSER"), io.Out, io.ErrOut), } } diff --git a/pkg/cmd/gist/create/create.go b/pkg/cmd/gist/create/create.go index 5ba3d56c7..4d088b338 100644 --- a/pkg/cmd/gist/create/create.go +++ b/pkg/cmd/gist/create/create.go @@ -23,6 +23,10 @@ import ( "github.com/spf13/cobra" ) +type browser interface { + Browse(string) error +} + type CreateOptions struct { IO *iostreams.IOStreams @@ -33,12 +37,14 @@ type CreateOptions struct { WebMode bool HttpClient func() (*http.Client, error) + Browser browser } func NewCmdCreate(f *cmdutil.Factory, runF func(*CreateOptions) error) *cobra.Command { opts := CreateOptions{ IO: f.IOStreams, HttpClient: f.HttpClient, + Browser: f.Browser, } cmd := &cobra.Command{ @@ -144,7 +150,7 @@ func createRun(opts *CreateOptions) error { if opts.WebMode { fmt.Fprintf(opts.IO.Out, "Opening %s in your browser.\n", utils.DisplayURL(gist.HTMLURL)) - return utils.OpenInBrowser(gist.HTMLURL) + return opts.Browser.Browse(gist.HTMLURL) } fmt.Fprintln(opts.IO.Out, gist.HTMLURL) diff --git a/pkg/cmd/gist/create/create_test.go b/pkg/cmd/gist/create/create_test.go index 70011d6e8..647a103b3 100644 --- a/pkg/cmd/gist/create/create_test.go +++ b/pkg/cmd/gist/create/create_test.go @@ -171,6 +171,7 @@ func Test_createRun(t *testing.T) { wantStderr string wantParams map[string]interface{} wantErr bool + wantBrowse string }{ { name: "public", @@ -264,6 +265,7 @@ func Test_createRun(t *testing.T) { wantOut: "Opening gist.github.com/aa5a315d61ae9438b18d in your browser.\n", wantStderr: "- Creating gist fixture.txt\n✓ Created gist fixture.txt\n", wantErr: false, + wantBrowse: "https://gist.github.com/aa5a315d61ae9438b18d", wantParams: map[string]interface{}{ "description": "", "updated_at": "0001-01-01T00:00:00Z", @@ -291,11 +293,11 @@ func Test_createRun(t *testing.T) { io, stdin, stdout, stderr := iostreams.Test() tt.opts.IO = io - cs, teardown := run.Stub() + browser := &cmdutil.TestBrowser{} + tt.opts.Browser = browser + + _, teardown := run.Stub() defer teardown(t) - if tt.opts.WebMode { - cs.Register(`https://gist\.github\.com/aa5a315d61ae9438b18d$`, 0, "") - } t.Run(tt.name, func(t *testing.T) { stdin.WriteString(tt.stdin) @@ -313,6 +315,7 @@ func Test_createRun(t *testing.T) { assert.Equal(t, tt.wantStderr, stderr.String()) assert.Equal(t, tt.wantParams, reqBody) reg.Verify(t) + browser.Verify(t, tt.wantBrowse) }) } } diff --git a/pkg/cmd/gist/view/view.go b/pkg/cmd/gist/view/view.go index 33946fe8b..f70650bb9 100644 --- a/pkg/cmd/gist/view/view.go +++ b/pkg/cmd/gist/view/view.go @@ -19,9 +19,14 @@ import ( "github.com/spf13/cobra" ) +type browser interface { + Browse(string) error +} + type ViewOptions struct { IO *iostreams.IOStreams HttpClient func() (*http.Client, error) + Browser browser Selector string Filename string @@ -34,6 +39,7 @@ func NewCmdView(f *cmdutil.Factory, runF func(*ViewOptions) error) *cobra.Comman opts := &ViewOptions{ IO: f.IOStreams, HttpClient: f.HttpClient, + Browser: f.Browser, } cmd := &cobra.Command{ @@ -94,7 +100,7 @@ func viewRun(opts *ViewOptions) error { if opts.IO.IsStderrTTY() { fmt.Fprintf(opts.IO.ErrOut, "Opening %s in your browser.\n", utils.DisplayURL(gistURL)) } - return utils.OpenInBrowser(gistURL) + return opts.Browser.Browse(gistURL) } if strings.Contains(gistID, "/") { diff --git a/pkg/cmd/issue/comment/comment.go b/pkg/cmd/issue/comment/comment.go index 94545cb6b..c25b9ccf5 100644 --- a/pkg/cmd/issue/comment/comment.go +++ b/pkg/cmd/issue/comment/comment.go @@ -9,7 +9,6 @@ import ( issueShared "github.com/cli/cli/pkg/cmd/issue/shared" prShared "github.com/cli/cli/pkg/cmd/pr/shared" "github.com/cli/cli/pkg/cmdutil" - "github.com/cli/cli/utils" "github.com/spf13/cobra" ) @@ -20,7 +19,7 @@ func NewCmdComment(f *cmdutil.Factory, runF func(*prShared.CommentableOptions) e EditSurvey: prShared.CommentableEditSurvey(f.Config, f.IOStreams), InteractiveEditSurvey: prShared.CommentableInteractiveEditSurvey(f.Config, f.IOStreams), ConfirmSubmitSurvey: prShared.CommentableConfirmSubmitSurvey, - OpenInBrowser: utils.OpenInBrowser, + OpenInBrowser: f.Browser.Browse, } var bodyFile string diff --git a/pkg/cmd/issue/comment/comment_test.go b/pkg/cmd/issue/comment/comment_test.go index 5274e7ad0..8e1fa9ad6 100644 --- a/pkg/cmd/issue/comment/comment_test.go +++ b/pkg/cmd/issue/comment/comment_test.go @@ -152,6 +152,7 @@ func TestNewCmdComment(t *testing.T) { f := &cmdutil.Factory{ IOStreams: io, + Browser: &cmdutil.TestBrowser{}, } argv, err := shlex.Split(tt.input) diff --git a/pkg/cmd/issue/create/create.go b/pkg/cmd/issue/create/create.go index 44ae3eb98..4b3ef910d 100644 --- a/pkg/cmd/issue/create/create.go +++ b/pkg/cmd/issue/create/create.go @@ -17,11 +17,16 @@ import ( "github.com/spf13/cobra" ) +type browser interface { + Browse(string) error +} + type CreateOptions struct { HttpClient func() (*http.Client, error) Config func() (config.Config, error) IO *iostreams.IOStreams BaseRepo func() (ghrepo.Interface, error) + Browser browser RootDirOverride string @@ -44,6 +49,7 @@ func NewCmdCreate(f *cmdutil.Factory, runF func(*CreateOptions) error) *cobra.Co IO: f.IOStreams, HttpClient: f.HttpClient, Config: f.Config, + Browser: f.Browser, } var bodyFile string @@ -171,7 +177,7 @@ func createRun(opts *CreateOptions) (err error) { if isTerminal { fmt.Fprintf(opts.IO.ErrOut, "Opening %s in your browser.\n", utils.DisplayURL(openURL)) } - return utils.OpenInBrowser(openURL) + return opts.Browser.Browse(openURL) } if isTerminal { @@ -286,7 +292,7 @@ func createRun(opts *CreateOptions) (err error) { if isTerminal { fmt.Fprintf(opts.IO.ErrOut, "Opening %s in your browser.\n", utils.DisplayURL(openURL)) } - return utils.OpenInBrowser(openURL) + return opts.Browser.Browse(openURL) } else if action == prShared.SubmitAction { params := map[string]interface{}{ "title": tb.Title, diff --git a/pkg/cmd/issue/create/create_test.go b/pkg/cmd/issue/create/create_test.go index 13fd3b98c..5ecde4599 100644 --- a/pkg/cmd/issue/create/create_test.go +++ b/pkg/cmd/issue/create/create_test.go @@ -8,7 +8,6 @@ import ( "net/http" "os" "path/filepath" - "strings" "testing" "github.com/MakeNowJust/heredoc" @@ -145,6 +144,7 @@ func runCommandWithRootDirOverridden(rt http.RoundTripper, isTTY bool, cli strin io.SetStdinTTY(isTTY) io.SetStderrTTY(isTTY) + browser := &cmdutil.TestBrowser{} factory := &cmdutil.Factory{ IOStreams: io, HttpClient: func() (*http.Client, error) { @@ -156,6 +156,7 @@ func runCommandWithRootDirOverridden(rt http.RoundTripper, isTTY bool, cli strin BaseRepo: func() (ghrepo.Interface, error) { return ghrepo.New("OWNER", "REPO"), nil }, + Browser: browser, } cmd := NewCmdCreate(factory, func(opts *CreateOptions) error { @@ -175,8 +176,9 @@ func runCommandWithRootDirOverridden(rt http.RoundTripper, isTTY bool, cli strin _, err = cmd.ExecuteC() return &test.CmdOut{ - OutBuf: stdout, - ErrBuf: stderr, + OutBuf: stdout, + ErrBuf: stderr, + BrowsedURL: browser.BrowsedURL(), }, err } @@ -355,6 +357,7 @@ func TestIssueCreate_nonLegacyTemplate(t *testing.T) { } assert.Equal(t, "https://github.com/OWNER/REPO/issues/12\n", output.String()) + assert.Equal(t, "", output.BrowsedURL) } func TestIssueCreate_continueInBrowser(t *testing.T) { @@ -388,14 +391,9 @@ func TestIssueCreate_continueInBrowser(t *testing.T) { }, }) - cs, cmdTeardown := run.Stub() + _, cmdTeardown := run.Stub() defer cmdTeardown(t) - cs.Register(`https://github\.com`, 0, "", func(args []string) { - url := strings.ReplaceAll(args[len(args)-1], "^", "") - assert.Equal(t, "https://github.com/OWNER/REPO/issues/new?body=body&title=hello", url) - }) - output, err := runCommand(http, true, `-b body`) if err != nil { t.Errorf("error running command `issue create`: %v", err) @@ -408,6 +406,7 @@ func TestIssueCreate_continueInBrowser(t *testing.T) { Opening github.com/OWNER/REPO/issues/new in your browser. `), output.Stderr()) + assert.Equal(t, "https://github.com/OWNER/REPO/issues/new?body=body&title=hello", output.BrowsedURL) } func TestIssueCreate_metadata(t *testing.T) { @@ -520,14 +519,9 @@ func TestIssueCreate_web(t *testing.T) { `), ) - cs, cmdTeardown := run.Stub() + _, cmdTeardown := run.Stub() defer cmdTeardown(t) - cs.Register(`https://github\.com`, 0, "", func(args []string) { - url := strings.ReplaceAll(args[len(args)-1], "^", "") - assert.Equal(t, "https://github.com/OWNER/REPO/issues/new?assignees=MonaLisa", url) - }) - output, err := runCommand(http, true, `--web -a @me`) if err != nil { t.Errorf("error running command `issue create`: %v", err) @@ -535,20 +529,16 @@ func TestIssueCreate_web(t *testing.T) { assert.Equal(t, "", output.String()) assert.Equal(t, "Opening github.com/OWNER/REPO/issues/new in your browser.\n", output.Stderr()) + assert.Equal(t, "https://github.com/OWNER/REPO/issues/new?assignees=MonaLisa", output.BrowsedURL) } func TestIssueCreate_webTitleBody(t *testing.T) { http := &httpmock.Registry{} defer http.Verify(t) - cs, cmdTeardown := run.Stub() + _, cmdTeardown := run.Stub() defer cmdTeardown(t) - cs.Register(`https://github\.com`, 0, "", func(args []string) { - url := strings.ReplaceAll(args[len(args)-1], "^", "") - assert.Equal(t, "https://github.com/OWNER/REPO/issues/new?body=mybody&title=mytitle", url) - }) - output, err := runCommand(http, true, `-w -t mytitle -b mybody`) if err != nil { t.Errorf("error running command `issue create`: %v", err) @@ -556,6 +546,7 @@ func TestIssueCreate_webTitleBody(t *testing.T) { assert.Equal(t, "", output.String()) assert.Equal(t, "Opening github.com/OWNER/REPO/issues/new in your browser.\n", output.Stderr()) + assert.Equal(t, "https://github.com/OWNER/REPO/issues/new?body=mybody&title=mytitle", output.BrowsedURL) } func TestIssueCreate_webTitleBodyAtMeAssignee(t *testing.T) { @@ -571,14 +562,9 @@ func TestIssueCreate_webTitleBodyAtMeAssignee(t *testing.T) { `), ) - cs, cmdTeardown := run.Stub() + _, cmdTeardown := run.Stub() defer cmdTeardown(t) - cs.Register(`https://github\.com`, 0, "", func(args []string) { - url := strings.ReplaceAll(args[len(args)-1], "^", "") - assert.Equal(t, "https://github.com/OWNER/REPO/issues/new?assignees=MonaLisa&body=mybody&title=mytitle", url) - }) - output, err := runCommand(http, true, `-w -t mytitle -b mybody -a @me`) if err != nil { t.Errorf("error running command `issue create`: %v", err) @@ -586,6 +572,7 @@ func TestIssueCreate_webTitleBodyAtMeAssignee(t *testing.T) { assert.Equal(t, "", output.String()) assert.Equal(t, "Opening github.com/OWNER/REPO/issues/new in your browser.\n", output.Stderr()) + assert.Equal(t, "https://github.com/OWNER/REPO/issues/new?assignees=MonaLisa&body=mybody&title=mytitle", output.BrowsedURL) } func TestIssueCreate_AtMeAssignee(t *testing.T) { @@ -666,14 +653,9 @@ func TestIssueCreate_webProject(t *testing.T) { } } } } `)) - cs, cmdTeardown := run.Stub() + _, cmdTeardown := run.Stub() defer cmdTeardown(t) - cs.Register(`https://github\.com`, 0, "", func(args []string) { - url := strings.ReplaceAll(args[len(args)-1], "^", "") - assert.Equal(t, "https://github.com/OWNER/REPO/issues/new?projects=OWNER%2FREPO%2F1&title=Title", url) - }) - output, err := runCommand(http, true, `-w -t Title -p Cleanup`) if err != nil { t.Errorf("error running command `issue create`: %v", err) @@ -681,4 +663,5 @@ func TestIssueCreate_webProject(t *testing.T) { assert.Equal(t, "", output.String()) assert.Equal(t, "Opening github.com/OWNER/REPO/issues/new in your browser.\n", output.Stderr()) + assert.Equal(t, "https://github.com/OWNER/REPO/issues/new?projects=OWNER%2FREPO%2F1&title=Title", output.BrowsedURL) } diff --git a/pkg/cmd/issue/list/list.go b/pkg/cmd/issue/list/list.go index c28d048dc..ec1ece7d9 100644 --- a/pkg/cmd/issue/list/list.go +++ b/pkg/cmd/issue/list/list.go @@ -17,11 +17,16 @@ import ( "github.com/spf13/cobra" ) +type browser interface { + Browse(string) error +} + type ListOptions struct { HttpClient func() (*http.Client, error) Config func() (config.Config, error) IO *iostreams.IOStreams BaseRepo func() (ghrepo.Interface, error) + Browser browser WebMode bool @@ -39,6 +44,7 @@ func NewCmdList(f *cmdutil.Factory, runF func(*ListOptions) error) *cobra.Comman IO: f.IOStreams, HttpClient: f.HttpClient, Config: f.Config, + Browser: f.Browser, } cmd := &cobra.Command{ @@ -124,7 +130,7 @@ func listRun(opts *ListOptions) error { if isTerminal { fmt.Fprintf(opts.IO.ErrOut, "Opening %s in your browser.\n", utils.DisplayURL(openURL)) } - return utils.OpenInBrowser(openURL) + return opts.Browser.Browse(openURL) } listResult, err := api.IssueList(apiClient, baseRepo, opts.State, opts.Labels, filterAssignee, opts.LimitResults, filterAuthor, filterMention, opts.Milestone) diff --git a/pkg/cmd/issue/list/list_test.go b/pkg/cmd/issue/list/list_test.go index b40701023..3fad89772 100644 --- a/pkg/cmd/issue/list/list_test.go +++ b/pkg/cmd/issue/list/list_test.go @@ -6,7 +6,6 @@ import ( "io/ioutil" "net/http" "regexp" - "strings" "testing" "github.com/MakeNowJust/heredoc" @@ -232,24 +231,42 @@ func TestIssueList_disabledIssues(t *testing.T) { } func TestIssueList_web(t *testing.T) { - http := &httpmock.Registry{} - defer http.Verify(t) + io, _, stdout, stderr := iostreams.Test() + io.SetStdoutTTY(true) + io.SetStderrTTY(true) + browser := &cmdutil.TestBrowser{} - cs, cmdTeardown := run.Stub() + reg := &httpmock.Registry{} + defer reg.Verify(t) + + _, cmdTeardown := run.Stub() defer cmdTeardown(t) - cs.Register(`https://github\.com`, 0, "", func(args []string) { - url := strings.ReplaceAll(args[len(args)-1], "^", "") - assert.Equal(t, "https://github.com/OWNER/REPO/issues?q=is%3Aissue+assignee%3Apeter+label%3Abug+label%3Adocs+author%3Ajohn+mentions%3Afrank+milestone%3Av1.1", url) + err := listRun(&ListOptions{ + IO: io, + Browser: browser, + HttpClient: func() (*http.Client, error) { + return &http.Client{Transport: reg}, nil + }, + BaseRepo: func() (ghrepo.Interface, error) { + return ghrepo.New("OWNER", "REPO"), nil + }, + WebMode: true, + State: "all", + Assignee: "peter", + Author: "john", + Labels: []string{"bug", "docs"}, + Mention: "frank", + Milestone: "v1.1", + LimitResults: 10, }) - - output, err := runCommand(http, true, "--web -a peter -A john -l bug -l docs -L 10 -s all --mention frank --milestone v1.1") if err != nil { t.Errorf("error running command `issue list` with `--web` flag: %v", err) } - assert.Equal(t, "", output.String()) - assert.Equal(t, "Opening github.com/OWNER/REPO/issues in your browser.\n", output.Stderr()) + assert.Equal(t, "", stdout.String()) + assert.Equal(t, "Opening github.com/OWNER/REPO/issues in your browser.\n", stderr.String()) + browser.Verify(t, "https://github.com/OWNER/REPO/issues?q=is%3Aissue+assignee%3Apeter+label%3Abug+label%3Adocs+author%3Ajohn+mentions%3Afrank+milestone%3Av1.1") } func TestIssueList_milestoneNotFound(t *testing.T) { diff --git a/pkg/cmd/issue/view/view.go b/pkg/cmd/issue/view/view.go index aaf415266..39e354133 100644 --- a/pkg/cmd/issue/view/view.go +++ b/pkg/cmd/issue/view/view.go @@ -20,10 +20,15 @@ import ( "github.com/spf13/cobra" ) +type browser interface { + Browse(string) error +} + type ViewOptions struct { HttpClient func() (*http.Client, error) IO *iostreams.IOStreams BaseRepo func() (ghrepo.Interface, error) + Browser browser SelectorArg string WebMode bool @@ -36,6 +41,7 @@ func NewCmdView(f *cmdutil.Factory, runF func(*ViewOptions) error) *cobra.Comman opts := &ViewOptions{ IO: f.IOStreams, HttpClient: f.HttpClient, + Browser: f.Browser, Now: time.Now, } @@ -86,7 +92,7 @@ func viewRun(opts *ViewOptions) error { if opts.IO.IsStdoutTTY() { fmt.Fprintf(opts.IO.ErrOut, "Opening %s in your browser.\n", utils.DisplayURL(openURL)) } - return utils.OpenInBrowser(openURL) + return opts.Browser.Browse(openURL) } if opts.Comments { diff --git a/pkg/cmd/issue/view/view_test.go b/pkg/cmd/issue/view/view_test.go index 8eaf4469b..dc5c5dfb5 100644 --- a/pkg/cmd/issue/view/view_test.go +++ b/pkg/cmd/issue/view/view_test.go @@ -5,7 +5,6 @@ import ( "fmt" "io/ioutil" "net/http" - "strings" "testing" "time" @@ -59,65 +58,45 @@ func runCommand(rt http.RoundTripper, isTTY bool, cli string) (*test.CmdOut, err } func TestIssueView_web(t *testing.T) { - http := &httpmock.Registry{} - defer http.Verify(t) + io, _, stdout, stderr := iostreams.Test() + io.SetStdoutTTY(true) + io.SetStderrTTY(true) + browser := &cmdutil.TestBrowser{} - http.Register( + reg := &httpmock.Registry{} + defer reg.Verify(t) + + reg.Register( httpmock.GraphQL(`query IssueByNumber\b`), httpmock.StringResponse(` { "data": { "repository": { "hasIssuesEnabled": true, "issue": { "number": 123, "url": "https://github.com/OWNER/REPO/issues/123" } } } } - `), - ) + `)) - cs, cmdTeardown := run.Stub() + _, cmdTeardown := run.Stub() defer cmdTeardown(t) - cs.Register(`https://github\.com`, 0, "", func(args []string) { - url := strings.ReplaceAll(args[len(args)-1], "^", "") - assert.Equal(t, "https://github.com/OWNER/REPO/issues/123", url) + err := viewRun(&ViewOptions{ + IO: io, + Browser: browser, + HttpClient: func() (*http.Client, error) { + return &http.Client{Transport: reg}, nil + }, + BaseRepo: func() (ghrepo.Interface, error) { + return ghrepo.New("OWNER", "REPO"), nil + }, + WebMode: true, + SelectorArg: "123", }) - - output, err := runCommand(http, true, "-w 123") if err != nil { t.Errorf("error running command `issue view`: %v", err) } - assert.Equal(t, "", output.String()) - assert.Equal(t, "Opening github.com/OWNER/REPO/issues/123 in your browser.\n", output.Stderr()) -} - -func TestIssueView_web_numberArgWithHash(t *testing.T) { - http := &httpmock.Registry{} - defer http.Verify(t) - - http.Register( - httpmock.GraphQL(`query IssueByNumber\b`), - httpmock.StringResponse(` - { "data": { "repository": { "hasIssuesEnabled": true, "issue": { - "number": 123, - "url": "https://github.com/OWNER/REPO/issues/123" - } } } } - `), - ) - - cs, cmdTeardown := run.Stub() - defer cmdTeardown(t) - - cs.Register(`https://github\.com`, 0, "", func(args []string) { - url := strings.ReplaceAll(args[len(args)-1], "^", "") - assert.Equal(t, "https://github.com/OWNER/REPO/issues/123", url) - }) - - output, err := runCommand(http, true, "-w \"#123\"") - if err != nil { - t.Errorf("error running command `issue view`: %v", err) - } - - assert.Equal(t, "", output.String()) - assert.Equal(t, "Opening github.com/OWNER/REPO/issues/123 in your browser.\n", output.Stderr()) + assert.Equal(t, "", stdout.String()) + assert.Equal(t, "Opening github.com/OWNER/REPO/issues/123 in your browser.\n", stderr.String()) + browser.Verify(t, "https://github.com/OWNER/REPO/issues/123") } func TestIssueView_nontty_Preview(t *testing.T) { @@ -318,36 +297,6 @@ func TestIssueView_disabledIssues(t *testing.T) { } } -func TestIssueView_web_urlArg(t *testing.T) { - http := &httpmock.Registry{} - defer http.Verify(t) - - http.Register( - httpmock.GraphQL(`query IssueByNumber\b`), - httpmock.StringResponse(` - { "data": { "repository": { "hasIssuesEnabled": true, "issue": { - "number": 123, - "url": "https://github.com/OWNER/REPO/issues/123" - } } } } - `), - ) - - cs, cmdTeardown := run.Stub() - defer cmdTeardown(t) - - cs.Register(`https://github\.com`, 0, "", func(args []string) { - url := strings.ReplaceAll(args[len(args)-1], "^", "") - assert.Equal(t, "https://github.com/OWNER/REPO/issues/123", url) - }) - - output, err := runCommand(http, true, "-w https://github.com/OWNER/REPO/issues/123") - if err != nil { - t.Errorf("error running command `issue view`: %v", err) - } - - assert.Equal(t, "", output.String()) -} - func TestIssueView_tty_Comments(t *testing.T) { tests := map[string]struct { cli string diff --git a/pkg/cmd/pr/checks/checks.go b/pkg/cmd/pr/checks/checks.go index 89eb8e5d3..83f4ce567 100644 --- a/pkg/cmd/pr/checks/checks.go +++ b/pkg/cmd/pr/checks/checks.go @@ -17,9 +17,14 @@ import ( "github.com/spf13/cobra" ) +type browser interface { + Browse(string) error +} + type ChecksOptions struct { HttpClient func() (*http.Client, error) IO *iostreams.IOStreams + Browser browser BaseRepo func() (ghrepo.Interface, error) Branch func() (string, error) Remotes func() (context.Remotes, error) @@ -36,6 +41,7 @@ func NewCmdChecks(f *cmdutil.Factory, runF func(*ChecksOptions) error) *cobra.Co Branch: f.Branch, Remotes: f.Remotes, BaseRepo: f.BaseRepo, + Browser: f.Browser, } cmd := &cobra.Command{ @@ -95,7 +101,7 @@ func checksRun(opts *ChecksOptions) error { if isTerminal { fmt.Fprintf(opts.IO.ErrOut, "Opening %s in your browser.\n", utils.DisplayURL(openURL)) } - return utils.OpenInBrowser(openURL) + return opts.Browser.Browse(openURL) } passing := 0 diff --git a/pkg/cmd/pr/checks/checks_test.go b/pkg/cmd/pr/checks/checks_test.go index aa1a9baba..fdcce3f9e 100644 --- a/pkg/cmd/pr/checks/checks_test.go +++ b/pkg/cmd/pr/checks/checks_test.go @@ -212,36 +212,28 @@ func TestChecksRun_web(t *testing.T) { isTTY bool wantStderr string wantStdout string + wantBrowse string }{ { name: "tty", isTTY: true, wantStderr: "Opening github.com/OWNER/REPO/pull/123/checks in your browser.\n", wantStdout: "", + wantBrowse: "https://github.com/OWNER/REPO/pull/123/checks", }, { name: "nontty", isTTY: false, wantStderr: "", wantStdout: "", + wantBrowse: "https://github.com/OWNER/REPO/pull/123/checks", }, } - - reg := &httpmock.Registry{} - - opts := &ChecksOptions{ - WebMode: true, - HttpClient: func() (*http.Client, error) { - return &http.Client{Transport: reg}, nil - }, - BaseRepo: func() (ghrepo.Interface, error) { - return ghrepo.New("OWNER", "REPO"), nil - }, - SelectorArg: "123", - } - for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { + browser := &cmdutil.TestBrowser{} + reg := &httpmock.Registry{} + reg.Register( httpmock.GraphQL(`query PullRequestByNumber\b`), httpmock.FileResponse("./fixtures/allPassing.json")) @@ -250,17 +242,26 @@ func TestChecksRun_web(t *testing.T) { io.SetStdinTTY(tc.isTTY) io.SetStderrTTY(tc.isTTY) - opts.IO = io - - cs, teardown := run.Stub() + _, teardown := run.Stub() defer teardown(t) - cs.Register(`https://github\.com/OWNER/REPO/pull/123/checks$`, 0, "") - err := checksRun(opts) + err := checksRun(&ChecksOptions{ + IO: io, + Browser: browser, + WebMode: true, + HttpClient: func() (*http.Client, error) { + return &http.Client{Transport: reg}, nil + }, + BaseRepo: func() (ghrepo.Interface, error) { + return ghrepo.New("OWNER", "REPO"), nil + }, + SelectorArg: "123", + }) assert.NoError(t, err) assert.Equal(t, tc.wantStdout, stdout.String()) assert.Equal(t, tc.wantStderr, stderr.String()) reg.Verify(t) + browser.Verify(t, tc.wantBrowse) }) } } diff --git a/pkg/cmd/pr/comment/comment.go b/pkg/cmd/pr/comment/comment.go index dc9341ebe..b993fd407 100644 --- a/pkg/cmd/pr/comment/comment.go +++ b/pkg/cmd/pr/comment/comment.go @@ -10,7 +10,6 @@ import ( "github.com/cli/cli/internal/ghrepo" "github.com/cli/cli/pkg/cmd/pr/shared" "github.com/cli/cli/pkg/cmdutil" - "github.com/cli/cli/utils" "github.com/spf13/cobra" ) @@ -21,7 +20,7 @@ func NewCmdComment(f *cmdutil.Factory, runF func(*shared.CommentableOptions) err EditSurvey: shared.CommentableEditSurvey(f.Config, f.IOStreams), InteractiveEditSurvey: shared.CommentableInteractiveEditSurvey(f.Config, f.IOStreams), ConfirmSubmitSurvey: shared.CommentableConfirmSubmitSurvey, - OpenInBrowser: utils.OpenInBrowser, + OpenInBrowser: f.Browser.Browse, } var bodyFile string diff --git a/pkg/cmd/pr/comment/comment_test.go b/pkg/cmd/pr/comment/comment_test.go index 56277874e..429af7cda 100644 --- a/pkg/cmd/pr/comment/comment_test.go +++ b/pkg/cmd/pr/comment/comment_test.go @@ -173,6 +173,7 @@ func TestNewCmdComment(t *testing.T) { f := &cmdutil.Factory{ IOStreams: io, + Browser: &cmdutil.TestBrowser{}, } argv, err := shlex.Split(tt.input) diff --git a/pkg/cmd/pr/create/create.go b/pkg/cmd/pr/create/create.go index 0de13bd49..22ca05de4 100644 --- a/pkg/cmd/pr/create/create.go +++ b/pkg/cmd/pr/create/create.go @@ -24,6 +24,10 @@ import ( "github.com/spf13/cobra" ) +type browser interface { + Browse(string) error +} + type CreateOptions struct { // This struct stores user input and factory functions HttpClient func() (*http.Client, error) @@ -31,6 +35,7 @@ type CreateOptions struct { IO *iostreams.IOStreams Remotes func() (context.Remotes, error) Branch func() (string, error) + Browser browser TitleProvided bool BodyProvided bool @@ -79,6 +84,7 @@ func NewCmdCreate(f *cmdutil.Factory, runF func(*CreateOptions) error) *cobra.Co Config: f.Config, Remotes: f.Remotes, Branch: f.Branch, + Browser: f.Browser, } var bodyFile string @@ -650,7 +656,7 @@ func previewPR(opts CreateOptions, ctx CreateContext, state shared.IssueMetadata if opts.IO.IsStdinTTY() && opts.IO.IsStdoutTTY() { fmt.Fprintf(opts.IO.ErrOut, "Opening %s in your browser.\n", utils.DisplayURL(openURL)) } - return utils.OpenInBrowser(openURL) + return opts.Browser.Browse(openURL) } diff --git a/pkg/cmd/pr/create/create_test.go b/pkg/cmd/pr/create/create_test.go index 7e94cf084..9d338bcad 100644 --- a/pkg/cmd/pr/create/create_test.go +++ b/pkg/cmd/pr/create/create_test.go @@ -8,7 +8,6 @@ import ( "net/http" "os" "path/filepath" - "strings" "testing" "github.com/MakeNowJust/heredoc" @@ -166,8 +165,10 @@ func runCommandWithRootDirOverridden(rt http.RoundTripper, remotes context.Remot io.SetStdinTTY(isTTY) io.SetStderrTTY(isTTY) + browser := &cmdutil.TestBrowser{} factory := &cmdutil.Factory{ IOStreams: io, + Browser: browser, HttpClient: func() (*http.Client, error) { return &http.Client{Transport: rt}, nil }, @@ -211,8 +212,9 @@ func runCommandWithRootDirOverridden(rt http.RoundTripper, remotes context.Remot _, err = cmd.ExecuteC() return &test.CmdOut{ - OutBuf: stdout, - ErrBuf: stderr, + OutBuf: stdout, + ErrBuf: stderr, + BrowsedURL: browser.BrowsedURL(), }, err } @@ -231,16 +233,13 @@ func TestPRCreate_nontty_web(t *testing.T) { cs.Register(`git status --porcelain`, 0, "") cs.Register(`git( .+)? log( .+)? origin/master\.\.\.feature`, 0, "") - cs.Register(`https://github\.com`, 0, "", func(args []string) { - url := strings.ReplaceAll(args[len(args)-1], "^", "") - assert.Equal(t, "https://github.com/OWNER/REPO/compare/master...feature?expand=1", url) - }) output, err := runCommand(http, nil, "feature", false, `--web --head=feature`) require.NoError(t, err) assert.Equal(t, "", output.String()) assert.Equal(t, "", output.Stderr()) + assert.Equal(t, "https://github.com/OWNER/REPO/compare/master...feature?expand=1", output.BrowsedURL) } func TestPRCreate_recover(t *testing.T) { @@ -823,10 +822,6 @@ func TestPRCreate_web(t *testing.T) { cs.Register(`git show-ref --verify -- HEAD refs/remotes/origin/feature`, 0, "") cs.Register(`git( .+)? log( .+)? origin/master\.\.\.feature`, 0, "") cs.Register(`git push --set-upstream origin HEAD:feature`, 0, "") - cs.Register(`https://github\.com`, 0, "", func(args []string) { - url := strings.ReplaceAll(args[len(args)-1], "^", "") - assert.Equal(t, "https://github.com/OWNER/REPO/compare/master...feature?expand=1", url) - }) ask, cleanupAsk := prompt.InitAskStubber() defer cleanupAsk() @@ -837,6 +832,7 @@ func TestPRCreate_web(t *testing.T) { assert.Equal(t, "", output.String()) assert.Equal(t, "Opening github.com/OWNER/REPO/compare/master...feature in your browser.\n", output.Stderr()) + assert.Equal(t, "https://github.com/OWNER/REPO/compare/master...feature?expand=1", output.BrowsedURL) } func TestPRCreate_webProject(t *testing.T) { @@ -877,10 +873,6 @@ func TestPRCreate_webProject(t *testing.T) { cs.Register(`git show-ref --verify -- HEAD refs/remotes/origin/feature`, 0, "") cs.Register(`git( .+)? log( .+)? origin/master\.\.\.feature`, 0, "") cs.Register(`git push --set-upstream origin HEAD:feature`, 0, "") - cs.Register(`https://github\.com`, 0, "", func(args []string) { - url := strings.ReplaceAll(args[len(args)-1], "^", "") - assert.Equal(t, "https://github.com/OWNER/REPO/compare/master...feature?expand=1&projects=ORG%2F1", url) - }) ask, cleanupAsk := prompt.InitAskStubber() defer cleanupAsk() @@ -891,6 +883,7 @@ func TestPRCreate_webProject(t *testing.T) { assert.Equal(t, "", output.String()) assert.Equal(t, "Opening github.com/OWNER/REPO/compare/master...feature in your browser.\n", output.Stderr()) + assert.Equal(t, "https://github.com/OWNER/REPO/compare/master...feature?expand=1&projects=ORG%2F1", output.BrowsedURL) } func Test_determineTrackingBranch_empty(t *testing.T) { diff --git a/pkg/cmd/pr/list/list.go b/pkg/cmd/pr/list/list.go index 3ed4b1ea1..62ce5f818 100644 --- a/pkg/cmd/pr/list/list.go +++ b/pkg/cmd/pr/list/list.go @@ -16,10 +16,15 @@ import ( "github.com/spf13/cobra" ) +type browser interface { + Browse(string) error +} + type ListOptions struct { HttpClient func() (*http.Client, error) IO *iostreams.IOStreams BaseRepo func() (ghrepo.Interface, error) + Browser browser WebMode bool LimitResults int @@ -33,6 +38,7 @@ func NewCmdList(f *cmdutil.Factory, runF func(*ListOptions) error) *cobra.Comman opts := &ListOptions{ IO: f.IOStreams, HttpClient: f.HttpClient, + Browser: f.Browser, } cmd := &cobra.Command{ @@ -98,7 +104,7 @@ func listRun(opts *ListOptions) error { if opts.IO.IsStdoutTTY() { fmt.Fprintf(opts.IO.ErrOut, "Opening %s in your browser.\n", utils.DisplayURL(openURL)) } - return utils.OpenInBrowser(openURL) + return opts.Browser.Browse(openURL) } var graphqlState []string diff --git a/pkg/cmd/pr/list/list_test.go b/pkg/cmd/pr/list/list_test.go index b8c5ecdb7..c134f3f0a 100644 --- a/pkg/cmd/pr/list/list_test.go +++ b/pkg/cmd/pr/list/list_test.go @@ -24,8 +24,10 @@ func runCommand(rt http.RoundTripper, isTTY bool, cli string) (*test.CmdOut, err io.SetStdinTTY(isTTY) io.SetStderrTTY(isTTY) + browser := &cmdutil.TestBrowser{} factory := &cmdutil.Factory{ IOStreams: io, + Browser: browser, HttpClient: func() (*http.Client, error) { return &http.Client{Transport: rt}, nil }, @@ -48,8 +50,9 @@ func runCommand(rt http.RoundTripper, isTTY bool, cli string) (*test.CmdOut, err _, err = cmd.ExecuteC() return &test.CmdOut{ - OutBuf: stdout, - ErrBuf: stderr, + OutBuf: stdout, + ErrBuf: stderr, + BrowsedURL: browser.BrowsedURL(), }, err } @@ -198,14 +201,9 @@ func TestPRList_web(t *testing.T) { http := initFakeHTTP() defer http.Verify(t) - cs, cmdTeardown := run.Stub() + _, cmdTeardown := run.Stub() defer cmdTeardown(t) - cs.Register(`https://github\.com`, 0, "", func(args []string) { - url := strings.ReplaceAll(args[len(args)-1], "^", "") - assert.Equal(t, "https://github.com/OWNER/REPO/pulls?q=is%3Apr+is%3Amerged+assignee%3Apeter+label%3Abug+label%3Adocs+base%3Atrunk", url) - }) - output, err := runCommand(http, true, "--web -a peter -l bug -l docs -L 10 -s merged -B trunk") if err != nil { t.Errorf("error running command `pr list` with `--web` flag: %v", err) @@ -213,4 +211,5 @@ func TestPRList_web(t *testing.T) { assert.Equal(t, "", output.String()) assert.Equal(t, "Opening github.com/OWNER/REPO/pulls in your browser.\n", output.Stderr()) + assert.Equal(t, "https://github.com/OWNER/REPO/pulls?q=is%3Apr+is%3Amerged+assignee%3Apeter+label%3Abug+label%3Adocs+base%3Atrunk", output.BrowsedURL) } diff --git a/pkg/cmd/pr/view/view.go b/pkg/cmd/pr/view/view.go index e38b0fb23..57cab8e5a 100644 --- a/pkg/cmd/pr/view/view.go +++ b/pkg/cmd/pr/view/view.go @@ -22,10 +22,15 @@ import ( "github.com/spf13/cobra" ) +type browser interface { + Browse(string) error +} + type ViewOptions struct { HttpClient func() (*http.Client, error) Config func() (config.Config, error) IO *iostreams.IOStreams + Browser browser BaseRepo func() (ghrepo.Interface, error) Remotes func() (context.Remotes, error) Branch func() (string, error) @@ -42,6 +47,7 @@ func NewCmdView(f *cmdutil.Factory, runF func(*ViewOptions) error) *cobra.Comman Config: f.Config, Remotes: f.Remotes, Branch: f.Branch, + Browser: f.Browser, } cmd := &cobra.Command{ @@ -96,7 +102,7 @@ func viewRun(opts *ViewOptions) error { if connectedToTerminal { fmt.Fprintf(opts.IO.ErrOut, "Opening %s in your browser.\n", utils.DisplayURL(openURL)) } - return utils.OpenInBrowser(openURL) + return opts.Browser.Browse(openURL) } opts.IO.DetectTerminalTheme() diff --git a/pkg/cmd/pr/view/view_test.go b/pkg/cmd/pr/view/view_test.go index 509d1cf68..cda0e266c 100644 --- a/pkg/cmd/pr/view/view_test.go +++ b/pkg/cmd/pr/view/view_test.go @@ -5,7 +5,6 @@ import ( "fmt" "io/ioutil" "net/http" - "strings" "testing" "github.com/cli/cli/context" @@ -118,8 +117,10 @@ func runCommand(rt http.RoundTripper, branch string, isTTY bool, cli string) (*t io.SetStdinTTY(isTTY) io.SetStderrTTY(isTTY) + browser := &cmdutil.TestBrowser{} factory := &cmdutil.Factory{ IOStreams: io, + Browser: browser, HttpClient: func() (*http.Client, error) { return &http.Client{Transport: rt}, nil }, @@ -156,8 +157,9 @@ func runCommand(rt http.RoundTripper, branch string, isTTY bool, cli string) (*t _, err = cmd.ExecuteC() return &test.CmdOut{ - OutBuf: stdout, - ErrBuf: stderr, + OutBuf: stdout, + ErrBuf: stderr, + BrowsedURL: browser.BrowsedURL(), }, err } @@ -585,10 +587,6 @@ func TestPRView_web_currentBranch(t *testing.T) { defer cmdTeardown(t) cs.Register(`git config --get-regexp.+branch\\\.blueberries\\\.`, 0, "") - cs.Register(`https://github\.com`, 0, "", func(args []string) { - url := strings.ReplaceAll(args[len(args)-1], "^", "") - assert.Equal(t, "https://github.com/OWNER/REPO/pull/10", url) - }) output, err := runCommand(http, "blueberries", true, "-w") if err != nil { @@ -597,6 +595,7 @@ func TestPRView_web_currentBranch(t *testing.T) { assert.Equal(t, "", output.String()) assert.Equal(t, "Opening github.com/OWNER/REPO/pull/10 in your browser.\n", output.Stderr()) + assert.Equal(t, "https://github.com/OWNER/REPO/pull/10", output.BrowsedURL) } func TestPRView_web_noResultsForBranch(t *testing.T) { @@ -627,137 +626,16 @@ func TestPRView_web_numberArg(t *testing.T) { } } } }`), ) - cs, cmdTeardown := run.Stub() + _, cmdTeardown := run.Stub() defer cmdTeardown(t) - cs.Register(`https://github\.com`, 0, "", func(args []string) { - url := strings.ReplaceAll(args[len(args)-1], "^", "") - assert.Equal(t, "https://github.com/OWNER/REPO/pull/23", url) - }) - output, err := runCommand(http, "master", true, "-w 23") if err != nil { t.Errorf("error running command `pr view`: %v", err) } assert.Equal(t, "", output.String()) -} - -func TestPRView_web_numberArgWithHash(t *testing.T) { - http := &httpmock.Registry{} - defer http.Verify(t) - - http.Register( - httpmock.GraphQL(`query PullRequestByNumber\b`), - httpmock.StringResponse(` - { "data": { "repository": { "pullRequest": { - "url": "https://github.com/OWNER/REPO/pull/23" - } } } }`), - ) - - cs, cmdTeardown := run.Stub() - defer cmdTeardown(t) - - cs.Register(`https://github\.com`, 0, "", func(args []string) { - url := strings.ReplaceAll(args[len(args)-1], "^", "") - assert.Equal(t, "https://github.com/OWNER/REPO/pull/23", url) - }) - - output, err := runCommand(http, "master", true, `-w "#23"`) - if err != nil { - t.Errorf("error running command `pr view`: %v", err) - } - - assert.Equal(t, "", output.String()) -} - -func TestPRView_web_urlArg(t *testing.T) { - http := &httpmock.Registry{} - defer http.Verify(t) - - http.Register( - httpmock.GraphQL(`query PullRequestByNumber\b`), - httpmock.StringResponse(` - { "data": { "repository": { "pullRequest": { - "url": "https://github.com/OWNER/REPO/pull/23" - } } } }`), - ) - - cs, cmdTeardown := run.Stub() - defer cmdTeardown(t) - - cs.Register(`https://github\.com`, 0, "", func(args []string) { - url := strings.ReplaceAll(args[len(args)-1], "^", "") - assert.Equal(t, "https://github.com/OWNER/REPO/pull/23", url) - }) - - output, err := runCommand(http, "master", true, "-w https://github.com/OWNER/REPO/pull/23/files") - if err != nil { - t.Errorf("error running command `pr view`: %v", err) - } - - assert.Equal(t, "", output.String()) -} - -func TestPRView_web_branchArg(t *testing.T) { - http := &httpmock.Registry{} - defer http.Verify(t) - - http.Register( - httpmock.GraphQL(`query PullRequestForBranch\b`), - httpmock.StringResponse(` - { "data": { "repository": { "pullRequests": { "nodes": [ - { "headRefName": "blueberries", - "isCrossRepository": false, - "url": "https://github.com/OWNER/REPO/pull/23" } - ] } } } }`), - ) - - cs, cmdTeardown := run.Stub() - defer cmdTeardown(t) - - cs.Register(`https://github\.com`, 0, "", func(args []string) { - url := strings.ReplaceAll(args[len(args)-1], "^", "") - assert.Equal(t, "https://github.com/OWNER/REPO/pull/23", url) - }) - - output, err := runCommand(http, "master", true, "-w blueberries") - if err != nil { - t.Errorf("error running command `pr view`: %v", err) - } - - assert.Equal(t, "", output.String()) -} - -func TestPRView_web_branchWithOwnerArg(t *testing.T) { - http := &httpmock.Registry{} - defer http.Verify(t) - - http.Register( - httpmock.GraphQL(`query PullRequestForBranch\b`), - httpmock.StringResponse(` - { "data": { "repository": { "pullRequests": { "nodes": [ - { "headRefName": "blueberries", - "isCrossRepository": true, - "headRepositoryOwner": { "login": "hubot" }, - "url": "https://github.com/hubot/REPO/pull/23" } - ] } } } }`), - ) - - cs, cmdTeardown := run.Stub() - defer cmdTeardown(t) - - cs.Register(`https://github\.com`, 0, "", func(args []string) { - url := strings.ReplaceAll(args[len(args)-1], "^", "") - assert.Equal(t, "https://github.com/hubot/REPO/pull/23", url) - }) - - output, err := runCommand(http, "master", true, "-w hubot:blueberries") - if err != nil { - t.Errorf("error running command `pr view`: %v", err) - } - - assert.Equal(t, "", output.String()) + assert.Equal(t, "https://github.com/OWNER/REPO/pull/23", output.BrowsedURL) } func TestPRView_tty_Comments(t *testing.T) { diff --git a/pkg/cmd/release/view/view.go b/pkg/cmd/release/view/view.go index 4dcfdeee2..9bcccb675 100644 --- a/pkg/cmd/release/view/view.go +++ b/pkg/cmd/release/view/view.go @@ -17,10 +17,15 @@ import ( "github.com/spf13/cobra" ) +type browser interface { + Browse(string) error +} + type ViewOptions struct { HttpClient func() (*http.Client, error) IO *iostreams.IOStreams BaseRepo func() (ghrepo.Interface, error) + Browser browser TagName string WebMode bool @@ -30,6 +35,7 @@ func NewCmdView(f *cmdutil.Factory, runF func(*ViewOptions) error) *cobra.Comman opts := &ViewOptions{ IO: f.IOStreams, HttpClient: f.HttpClient, + Browser: f.Browser, } cmd := &cobra.Command{ @@ -91,7 +97,7 @@ func viewRun(opts *ViewOptions) error { if opts.IO.IsStdoutTTY() { fmt.Fprintf(opts.IO.ErrOut, "Opening %s in your browser.\n", utils.DisplayURL(release.HTMLURL)) } - return utils.OpenInBrowser(release.HTMLURL) + return opts.Browser.Browse(release.HTMLURL) } if opts.IO.IsStdoutTTY() { diff --git a/pkg/cmd/repo/view/view.go b/pkg/cmd/repo/view/view.go index 4a1def8b8..3fa70268e 100644 --- a/pkg/cmd/repo/view/view.go +++ b/pkg/cmd/repo/view/view.go @@ -21,10 +21,15 @@ import ( "github.com/spf13/cobra" ) +type browser interface { + Browse(string) error +} + type ViewOptions struct { HttpClient func() (*http.Client, error) IO *iostreams.IOStreams BaseRepo func() (ghrepo.Interface, error) + Browser browser RepoArg string Web bool @@ -36,6 +41,7 @@ func NewCmdView(f *cmdutil.Factory, runF func(*ViewOptions) error) *cobra.Comman IO: f.IOStreams, HttpClient: f.HttpClient, BaseRepo: f.BaseRepo, + Browser: f.Browser, } cmd := &cobra.Command{ @@ -106,7 +112,7 @@ func viewRun(opts *ViewOptions) error { if opts.IO.IsStdoutTTY() { fmt.Fprintf(opts.IO.ErrOut, "Opening %s in your browser.\n", utils.DisplayURL(openURL)) } - return utils.OpenInBrowser(openURL) + return opts.Browser.Browse(openURL) } fullName := ghrepo.FullName(toView) diff --git a/pkg/cmd/repo/view/view_test.go b/pkg/cmd/repo/view/view_test.go index 005ec8515..fd91dcd70 100644 --- a/pkg/cmd/repo/view/view_test.go +++ b/pkg/cmd/repo/view/view_test.go @@ -99,15 +99,19 @@ func Test_RepoView_Web(t *testing.T) { name string stdoutTTY bool wantStderr string + wantBrowse string }{ { name: "tty", stdoutTTY: true, wantStderr: "Opening github.com/OWNER/REPO in your browser.\n", + wantBrowse: "https://github.com/OWNER/REPO", }, { name: "nontty", + stdoutTTY: false, wantStderr: "", + wantBrowse: "https://github.com/OWNER/REPO", }, } @@ -115,6 +119,7 @@ func Test_RepoView_Web(t *testing.T) { reg := &httpmock.Registry{} reg.StubRepoInfoResponse("OWNER", "REPO", "main") + browser := &cmdutil.TestBrowser{} opts := &ViewOptions{ Web: true, HttpClient: func() (*http.Client, error) { @@ -123,6 +128,7 @@ func Test_RepoView_Web(t *testing.T) { BaseRepo: func() (ghrepo.Interface, error) { return ghrepo.New("OWNER", "REPO"), nil }, + Browser: browser, } io, _, stdout, stderr := iostreams.Test() @@ -132,9 +138,8 @@ func Test_RepoView_Web(t *testing.T) { t.Run(tt.name, func(t *testing.T) { io.SetStdoutTTY(tt.stdoutTTY) - cs, teardown := run.Stub() + _, teardown := run.Stub() defer teardown(t) - cs.Register(`https://github\.com/OWNER/REPO$`, 0, "") if err := viewRun(opts); err != nil { t.Errorf("viewRun() error = %v", err) @@ -142,6 +147,7 @@ func Test_RepoView_Web(t *testing.T) { assert.Equal(t, "", stdout.String()) assert.Equal(t, tt.wantStderr, stderr.String()) reg.Verify(t) + browser.Verify(t, tt.wantBrowse) }) } } diff --git a/pkg/cmdutil/factory.go b/pkg/cmdutil/factory.go index eb9546b52..96b6ec0b8 100644 --- a/pkg/cmdutil/factory.go +++ b/pkg/cmdutil/factory.go @@ -9,8 +9,14 @@ import ( "github.com/cli/cli/pkg/iostreams" ) +type Browser interface { + Browse(string) error +} + type Factory struct { - IOStreams *iostreams.IOStreams + IOStreams *iostreams.IOStreams + Browser Browser + HttpClient func() (*http.Client, error) BaseRepo func() (ghrepo.Interface, error) Remotes func() (context.Remotes, error) diff --git a/pkg/cmdutil/web_browser.go b/pkg/cmdutil/web_browser.go new file mode 100644 index 000000000..aa33373cb --- /dev/null +++ b/pkg/cmdutil/web_browser.go @@ -0,0 +1,83 @@ +package cmdutil + +import ( + "io" + "os/exec" + + "github.com/cli/browser" + "github.com/cli/safeexec" + "github.com/google/shlex" +) + +func NewBrowser(launcher string, stdout, stderr io.Writer) Browser { + return &webBrowser{ + launcher: launcher, + stdout: stdout, + stderr: stderr, + } +} + +type webBrowser struct { + launcher string + stdout io.Writer + stderr io.Writer +} + +func (b *webBrowser) Browse(url string) error { + if b.launcher != "" { + launcherArgs, err := shlex.Split(b.launcher) + if err != nil { + return err + } + launcherExe, err := safeexec.LookPath(launcherArgs[0]) + if err != nil { + return err + } + args := append(launcherArgs[1:], url) + cmd := exec.Command(launcherExe, args...) + cmd.Stdout = b.stdout + cmd.Stderr = b.stderr + return cmd.Run() + } + + return browser.OpenURL(url) +} + +type TestBrowser struct { + urls []string +} + +func (b *TestBrowser) Browse(url string) error { + b.urls = append(b.urls, url) + return nil +} + +func (b *TestBrowser) BrowsedURL() string { + if len(b.urls) > 0 { + return b.urls[0] + } + return "" +} + +type _testing interface { + Errorf(string, ...interface{}) + Helper() +} + +func (b *TestBrowser) Verify(t _testing, url string) { + t.Helper() + if url != "" { + switch len(b.urls) { + case 0: + t.Errorf("expected browser to open URL %q, but it was never invoked", url) + case 1: + if url != b.urls[0] { + t.Errorf("expected browser to open URL %q, got %q", url, b.urls[0]) + } + default: + t.Errorf("expected browser to open one URL, but was invoked %d times", len(b.urls)) + } + } else if len(b.urls) > 0 { + t.Errorf("expected no browser to open, but was invoked %d times: %v", len(b.urls), b.urls) + } +} diff --git a/test/helpers.go b/test/helpers.go index 0cef94802..5ca900f59 100644 --- a/test/helpers.go +++ b/test/helpers.go @@ -7,7 +7,9 @@ import ( // TODO copypasta from command package type CmdOut struct { - OutBuf, ErrBuf *bytes.Buffer + OutBuf *bytes.Buffer + ErrBuf *bytes.Buffer + BrowsedURL string } func (c CmdOut) String() string { diff --git a/utils/utils.go b/utils/utils.go index 57b39b5ba..e614d3dd0 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -5,27 +5,8 @@ import ( "net/url" "strings" "time" - - "github.com/cli/cli/internal/run" - "github.com/cli/cli/pkg/browser" ) -// OpenInBrowser opens the url in a web browser based on OS and $BROWSER environment variable -func OpenInBrowser(url string) error { - browseCmd, err := browser.Command(url) - if err != nil { - return err - } - err = run.PrepareCmd(browseCmd).Run() - if err != nil { - browserEnv := browser.FromEnv() - if browserEnv != "" { - return fmt.Errorf("%w\nNote: check your BROWSER environment variable", err) - } - } - return err -} - func Pluralize(num int, thing string) string { if num == 1 { return fmt.Sprintf("%d %s", num, thing) From e53d02b680b1e8dd3ecc84462cdb53bf883a5c13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mislav=20Marohni=C4=87?= Date: Mon, 22 Mar 2021 12:55:04 +0100 Subject: [PATCH 2/2] Add back isolated tests for issue/PR lookup by argument --- pkg/cmd/issue/shared/lookup_test.go | 102 ++++++++++++++++++++++++++ pkg/cmd/pr/shared/lookup_test.go | 108 ++++++++++++++++++++++++++++ 2 files changed, 210 insertions(+) create mode 100644 pkg/cmd/issue/shared/lookup_test.go create mode 100644 pkg/cmd/pr/shared/lookup_test.go diff --git a/pkg/cmd/issue/shared/lookup_test.go b/pkg/cmd/issue/shared/lookup_test.go new file mode 100644 index 000000000..a34c1fbb5 --- /dev/null +++ b/pkg/cmd/issue/shared/lookup_test.go @@ -0,0 +1,102 @@ +package shared + +import ( + "net/http" + "testing" + + "github.com/cli/cli/api" + "github.com/cli/cli/internal/ghrepo" + "github.com/cli/cli/pkg/httpmock" +) + +func TestIssueFromArg(t *testing.T) { + type args struct { + baseRepoFn func() (ghrepo.Interface, error) + selector string + } + tests := []struct { + name string + args args + httpStub func(*httpmock.Registry) + wantIssue int + wantRepo string + wantErr bool + }{ + { + name: "number argument", + args: args{ + selector: "13", + baseRepoFn: func() (ghrepo.Interface, error) { + return ghrepo.FromFullName("OWNER/REPO") + }, + }, + httpStub: func(r *httpmock.Registry) { + r.Register( + httpmock.GraphQL(`query IssueByNumber\b`), + httpmock.StringResponse(`{"data":{"repository":{ + "hasIssuesEnabled": true, + "issue":{"number":13} + }}}`)) + }, + wantIssue: 13, + wantRepo: "https://github.com/OWNER/REPO", + }, + { + name: "number with hash argument", + args: args{ + selector: "#13", + baseRepoFn: func() (ghrepo.Interface, error) { + return ghrepo.FromFullName("OWNER/REPO") + }, + }, + httpStub: func(r *httpmock.Registry) { + r.Register( + httpmock.GraphQL(`query IssueByNumber\b`), + httpmock.StringResponse(`{"data":{"repository":{ + "hasIssuesEnabled": true, + "issue":{"number":13} + }}}`)) + }, + wantIssue: 13, + wantRepo: "https://github.com/OWNER/REPO", + }, + { + name: "URL argument", + args: args{ + selector: "https://example.org/OWNER/REPO/issues/13#comment-123", + baseRepoFn: nil, + }, + httpStub: func(r *httpmock.Registry) { + r.Register( + httpmock.GraphQL(`query IssueByNumber\b`), + httpmock.StringResponse(`{"data":{"repository":{ + "hasIssuesEnabled": true, + "issue":{"number":13} + }}}`)) + }, + wantIssue: 13, + wantRepo: "https://example.org/OWNER/REPO", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + reg := &httpmock.Registry{} + if tt.httpStub != nil { + tt.httpStub(reg) + } + httpClient := &http.Client{Transport: reg} + issue, repo, err := IssueFromArg(api.NewClientFromHTTP(httpClient), tt.args.baseRepoFn, tt.args.selector) + if (err != nil) != tt.wantErr { + t.Errorf("IssueFromArg() error = %v, wantErr %v", err, tt.wantErr) + return + } + if issue.Number != tt.wantIssue { + t.Errorf("want issue #%d, got #%d", tt.wantIssue, issue.Number) + } + repoURL := ghrepo.GenerateRepoURL(repo, "") + if repoURL != tt.wantRepo { + t.Errorf("want repo %s, got %s", tt.wantRepo, repoURL) + } + }) + } +} diff --git a/pkg/cmd/pr/shared/lookup_test.go b/pkg/cmd/pr/shared/lookup_test.go new file mode 100644 index 000000000..4d843d7ae --- /dev/null +++ b/pkg/cmd/pr/shared/lookup_test.go @@ -0,0 +1,108 @@ +package shared + +import ( + "net/http" + "testing" + + "github.com/cli/cli/api" + "github.com/cli/cli/context" + "github.com/cli/cli/internal/ghrepo" + "github.com/cli/cli/pkg/httpmock" +) + +func TestPRFromArgs(t *testing.T) { + type args struct { + baseRepoFn func() (ghrepo.Interface, error) + branchFn func() (string, error) + remotesFn func() (context.Remotes, error) + selector string + } + tests := []struct { + name string + args args + httpStub func(*httpmock.Registry) + wantPR int + wantRepo string + wantErr bool + }{ + { + name: "number argument", + args: args{ + selector: "13", + baseRepoFn: func() (ghrepo.Interface, error) { + return ghrepo.FromFullName("OWNER/REPO") + }, + }, + httpStub: func(r *httpmock.Registry) { + r.Register( + httpmock.GraphQL(`query PullRequestByNumber\b`), + httpmock.StringResponse(`{"data":{"repository":{ + "pullRequest":{"number":13} + }}}`)) + }, + wantPR: 13, + wantRepo: "https://github.com/OWNER/REPO", + }, + { + name: "number with hash argument", + args: args{ + selector: "#13", + baseRepoFn: func() (ghrepo.Interface, error) { + return ghrepo.FromFullName("OWNER/REPO") + }, + }, + httpStub: func(r *httpmock.Registry) { + r.Register( + httpmock.GraphQL(`query PullRequestByNumber\b`), + httpmock.StringResponse(`{"data":{"repository":{ + "pullRequest":{"number":13} + }}}`)) + }, + wantPR: 13, + wantRepo: "https://github.com/OWNER/REPO", + }, + { + name: "URL argument", + args: args{ + selector: "https://example.org/OWNER/REPO/pull/13/files", + baseRepoFn: nil, + }, + httpStub: func(r *httpmock.Registry) { + r.Register( + httpmock.GraphQL(`query PullRequest_fields\b`), + httpmock.StringResponse(`{"data":{}}`)) + r.Register( + httpmock.GraphQL(`query PullRequest_fields2\b`), + httpmock.StringResponse(`{"data":{}}`)) + r.Register( + httpmock.GraphQL(`query PullRequestByNumber\b`), + httpmock.StringResponse(`{"data":{"repository":{ + "pullRequest":{"number":13} + }}}`)) + }, + wantPR: 13, + wantRepo: "https://example.org/OWNER/REPO", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + reg := &httpmock.Registry{} + if tt.httpStub != nil { + tt.httpStub(reg) + } + httpClient := &http.Client{Transport: reg} + pr, repo, err := PRFromArgs(api.NewClientFromHTTP(httpClient), tt.args.baseRepoFn, tt.args.branchFn, tt.args.remotesFn, tt.args.selector) + if (err != nil) != tt.wantErr { + t.Errorf("IssueFromArg() error = %v, wantErr %v", err, tt.wantErr) + return + } + if pr.Number != tt.wantPR { + t.Errorf("want issue #%d, got #%d", tt.wantPR, pr.Number) + } + repoURL := ghrepo.GenerateRepoURL(repo, "") + if repoURL != tt.wantRepo { + t.Errorf("want repo %s, got %s", tt.wantRepo, repoURL) + } + }) + } +}