git_go 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ .DS_Store
2
+ .yardoc
3
+ /tmp
4
+ /pkg
5
+
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/.travis.yml ADDED
@@ -0,0 +1,11 @@
1
+ language: ruby
2
+ rvm:
3
+ - "1.8.7"
4
+ - "1.9.2"
5
+ - "1.9.3"
6
+ - "rbx-18mode"
7
+ - "rbx-19mode"
8
+ - "jruby-18mode"
9
+ - "jruby-19mode"
10
+ script: bundle exec rspec spec
11
+
data/Gemfile ADDED
@@ -0,0 +1,18 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gemspec
6
+
7
+ group :development do
8
+ gem "rake"
9
+ gem "yard"
10
+ gem "rspec"
11
+ gem "mocha"
12
+ gem "guard-rspec"
13
+ gem "rb-inotify", :require => false
14
+ gem "rb-fsevent", :require => false
15
+ gem "rb-fchange", :require => false
16
+ gem "growl"
17
+ end
18
+
data/Gemfile.lock ADDED
@@ -0,0 +1,65 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ git_go (0.0.1)
5
+ net-ssh (~> 2.6.2)
6
+ thor (~> 0.16.0)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ coderay (1.0.8)
12
+ diff-lcs (1.1.3)
13
+ ffi (1.2.0)
14
+ growl (1.0.3)
15
+ guard (1.5.4)
16
+ listen (>= 0.4.2)
17
+ lumberjack (>= 1.0.2)
18
+ pry (>= 0.9.10)
19
+ thor (>= 0.14.6)
20
+ guard-rspec (2.3.1)
21
+ guard (>= 1.1)
22
+ rspec (~> 2.11)
23
+ listen (0.6.0)
24
+ lumberjack (1.0.2)
25
+ metaclass (0.0.1)
26
+ method_source (0.8.1)
27
+ mocha (0.13.1)
28
+ metaclass (~> 0.0.1)
29
+ net-ssh (2.6.2)
30
+ pry (0.9.10)
31
+ coderay (~> 1.0.5)
32
+ method_source (~> 0.8)
33
+ slop (~> 3.3.1)
34
+ rake (10.0.2)
35
+ rb-fchange (0.0.5)
36
+ ffi
37
+ rb-fsevent (0.9.2)
38
+ rb-inotify (0.8.8)
39
+ ffi (>= 0.5.0)
40
+ rspec (2.12.0)
41
+ rspec-core (~> 2.12.0)
42
+ rspec-expectations (~> 2.12.0)
43
+ rspec-mocks (~> 2.12.0)
44
+ rspec-core (2.12.1)
45
+ rspec-expectations (2.12.0)
46
+ diff-lcs (~> 1.1.3)
47
+ rspec-mocks (2.12.0)
48
+ slop (3.3.3)
49
+ thor (0.16.0)
50
+ yard (0.8.2.1)
51
+
52
+ PLATFORMS
53
+ ruby
54
+
55
+ DEPENDENCIES
56
+ git_go!
57
+ growl
58
+ guard-rspec
59
+ mocha
60
+ rake
61
+ rb-fchange
62
+ rb-fsevent
63
+ rb-inotify
64
+ rspec
65
+ yard
data/Guardfile ADDED
@@ -0,0 +1,8 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ guard "rspec" do
4
+ watch(%r{^spec/.+_spec\.rb$})
5
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
6
+ watch("spec/spec_helper.rb") { "spec" }
7
+ end
8
+
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Michael van Rooijen
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,50 @@
1
+ # Git Go
2
+
3
+ [![Build Status](https://travis-ci.org/meskyanichi/git_go.png)](https://travis-ci.org/meskyanichi/git_go)
4
+
5
+ Git Go is a small command-line utility distributed as a RubyGem that allows you to easily create/destroy/rename/list all your private-hosted git repositories on your own server. All you need is a small VPS (256MB RAM / 10GB HDD / 1vCPUCore should be sufficient).
6
+
7
+ Git Go also provides you with detailed instructions on how to set up your server in order to be able to create remote repositories from your local machine using the provided command-line utility. The instructions also show you how to set up automatic/daily compressed/archived backups of all your git repositories, store them on Amazon S3, cycle them and send you success/error notifications by email, all using the [Backup](http://github.com/meskyanichi/backup) RubyGem. It's easy, and only takes a few minutes to set up.
8
+
9
+ ## Get Git Go
10
+
11
+ Install Git Go with the following command:
12
+
13
+ ```sh
14
+ gem install git_go
15
+ ```
16
+
17
+ This will provide you with the `gg` executable. Simply run it without any arguments to see the list of tasks it can perform.
18
+
19
+ ```sh
20
+ $ gg
21
+
22
+ Tasks:
23
+ gg create NAME # Create a new remote repository named NAME
24
+ gg destroy NAME # Destroy the remote repository named NAME
25
+ gg help [TASK] # Describe available tasks or one specific task
26
+ gg instructions # Display a detailed guide to setup Git Go locally and remotely
27
+ gg list # Display a list of all the remote repositories and their URL
28
+ gg rename NAME NEW_NAME # Rename the remote repository named NAME to NEW_NAME
29
+ ```
30
+
31
+ I recommend you run `gg instructions` to get up and running. The instructions cover:
32
+
33
+ * Setting up two local environment variables in your .bashrc or .zshrc.
34
+ * Adding a git user to your remote machine.
35
+ * Setting up SSH keys.
36
+ * Setting up the Backup RubyGem to perform daily backups with:
37
+ * GZip Compression
38
+ * Email Notification on success/warning/error
39
+ * Amazon S3 storage
40
+ * Cycling (e.g. 30 day backup retention, then pop old backups for new backups)
41
+
42
+
43
+ ## License
44
+
45
+ "Git Go" is released under the MIT License. See `LICENSE`.
46
+
47
+ ## Author
48
+
49
+ Michael van Rooijen | [@meskyanichi](http://twitter.com/meskyanichi) | http://github.com/meskyanichi
50
+
data/Rakefile ADDED
@@ -0,0 +1,3 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require "bundler/gem_tasks"
3
+
@@ -0,0 +1,164 @@
1
+
2
+ GIT GO INSTRUCTIONS
3
+ ===================
4
+
5
+ http://github.com/meskyanichi/git_go
6
+
7
+ Here are the instructions for getting your environments set up.
8
+
9
+ Local Environment
10
+ -----------------
11
+
12
+ Add the following to your .bashrc or .zshrc file:
13
+
14
+ export GIT_GO_USER=user
15
+ export GIT_GO_HOST=host
16
+
17
+ For example:
18
+
19
+ export GIT_GO_USER=git
20
+ export GIT_GO_HOST=repositories.example.com
21
+
22
+ GIT_GO_HOST can also be a direct IP, for example:
23
+
24
+ export GIT_GO_HOST=207.97.227.239
25
+
26
+ Remote Environment
27
+ ------------------
28
+
29
+ The following guide assumes you are `root`. If you aren't root, prefix all commands with `sudo` or simply `sudo su` to become `root`.
30
+
31
+ The guide also assumes you want a user called `git` under which we'll create all the repositories. You can substitute `git` with anything you like.
32
+
33
+ Finally, the guide assumes you're using Ubuntu 12.10 as the remote machine, but this will likely work on some older versions of Ubuntu as well.
34
+
35
+ Log in to your remote machine using SSH.
36
+ Be sure you have root priviledges, either through sudo or as root.
37
+
38
+ Create the "git" user:
39
+
40
+ adduser \
41
+ --home /home/git \
42
+ --shell /bin/bash \
43
+ --disabled-password \
44
+ git
45
+
46
+ Create the .ssh folder and authorized_keys file for the git user.
47
+
48
+ mkdir /home/git/.ssh
49
+
50
+ Now add your key (usually located in `~/.ssh/id_rsa.pub` on your local machine) to `/home/git/.ssh/authorized_keys`. Finally, fix the owner and permissions.
51
+
52
+ chmod 700 /home/git/.ssh
53
+ chmod 600 /home/git/.ssh/authorized_keys
54
+ chown -R git:git /home/git/.ssh
55
+
56
+
57
+ Using Git Go
58
+ ------------
59
+
60
+ With that, you're ready to use Git Go. Simply run `gg help` and try out any of the commands that Git Go provides.
61
+
62
+
63
+ Backups
64
+ -------
65
+
66
+ It is always a good idea to set up backups for your git repositories. Here's a nifty and clean way to set up daily backups for all your git repositories, along with email notifications, gzip compression, Amazon S3 storing and cycling old for new backups.
67
+
68
+ First, we'll need a recent version of Ruby. If Ruby isn't already installed on your machine, simply run the following:
69
+
70
+ apt-get update && apt-get upgrade -y
71
+ apt-get install -y python-software-properties software-properties-common
72
+ apt-add-repository ppa:brightbox/ruby-ng
73
+ apt-get update
74
+ apt-get install -y ruby rubygems ruby-switch ruby1.9.3 ruby1.9.1-dev \
75
+ libxml2 libxslt-dev libxml2-dev libopenssl-ruby
76
+
77
+ What this will do, is it will add a 3rd party repository Ruby, provided by Brightbox Systemd Ltd. It includes up-to-date Ruby versions which you can install directly through the apt package manager. After adding it and updating the repository list, we can simply install the latest 1.9.x Ruby (Ruby 1.9.3). We also include some other packages which are required for compiling certain extensions that come with the gems we'll be installing.
78
+
79
+ With Ruby installed, let us set up our backup system.
80
+
81
+ gem install backup fog mail
82
+
83
+ We'll be using `backup` for performing backups. We'll use `mail` with `backup` to send email notifications after each backup operation. We'll use `fog` with `backup` to send the backups to Amazon S3.
84
+
85
+ Note that Backup is flexible can support a few other storage locations, including Rackspace, Dropbox, etc. The same goes for the notifier, you can use Email, Twitter or a few other notification solutions. We'll stick to mail and Amazon S3 for this guide.
86
+
87
+ It is recommended that you become root before proceeding. Become root with the following command if you aren't already:
88
+
89
+ sudo su
90
+
91
+ Generate the initial Backup configuration and model.
92
+
93
+ backup generate:config
94
+ backup generate:model --trigger gitrepositories \
95
+ --archives --compressors=gzip --notifiers=mail --storages=s3
96
+
97
+ This should generate a configuration file in `/root/Backup/models/`. Basically what you want is the following:
98
+
99
+ Backup::Model.new(:gitrepositories, 'git repository backups') do
100
+ split_into_chunks_of 500
101
+
102
+ archive :git do |archive|
103
+ archive.add "/home/git/"
104
+ end
105
+
106
+ store_with S3 do |s3|
107
+ s3.access_key_id = "YOUR_ACCESS_KEY_ID"
108
+ s3.secret_access_key = "YOUR_SECRET_ACCESS_KEY"
109
+ s3.region = "us-east-1"
110
+ s3.bucket = "your_bucket_name"
111
+ s3.path = "/backup"
112
+ s3.keep = 30
113
+ end
114
+
115
+ compress_with Gzip
116
+
117
+ notify_by Mail do |mail|
118
+ mail.on_success = true
119
+ mail.on_warning = true
120
+ mail.on_failure = true
121
+
122
+ mail.from = "your@email.com"
123
+ mail.to = "your@email.com"
124
+ mail.address = "smtp.gmail.com"
125
+ mail.port = 587
126
+ mail.domain = "repositories.example.com"
127
+ mail.user_name = "username@email.com"
128
+ mail.password = "password"
129
+ mail.authentication = "plain"
130
+ mail.enable_starttls_auto = true
131
+ end
132
+ end
133
+
134
+ Just fill out all the settings. Once done, try it out by running:
135
+
136
+ backup perform -t gitrepositories
137
+
138
+ If all goes well, you should have received an email, and the backup should have been stored on your Amazon S3 account in the specified bucket under /backups.
139
+
140
+ Check out https://github.com/meskyanichi/backup for more information.
141
+
142
+ Now let's set up cron to automatically invoke this command every day. First, check to see where exactly backup has been installed on your machine.
143
+
144
+ which backup
145
+
146
+ You'll likely see the following:
147
+
148
+ /usr/local/bin/backup
149
+
150
+ Copy this path and we'll use it in the crontab. Edit the crontab by running:
151
+
152
+ crontab -e
153
+
154
+ Add this line to the crontab:
155
+
156
+ 0 0 * * * /bin/bash -l -c '/usr/local/bin/backup perform -t gitrepositories'
157
+
158
+ Save and close. Now you should have set up daily backups of your entire /home/git directory. This is nice because if you experience any data loss due to for example hardware failures, just spin up or reset the server, create the git user again and simply extract the `git.tar.gz` to the `/home/git` directory to restore all your repositories and ssh configuration.
159
+
160
+
161
+ Michael van Rooijen
162
+ > http://github.com/meskyanichi
163
+ > http://twitter.com/meskyanichi
164
+
data/bin/gg ADDED
@@ -0,0 +1,10 @@
1
+ #! /usr/bin/env ruby
2
+ # -*- encoding : utf-8 -*-
3
+
4
+ $: << File.expand_path("../../lib", __FILE__)
5
+
6
+ require "git_go"
7
+ require "git_go/cli/gg"
8
+
9
+ GitGo::Repository.start
10
+
data/gitgo.gemspec ADDED
@@ -0,0 +1,24 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ lib = File.expand_path("../lib", __FILE__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require "git_go/version"
6
+
7
+ Gem::Specification.new do |gem|
8
+ gem.name = "git_go"
9
+ gem.version = GitGo::VERSION
10
+ gem.authors = ["Michael van Rooijen"]
11
+ gem.email = ["meskyanichi@gmail.com"]
12
+ gem.description = %q{Git Go is a small command-line utility distributed as a RubyGem that allows you to easily create/destroy/rename/list all your private-hosted git repositories on your own server.}
13
+ gem.summary = gem.description
14
+ gem.homepage = "http://github.com/meskyanichi/git_go"
15
+
16
+ gem.files = `git ls-files`.split($/)
17
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
18
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
19
+ gem.require_paths = ["lib"]
20
+
21
+ gem.add_dependency "thor", ["~> 0.16.0"]
22
+ gem.add_dependency "net-ssh", ["~> 2.6.2"]
23
+ end
24
+
@@ -0,0 +1,120 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ module GitGo
4
+ class Repository < Thor
5
+ include Thor::Actions
6
+
7
+ desc "create NAME", "Create a new remote repository named NAME"
8
+ def create(name)
9
+ core = GitGo::Core.new
10
+ conn = core.connection
11
+ if conn.directory?("#{name}.git")
12
+ puts "'#{name}' already exists."
13
+ conn.close_and_exit(1)
14
+ else
15
+ conn.mkdir("#{name}.git")
16
+ conn.cd("#{name}.git")
17
+ response = conn.bash("git init --bare")
18
+
19
+ if response[:exit_code] == 0
20
+ puts "Repository created at: #{core.user}@#{core.host}:#{name}.git"
21
+ else
22
+ puts "Failed to create repository."
23
+ puts response[:stderr] + response[:stdout]
24
+ end
25
+ end
26
+
27
+ conn.close
28
+ end
29
+
30
+ desc "destroy NAME", "Destroy the remote repository named NAME"
31
+ def destroy(name)
32
+ core = GitGo::Core.new
33
+ conn = core.connection
34
+ repository = "#{name}.git"
35
+ if conn.directory?(repository)
36
+ if yes?("Are you sure you want to destroy '#{name}'?")
37
+ response = conn.rm(repository)
38
+ if response[:exit_code] == 0
39
+ puts "Repository '#{name}' was destroyed."
40
+ else
41
+ puts "Repository '#{name}' could not be destroyed."
42
+ puts response[:stdout] + response[:stderr]
43
+ conn.close_and_exit(1)
44
+ end
45
+ end
46
+ else
47
+ puts "Repository '#{name}' does not exist."
48
+ conn.close_and_exit(1)
49
+ end
50
+
51
+ conn.close
52
+ end
53
+
54
+ desc "rename NAME NEW_NAME", "Rename the remote repository named NAME to NEW_NAME"
55
+ def rename(name, new_name)
56
+ core = GitGo::Core.new
57
+ conn = core.connection
58
+ repository = "#{name}.git"
59
+ new_repository = "#{new_name}.git"
60
+
61
+ if not conn.directory?(repository)
62
+ puts "Repository '#{name}' does not exist."
63
+ conn.close_and_exit(1)
64
+ end
65
+
66
+ if conn.directory?(new_repository)
67
+ puts "Repository '#{new_name}' already exists."
68
+ conn.close_and_exit(1)
69
+ end
70
+
71
+ response = conn.mv(repository, new_repository)
72
+ if response[:exit_code] == 0
73
+ puts "Repository '#{name}' renamed to '#{new_name}'."
74
+ else
75
+ puts "Could not rename '#{name}' to '#{new_name}'."
76
+ puts response[:stderr] + response[:stdout]
77
+ conn.close_and_exit(1)
78
+ end
79
+
80
+ conn.close
81
+ end
82
+
83
+ desc "list", "Display a list of all the remote repositories and their URL"
84
+ def list
85
+ core = GitGo::Core.new
86
+ conn = core.connection
87
+
88
+ response = conn.bash("find ./*.git/ -mindepth 0 -maxdepth 0")
89
+
90
+ if response[:exit_code] == 0
91
+ repositories = response[:stdout].split("\n").map { |r| File.basename(r) }
92
+ list = [["Repository", "Git Fetch/Push URL"]]
93
+
94
+ repositories.each do |repository|
95
+ list << [
96
+ repository.sub(".git", ""),
97
+ "#{core.user}@#{core.host}:#{repository}"
98
+ ]
99
+ end
100
+
101
+ amount = list.count - 1
102
+ out = Formatter.columns(list, :spacing => 4, :header => true)
103
+
104
+ puts "\n" + out
105
+ puts "\nThere #{amount == 1 ? "is" : "are"} #{amount} git " +
106
+ "#{amount == 1 ? "repository" : "repositories"}.\n"
107
+ else
108
+ puts "There are no repositories at #{core.user}@#{core.host}."
109
+ end
110
+
111
+ conn.close
112
+ end
113
+
114
+ desc "instructions", "Display a detailed guide to setup Git Go locally and remotely"
115
+ def instructions
116
+ puts Core.instructions
117
+ end
118
+ end
119
+ end
120
+
@@ -0,0 +1,139 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ module GitGo
4
+ class Connection
5
+
6
+ # @return [GitGo::Core] instance.
7
+ attr_reader :core
8
+
9
+ # @return [Net::SSH::Connection::Session] instance.
10
+ attr_reader :ssh
11
+
12
+ # @return [String] the current path on the host.
13
+ attr_reader :current_path
14
+
15
+ # Creates a new instane of GitGo::Connection. Establishes
16
+ # a session with the host and stores that session in #ssh.
17
+ #
18
+ # @param [GitGo::Core] core takes and assigns a reference to parent class in #core.
19
+ # @return [GitGo::Connection] an instance of GitGo::Connection.
20
+ def initialize(core)
21
+ @core = core
22
+ @ssh = Net::SSH.start(core.host, core.user)
23
+ @current_path = ""
24
+ end
25
+
26
+ # Moves the session to the provided path and remembers it for the next
27
+ # action performed through the #bash method. It keeps the current path
28
+ # in #current_path
29
+ #
30
+ # @see #bash
31
+ # @see #current_path
32
+ # @param [String] path the path to navigate to.
33
+ # @return [String] the new path.
34
+ def cd(path)
35
+ @current_path = bash("cd '#{current_path}'; cd '#{path}'; pwd")[:stdout].chomp
36
+ end
37
+
38
+ # Tests to see whether the provided path is a directory.
39
+ # It will take the path relative from #current_path if no absolute
40
+ # path was provided.
41
+ #
42
+ # @param [String] path the path to test.
43
+ # @return [true, false]
44
+ def directory?(path)
45
+ bash("[ -d '#{path}' ]")[:exit_code] == 0
46
+ end
47
+
48
+ # Tests to see whether the provided path is a file.
49
+ # It will take the path relative from #current_path if no absolute
50
+ # path was provided.
51
+ #
52
+ # @param [String] path the path to test.
53
+ # @return [true, false]
54
+ def file?(path)
55
+ bash("[ -f '#{path}' ]")[:exit_code] == 0
56
+ end
57
+
58
+ # Creates a directory at the provided path.
59
+ # It will take the path relative from #current_path if no absolute
60
+ # path was provided.
61
+ #
62
+ # @param [String] path the path to create one or more directories for.
63
+ # @return [Hash] containing #bash response data.
64
+ def mkdir(path)
65
+ bash("mkdir -p '#{path}'")
66
+ end
67
+
68
+ # Removes a file or directory at the provided path.
69
+ # It will take the path relative from #current_path if no absolute
70
+ # path was provided.
71
+ #
72
+ # @param [String] path the path to the file or directory to remove.
73
+ # @return [Hash] containing #bash response data.
74
+ def rm(path)
75
+ bash("rm -rf '#{path}'")
76
+ end
77
+
78
+ # Moves a file or directory from the provided path to the newly specified path.
79
+ # It will take the path relative from #current_path if no absolute
80
+ # path was provided.
81
+ #
82
+ # @param [String] path the path to the file or directory to move.
83
+ # @param [String] new_path the path to where the new file or directory should be placed.
84
+ # @return [Hash] containing #bash response data.
85
+ def mv(path, new_path)
86
+ bash("mv '#{path}' '#{new_path}'")
87
+ end
88
+
89
+ # Closes the #ssh connection and clears the `@connection` instance
90
+ # variable of this objects parent `Core` instance so new connections
91
+ # can be established.
92
+ def close
93
+ ssh.close
94
+ core.clear_connection
95
+ end
96
+
97
+ # Closes the #ssh connection and exits the program with CODE.
98
+ #
99
+ # @see #close
100
+ # @param [Integer] code the code to exit the program with.
101
+ def close_and_exit(code = 0)
102
+ close
103
+ exit(code)
104
+ end
105
+
106
+ # Opens a channel for the #ssh connection and executes the provided COMMAND.
107
+ # This is a blocking (non-asynchronous) operation.
108
+ #
109
+ # @param [String] command the command to perform on the host.
110
+ # @return [Hash] containing response data (:stdout, :stderr, :exit_code, :exit_signal).
111
+ def bash(command)
112
+ stdout_data = ""
113
+ stderr_data = ""
114
+ exit_code = nil
115
+ exit_signal = nil
116
+
117
+ ssh.open_channel do |channel|
118
+ channel.exec("cd '#{current_path}'; #{command}") do |ch, success|
119
+ abort "FAILED: couldn't execute command (ssh.channel.exec)" unless success
120
+
121
+ channel.on_data { |ch,data| stdout_data += data }
122
+ channel.on_extended_data { |ch,type,data| stderr_data += data }
123
+ channel.on_request("exit-status") { |ch,data| exit_code = data.read_long }
124
+ channel.on_request("exit-signal") { |ch, data|exit_signal = data.read_long }
125
+ end
126
+ end
127
+
128
+ ssh.loop
129
+
130
+ {
131
+ :stdout => stdout_data,
132
+ :stderr => stderr_data,
133
+ :exit_code => exit_code,
134
+ :exit_signal => exit_signal
135
+ }
136
+ end
137
+ end
138
+ end
139
+
@@ -0,0 +1,55 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ module GitGo
4
+ class Formatter
5
+
6
+ # Creates a nice column format to output an Array to the console.
7
+ #
8
+ # @param [Array] array the Array to output in a column format.
9
+ # @param [Hash] options the options to create the column format with.
10
+ # @option options [Integer] :spacing (4) the amount of spaces between the columns.
11
+ # @option options [true, false] :header (false) whether there is a header present or not.
12
+ # @return [String] the formatted Array.
13
+ def self.columns(array, options = {})
14
+ options[:spacing] ||= 4
15
+ options[:header] ||= false
16
+
17
+ columns = 1
18
+ column_lengths = []
19
+ out = ""
20
+
21
+ array.each { |row| columns = row.length if row.length > columns }
22
+
23
+ columns.times do |i|
24
+ array.each do |row|
25
+ column_lengths[i] ||= 0
26
+ column_lengths[i] = row[i].length if row[i].length > column_lengths[i]
27
+ end
28
+ end
29
+
30
+ array.each_with_index do |row, row_index|
31
+ row.each_with_index do |column, index|
32
+ column_spacing = index + 1 == columns ? 0 : options[:spacing]
33
+ fill = column_lengths[index] - column.length
34
+
35
+ out << column + " " * (fill + column_spacing)
36
+ out << "\n" if index + 1 == columns
37
+ end
38
+
39
+ if options[:header]
40
+ row.each_with_index do |column, index|
41
+ if row_index == 0
42
+ column_spacing = index + 1 == columns ? 0 : options[:spacing]
43
+ out << "-" * column_lengths[index] + " " * column_spacing
44
+ end
45
+
46
+ out << "\n" if index + 1 == columns && row_index == 0
47
+ end
48
+ end
49
+ end
50
+
51
+ out
52
+ end
53
+ end
54
+ end
55
+
@@ -0,0 +1,8 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ module GitGo
4
+
5
+ # @return [String] the current GitGo version.
6
+ VERSION = "0.0.1"
7
+ end
8
+
data/lib/git_go.rb ADDED
@@ -0,0 +1,62 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ require "rubygems"
4
+ require "thor"
5
+ require "net/ssh"
6
+
7
+ require "git_go/connection"
8
+ require "git_go/formatter"
9
+ require "git_go/version"
10
+
11
+ module GitGo
12
+ class Core
13
+
14
+ # @return [String] the user for the #host.
15
+ attr_reader :user
16
+
17
+ # @return [String] the host/domain/ip of the remote machine.
18
+ attr_reader :host
19
+
20
+ # Creates a new instance of GitGo::Core. Will exit(1)
21
+ # the program if GIT_GO_USER or GIT_GO_HOST environment
22
+ # variables aren't present.
23
+ #
24
+ # @return [GitGo::Core] an instance of GitGo::Core
25
+ def initialize
26
+ @user = ENV["GIT_GO_USER"]
27
+ @host = ENV["GIT_GO_HOST"]
28
+
29
+ if [user, host].include?(nil)
30
+ puts "GIT_GO_USER and GIT_GO_HOST not set."
31
+ puts "Run `gg instructions` for more details."
32
+ exit(1)
33
+ end
34
+ end
35
+
36
+ # Creates and returns a new instance of GitGo::Connection.
37
+ # Subsequent calls to this method will returned the same cached
38
+ # reference to the previously created instance.
39
+ #
40
+ # @return [GitGo::Connection] an instance of GitGo::Connection
41
+ def connection
42
+ @connection ||= Connection.new(self)
43
+ end
44
+
45
+ # Clears a previously established connection reference. After
46
+ # invoking this method, it is again possible to establish a new
47
+ # connection with the remote machine using #connection.
48
+ #
49
+ # @see #connection
50
+ # @return [nil]
51
+ def clear_connection
52
+ @connection = nil
53
+ end
54
+
55
+ # Prints installation instructions to the console.
56
+ def self.instructions
57
+ file = File.expand_path("../../assets/instructions", __FILE__)
58
+ File.read(file)
59
+ end
60
+ end
61
+ end
62
+
@@ -0,0 +1,108 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ require "spec_helper"
4
+
5
+ describe GitGo::Connection do
6
+
7
+ let(:core) { GitGo::Core.new }
8
+ let(:conn) { GitGo::Connection.new(core) }
9
+ let(:ssh) { mock("Net::SSH") }
10
+
11
+ before do
12
+ ENV["GIT_GO_USER"] = "git"
13
+ ENV["GIT_GO_HOST"] = "127.0.0.1"
14
+ Net::SSH.stubs(:start).returns(ssh)
15
+ end
16
+
17
+ it("test if propperly mocked") { core; conn; ssh }
18
+
19
+ describe "#cd" do
20
+ it "should update the #current_path" do
21
+ conn.expects(:bash).with("cd ''; cd '/some/path'; pwd").
22
+ returns(:stdout => "/some/path\n")
23
+
24
+ conn.cd "/some/path"
25
+ end
26
+
27
+ it "should travel relative from the #current_path" do
28
+ conn.instance_variable_set("@current_path", "/some/path")
29
+
30
+ conn.expects(:bash).with("cd '/some/path'; cd '..'; pwd").
31
+ returns(:stdout => "/some\n")
32
+
33
+ conn.cd ".."
34
+ end
35
+ end
36
+
37
+ describe "#directory?" do
38
+ it "should return true" do
39
+ conn.expects(:bash).with("[ -d '/some/path' ]").returns({:exit_code => 0})
40
+ conn.directory?("/some/path").should be_true
41
+ end
42
+
43
+ it "should return false" do
44
+ conn.expects(:bash).with("[ -d '/some/path' ]").returns({:exit_code => 1})
45
+ conn.directory?("/some/path").should be_false
46
+ end
47
+ end
48
+
49
+ describe "#file?" do
50
+ it "should return true" do
51
+ conn.expects(:bash).with("[ -f '/some/path' ]").returns({:exit_code => 0})
52
+ conn.file?("/some/path").should be_true
53
+ end
54
+
55
+ it "should return false" do
56
+ conn.expects(:bash).with("[ -f '/some/path' ]").returns({:exit_code => 1})
57
+ conn.file?("/some/path").should be_false
58
+ end
59
+ end
60
+
61
+ describe "#mkdir" do
62
+ it "should run the unix command to make a directory recursively" do
63
+ conn.expects(:bash).with("mkdir -p '/some/path'")
64
+ conn.mkdir("/some/path")
65
+ end
66
+ end
67
+
68
+ describe "#rm" do
69
+ it "should run the unix command to remove a file or directory recursively" do
70
+ conn.expects(:bash).with("rm -rf '/some/path'")
71
+ conn.rm("/some/path")
72
+ end
73
+ end
74
+
75
+ describe "#mv" do
76
+ it "should run the unix command to mv a file or directory" do
77
+ conn.expects(:bash).with("mv '/some/path' '/new/path'")
78
+ conn.mv("/some/path", "/new/path")
79
+ end
80
+ end
81
+
82
+ describe "#close" do
83
+ it "should close the ssh connection and unset the instance variable on `core`" do
84
+ ssh.expects(:close)
85
+
86
+ core.connection
87
+ core.instance_variable_get("@connection").should_not be_nil
88
+
89
+ conn.close
90
+ core.instance_variable_get("@connection").should be_nil
91
+ end
92
+ end
93
+
94
+ describe "#close_and_exit" do
95
+ it "should #close the connection, and #exit the program" do
96
+ conn.expects(:close)
97
+ conn.expects(:exit).with(0)
98
+ conn.close_and_exit
99
+ end
100
+
101
+ it "should #close the connection, and #exit the program" do
102
+ conn.expects(:close)
103
+ conn.expects(:exit).with(1)
104
+ conn.close_and_exit(1)
105
+ end
106
+ end
107
+ end
108
+
@@ -0,0 +1,37 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ require "spec_helper"
4
+
5
+ describe GitGo::Formatter do
6
+
7
+ describe "#columns" do
8
+ it "should format without a header and with 4 spaces" do
9
+ array = [["Column 1", "Column 2"], ["Column 10", "Column 20"]]
10
+ out = GitGo::Formatter.columns(array)
11
+
12
+ out.should == (<<-EOS
13
+ Column 1 Column 2\s
14
+ Column 10 Column 20
15
+ EOS
16
+ ).gsub(/^\s+/, "")
17
+ end
18
+
19
+ it "should format with a header and 2 spaces" do
20
+ array = [
21
+ ["Example 1", "Example 2"],
22
+ ["Column 1", "Column 2"],
23
+ ["Column 3", "Column 4"]
24
+ ]
25
+ out = GitGo::Formatter.columns(array, :header => true, :spaces => 2)
26
+
27
+ out.should == (<<-EOS
28
+ Example 1 Example 2
29
+ --------- ---------
30
+ Column 1 Column 2\s
31
+ Column 3 Column 4\s
32
+ EOS
33
+ ).gsub(/^\s+/, "")
34
+ end
35
+ end
36
+ end
37
+
@@ -0,0 +1,10 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ require "spec_helper"
4
+
5
+ describe GitGo::VERSION do
6
+ it "should be a string" do
7
+ GitGo::VERSION.class.should == String
8
+ end
9
+ end
10
+
@@ -0,0 +1,23 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ require "spec_helper"
4
+
5
+ describe GitGo do
6
+ it "should exit when initializing without the environment variables" do
7
+ ENV["GIT_GO_USER"] = nil
8
+ ENV["GIT_GO_HOST"] = nil
9
+
10
+ GitGo::Core.any_instance.expects(:exit).with(1)
11
+ GitGo::Core.any_instance.stubs(:puts)
12
+ GitGo::Core.new
13
+ end
14
+
15
+ it "should not exit when initializing with the environment variables" do
16
+ ENV["GIT_GO_USER"] = "git"
17
+ ENV["GIT_GO_HOST"] = "127.0.0.1"
18
+
19
+ GitGo::Core.any_instance.expects(:exit).never
20
+ GitGo::Core.new
21
+ end
22
+ end
23
+
@@ -0,0 +1,12 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ require "git_go"
4
+
5
+ RSpec.configure do |config|
6
+ config.treat_symbols_as_metadata_keys_with_true_values = true
7
+ config.run_all_when_everything_filtered = true
8
+ config.filter_run :focus
9
+ config.mock_framework = :mocha
10
+ config.order = "random"
11
+ end
12
+
metadata ADDED
@@ -0,0 +1,116 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: git_go
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Michael van Rooijen
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-12-11 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: thor
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 0.16.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 0.16.0
30
+ - !ruby/object:Gem::Dependency
31
+ name: net-ssh
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: 2.6.2
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 2.6.2
46
+ description: Git Go is a small command-line utility distributed as a RubyGem that
47
+ allows you to easily create/destroy/rename/list all your private-hosted git repositories
48
+ on your own server.
49
+ email:
50
+ - meskyanichi@gmail.com
51
+ executables:
52
+ - gg
53
+ extensions: []
54
+ extra_rdoc_files: []
55
+ files:
56
+ - .gitignore
57
+ - .rspec
58
+ - .travis.yml
59
+ - Gemfile
60
+ - Gemfile.lock
61
+ - Guardfile
62
+ - LICENSE
63
+ - README.md
64
+ - Rakefile
65
+ - assets/instructions
66
+ - bin/gg
67
+ - gitgo.gemspec
68
+ - lib/git_go.rb
69
+ - lib/git_go/cli/gg.rb
70
+ - lib/git_go/connection.rb
71
+ - lib/git_go/formatter.rb
72
+ - lib/git_go/version.rb
73
+ - spec/lib/git_go/connection_spec.rb
74
+ - spec/lib/git_go/formatter_spec.rb
75
+ - spec/lib/git_go/version_spec.rb
76
+ - spec/lib/git_go_spec.rb
77
+ - spec/spec_helper.rb
78
+ homepage: http://github.com/meskyanichi/git_go
79
+ licenses: []
80
+ post_install_message:
81
+ rdoc_options: []
82
+ require_paths:
83
+ - lib
84
+ required_ruby_version: !ruby/object:Gem::Requirement
85
+ none: false
86
+ requirements:
87
+ - - ! '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ segments:
91
+ - 0
92
+ hash: -1366446642123636879
93
+ required_rubygems_version: !ruby/object:Gem::Requirement
94
+ none: false
95
+ requirements:
96
+ - - ! '>='
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ segments:
100
+ - 0
101
+ hash: -1366446642123636879
102
+ requirements: []
103
+ rubyforge_project:
104
+ rubygems_version: 1.8.23
105
+ signing_key:
106
+ specification_version: 3
107
+ summary: Git Go is a small command-line utility distributed as a RubyGem that allows
108
+ you to easily create/destroy/rename/list all your private-hosted git repositories
109
+ on your own server.
110
+ test_files:
111
+ - spec/lib/git_go/connection_spec.rb
112
+ - spec/lib/git_go/formatter_spec.rb
113
+ - spec/lib/git_go/version_spec.rb
114
+ - spec/lib/git_go_spec.rb
115
+ - spec/spec_helper.rb
116
+ has_rdoc: