diff -Nru gist-5.1.0/build/gist gist-6.0.0/build/gist --- gist-5.1.0/build/gist 2020-02-12 18:47:31.000000000 +0000 +++ gist-6.0.0/build/gist 2020-08-27 02:27:56.000000000 +0000 @@ -1318,7 +1318,7 @@ module Gist extend self - VERSION = '5.1.0' + VERSION = '6.0.0' # A list of clipboard commands with copy and paste support. CLIPBOARD_COMMANDS = { @@ -1329,12 +1329,16 @@ } GITHUB_API_URL = URI("https://api.github.com/") + GITHUB_URL = URI("https://github.com/") GIT_IO_URL = URI("https://git.io") GITHUB_BASE_PATH = "" GHE_BASE_PATH = "/api/v3" + GITHUB_CLIENT_ID = '4f7ec0d4eab38e74384e' + URL_ENV_NAME = "GITHUB_URL" + CLIENT_ID_ENV_NAME = "GIST_CLIENT_ID" USER_AGENT = "gist/#{VERSION} (Net::HTTP, #{RUBY_DESCRIPTION})" @@ -1350,7 +1354,7 @@ module AuthTokenFile def self.filename if ENV.key?(URL_ENV_NAME) - File.expand_path "~/.gist.#{ENV[URL_ENV_NAME].gsub(/:/, '.').gsub(/[^a-z0-9.]/, '')}" + File.expand_path "~/.gist.#{ENV[URL_ENV_NAME].gsub(/:/, '.').gsub(/[^a-z0-9.-]/, '')}" else File.expand_path "~/.gist" end @@ -1506,19 +1510,12 @@ url = "#{base_path}" if user == "" - access_token = auth_token() - if access_token.to_s != '' - url << "/gists?per_page=100" - get_gist_pages(url, access_token) - else - raise Error, "Not authenticated. Use 'gist --login' to login or 'gist -l username' to view public gists." - end - + url << "/gists?per_page=100" else url << "/users/#{user}/gists?per_page=100" - get_gist_pages(url) end + get_gist_pages(url, auth_token()) end def read_gist(id, file_name=nil) @@ -1642,15 +1639,71 @@ # Log the user into gist. # + def login!(credentials={}) + if (login_url == GITHUB_URL || ENV.key?(CLIENT_ID_ENV_NAME)) && credentials.empty? && !ENV.key?('GIST_USE_USERNAME_AND_PASSWORD') + device_flow_login! + else + access_token_login!(credentials) + end + end + + def device_flow_login! + puts "Requesting login parameters..." + request = Net::HTTP::Post.new("/login/device/code") + request.body = JSON.dump({ + :scope => 'gist', + :client_id => client_id, + }) + request.content_type = 'application/json' + request['accept'] = "application/json" + response = http(login_url, request) + + if response.code != '200' + raise Error, "HTTP #{response.code}: #{response.body}" + end + + body = JSON.parse(response.body) + + puts "Please sign in at #{body['verification_uri']}" + puts " and enter code: #{body['user_code']}" + device_code = body['device_code'] + interval = body['interval'] + + loop do + sleep(interval.to_i) + request = Net::HTTP::Post.new("/login/oauth/access_token") + request.body = JSON.dump({ + :client_id => client_id, + :grant_type => 'urn:ietf:params:oauth:grant-type:device_code', + :device_code => device_code + }) + request.content_type = 'application/json' + request['Accept'] = 'application/json' + response = http(login_url, request) + if response.code != '200' + raise Error, "HTTP #{response.code}: #{response.body}" + end + body = JSON.parse(response.body) + break unless body['error'] == 'authorization_pending' + end + + if body['error'] + raise Error, body['error_description'] + end + + AuthTokenFile.write JSON.parse(response.body)['access_token'] + + puts "Success! #{ENV[URL_ENV_NAME] || "https://github.com/"}settings/connections/applications/#{client_id}" + end + + # Logs the user into gist. + # # This method asks the user for a username and password, and tries to obtain # and OAuth2 access token, which is then stored in ~/.gist # # @raise [Gist::Error] if something went wrong - # @param [Hash] credentials login details - # @option credentials [String] :username - # @option credentials [String] :password # @see http://developer.github.com/v3/oauth/ - def login!(credentials={}) + def access_token_login!(credentials={}) puts "Obtaining OAuth2 access_token from GitHub." loop do print "GitHub username: " @@ -1707,7 +1760,11 @@ env = ENV['http_proxy'] || ENV['HTTP_PROXY'] connection = if env proxy = URI(env) - Net::HTTP::Proxy(proxy.host, proxy.port).new(uri.host, uri.port) + if proxy.user + Net::HTTP::Proxy(proxy.host, proxy.port, proxy.user, proxy.password).new(uri.host, uri.port) + else + Net::HTTP::Proxy(proxy.host, proxy.port).new(uri.host, uri.port) + end else Net::HTTP.new(uri.host, uri.port) end @@ -1861,11 +1918,19 @@ ENV.key?(URL_ENV_NAME) ? GHE_BASE_PATH : GITHUB_BASE_PATH end + def login_url + ENV.key?(URL_ENV_NAME) ? URI(ENV[URL_ENV_NAME]) : GITHUB_URL + end + # Get the API URL def api_url ENV.key?(URL_ENV_NAME) ? URI(ENV[URL_ENV_NAME]) : GITHUB_API_URL end + def client_id + ENV.key?(CLIENT_ID_ENV_NAME) ? URI(ENV[CLIENT_ID_ENV_NAME]) : GITHUB_CLIENT_ID + end + def legacy_private_gister? return unless which('git') `git config --global gist.private` =~ /\Ayes|1|true|on\z/i diff -Nru gist-5.1.0/build/gist.1 gist-6.0.0/build/gist.1 --- gist-5.1.0/build/gist.1 2020-02-12 18:47:31.000000000 +0000 +++ gist-6.0.0/build/gist.1 2020-08-27 02:27:56.000000000 +0000 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "GIST" "1" "May 2018" "" "Gist manual" +.TH "GIST" "1" "August 2020" "" "Gist manual" . .SH "NAME" \fBgist\fR \- upload code to https://gist\.github\.com @@ -29,6 +29,12 @@ .IP brew install gist . +.IP "\(bu" 4 +For FreeBSD, gist lives in ports +. +.IP +pkg install gist +. .IP "" 0 . .SH "Command" @@ -121,14 +127,45 @@ .IP "" 0 . .SH "Login" -If you want to associate your gists with your GitHub account, you need to login with gist\. It doesn\'t store your username and password, it just uses them to get an OAuth2 token (with the "gist" permission)\. +Before you use \fBgist\fR for the first time you will need to log in\. There are two supported login flows: +. +.IP "1." 4 +The Github device\-code Oauth flow\. This is the default for authenticating to github\.com, and can be enabled for Github Enterprise by creating an Oauth app, and exporting the environment variable \fBGIST_CLIENT_ID\fR with the client id of the Oauth app\. +. +.IP "2." 4 +The (deprecated) username and password token exchange flow\. This is the default for GitHub Enterprise, and can be used to log into github\.com by exporting the environment variable \fBGIST_USE_USERNAME_AND_PASSWORD\fR\. +. +.IP "" 0 +. +.SS "The device\-code flow" +This flow allows you to obtain a token by logging into GitHub in the browser and typing a verification code\. This is the preferred mechanism\. +. +.IP "" 4 +. +.nf + +gist \-\-login +Requesting login parameters\.\.\. +Please sign in at https://github\.com/login/device + and enter code: XXXX\-XXXX +Success! https://github\.com/settings/connections/applications/4f7ec0d4eab38e74384e +. +.fi +. +.IP "" 0 +. +.P +The returned access_token is stored in \fB~/\.gist\fR and used for all future gisting\. If you need to you can revoke access from https://github\.com/settings/connections/applications/4f7ec0d4eab38e74384e\. +. +.SS "The username\-password flow" +This flow asks for your GitHub username and password (and 2FA code), and exchanges them for a token with the "gist" permission (your username and password are not stored)\. This mechanism is deprecated by GitHub, but may still work with GitHub Enterprise\. . .IP "" 4 . .nf gist \-\-login -Obtaining OAuth2 access_token from github\. +Obtaining OAuth2 access_token from GitHub\. GitHub username: ConradIrwin GitHub password: 2\-factor auth code: @@ -141,19 +178,11 @@ .P This token is stored in \fB~/\.gist\fR and used for all future gisting\. If you need to you can revoke it from https://github\.com/settings/tokens, or just delete the file\. . -.IP "\(bu" 4 -After you\'ve done this, you can still upload gists anonymously with \fB\-a\fR\. -. -.IP -gist \-a a\.rb -. -.IP "" 0 -. .P -If you have a complicated authorization requirement you can manually create a token file by pasting a Github token with only the \fBgist\fR permission into a file called \fB~/\.gist\fR\. You can create one from https://github\.com/settings/tokens +If you have a complicated authorization requirement you can manually create a token file by pasting a GitHub token with \fBgist\fR scope (and maybe the \fBuser:email\fR for GitHub Enterprise) into a file called \fB~/\.gist\fR\. You can create one from https://github\.com/settings/tokens . .P -This file should contain only the token (~40 hex characters), and to make it easier to edit, can optionally have a final newline (\en or \er\en)\. +This file should contain only the token (~40 hex characters), and to make it easier to edit, can optionally have a final newline (\fB\en\fR or \fB\er\en\fR)\. . .P For example, one way to create this file would be to run: @@ -162,12 +191,15 @@ . .nf -echo MY_SECRET_TOKEN > ~/\.gist +(umask 0077 && echo MY_SECRET_TOKEN > ~/\.gist) . .fi . .IP "" 0 . +.P +The \fBumask\fR ensures that the file is only accessible from your user account\. +. .SS "GitHub Enterprise" If you\'d like \fBgist\fR to use your locally installed GitHub Enterprise \fIhttps://enterprise\.github\.com/\fR, you need to export the \fBGITHUB_URL\fR environment variable (usually done in your \fB~/\.bashrc\fR)\. . @@ -182,7 +214,7 @@ .IP "" 0 . .P -Once you\'ve done this and restarted your terminal (or run \fBsource ~/\.bashrc\fR), gist will automatically use github enterprise instead of the public github\.com +Once you\'ve done this and restarted your terminal (or run \fBsource ~/\.bashrc\fR), gist will automatically use GitHub Enterprise instead of the public github\.com . .P Your token for GitHub Enterprise will be stored in \fB\.gist\.\.[\.]\fR (e\.g\. \fB~/\.gist\.http\.github\.internal\.example\.com\fR for the GITHUB_URL example above) instead of \fB~/\.gist\fR\. @@ -218,9 +250,6 @@ \fB:update\fR to update an existing gist (can be a URL or an id)\. . .IP "\(bu" 4 -\fB:anonymous\fR to submit an anonymous gist (default is false)\. -. -.IP "\(bu" 4 \fB:copy\fR to copy the resulting URL to the clipboard (default is false)\. . .IP "\(bu" 4 @@ -229,7 +258,7 @@ .IP "" 0 . .P -NOTE: The access_token must have the "gist" scope\. +NOTE: The access_token must have the \fBgist\fR scope and may also require the \fBuser:email\fR scope\. . .IP "\(bu" 4 If you want to upload multiple files in the same gist, you can: diff -Nru gist-5.1.0/debian/changelog gist-6.0.0/debian/changelog --- gist-5.1.0/debian/changelog 2020-04-01 22:01:33.000000000 +0000 +++ gist-6.0.0/debian/changelog 2020-11-08 01:52:06.000000000 +0000 @@ -1,3 +1,15 @@ +gist (6.0.0-1) unstable; urgency=medium + + * Team upload. + * New upstream release. + * d/watch: Use the github sources. + * d/patches/webmock: Remove patch. Applied upstream. + * d/patches/gemspec-no-git: Refresh patch. + * d/patches/rename: Ditto. + * d/patches/series: Ditto. + + -- Daniel Leidert Sun, 08 Nov 2020 02:52:06 +0100 + gist (5.1.0-1) unstable; urgency=medium * Team upload. diff -Nru gist-5.1.0/debian/control gist-6.0.0/debian/control --- gist-5.1.0/debian/control 2020-04-01 22:01:33.000000000 +0000 +++ gist-6.0.0/debian/control 2020-11-08 01:52:06.000000000 +0000 @@ -1,5 +1,5 @@ Source: gist -Maintainer: Debian Ruby Extras Maintainers +Maintainer: Debian Ruby Team Uploaders: Chris Hofstaedtler Section: net Testsuite: autopkgtest-pkg-ruby diff -Nru gist-5.1.0/debian/patches/gemspec-no-git gist-6.0.0/debian/patches/gemspec-no-git --- gist-5.1.0/debian/patches/gemspec-no-git 2020-04-01 22:01:33.000000000 +0000 +++ gist-6.0.0/debian/patches/gemspec-no-git 2020-11-08 01:52:06.000000000 +0000 @@ -2,20 +2,21 @@ Date: Tue, 11 Jul 2017 19:49:50 +0000 Subject: Avoid git in gemspec +Forwarded: not-needed --- gist.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gist.gemspec b/gist.gemspec -index 915e7fe..91fd79e 100644 +index 7708a38..dd5f711 100644 --- a/gist.gemspec +++ b/gist.gemspec @@ -9,7 +9,7 @@ Gem::Specification.new do |s| s.email = ['conrad.irwin@gmail.com', 'rkingist@sharpsaw.org'] s.authors = ['Conrad Irwin', '☈king'] s.license = 'MIT' -- s.files = `git ls-files`.split("\n") -+ s.files = Dir.glob('bin/**') + Dir.glob('lib/**') + Dir.glob('spec/**') + ['README.md', 'LICENSE.MIT'] +- s.files = `git ls-files`.split("\n") - Dir.glob("build/*") - [".gitignore"] ++ s.files = Dir.glob("**/*").select {|v| v !~ /^debian/} s.require_paths = ["lib"] s.executables << 'gist-paste' diff -Nru gist-5.1.0/debian/patches/mochafix gist-6.0.0/debian/patches/mochafix --- gist-5.1.0/debian/patches/mochafix 2020-04-01 22:01:33.000000000 +0000 +++ gist-6.0.0/debian/patches/mochafix 2020-11-08 01:52:06.000000000 +0000 @@ -3,6 +3,7 @@ Subject: Fix mochas Bug-Debian: https://bugs.debian.org/952028 +Forwarded: https://github.com/defunkt/gist/issues/313 --- spec/auth_token_file_spec.rb | 2 ++ 1 file changed, 2 insertions(+) diff -Nru gist-5.1.0/debian/patches/rename gist-6.0.0/debian/patches/rename --- gist-5.1.0/debian/patches/rename 2020-04-01 22:01:33.000000000 +0000 +++ gist-6.0.0/debian/patches/rename 2020-11-08 01:52:06.000000000 +0000 @@ -2,16 +2,17 @@ Date: Sun, 6 Oct 2013 20:00:40 +0200 Subject: Rename gist script to gist-paste, to not conflict with yorick. +Forwarded: not-needed Bug-Debian: https://bugs.debian.org/722662 --- - README.md | 62 ++++++++++++++++++++++++++++++------------------------------ + README.md | 68 ++++++++++++++++++++++++++++++------------------------------ Rakefile | 2 +- - build/gist.1 | 26 ++++++++++++------------- + build/gist.1 | 58 ++++++++++++++++++++++++++------------------------- gist.gemspec | 2 +- - 4 files changed, 46 insertions(+), 46 deletions(-) + 4 files changed, 66 insertions(+), 64 deletions(-) diff --git a/README.md b/README.md -index a2c1f60..8a13ba0 100644 +index c55f322..7ce6a65 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ @@ -27,7 +28,7 @@ upload content to https://gist.github.com/. ## Installation -@@ -30,65 +30,65 @@ upload content to https://gist.github.com/. +@@ -30,61 +30,61 @@ upload content to https://gist.github.com/. ‌To upload the contents of `a.rb` just: @@ -79,7 +80,7 @@ + gist-paste -e "Foo.bar", "a.py" => "Foo.bar") + +-‌If you'd rather use gist's builtin access_token, then you can force the user ++‌If you'd rather use gist-paste's builtin access_token, then you can force the user + to obtain one by calling: + + Gist.login! +@@ -190,12 +190,12 @@ in `~/.gist`, where it can later be read by `Gist.gist` ## Configuration @@ -161,7 +193,7 @@ environment variable: export BROWSER=google-chrome -@@ -186,7 +186,7 @@ If clipboard or browser integration don't work on your platform, please file a b +@@ -204,7 +204,7 @@ If clipboard or browser integration don't work on your platform, please file a b (more ideally) a pull request. If you need to use an HTTP proxy to access the internet, export the `HTTP_PROXY` or @@ -184,10 +216,23 @@ task :standalone do diff --git a/build/gist.1 b/build/gist.1 -index 66fb503..14ee1d3 100644 +index 850c556..6e52791 100644 --- a/build/gist.1 +++ b/build/gist.1 -@@ -37,67 +37,67 @@ brew install gist +@@ -4,10 +4,10 @@ + .TH "GIST" "1" "August 2020" "" "Gist manual" + . + .SH "NAME" +-\fBgist\fR \- upload code to https://gist\.github\.com ++\fBgist-paste\fR \- upload code to https://gist\.github\.com + . + .SH "Synopsis" +-The gist gem provides a \fBgist\fR command that you can use from your terminal to upload content to https://gist\.github\.com/\. ++The gist gem provides a \fBgist-paste\fR command that you can use from your terminal to upload content to https://gist\.github\.com/\. + . + .SH "Installation" + . +@@ -43,67 +43,69 @@ pkg install gist To upload the contents of \fBa\.rb\fR just: . .IP @@ -251,7 +296,8 @@ +gist-paste \-e \.[\.]\fR (e\.g\. \fB~/\.gist\.http\.github\.internal\.example\.com\fR for the GITHUB_URL example above) instead of \fB~/\.gist\fR\. + . + .P +-If you have multiple servers or use Enterprise and public GitHub often, you can work around this by creating scripts that set the env var and then run \fBgist\fR\. Keep in mind that to use the public GitHub you must unset the env var\. Just setting it to the public URL will not work\. Use \fBunset GITHUB_URL\fR ++If you have multiple servers or use Enterprise and public GitHub often, you can work around this by creating scripts that set the env var and then run \fBgist-paste\fR\. Keep in mind that to use the public GitHub you must unset the env var\. Just setting it to the public URL will not work\. Use \fBunset GITHUB_URL\fR + . + .SS "Token file format" +-If you cannot use passwords, as most Enterprise installations do, you can generate the token via the web interface and then simply save the string in the correct file\. Avoid line breaks or you might see: \fB$ gist \-l Error: Bad credentials\fR ++If you cannot use passwords, as most Enterprise installations do, you can generate the token via the web interface and then simply save the string in the correct file\. Avoid line breaks or you might see: \fB$ gist-paste \-l Error: Bad credentials\fR + . + .TP + You can also use Gist as a library from inside your ruby code: +@@ -267,7 +269,7 @@ If you want to upload multiple files in the same gist, you can: + Gist\.multi_gist("a\.rb" => "Foo\.bar", "a\.py" => "Foo\.bar") + . + .IP "\(bu" 4 +-If you\'d rather use gist\'s builtin access_token, then you can force the user to obtain one by calling: ++If you\'d rather use gist-paste\'s builtin access_token, then you can force the user to obtain one by calling: + . + .IP + Gist\.login! +@@ -280,13 +282,13 @@ This will take them through the process of obtaining an OAuth2 token, and storin + .SH "Configuration" + . + .IP "\(bu" 4 +-If you\'d like \fB\-o\fR or \fB\-c\fR to be the default when you use the gist executable, add an alias to your \fB~/\.bashrc\fR (or equivalent)\. For example: ++If you\'d like \fB\-o\fR or \fB\-c\fR to be the default when you use the gist-paste executable, add an alias to your \fB~/\.bashrc\fR (or equivalent)\. For example: + . + .IP + alias gist=\'gist \-c\' + . + .IP "\(bu" 4 +-If you\'d prefer gist to open a different browser, then you can export the BROWSER environment variable: ++If you\'d prefer gist-paste to open a different browser, then you can export the BROWSER environment variable: + . + .IP + export BROWSER=google\-chrome +@@ -297,7 +299,7 @@ export BROWSER=google\-chrome + If clipboard or browser integration don\'t work on your platform, please file a bug or (more ideally) a pull request\. + . + .P +-If you need to use an HTTP proxy to access the internet, export the \fBHTTP_PROXY\fR or \fBhttp_proxy\fR environment variable and gist will use it\. ++If you need to use an HTTP proxy to access the internet, export the \fBHTTP_PROXY\fR or \fBhttp_proxy\fR environment variable and gist-paste will use it\. + . + .SH "Meta\-fu" + Thanks to @defunkt and @indirect for writing and maintaining versions 1 through 3\. Thanks to @rking and @ConradIrwin for maintaining version 4\. diff --git a/gist.gemspec b/gist.gemspec -index 3367e55..915e7fe 100644 +index 4d7a2f3..7708a38 100644 --- a/gist.gemspec +++ b/gist.gemspec @@ -12,7 +12,7 @@ Gem::Specification.new do |s| - s.files = `git ls-files`.split("\n") + s.files = `git ls-files`.split("\n") - Dir.glob("build/*") - [".gitignore"] s.require_paths = ["lib"] - s.executables << 'gist' diff -Nru gist-5.1.0/debian/patches/series gist-6.0.0/debian/patches/series --- gist-5.1.0/debian/patches/series 2020-04-01 22:01:33.000000000 +0000 +++ gist-6.0.0/debian/patches/series 2020-11-08 01:52:06.000000000 +0000 @@ -1,4 +1,3 @@ rename -webmock gemspec-no-git mochafix diff -Nru gist-5.1.0/debian/patches/webmock gist-6.0.0/debian/patches/webmock --- gist-5.1.0/debian/patches/webmock 2020-04-01 22:01:33.000000000 +0000 +++ gist-6.0.0/debian/patches/webmock 1970-01-01 00:00:00.000000000 +0000 @@ -1,25 +0,0 @@ -From: Christian Hofstaedtler -Date: Tue, 11 Jul 2017 19:57:07 +0000 -Subject: Support building with current webmock - -Bug-Debian: https://bugs.debian.org/867646 ---- - spec/ghe_spec.rb | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/spec/ghe_spec.rb b/spec/ghe_spec.rb -index 5d88a2f..743cc3e 100644 ---- a/spec/ghe_spec.rb -+++ b/spec/ghe_spec.rb -@@ -5,9 +5,9 @@ describe '...' do - MOCK_USER = 'foo' - MOCK_PASSWORD = 'bar' - -- MOCK_AUTHZ_GHE_URL = "#{MOCK_GHE_PROTOCOL}://#{MOCK_USER}:#{MOCK_PASSWORD}@#{MOCK_GHE_HOST}/api/v3/" -+ MOCK_AUTHZ_GHE_URL = "#{MOCK_GHE_PROTOCOL}://#{MOCK_GHE_HOST}/api/v3/" - MOCK_GHE_URL = "#{MOCK_GHE_PROTOCOL}://#{MOCK_GHE_HOST}/api/v3/" -- MOCK_AUTHZ_GITHUB_URL = "https://#{MOCK_USER}:#{MOCK_PASSWORD}@api.github.com/" -+ MOCK_AUTHZ_GITHUB_URL = "https://api.github.com/" - MOCK_GITHUB_URL = "https://api.github.com/" - - before do diff -Nru gist-5.1.0/debian/watch gist-6.0.0/debian/watch --- gist-5.1.0/debian/watch 2020-04-01 22:01:33.000000000 +0000 +++ gist-6.0.0/debian/watch 2020-11-08 01:52:06.000000000 +0000 @@ -1,2 +1,4 @@ version=4 -https://gemwatch.debian.net/gist .*/gist-(.*).tar.gz +opts=uversionmangle=s/[_.+-]?(rc|pre|dev|beta|alpha|b|a)/~$1/i,\ +filenamemangle=s%.+/v?@ANY_VERSION@(@ARCHIVE_EXT@)%@PACKAGE@-$1$2% \ + https://github.com/defunkt/gist/releases .*/v?@ANY_VERSION@@ARCHIVE_EXT@ diff -Nru gist-5.1.0/gist.gemspec gist-6.0.0/gist.gemspec --- gist-5.1.0/gist.gemspec 2020-02-12 18:47:31.000000000 +0000 +++ gist-6.0.0/gist.gemspec 2020-08-27 02:27:56.000000000 +0000 @@ -9,7 +9,7 @@ s.email = ['conrad.irwin@gmail.com', 'rkingist@sharpsaw.org'] s.authors = ['Conrad Irwin', '☈king'] s.license = 'MIT' - s.files = `git ls-files`.split("\n") + s.files = `git ls-files`.split("\n") - Dir.glob("build/*") - [".gitignore"] s.require_paths = ["lib"] s.executables << 'gist' diff -Nru gist-5.1.0/lib/gist.rb gist-6.0.0/lib/gist.rb --- gist-5.1.0/lib/gist.rb 2020-02-12 18:47:31.000000000 +0000 +++ gist-6.0.0/lib/gist.rb 2020-08-27 02:27:56.000000000 +0000 @@ -12,7 +12,7 @@ module Gist extend self - VERSION = '5.1.0' + VERSION = '6.0.0' # A list of clipboard commands with copy and paste support. CLIPBOARD_COMMANDS = { @@ -23,12 +23,16 @@ } GITHUB_API_URL = URI("https://api.github.com/") + GITHUB_URL = URI("https://github.com/") GIT_IO_URL = URI("https://git.io") GITHUB_BASE_PATH = "" GHE_BASE_PATH = "/api/v3" + GITHUB_CLIENT_ID = '4f7ec0d4eab38e74384e' + URL_ENV_NAME = "GITHUB_URL" + CLIENT_ID_ENV_NAME = "GIST_CLIENT_ID" USER_AGENT = "gist/#{VERSION} (Net::HTTP, #{RUBY_DESCRIPTION})" @@ -44,7 +48,7 @@ module AuthTokenFile def self.filename if ENV.key?(URL_ENV_NAME) - File.expand_path "~/.gist.#{ENV[URL_ENV_NAME].gsub(/:/, '.').gsub(/[^a-z0-9.]/, '')}" + File.expand_path "~/.gist.#{ENV[URL_ENV_NAME].gsub(/:/, '.').gsub(/[^a-z0-9.-]/, '')}" else File.expand_path "~/.gist" end @@ -200,19 +204,12 @@ url = "#{base_path}" if user == "" - access_token = auth_token() - if access_token.to_s != '' - url << "/gists?per_page=100" - get_gist_pages(url, access_token) - else - raise Error, "Not authenticated. Use 'gist --login' to login or 'gist -l username' to view public gists." - end - + url << "/gists?per_page=100" else url << "/users/#{user}/gists?per_page=100" - get_gist_pages(url) end + get_gist_pages(url, auth_token()) end def read_gist(id, file_name=nil) @@ -336,15 +333,71 @@ # Log the user into gist. # + def login!(credentials={}) + if (login_url == GITHUB_URL || ENV.key?(CLIENT_ID_ENV_NAME)) && credentials.empty? && !ENV.key?('GIST_USE_USERNAME_AND_PASSWORD') + device_flow_login! + else + access_token_login!(credentials) + end + end + + def device_flow_login! + puts "Requesting login parameters..." + request = Net::HTTP::Post.new("/login/device/code") + request.body = JSON.dump({ + :scope => 'gist', + :client_id => client_id, + }) + request.content_type = 'application/json' + request['accept'] = "application/json" + response = http(login_url, request) + + if response.code != '200' + raise Error, "HTTP #{response.code}: #{response.body}" + end + + body = JSON.parse(response.body) + + puts "Please sign in at #{body['verification_uri']}" + puts " and enter code: #{body['user_code']}" + device_code = body['device_code'] + interval = body['interval'] + + loop do + sleep(interval.to_i) + request = Net::HTTP::Post.new("/login/oauth/access_token") + request.body = JSON.dump({ + :client_id => client_id, + :grant_type => 'urn:ietf:params:oauth:grant-type:device_code', + :device_code => device_code + }) + request.content_type = 'application/json' + request['Accept'] = 'application/json' + response = http(login_url, request) + if response.code != '200' + raise Error, "HTTP #{response.code}: #{response.body}" + end + body = JSON.parse(response.body) + break unless body['error'] == 'authorization_pending' + end + + if body['error'] + raise Error, body['error_description'] + end + + AuthTokenFile.write JSON.parse(response.body)['access_token'] + + puts "Success! #{ENV[URL_ENV_NAME] || "https://github.com/"}settings/connections/applications/#{client_id}" + end + + # Logs the user into gist. + # # This method asks the user for a username and password, and tries to obtain # and OAuth2 access token, which is then stored in ~/.gist # # @raise [Gist::Error] if something went wrong - # @param [Hash] credentials login details - # @option credentials [String] :username - # @option credentials [String] :password # @see http://developer.github.com/v3/oauth/ - def login!(credentials={}) + def access_token_login!(credentials={}) puts "Obtaining OAuth2 access_token from GitHub." loop do print "GitHub username: " @@ -401,7 +454,11 @@ env = ENV['http_proxy'] || ENV['HTTP_PROXY'] connection = if env proxy = URI(env) - Net::HTTP::Proxy(proxy.host, proxy.port).new(uri.host, uri.port) + if proxy.user + Net::HTTP::Proxy(proxy.host, proxy.port, proxy.user, proxy.password).new(uri.host, uri.port) + else + Net::HTTP::Proxy(proxy.host, proxy.port).new(uri.host, uri.port) + end else Net::HTTP.new(uri.host, uri.port) end @@ -555,11 +612,19 @@ ENV.key?(URL_ENV_NAME) ? GHE_BASE_PATH : GITHUB_BASE_PATH end + def login_url + ENV.key?(URL_ENV_NAME) ? URI(ENV[URL_ENV_NAME]) : GITHUB_URL + end + # Get the API URL def api_url ENV.key?(URL_ENV_NAME) ? URI(ENV[URL_ENV_NAME]) : GITHUB_API_URL end + def client_id + ENV.key?(CLIENT_ID_ENV_NAME) ? URI(ENV[CLIENT_ID_ENV_NAME]) : GITHUB_CLIENT_ID + end + def legacy_private_gister? return unless which('git') `git config --global gist.private` =~ /\Ayes|1|true|on\z/i diff -Nru gist-5.1.0/README.md gist-6.0.0/README.md --- gist-5.1.0/README.md 2020-02-12 18:47:31.000000000 +0000 +++ gist-6.0.0/README.md 2020-08-27 02:27:56.000000000 +0000 @@ -84,9 +84,26 @@ ## Login -If you want to associate your gists with your GitHub account, you need to login -with gist. It doesn't store your username and password, it just uses them to get -an OAuth2 token (with the "gist" permission). +Before you use `gist` for the first time you will need to log in. There are two supported login flows: + +1. The Github device-code Oauth flow. This is the default for authenticating to github.com, and can be enabled for Github Enterprise by creating an Oauth app, and exporting the environment variable `GIST_CLIENT_ID` with the client id of the Oauth app. +2. The (deprecated) username and password token exchange flow. This is the default for GitHub Enterprise, and can be used to log into github.com by exporting the environment variable `GIST_USE_USERNAME_AND_PASSWORD`. + +### The device-code flow + +This flow allows you to obtain a token by logging into GitHub in the browser and typing a verification code. This is the preferred mechanism. + + gist --login + Requesting login parameters... + Please sign in at https://github.com/login/device + and enter code: XXXX-XXXX + Success! https://github.com/settings/connections/applications/4f7ec0d4eab38e74384e + +The returned access_token is stored in `~/.gist` and used for all future gisting. If you need to you can revoke access from https://github.com/settings/connections/applications/4f7ec0d4eab38e74384e. + +### The username-password flow + +This flow asks for your GitHub username and password (and 2FA code), and exchanges them for a token with the "gist" permission (your username and password are not stored). This mechanism is deprecated by GitHub, but may still work with GitHub Enterprise. gist --login Obtaining OAuth2 access_token from GitHub. @@ -102,8 +119,9 @@ #### Password-less login If you have a complicated authorization requirement you can manually create a -token file by pasting a GitHub token with only the `gist` permission into a -file called `~/.gist`. You can create one from https://github.com/settings/tokens +token file by pasting a GitHub token with `gist` scope (and maybe the `user:email` +for GitHub Enterprise) into a file called `~/.gist`. You can create one from +https://github.com/settings/tokens This file should contain only the token (~40 hex characters), and to make it easier to edit, can optionally have a final newline (`\n` or `\r\n`). @@ -156,7 +174,7 @@ * `:copy` to copy the resulting URL to the clipboard (default is false). * `:open` to open the resulting URL in a browser (default is false). -NOTE: The access_token must have the "gist" scope. +NOTE: The access_token must have the `gist` scope and may also require the `user:email` scope. ‌If you want to upload multiple files in the same gist, you can: diff -Nru gist-5.1.0/spec/ghe_spec.rb gist-6.0.0/spec/ghe_spec.rb --- gist-5.1.0/spec/ghe_spec.rb 2020-02-12 18:47:31.000000000 +0000 +++ gist-6.0.0/spec/ghe_spec.rb 2020-08-27 02:27:56.000000000 +0000 @@ -5,10 +5,10 @@ MOCK_USER = 'foo' MOCK_PASSWORD = 'bar' - MOCK_AUTHZ_GHE_URL = "#{MOCK_GHE_PROTOCOL}://#{MOCK_USER}:#{MOCK_PASSWORD}@#{MOCK_GHE_HOST}/api/v3/" + MOCK_AUTHZ_GHE_URL = "#{MOCK_GHE_PROTOCOL}://#{MOCK_GHE_HOST}/api/v3/" MOCK_GHE_URL = "#{MOCK_GHE_PROTOCOL}://#{MOCK_GHE_HOST}/api/v3/" - MOCK_AUTHZ_GITHUB_URL = "https://#{MOCK_USER}:#{MOCK_PASSWORD}@api.github.com/" MOCK_GITHUB_URL = "https://api.github.com/" + MOCK_LOGIN_URL = "https://github.com/" before do @saved_env = ENV[Gist::URL_ENV_NAME] @@ -20,8 +20,15 @@ # stub requests for /authorizations stub_request(:post, /#{MOCK_AUTHZ_GHE_URL}authorizations/). to_return(:status => 201, :body => '{"token": "asdf"}') - stub_request(:post, /#{MOCK_AUTHZ_GITHUB_URL}authorizations/). + stub_request(:post, /#{MOCK_GITHUB_URL}authorizations/). + with(headers: {'Authorization'=>'Basic Zm9vOmJhcg=='}). to_return(:status => 201, :body => '{"token": "asdf"}') + + stub_request(:post, /#{MOCK_LOGIN_URL}login\/device\/code/). + to_return(:status => 200, :body => '{"interval": "0.1", "user_code":"XXXX-XXXX", "device_code": "xxxx", "verification_uri": "https://github.com/login/device"}') + + stub_request(:post, /#{MOCK_LOGIN_URL}login\/oauth\/access_token/). + to_return(:status => 200, :body => '{"access_token":"zzzz"}') end after do @@ -48,7 +55,7 @@ Gist.login! - assert_requested(:post, /#{MOCK_AUTHZ_GITHUB_URL}authorizations/) + assert_requested(:post, /#{MOCK_LOGIN_URL}login\/oauth\/access_token/) end it "should access to #{MOCK_GHE_HOST} when $#{Gist::URL_ENV_NAME} was set" do @@ -65,7 +72,7 @@ $stdin = StringIO.new "#{MOCK_USER}_wrong\n#{MOCK_PASSWORD}_wrong\n" Gist.login! :username => MOCK_USER, :password => MOCK_PASSWORD - assert_requested(:post, /#{MOCK_AUTHZ_GITHUB_URL}authorizations/) + assert_requested(:post, /#{MOCK_GITHUB_URL}authorizations/) end end