From 0fc3ad0adb66387e94e37921bd331d08922761e5 Mon Sep 17 00:00:00 2001 From: Jelani Woods Date: Tue, 22 Jun 2021 22:17:13 -0500 Subject: [PATCH 01/12] Add script and route to login to Heroku --- lib/scripts/heroku_login.exp | 58 ++++++++++++++++++++++++++++++++++++ lib/views/status.erb | 35 ++++++++++++++++++++-- lib/web_git.rb | 17 +++++++++++ lib/web_git/heroku.rb | 12 ++++++++ web_git.gemspec | 1 + 5 files changed, 121 insertions(+), 2 deletions(-) create mode 100755 lib/scripts/heroku_login.exp create mode 100644 lib/web_git/heroku.rb diff --git a/lib/scripts/heroku_login.exp b/lib/scripts/heroku_login.exp new file mode 100755 index 0000000..b09899a --- /dev/null +++ b/lib/scripts/heroku_login.exp @@ -0,0 +1,58 @@ +#!/usr/bin/expect -f +# +# This Expect script was generated by autoexpect on Tue Jun 22 17:49:07 2021 +# Expect and autoexpect were both written by Don Libes, NIST. +# +# Note that autoexpect does not guarantee a working script. It +# necessarily has to guess about certain things. Two reasons a script +# might fail are: +# +# 1) timing - A surprising number of programs (rn, ksh, zsh, telnet, +# etc.) and devices discard or ignore keystrokes that arrive "too +# quickly" after prompts. If you find your new script hanging up at +# one spot, try adding a short sleep just before the previous send. +# Setting "force_conservative" to 1 (see below) makes Expect do this +# automatically - pausing briefly before sending each character. This +# pacifies every program I know of. The -c flag makes the script do +# this in the first place. The -C flag allows you to define a +# character to toggle this mode off and on. + +set force_conservative 0 ;# set to 1 to force conservative mode even if + ;# script wasn't run conservatively originally +if {$force_conservative} { + set send_slow {1 .1} + proc send {ignore arg} { + sleep .1 + exp_send -s -- $arg + } +} + +# +# 2) differing output - Some programs produce different output each time +# they run. The "date" command is an obvious example. Another is +# ftp, if it produces throughput statistics at the end of a file +# transfer. If this causes a problem, delete these patterns or replace +# them with wildcards. An alternative is to use the -p flag (for +# "prompt") which makes Expect only look for the last line of output +# (i.e., the prompt). The -P flag allows you to define a character to +# toggle this mode off and on. +# +# Read the man page for more info. +# +# -Don + +set email [lindex $argv 0]; +set password [lindex $argv 1]; + +set timeout -1 +spawn heroku login --interactive +match_max 100000 +expect -re {(.|\n)*Enter your login credentials\r} +send -- "$email" +expect -exact "$email" +send -- "\r" +expect -re {(.|\n)*Password:} +send -- "$password" +expect -re {\*.*} +send -- "\r" +expect eof diff --git a/lib/views/status.erb b/lib/views/status.erb index 48bf557..0a11ed6 100644 --- a/lib/views/status.erb +++ b/lib/views/status.erb @@ -16,6 +16,14 @@ .commit:hover { color: red; } + .heroku { + background-color: #7952b3; + } + .btn-outline-heroku { + color: #7952b3; + border-color: #7952b3; + background-color: #fff; + } Git Client @@ -157,9 +165,8 @@ <%= @remotes.first %> -
<%= @status %>
- + <%# TODO why is this here? %> <% if @current_branch != "master" %>
+
+ +
  • New Branch
  • diff --git a/lib/web_git.rb b/lib/web_git.rb index 78815de..2fa0344 100644 --- a/lib/web_git.rb +++ b/lib/web_git.rb @@ -5,6 +5,7 @@ module WebGit require "active_support" require "web_git/diff" require "web_git/graph" + require "web_git/heroku" require "web_git/string" require "sinatra" require "date" @@ -47,6 +48,7 @@ class Server < Sinatra::Base @current_branch = git.current_branch # g.branch(@current_branch).checkout # maybe? + # TODO use git gem for status @status = `git status` @diff = git.diff @diff = Diff.diff_to_html(git.diff.to_s) @@ -74,6 +76,8 @@ class Server < Sinatra::Base branch_b[:log].last[:date] <=> branch_a[:log].last[:date] end + # TODO heroku stuff + @heroku_auth = WebGit::Heroku.whoami erb :status end @@ -137,6 +141,19 @@ class Server < Sinatra::Base redirect to("/") end + post "/heroku/login" do + # TODO ensure required fields + email = params[:heroku_email] + password = params[:heroku_password] + + WebGit::Heroku.authenticate(email, password) + + # TODO actually handle these + set_flash(:notice, "Successfully logged into Heroku.") + set_flash(:alert, "Failed to log in to Heroku successfully.") + redirect to("/") + end + protected def git diff --git a/lib/web_git/heroku.rb b/lib/web_git/heroku.rb new file mode 100644 index 0000000..0ca52f5 --- /dev/null +++ b/lib/web_git/heroku.rb @@ -0,0 +1,12 @@ +module WebGit + class Heroku + def self.authenticate(email, password) + script = File.join( File.dirname(__FILE__), '/../scripts/heroku_login.exp') + `#{script} #{email} #{password}` + end + + def whoami + `heroku whoami`.chomp + end + end +end diff --git a/web_git.gemspec b/web_git.gemspec index aeb390c..a1b6762 100644 --- a/web_git.gemspec +++ b/web_git.gemspec @@ -29,6 +29,7 @@ Gem::Specification.new do |s| "Rakefile", "VERSION", "lib/generators/web_git/install_generator.rb", + "lib/scripts/heroku_login.exp", "lib/views/status.erb", "lib/web_git.rb", "lib/web_git/diff.rb", From 3c7defe3f971980b75b79bf62055a78258921ad8 Mon Sep 17 00:00:00 2001 From: Jelani Woods Date: Wed, 23 Jun 2021 19:54:19 -0500 Subject: [PATCH 02/12] Make whoami a class method How did it work when I tested before? --- lib/web_git/heroku.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web_git/heroku.rb b/lib/web_git/heroku.rb index 0ca52f5..5052ab6 100644 --- a/lib/web_git/heroku.rb +++ b/lib/web_git/heroku.rb @@ -5,7 +5,7 @@ def self.authenticate(email, password) `#{script} #{email} #{password}` end - def whoami + def self.whoami `heroku whoami`.chomp end end From b61de848d82030cd2f521168ec85215c5a1679f5 Mon Sep 17 00:00:00 2001 From: Jelani Woods Date: Wed, 23 Jun 2021 21:08:12 -0500 Subject: [PATCH 03/12] Fix timeout handling --- lib/web_git.rb | 12 ++++++------ lib/web_git/heroku.rb | 15 ++++++++++++++- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/lib/web_git.rb b/lib/web_git.rb index 2fa0344..13f4c8e 100644 --- a/lib/web_git.rb +++ b/lib/web_git.rb @@ -142,15 +142,15 @@ class Server < Sinatra::Base end post "/heroku/login" do - # TODO ensure required fields email = params[:heroku_email] password = params[:heroku_password] - WebGit::Heroku.authenticate(email, password) - - # TODO actually handle these - set_flash(:notice, "Successfully logged into Heroku.") - set_flash(:alert, "Failed to log in to Heroku successfully.") + begin + WebGit::Heroku.authenticate(email, password) + set_flash(:notice, "Successfully logged into Heroku.") + rescue => exception + set_flash(:alert, "Failed to log in to Heroku successfully. #{exception.message}") + end redirect to("/") end diff --git a/lib/web_git/heroku.rb b/lib/web_git/heroku.rb index 5052ab6..5cf8d96 100644 --- a/lib/web_git/heroku.rb +++ b/lib/web_git/heroku.rb @@ -1,8 +1,21 @@ +require 'timeout' module WebGit class Heroku def self.authenticate(email, password) + raise ArgumentError.new("Email and password cannot be blank.") if email.blank? || password.blank? script = File.join( File.dirname(__FILE__), '/../scripts/heroku_login.exp') - `#{script} #{email} #{password}` + script_pid = Process.spawn("#{script} #{email} #{password}") + begin + status = Timeout.timeout(30) do + # TODO how to check if auth fails? + p Process.wait(script_pid) + end + puts "--------" + p status + rescue Timeout::Error + Process.kill('TERM', script_pid) + raise Timeout::Error.new("Sign in took longer than 30 seconds.") + end end def self.whoami From bb98b931e6577b76c75afa1f8dfba76b6379034a Mon Sep 17 00:00:00 2001 From: Jelani Woods Date: Thu, 24 Jun 2021 14:17:00 -0500 Subject: [PATCH 04/12] Define/Raise Heroku related exceptions on login --- lib/web_git/exceptions.rb | 3 +++ lib/web_git/heroku.rb | 19 ++++++++++++++----- web_git.gemspec | 1 + 3 files changed, 18 insertions(+), 5 deletions(-) create mode 100644 lib/web_git/exceptions.rb diff --git a/lib/web_git/exceptions.rb b/lib/web_git/exceptions.rb new file mode 100644 index 0000000..cf2b447 --- /dev/null +++ b/lib/web_git/exceptions.rb @@ -0,0 +1,3 @@ +module WebGit + class AuthenticationError < StandardError; end +end diff --git a/lib/web_git/heroku.rb b/lib/web_git/heroku.rb index 5cf8d96..53335a0 100644 --- a/lib/web_git/heroku.rb +++ b/lib/web_git/heroku.rb @@ -1,17 +1,26 @@ require 'timeout' +require 'web_git/exceptions' + module WebGit class Heroku def self.authenticate(email, password) raise ArgumentError.new("Email and password cannot be blank.") if email.blank? || password.blank? script = File.join( File.dirname(__FILE__), '/../scripts/heroku_login.exp') - script_pid = Process.spawn("#{script} #{email} #{password}") + + command = "#{script} #{email} #{password}" + rout, wout = IO.pipe + pid = Process.spawn(command, :out => wout) + begin status = Timeout.timeout(30) do - # TODO how to check if auth fails? - p Process.wait(script_pid) + _, status = Process.wait2(pid) + wout.close end - puts "--------" - p status + stdout = rout.readlines.join("\n") + rout.close + message = stdout.match(/Error.*\./).to_s + raise WebGit::AuthenticationError.new(message) if stdout.include?("Error") + rescue Timeout::Error Process.kill('TERM', script_pid) raise Timeout::Error.new("Sign in took longer than 30 seconds.") diff --git a/web_git.gemspec b/web_git.gemspec index a1b6762..32e1e45 100644 --- a/web_git.gemspec +++ b/web_git.gemspec @@ -33,6 +33,7 @@ Gem::Specification.new do |s| "lib/views/status.erb", "lib/web_git.rb", "lib/web_git/diff.rb", + "lib/web_git/exceptions.rb", "lib/web_git/graph.rb", "lib/web_git/string.rb", "lib/web_git/version.rb", From 6ded5567d6c7832b84286f707a4790dd9f3fc978 Mon Sep 17 00:00:00 2001 From: Jelani Woods Date: Thu, 24 Jun 2021 14:22:58 -0500 Subject: [PATCH 05/12] Add tooltip to Heroku sign in --- lib/views/status.erb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/views/status.erb b/lib/views/status.erb index 0a11ed6..766c904 100644 --- a/lib/views/status.erb +++ b/lib/views/status.erb @@ -212,7 +212,8 @@
  • Heroku <% if @heroku_auth.empty? %> - <% else %> From 5bccfb60ea29c65bd732726df56b05646127e44f Mon Sep 17 00:00:00 2001 From: Jelani Woods Date: Thu, 24 Jun 2021 14:34:18 -0500 Subject: [PATCH 06/12] Fix tooltip --- lib/views/status.erb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/views/status.erb b/lib/views/status.erb index 766c904..d4ab55c 100644 --- a/lib/views/status.erb +++ b/lib/views/status.erb @@ -212,8 +212,7 @@
  • Heroku <% if @heroku_auth.empty? %> - <% else %> @@ -230,7 +229,8 @@ - +
  • From 406897e05ea01a75e324af7191d36a0c1263c748 Mon Sep 17 00:00:00 2001 From: Jelani Woods Date: Mon, 28 Jun 2021 15:57:26 -0500 Subject: [PATCH 07/12] Update installer to install expect --- lib/generators/web_git/install_generator.rb | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/generators/web_git/install_generator.rb b/lib/generators/web_git/install_generator.rb index 465051b..a85eebc 100644 --- a/lib/generators/web_git/install_generator.rb +++ b/lib/generators/web_git/install_generator.rb @@ -12,7 +12,7 @@ def generate_server end end - map '/' do\n\t + map '/' do RUBY filename = "config.ru" @@ -20,6 +20,14 @@ def generate_server insert_into_file filename, contents, before: match_text insert_into_file filename, "\nend", after: match_text, force: true + gsub_file filename, match_text, "\t#{match_text}" + + expect_installed = run("which expect") + + unless expect_installed + log :insert, "Installing expect." + run "sudo apt install -y expect" + end end end end From aeb903a8b44da6d048f68a39bee9e53c6ba7c185 Mon Sep 17 00:00:00 2001 From: Jelani Woods Date: Mon, 28 Jun 2021 17:04:34 -0500 Subject: [PATCH 08/12] Update redirect message and layout --- lib/views/status.erb | 151 ++++++++++++++++++++++++------------------- lib/web_git.rb | 2 +- 2 files changed, 85 insertions(+), 68 deletions(-) diff --git a/lib/views/status.erb b/lib/views/status.erb index d4ab55c..370b6fe 100644 --- a/lib/views/status.erb +++ b/lib/views/status.erb @@ -208,79 +208,92 @@
    -
      -
    • - Heroku - <% if @heroku_auth.empty? %> - - <% else %> - <%= @heroku_auth %> - <% end %> + +
      +
      +
        +
      • + New Branch +
      • -
      • -
        -
        - - -
        - -
        -
      • -
      • - Existing branches -
      • - <% @branches.each do |branch| %> -
      • - <%= branch %> -
        - +
      • - -
        -
        -
        -
      -
    • - <% end %> -
    + +
  • + Existing branches +
  • + <% @branches.each do |branch| %> +
  • + <%= branch %> +
    + +
    + +
    +
    + +
    +
    +
  • + <% end %> + +
    +
    +
      +
    • + Heroku + <% if !@heroku_auth.empty? %> + <%= @heroku_auth %> + <% end %> +
    • + <% if @heroku_auth.empty? %> +
    • +
      +
      + + +
      +
      + + +
      + +
      +
    • + <% end %> +
    +
    + <% end %> @@ -340,6 +353,10 @@ $(function () { $('[data-toggle="tooltip"]').tooltip() }) + $('.nav-tabs a').on('click', function (event) { + event.preventDefault() + $(this).tab('show') + }) }); diff --git a/lib/web_git.rb b/lib/web_git.rb index 13f4c8e..2caa92a 100644 --- a/lib/web_git.rb +++ b/lib/web_git.rb @@ -149,7 +149,7 @@ class Server < Sinatra::Base WebGit::Heroku.authenticate(email, password) set_flash(:notice, "Successfully logged into Heroku.") rescue => exception - set_flash(:alert, "Failed to log in to Heroku successfully. #{exception.message}") + set_flash(:alert, "There was a problem logging into Heroku. #{exception.message}") end redirect to("/") end From bd255e3ca81bef4e9cf14e491edb73d1c2072aae Mon Sep 17 00:00:00 2001 From: Jelani Woods Date: Tue, 29 Jun 2021 09:57:49 -0500 Subject: [PATCH 09/12] Update CSS for Heroku form and tab --- lib/views/status.erb | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/lib/views/status.erb b/lib/views/status.erb index 370b6fe..d1d79e1 100644 --- a/lib/views/status.erb +++ b/lib/views/status.erb @@ -19,11 +19,26 @@ .heroku { background-color: #7952b3; } + + .btn-heroku { + color: #fff; + background-color: #7952b3; + border-color: #7952b3; + } + + .btn-heroku:hover { + color: #fff; + background-color: #614092; + border-color: #6d48a4; + } .btn-outline-heroku { color: #7952b3; border-color: #7952b3; background-color: #fff; } + .border-heroku { + border-color: #7952b3 !important; + } Git Client @@ -216,7 +231,7 @@ Heroku -
    +
    • @@ -270,13 +285,15 @@
      • - Heroku - <% if !@heroku_auth.empty? %> + <% if @heroku_auth.empty? %> + Log in + <% else %> + Logged in as: <%= @heroku_auth %> <% end %>
      • <% if @heroku_auth.empty? %> -
      • +
        @@ -287,9 +304,10 @@
        + data-placement="top" title="heroku login -i" class="btn btn-heroku btn-block">Log in
        -
      • +
      + <% end %>
    From b7800044371196a2370e787cc57685476658785c Mon Sep 17 00:00:00 2001 From: Jelani Woods Date: Thu, 1 Jul 2021 13:52:51 -0500 Subject: [PATCH 10/12] Update nav tabs based on review --- lib/views/status.erb | 401 +++++++++++++++++++++---------------------- 1 file changed, 200 insertions(+), 201 deletions(-) diff --git a/lib/views/status.erb b/lib/views/status.erb index d1d79e1..cedcc49 100644 --- a/lib/views/status.erb +++ b/lib/views/status.erb @@ -66,173 +66,172 @@ <% end %>
    -
    -
    -
    -

    - <% if !@diff.nil? %> - On branch <%= @current_branch %> - <% else %> - git status - <% end %> -

    + -
    - <%# if false %> - <% unless @status.include?("nothing to commit, working tree clean") %> -

    - You have changes -

    - <% @statuses.each do |status| %> - <% filelist = status[:file_list].join("\n") %> - <% if filelist.length > 0 %> - <%= status[:name] %> -
    <%= filelist.strip %>
    +
    +
    +
    +
    +
    +

    + <% if !@diff.nil? %> + On branch <%= @current_branch %> + <% else %> + git status <% end %> - <% end %> - - <% if !@diff.nil? %> - <%= @diff %> - <% end %> - -

    - What do you want to do? -

    - -
    -
    -
    -
    - Commit your changes to the current branch -
    +

    -
    -
    -
    - - -
    +
    + <%# if false %> + <% unless @status.include?("nothing to commit, working tree clean") %> +

    + You have changes +

    + <% @statuses.each do |status| %> + <% filelist = status[:file_list].join("\n") %> + <% if filelist.length > 0 %> + <%= status[:name] %> +
    <%= filelist.strip %>
    + <% end %> + <% end %> -
    - - -
    + <% if !@diff.nil? %> + <%= @diff %> + <% end %> - - -
    -
    -
    +

    + What do you want to do? +

    -
    -
    +
    - Switch to a new branch + Commit your changes to the current branch
    -
    +
    - - - + +
    -
    -
    -
    -
    -
    - Discard your changes +
    +
    +
    +
    + Switch to a new branch +
    + +
    +
    +
    + + +
    + +
    +
    +
    +
    -
    - - - Discard your changes - +
    +
    +
    +
    + Discard your changes +
    + + +
    -
    -
    - <% else %> + <% else %> -
    -
    -
    -
    - Remote: -
    - <%= @remotes.first %> -
    -
    <%= @status %>
    - <%# TODO why is this here? %> - <% if @current_branch != "master" %> -
    - -
    - <% else %> -
    - -
    - <% end %> -
    - -
    -

    - The last thing you did was: -

    +
    +
    +
    +
    + Remote: +
    + <%= @remotes.first %> +
    +
    <%= @status %>
    + <%# TODO why is this here? %> + <% if @current_branch != "master" %> +
    + +
    + <% else %> +
    + +
    + <% end %> +
    + +
    +

    + The last thing you did was: +

    -
    - <%= @last_commit_message %> -
    - +
    + <%= @last_commit_message %> +
    + -
    -
    - <%= @last_diff_html %> +
    + +
    -
    -
    -
    - -
    -
    +
    • New Branch @@ -282,83 +281,83 @@ <% end %>
    -
    -
      -
    • - <% if @heroku_auth.empty? %> - Log in - <% else %> - Logged in as: - <%= @heroku_auth %> - <% end %> -
    • - <% if @heroku_auth.empty? %> -
      -
      -
      - - -
      -
      - - -
      - -
      -
      - - <% end %> -
    -
    -
    + <% end %>
    - <% end %> +
    -
    -
    -
    -
    -
    -

    - History -

    +
    +
    +
    +

    + History +

    -
    -

    Do you want to jump back in time and go down a different path?

    -
    -
    - - -
    - -
    - - -
    +
    +

    Do you want to jump back in time and go down a different path?

    + +
    + + +
    - <% if @changed_files.count > 0 %> - - <% else %> - - - <% end%> - +
    + + +
    + + <% if @changed_files.count > 0 %> + + <% else %> + + + <% end%> + +
    +
    <%= @cli_graph_interactive %>
    +
    -
    <%= @cli_graph_interactive %>
    +
    +
      +
    • + <% if @heroku_auth.empty? %> + Log in + <% else %> + Logged in as: + <%= @heroku_auth %> + <% end %> +
    • + <% if @heroku_auth.empty? %> +
      +
      +
      + + +
      +
      + + +
      + +
      +
      + + <% end %> +
    +