web_git 0.0.2.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,114 @@
1
+ module WebGit
2
+ require "git"
3
+ require "ansispan"
4
+
5
+ class Graph
6
+ require "action_view"
7
+ require "action_view/helpers"
8
+ include ActionView::Helpers::DateHelper
9
+ attr_accessor :heads
10
+
11
+ def initialize(git)
12
+ @git = git
13
+ @full_list = []
14
+ @heads = {}
15
+ end
16
+
17
+ def to_hash
18
+
19
+ has_changes = has_untracked_changes?
20
+ if has_changes
21
+ temporarily_stash_changes
22
+ end
23
+
24
+ draw_graph
25
+
26
+ if has_changes
27
+ stash_pop
28
+ end
29
+ @full_list
30
+ end
31
+
32
+ def self.project_root
33
+ if defined?(Rails) && Rails.respond_to?("root")
34
+ return Rails.root
35
+ end
36
+
37
+ if defined?(Bundler)
38
+ return Bundler.root
39
+ end
40
+
41
+ Dir.pwd
42
+ end
43
+
44
+ def cli_graph
45
+ Dir.chdir(Graph.project_root) do
46
+ @cli_graph = `git log --oneline --decorate --graph --all --color`
47
+ all_commits = `git log --all --format=format:%H`.split("\n").map{|a| a.slice(0,7)}
48
+
49
+ @cli_graph = Ansispan.convert(@cli_graph)
50
+ all_commits.each do |sha|
51
+ sha_button = "<span class=\"commit\"><button class=\"btn btn-link sha\">#{sha}</button></span>"
52
+ @cli_graph.gsub!(sha, sha_button)
53
+ end
54
+ end
55
+ @cli_graph
56
+ end
57
+
58
+ def has_untracked_changes?
59
+ @git.diff.size > 0
60
+ end
61
+
62
+ def temporarily_stash_changes
63
+ @git.add(all: true)
64
+ stash_count = Git::Stashes.new(@git).count
65
+ Git::Stash.new(@git, "Temporary Stash #{stash_count}")
66
+ end
67
+
68
+ def stash_pop
69
+ stashes = Git::Stashes.new(@git)
70
+ stashes.apply(0)
71
+ end
72
+
73
+ def draw_graph
74
+ starting_branch = @git.current_branch
75
+ branches = @git.branches.local.map(&:name)
76
+ branches.each do |branch_name|
77
+ branch = { branch: branch_name }
78
+ @git.checkout(branch_name)
79
+ log_commits = build_array_of_commit_hashes
80
+ branch[:log] = log_commits
81
+ branch[:head] = log_commits.last[:sha]
82
+ @full_list.push branch
83
+ end
84
+ @git.checkout(starting_branch)
85
+
86
+ @full_list.each do |branch_hash|
87
+ head_sha = branch_hash[:head]
88
+ branch_name = branch_hash[:branch]
89
+
90
+ if @heads[head_sha].nil?
91
+ @heads[head_sha] = [branch_name]
92
+ else
93
+ @heads[head_sha].push branch_name
94
+ end
95
+ end
96
+
97
+ end
98
+
99
+ def build_array_of_commit_hashes
100
+ log_commits = []
101
+ @git.log.sort_by(&:date).each do |git_commit_object|
102
+ commit = {}
103
+ commit[:sha] = git_commit_object.sha.slice(0..7)
104
+ commit[:date] = git_commit_object.date
105
+ commit[:formatted_date] = time_ago_in_words(git_commit_object.date)
106
+ commit[:message] = git_commit_object.message
107
+ commit[:author] = git_commit_object.author.name
108
+ commit[:heads] = []
109
+ log_commits.push commit
110
+ end
111
+ log_commits
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,34 @@
1
+ require 'timeout'
2
+ require 'web_git/exceptions'
3
+
4
+ module WebGit
5
+ class Heroku
6
+ def self.authenticate(email, password)
7
+ raise ArgumentError.new("Email and password cannot be blank.") if email.blank? || password.blank?
8
+ script = File.join( File.dirname(__FILE__), '/../scripts/heroku_login.exp')
9
+
10
+ command = "#{script} #{email} #{password}"
11
+ rout, wout = IO.pipe
12
+ pid = Process.spawn(command, :out => wout)
13
+
14
+ begin
15
+ status = Timeout.timeout(30) do
16
+ _, status = Process.wait2(pid)
17
+ wout.close
18
+ end
19
+ stdout = rout.readlines.join("\n")
20
+ rout.close
21
+ message = stdout.match(/Error.*\./).to_s
22
+ raise WebGit::AuthenticationError.new(message) if stdout.include?("Error")
23
+
24
+ rescue Timeout::Error
25
+ Process.kill('TERM', script_pid)
26
+ raise Timeout::Error.new("Sign in took longer than 30 seconds.")
27
+ end
28
+ end
29
+
30
+ def self.whoami
31
+ `heroku whoami`.chomp
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,8 @@
1
+ # required for git
2
+ class String
3
+ def strip_heredoc
4
+ indent = scan(/^[ \t]*(?=\S)/).min.size || 0
5
+ gsub(/^[ \t]{#{indent}}/, '')
6
+ end
7
+ end
8
+
data/lib/web_git.rb CHANGED
@@ -1,10 +1,207 @@
1
- require "web_git/engine"
2
- require "jquery-rails"
3
- require "octicons_helper"
4
- require "tether-rails"
5
- require "turbolinks"
6
- require "git_clone_url"
1
+
2
+ require "web_git/version"
7
3
 
8
4
  module WebGit
9
- # Your code goes here...
5
+ require "active_support"
6
+ require "web_git/diff"
7
+ require "web_git/graph"
8
+ require "web_git/heroku"
9
+ require "web_git/string"
10
+ require "sinatra"
11
+ require "date"
12
+ require "git"
13
+ class Server < Sinatra::Base
14
+ enable :sessions
15
+
16
+ get '/log' do
17
+ graph = WebGit::Graph.new(git)
18
+ graph.to_hash.to_json
19
+ #sha = commit.sha.slice(0..7)
20
+ # commit_date = Date.parse commit.date
21
+ # strftime("%a, %d %b %Y, %H:%M %z") -> time_ago_in_words(commit_date)
22
+ # * 76eff73 - Wed, 11 Mar 2020 19:58:21 +0000 (13 days ago) (HEAD -> current_branch)
23
+ # | blease - Jelani Woods
24
+
25
+ # " * " + sha + " - " + commit_date + " (" + time_ago_in_words(commit_date) + ") " + "\n\t| " + commit.message
26
+ end
27
+
28
+ get "/" do
29
+ initialize_flash
30
+ clear_flash
31
+ # Update git index
32
+ git.status.changed.each do
33
+ git.diff.entries
34
+ end
35
+ status = git.status
36
+ # Just need the file names
37
+ @changed_files = status.changed.keys
38
+ @deleted_files = status.added.keys
39
+ @untracked_files = status.untracked.keys
40
+ @added_files = status.deleted.keys
41
+
42
+ @statuses = [
43
+ { name: "Changed Files:", file_list: @changed_files },
44
+ { name: "Untracked Files:", file_list: @untracked_files },
45
+ { name: "Deleted Files:", file_list: @deleted_files },
46
+ { name: "Added Files:", file_list: @added_files }
47
+ ]
48
+
49
+ @current_branch = git.current_branch
50
+ # g.branch(@current_branch).checkout # maybe?
51
+ # TODO use git gem for status
52
+ @status = `git status`
53
+ @diff = git.diff
54
+ @diff = Diff.diff_to_html(git.diff.to_s)
55
+ if git.log.count > 1
56
+ last_diff = git.diff("HEAD~1", "HEAD").to_s + "\n"
57
+ @last_diff_html = Diff.last_to_html(last_diff)
58
+ end
59
+ @last_diff_html = ""
60
+ @branches = git.branches.local.map(&:full)
61
+
62
+ logs = git.log
63
+ @last_commit_message = logs.first.message
64
+ @head = git.show.split("\n").first.split[1].slice(0..7)
65
+ @list = []
66
+ # (HEAD -> jw-non-sweet)
67
+ # TODO show where branches are on different remotes
68
+ @remotes = git.remotes.map {|remote| "#{remote.name}: #{remote.url}" }
69
+ # (origin/master, origin/jw-non-sweet, origin/HEAD)
70
+ # git.branches[:master].gcommit
71
+
72
+ graph = WebGit::Graph.new(git)
73
+ @graph_hash = graph.to_hash
74
+ @cli_graph_interactive = graph.cli_graph
75
+ @graph_branches = @graph_hash.sort do |branch_a, branch_b|
76
+ branch_b[:log].last[:date] <=> branch_a[:log].last[:date]
77
+ end
78
+
79
+ # TODO heroku stuff
80
+ @heroku_auth = WebGit::Heroku.whoami
81
+ erb :status
82
+ end
83
+
84
+ post "/commit" do
85
+ title = params[:title]
86
+ description = params[:description]
87
+
88
+ # TODO validate commit message
89
+ if title.nil? || title.gsub(" ", "").length == 0
90
+ session[:alert] = "You need to make a commit message."
91
+ redirect to("/")
92
+ end
93
+
94
+ unless description.nil?
95
+ title += "\n#{description}"
96
+ end
97
+
98
+ safe_git_action(:commit, args: title, notice: "Commit created successfully", alert: "Failed to create commit")
99
+ redirect to("/")
100
+ end
101
+
102
+ get "/stash" do
103
+ safe_git_action(:stash, notice: "Changes stashed.", alert: "Failed to stash changes")
104
+ redirect to("/")
105
+ end
106
+
107
+ post "/branch/checkout/new" do
108
+ # TODO validate branch name
109
+ name = params.fetch(:branch_name).downcase.gsub(" ", "_")
110
+ commit = params.fetch(:commit_hash)
111
+
112
+ safe_git_action(:checkout, args: name, notice: "Branch #{name}, created successfully.", alert: "Failed to create branch")
113
+ safe_git_action(:reset_hard, args: commit, alert: "Failed to checkout branch at #{commit}")
114
+ redirect to("/")
115
+ end
116
+
117
+ post "/branch/checkout" do
118
+ name = params.fetch(:branch_name).downcase.gsub(" ", "_")
119
+
120
+ safe_git_action(:checkout, args: name, notice: "Switched to branch: #{name} successfully.", alert: "Failed to switch branch")
121
+ redirect to("/")
122
+ end
123
+
124
+ # TODO make delete request somehow with the links
125
+ post "/branch/delete" do
126
+ name = params.fetch(:branch_name).downcase.gsub(" ", "_")
127
+
128
+ safe_git_action(:delete, args: name, notice: "Deleted branch: #{name} successfully.", alert: "Failed to delete branch")
129
+ redirect to("/")
130
+ end
131
+
132
+ post "/push" do
133
+ # TODO push to heroku eventually, multiple remotes
134
+
135
+ safe_git_action(:push, notice: "Pushed to GitHub successfully.", alert: "Failed to push")
136
+ redirect to("/")
137
+ end
138
+
139
+ post "/pull" do
140
+ safe_git_action(:pull, notice: "Pulled successfully.", alert: "Git Pull failed")
141
+ redirect to("/")
142
+ end
143
+
144
+ post "/heroku/login" do
145
+ email = params[:heroku_email]
146
+ password = params[:heroku_password]
147
+
148
+ begin
149
+ WebGit::Heroku.authenticate(email, password)
150
+ set_flash(:notice, "Successfully logged into Heroku.")
151
+ rescue => exception
152
+ set_flash(:alert, "There was a problem logging into Heroku. #{exception.message}")
153
+ end
154
+ redirect to("/")
155
+ end
156
+
157
+ protected
158
+
159
+ def git
160
+ working_dir = File.exist?(Dir.pwd + "/.git") ? Dir.pwd : Dir.pwd + "/.."
161
+ @git ||= Git.open(working_dir)
162
+ end
163
+
164
+ def safe_git_action(method, **options)
165
+ begin
166
+ case method
167
+ when :push
168
+ git.push('origin', git.current_branch)
169
+ when :pull
170
+ git.pull
171
+ when :stash
172
+ git.add(all: true)
173
+ stash_count = Git::Stashes.new(git).count
174
+ Git::Stash.new(git, "Stash #{stash_count}")
175
+ when :commit
176
+ git.add(all: true)
177
+ git.commit(options[:args])
178
+ when :checkout
179
+ git.branch(options[:args]).checkout
180
+ when :reset_hard
181
+ commit = git.gcommit(options[:args])
182
+ git.reset_hard(commit)
183
+ when :delete
184
+ git.branch(options[:args]).delete
185
+ end
186
+ set_flash(:notice, options[:notice])
187
+ rescue Git::GitExecuteError => exception
188
+ alert_message = "#{options[:alert]}: #{exception.message.split("\n").last}"
189
+ set_flash(:alert, alert_message)
190
+ end
191
+ end
192
+
193
+ def set_flash(name, message)
194
+ session[name] = message
195
+ end
196
+
197
+ def clear_flash
198
+ session[:alert] = nil
199
+ session[:notice] = nil
200
+ end
201
+
202
+ def initialize_flash
203
+ @alert = session[:alert]
204
+ @notice = session[:notice]
205
+ end
206
+ end
10
207
  end
data/web_git.gemspec CHANGED
@@ -2,18 +2,18 @@
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
3
  # Instead, edit Juwelier::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
- # stub: web_git 0.0.3 ruby lib
5
+ # stub: web_git 0.1.0 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "web_git".freeze
9
- s.version = "0.0.3"
9
+ s.version = "0.1.0"
10
10
 
11
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
12
12
  s.require_paths = ["lib".freeze]
13
- s.authors = ["Raghu Betina".freeze]
14
- s.date = "2019-07-11"
15
- s.description = "WebGit is a Rails Engine that provides an in-browser visual interface to a simple but effective Git workflow. For educational purposes.".freeze
16
- s.email = "raghu@firstdraft.com".freeze
13
+ s.authors = ["Raghu Betina".freeze, "Jelani Woods".freeze]
14
+ s.date = "2022-03-30"
15
+ s.description = "WebGit is an embeddable Sinatra app that provides an in-browser visual interface to a simple but effective Git workflow. For educational purposes.".freeze
16
+ s.email = ["raghu@firstdraft.com".freeze, "jelani@firstdraft.com".freeze]
17
17
  s.extra_rdoc_files = [
18
18
  "LICENSE.txt",
19
19
  "README.markdown"
@@ -28,88 +28,47 @@ Gem::Specification.new do |s|
28
28
  "README.markdown",
29
29
  "Rakefile",
30
30
  "VERSION",
31
- "ansi2html.sh",
32
- "app/assets/javascripts/web_git/application.js",
33
- "app/assets/javascripts/web_git/bootstrap.min.js",
34
- "app/assets/javascripts/web_git/popper.min.js",
35
- "app/assets/stylesheets/web_git/application.scss",
36
- "app/assets/stylesheets/web_git/bootstrap.min.css",
37
- "app/assets/stylesheets/web_git/font-awesome.min.css",
38
- "app/assets/stylesheets/web_git/octicons.css",
39
- "app/controllers/web_git/application_controller.rb",
40
- "app/controllers/web_git/branches_controller.rb",
41
- "app/controllers/web_git/commands_controller.rb",
42
- "app/controllers/web_git/commits_controller.rb",
43
- "app/views/layouts/web_git/application.html.erb",
44
- "app/views/web_git/commands/hello.html.erb",
45
- "app/views/web_git/commands/status.html.erb",
46
- "config/routes.rb",
31
+ "lib/generators/web_git/install_generator.rb",
32
+ "lib/scripts/heroku_login.exp",
33
+ "lib/views/status.erb",
47
34
  "lib/web_git.rb",
48
- "lib/web_git/engine.rb",
35
+ "lib/web_git/diff.rb",
36
+ "lib/web_git/exceptions.rb",
37
+ "lib/web_git/graph.rb",
38
+ "lib/web_git/heroku.rb",
39
+ "lib/web_git/string.rb",
49
40
  "lib/web_git/version.rb",
50
- "web_git-0.0.2.gem",
51
41
  "web_git.gemspec"
52
42
  ]
53
43
  s.homepage = "http://github.com/firstdraft/web_git".freeze
54
44
  s.licenses = ["MIT".freeze]
55
- s.rubygems_version = "2.7.8".freeze
45
+ s.rubygems_version = "3.1.6".freeze
56
46
  s.summary = "An in-browser Git GUI for your Rails project".freeze
57
47
 
58
48
  if s.respond_to? :specification_version then
59
49
  s.specification_version = 4
50
+ end
60
51
 
61
- if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
62
- s.add_runtime_dependency(%q<tzinfo-data>.freeze, [">= 0"])
63
- s.add_runtime_dependency(%q<tether-rails>.freeze, [">= 0"])
64
- s.add_runtime_dependency(%q<octicons_helper>.freeze, [">= 0"])
65
- s.add_runtime_dependency(%q<turbolinks>.freeze, ["~> 5"])
66
- s.add_runtime_dependency(%q<jquery-rails>.freeze, [">= 0"])
67
- s.add_runtime_dependency(%q<git_clone_url>.freeze, [">= 0"])
68
- s.add_development_dependency(%q<rspec>.freeze, ["~> 3.5.0"])
69
- s.add_development_dependency(%q<rdoc>.freeze, ["~> 3.12"])
70
- s.add_development_dependency(%q<juwelier>.freeze, ["~> 2.1.0"])
71
- s.add_development_dependency(%q<simplecov>.freeze, [">= 0"])
72
- s.add_development_dependency(%q<pry>.freeze, ["~> 0"])
73
- s.add_development_dependency(%q<pry-byebug>.freeze, ["~> 3"])
74
- s.add_development_dependency(%q<pry-doc>.freeze, ["~> 0"])
75
- s.add_development_dependency(%q<pry-remote>.freeze, ["~> 0"])
76
- s.add_development_dependency(%q<pry-rescue>.freeze, ["~> 1"])
77
- s.add_development_dependency(%q<pry-stack_explorer>.freeze, ["~> 0"])
78
- else
79
- s.add_dependency(%q<tzinfo-data>.freeze, [">= 0"])
80
- s.add_dependency(%q<tether-rails>.freeze, [">= 0"])
81
- s.add_dependency(%q<octicons_helper>.freeze, [">= 0"])
82
- s.add_dependency(%q<turbolinks>.freeze, ["~> 5"])
83
- s.add_dependency(%q<jquery-rails>.freeze, [">= 0"])
84
- s.add_dependency(%q<git_clone_url>.freeze, [">= 0"])
85
- s.add_dependency(%q<rspec>.freeze, ["~> 3.5.0"])
86
- s.add_dependency(%q<rdoc>.freeze, ["~> 3.12"])
87
- s.add_dependency(%q<juwelier>.freeze, ["~> 2.1.0"])
88
- s.add_dependency(%q<simplecov>.freeze, [">= 0"])
89
- s.add_dependency(%q<pry>.freeze, ["~> 0"])
90
- s.add_dependency(%q<pry-byebug>.freeze, ["~> 3"])
91
- s.add_dependency(%q<pry-doc>.freeze, ["~> 0"])
92
- s.add_dependency(%q<pry-remote>.freeze, ["~> 0"])
93
- s.add_dependency(%q<pry-rescue>.freeze, ["~> 1"])
94
- s.add_dependency(%q<pry-stack_explorer>.freeze, ["~> 0"])
95
- end
52
+ if s.respond_to? :add_runtime_dependency then
53
+ s.add_runtime_dependency(%q<actionview>.freeze, [">= 0"])
54
+ s.add_runtime_dependency(%q<ansispan>.freeze, [">= 0"])
55
+ s.add_runtime_dependency(%q<diffy>.freeze, [">= 0"])
56
+ s.add_runtime_dependency(%q<git>.freeze, [">= 0"])
57
+ s.add_runtime_dependency(%q<sinatra>.freeze, [">= 0"])
58
+ s.add_runtime_dependency(%q<tzinfo-data>.freeze, [">= 0"])
59
+ s.add_development_dependency(%q<juwelier>.freeze, ["~> 2.1.0"])
60
+ s.add_development_dependency(%q<rdoc>.freeze, [">= 6.3.1"])
61
+ s.add_development_dependency(%q<rspec>.freeze, ["~> 3.5.0"])
96
62
  else
63
+ s.add_dependency(%q<actionview>.freeze, [">= 0"])
64
+ s.add_dependency(%q<ansispan>.freeze, [">= 0"])
65
+ s.add_dependency(%q<diffy>.freeze, [">= 0"])
66
+ s.add_dependency(%q<git>.freeze, [">= 0"])
67
+ s.add_dependency(%q<sinatra>.freeze, [">= 0"])
97
68
  s.add_dependency(%q<tzinfo-data>.freeze, [">= 0"])
98
- s.add_dependency(%q<tether-rails>.freeze, [">= 0"])
99
- s.add_dependency(%q<octicons_helper>.freeze, [">= 0"])
100
- s.add_dependency(%q<turbolinks>.freeze, ["~> 5"])
101
- s.add_dependency(%q<jquery-rails>.freeze, [">= 0"])
102
- s.add_dependency(%q<git_clone_url>.freeze, [">= 0"])
103
- s.add_dependency(%q<rspec>.freeze, ["~> 3.5.0"])
104
- s.add_dependency(%q<rdoc>.freeze, ["~> 3.12"])
105
69
  s.add_dependency(%q<juwelier>.freeze, ["~> 2.1.0"])
106
- s.add_dependency(%q<simplecov>.freeze, [">= 0"])
107
- s.add_dependency(%q<pry>.freeze, ["~> 0"])
108
- s.add_dependency(%q<pry-byebug>.freeze, ["~> 3"])
109
- s.add_dependency(%q<pry-doc>.freeze, ["~> 0"])
110
- s.add_dependency(%q<pry-remote>.freeze, ["~> 0"])
111
- s.add_dependency(%q<pry-rescue>.freeze, ["~> 1"])
112
- s.add_dependency(%q<pry-stack_explorer>.freeze, ["~> 0"])
70
+ s.add_dependency(%q<rdoc>.freeze, [">= 6.3.1"])
71
+ s.add_dependency(%q<rspec>.freeze, ["~> 3.5.0"])
113
72
  end
114
73
  end
115
74