diff --git a/app/controllers/admin/upgrade_controller.rb b/app/controllers/admin/upgrade_controller.rb
new file mode 100644
index 000000000..096265108
--- /dev/null
+++ b/app/controllers/admin/upgrade_controller.rb
@@ -0,0 +1,20 @@
+# EXPERIMENTAL: front end for upgrading your instance using the web UI
+
+class Admin::UpgradeController < Admin::AdminController
+
+ before_filter :ensure_admin
+ skip_before_filter :check_xhr
+
+ layout 'no_js'
+
+ def index
+ require_dependency 'upgrade/git_repo'
+ @main_repo = Upgrade::GitRepo.new(Rails.root)
+ end
+
+ protected
+
+ def ensure_admin
+ raise Discourse::InvalidAccess.new unless current_user.admin?
+ end
+end
diff --git a/app/views/admin/upgrade/.index.html.erb.swn b/app/views/admin/upgrade/.index.html.erb.swn
new file mode 100644
index 000000000..c3303d86e
Binary files /dev/null and b/app/views/admin/upgrade/.index.html.erb.swn differ
diff --git a/app/views/admin/upgrade/_git_status.html.erb b/app/views/admin/upgrade/_git_status.html.erb
new file mode 100644
index 000000000..eb615ddb1
--- /dev/null
+++ b/app/views/admin/upgrade/_git_status.html.erb
@@ -0,0 +1,10 @@
+<% if repo.valid? %>
+ Current version: <%= repo.latest_local_commit %> (<%= time_ago_in_words repo.latest_local_commit_date %> ago),
+ Remote version: <%= repo.latest_origin_commit %> (<%= time_ago_in_words repo.latest_origin_commit_date %> ago)
+ <% if repo.commits_behind > 0 %>
+ commits behind: <%= repo.commits_behind %>
+
+ <% end %>
+<% else %>
+ Not under source control.
+<% end %>
diff --git a/app/views/admin/upgrade/index.html.erb b/app/views/admin/upgrade/index.html.erb
new file mode 100644
index 000000000..5c2325902
--- /dev/null
+++ b/app/views/admin/upgrade/index.html.erb
@@ -0,0 +1,18 @@
+
Discourse
+
+<%= render partial: 'git_status', locals: {repo: @main_repo} %>
+
+
+Plugins
+
+ <% Discourse.plugins.each do |plugin| %>
+ -
+ <%= plugin.name %> -
+ <%= render partial: 'git_status', locals: {repo: Upgrade::GitRepo.new(File.dirname(plugin.path))} %>
+
+ <% end %>
+
+
+Processes
+
+Log
diff --git a/config/routes.rb b/config/routes.rb
index 3b578152c..48db18e57 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -21,6 +21,7 @@ Discourse::Application.routes.draw do
namespace :admin, constraints: StaffConstraint.new do
get '' => 'admin#index'
+
resources :site_settings, constraints: AdminConstraint.new
get 'reports/:type' => 'reports#show'
@@ -96,7 +97,10 @@ Discourse::Application.routes.draw do
delete 'key' => 'api#revoke_key'
end
end
- end
+
+ get 'upgrade' => 'upgrade#index'
+
+ end # admin namespace
get 'email_preferences' => 'email#preferences_redirect', :as => 'email_preferences_redirect'
get 'email/unsubscribe/:key' => 'email#unsubscribe', as: 'email_unsubscribe'
diff --git a/lib/upgrade/git_repo.rb b/lib/upgrade/git_repo.rb
new file mode 100644
index 000000000..c4499185f
--- /dev/null
+++ b/lib/upgrade/git_repo.rb
@@ -0,0 +1,69 @@
+module Upgrade; end
+
+# like Grit just very very minimal
+class Upgrade::GitRepo
+ attr_reader :path
+
+ def initialize(path)
+ @path = path
+ @memoize = {}
+ end
+
+ def valid?
+ File.directory?("#{path}/.git")
+ end
+
+ def latest_local_commit
+ run "rev-parse --short HEAD"
+ end
+
+ def latest_origin_commit
+ run "rev-parse --short #{tracking_branch}"
+ end
+
+ def latest_origin_commit_date
+ commit_date(latest_origin_commit)
+ end
+
+ def latest_local_commit_date
+ commit_date(latest_local_commit)
+ end
+
+ def commits_behind
+ run("rev-list --count #{tracking_branch}..HEAD").to_i
+ end
+
+ def url
+ url = run "config --get remote.origin.url"
+ if url =~ /^git/
+ # hack so it works with git urls
+ url = "https://github.com/#{url.split(":")[1]}"
+ end
+ end
+
+ protected
+
+ def commit_date(commit)
+ unix_timestamp = run('show -s --format="%ct" ' << commit).to_i
+ Time.at(unix_timestamp).to_datetime
+ end
+
+ def tracking_branch
+ run "for-each-ref --format='%(upstream:short)' $(git symbolic-ref -q HEAD)"
+ end
+
+ def ensure_updated
+ @updated ||= Thread.new do
+ # this is a very slow operation, make it async
+ `cd #{path} && git remote update`
+ end
+ end
+
+ def run(cmd)
+ ensure_updated
+ @memoize[cmd] ||= `cd #{path} && git #{cmd}`.strip
+ rescue => e
+ p e
+ end
+
+end