dotfu 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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: cfd33a64430a135ebb5e9d655946cc5075c3e857
4
+ data.tar.gz: 8b5ea0b776586d28228074c837cfc2bf0694b050
5
+ SHA512:
6
+ metadata.gz: 7bd34205b448adfc2293bf0d3eb7e924f0868e32b62049b1ac98caca400f19b593c4ed8e411ad6efea90beb58a511dd19461577961a8261f8132d8fc00b668af
7
+ data.tar.gz: d69cccac6a0472eb8d6bf1e20a65ccdb1f6055381c2d435dc1037bd73370e044536e4da9c9b6f8344a837af17ab301cd36792283d6a6e5910e45767e4518ecca
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/.travis.yml ADDED
@@ -0,0 +1,11 @@
1
+ bundler_args: --without development
2
+
3
+ rvm:
4
+ - 1.9.2
5
+ - 1.9.3
6
+ - rbx-19mode
7
+
8
+ branches:
9
+ only:
10
+ - master
11
+ - development
data/.yardopts ADDED
@@ -0,0 +1 @@
1
+ --no-private - LICENSE.txt CHANGELOG.md
data/CHANGELOG.md ADDED
File without changes
data/Gemfile ADDED
@@ -0,0 +1,29 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ group :development do
6
+ gem "bundler"
7
+ gem "rake"
8
+ gem "pry"
9
+ gem "pry-debugger"
10
+ gem "redcarpet"
11
+ gem "guard"
12
+ gem "guard-bundler"
13
+ gem "guard-rspec"
14
+ gem "guard-yard"
15
+ gem "guard-shell"
16
+ gem 'libnotify', :require => false
17
+ gem 'growl', :require => false
18
+ gem 'rb-inotify', :require => false
19
+ gem 'rb-fsevent', :require => false
20
+ gem 'rb-fchange', :require => false
21
+ end
22
+
23
+ group :test do
24
+ gem "rspec"
25
+ gem "webmock"
26
+ gem 'simplecov', :require => false
27
+ gem 'simplecov-rcov', :require => false
28
+ end
29
+
data/Guardfile ADDED
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/ruby
2
+
3
+ # Must be an array
4
+ test_cmd = [
5
+ "bundle exec dotfu install zsh",
6
+ "bundle exec dotfu uninstall zsh"
7
+ ]
8
+
9
+ guard :bundler do
10
+ watch 'Gemfile'
11
+ watch '*.gemspec'
12
+ end
13
+
14
+ guard :rspec do
15
+ watch(%r{^spec/.+_spec\.rb$})
16
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec spec/lib/#{m[1]}_spec.rb" }
17
+ watch(%r{^bin/(.+)\.rb$}) { |m| "spec/bin/#{m[1]}_spec.rb" }
18
+ watch('spec/spec_helper.rb') { "spec" }
19
+ end
20
+
21
+ # guard :yard do
22
+ # watch(%r{^lib/(.+)\.rb$})
23
+ # end
24
+
25
+ guard :shell do
26
+ watch /.*/ do |m|
27
+ puts "Time: #{Time.now}, file saved: #{m}"
28
+ test_cmd.each do |cmd|
29
+
30
+ puts "=" * 80
31
+ puts "cmd: #{cmd}"
32
+ puts `#{cmd}`
33
+ end
34
+ end
35
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,36 @@
1
+ All software in this package is covered by the MIT license and beerware (rev42).
2
+
3
+ Copyright (c) 2011 Ernie Brodeur
4
+
5
+ Permission is hereby granted, free of charge, to any person
6
+ obtaining a copy of this software and associated documentation
7
+ files (the "Software"), to deal in the Software without
8
+ restriction, including without limitation the rights to use,
9
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the
11
+ Software is furnished to do so, subject to the following
12
+ conditions:
13
+
14
+ The above copyright notice and this permission notice shall be
15
+ included in all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
19
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24
+ OTHER DEALINGS IN THE SOFTWARE.
25
+
26
+ /*
27
+ * ----------------------------------------------------------------------------
28
+ * "THE BEER-WARE LICENSE" (Revision 42):
29
+ * Ernie Brodeur wrote this package. As long as you retain this notice you
30
+ * can do whatever you want with this stuff. If we meet some day, and you think
31
+ * this stuff is worth it, you can buy me a beer in return.
32
+ * ----------------------------------------------------------------------------
33
+ */
34
+
35
+ All contributed code is licensed accordingly and accredited whenever possible.
36
+ If I make any mistake about this, please feel free to contact me at ebrodeur@ujami.net.
data/README.md ADDED
@@ -0,0 +1,73 @@
1
+ # Description
2
+
3
+ Dotfu is a shell command to help all of us manage our dotfiles. It is designed
4
+ to clone dotfiles from github and install them into your home directory in one command.
5
+
6
+ It will also allow you to manage/switch/test other peoples dotfiles on the fly.
7
+
8
+ Right now it will install/uninstall/fetch files. It will not overwrite your current files, it will abort and let you move them.
9
+
10
+ Later backup originals, generate repo, search, ...
11
+
12
+ # Usage
13
+
14
+ Dotfu is a subcommand based cli like git. It supplies various subcommands that do
15
+ the tasks for you. ```--help``` works, but it is rough.
16
+
17
+ ## Arguments
18
+
19
+ Before we go over the commands, it is important to understand the repo format you use.
20
+ I don't know of a github uri, so we will use some shorthand like this ```user@repo:branch```. Even if we do switch to a uri, I will leave this shorthand.
21
+
22
+ More about the fields:
23
+
24
+ * user: optional, if it's not supplied we try the config file for github.user
25
+ * repo: required, can be in the form of ```dotfiles-zsh``` or ```zsh```.
26
+ * branch: optional, if you want to specify the branch to use. Will switch on install, not fetch.
27
+
28
+ ## Commands
29
+
30
+ ### Currently working:
31
+
32
+ * list: list all dotfiles-* directores
33
+ * fetch: Pull down a dotfiles-<n> repo into the cache.
34
+ * install: implies fetch, then installs dotfiles-<n> based on the dotfiles.json
35
+ * uninstall: remove the contents of a repo.
36
+ * search: search github for repo's based on <pattern>.
37
+
38
+ ### Still to build
39
+
40
+ * clean: clean unused items from the cache.
41
+ * status: List which df repos that need to get updated.
42
+ * update: update repos.
43
+
44
+ ## Configfile
45
+
46
+ The config file will be a json blob. It will be named ```dotfu.json``` and
47
+ should be present at the base of the repo.
48
+
49
+ ## Fields
50
+
51
+ Currently only target_directory works.
52
+
53
+ * target_directory (optional): if not your home dir, where?
54
+ * ignore_patterns (optional): files in the repo to never install (good for readmes).
55
+ * segments (optional): see below
56
+
57
+ # Segments
58
+
59
+ Segments are labeled selections of files in a repo to install selectively.
60
+
61
+ * pre_install_script
62
+ * post_install_script
63
+ * routes
64
+
65
+ Routes are glob patterns and a target directory for them.
66
+
67
+ ## Contributing
68
+
69
+ 1. Fork it
70
+ 2. Create your feature branch (`git checkout -b feature-something`)
71
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
72
+ 4. Push to the branch (`git push origin feature-something`)
73
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require 'rspec/core/rake_task'
2
+
3
+ RSpec::Core::RakeTask.new(:spec)
4
+
5
+ task :default => :spec
6
+
7
+ require "bundler/gem_tasks"
data/bin/dotfu ADDED
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'dotfu'
4
+ require 'yaml'
5
+
6
+
7
+ require 'slop'
8
+
9
+ if !Dotfu.installed?
10
+ puts "Git does not appear to be installed, aporting."
11
+ exit 1
12
+ end
13
+
14
+ opts = Slop.parse(help:true) do
15
+ banner "Dotfu:\n"\
16
+ "DOTFILES is a string that can be user:repo or user@repo. If no user is "\
17
+ "provided, it will attempt to look one up from the git config file.\n"
18
+
19
+ on(:v, :verbose, 'Enable verbose mode')
20
+ on(:version, 'Print the version') {puts "Version #{Dotfu::VERSION}" }
21
+ on(:data_directory, "Root directory to fetch into (default: #{Bini.data_dir}/repos)")
22
+ on(:target_directory, "Change the default target directory from your home: #{Dir.home}")
23
+
24
+ # not sure that this is the right dir after install. Need to determine that.
25
+ Dir.glob("commands/*.rb").each do |file|
26
+ instance_eval open(file).read
27
+ end
28
+
29
+ command 'search' do
30
+ banner "Search for a repo globally or in a user's directory."
31
+ end
32
+ command 'clean' do
33
+ banner "Purge any unused files from the cached repos."
34
+ end
35
+ command 'status' do
36
+ banner "Spit out various stats about the cached/installed repos."
37
+ end
38
+ end
data/commands/fetch.rb ADDED
@@ -0,0 +1,16 @@
1
+ command 'fetch' do
2
+ banner "Fetch a repo into it's cache dir.\n"\
3
+ "Usage: dotfu fetch [options] DOTFILES\n"
4
+
5
+ run do |opts, args|
6
+ return nil if !args
7
+
8
+ # POC only, it still needs to check for output errors and a few other things.
9
+ args.each do |a|
10
+ repo = Dotfu::Repos.new a
11
+ output = repo.fetch
12
+ puts "Repo #{repo.repo} from: #{repo.user}:\n#{output[1]}"
13
+ end
14
+ end
15
+ end
16
+
@@ -0,0 +1,20 @@
1
+ command 'install' do
2
+ banner "Install a dotfiles from Github into your home directory."
3
+
4
+ run do |opts, args|
5
+ unless args.any?
6
+ puts 'install what?'
7
+ exit 1
8
+ end
9
+
10
+ args.each do |target|
11
+ repo = Dotfu::Repos.new target
12
+
13
+ puts "Fetching repo #{target}"
14
+ puts repo.fetch[:out]
15
+ puts "Installing #{target} to #{repo.target_dir}"
16
+ repo.install
17
+ end
18
+ puts "Complete."
19
+ end
20
+ end
data/commands/list.rb ADDED
@@ -0,0 +1,26 @@
1
+ command 'list' do
2
+ banner "List the repos of a given user."
3
+ run do |opts, args|
4
+ if args.any?
5
+ username = args.first
6
+ else
7
+ username = Dotfu.config_user
8
+ end
9
+
10
+ begin
11
+ results = Dotfu::GITHUB.repos.list(user:username).select {|r| r.name.start_with? 'dotfiles' };
12
+ rescue Exception => e
13
+ puts e
14
+ end
15
+
16
+ unless results && results.any?
17
+ puts "No suitable repositories found."
18
+ else
19
+ puts "Repositories for #{username}:"
20
+ results.each do |repo|
21
+ puts " #{repo.name}"
22
+ end
23
+ end
24
+ end
25
+ end
26
+
@@ -0,0 +1,23 @@
1
+ command 'uninstall' do
2
+ banner "Uninstall dotfiles from your home directory."
3
+
4
+ run do |opts, args|
5
+ unless args.any?
6
+ puts 'install what?'
7
+ exit 1
8
+ end
9
+
10
+ args.each do |target|
11
+ repo = Dotfu::Repos.new target
12
+
13
+ unless repo.installed?
14
+ puts "#{target} is not installed."
15
+ exit
16
+ end
17
+ puts "Uninstalling #{target} from #{repo.target_dir}"
18
+ repo.uninstall
19
+ end
20
+ puts "Complete."
21
+ end
22
+ end
23
+
@@ -0,0 +1,26 @@
1
+ // General options
2
+ // Everything is relative to this directory.
3
+ // There are no directories past this point, only files.
4
+ {
5
+ "target_directory":"/home/ebrodeur/.config/powerline",
6
+
7
+ // Ignore Patterns (optional)
8
+ // Takes an array or entry, if array it is a collection of glob or regex.
9
+ // Regex is stored in a string and detection done in application.
10
+
11
+ "ignore_patterns":["README.md", "**/*.md", "/.*/" ],
12
+
13
+ // Segments (optional)
14
+ // Segments allow for smaller parts to be installed as needed/requested.
15
+ // Each segment is a named object with a collection of pattern/location pairs.
16
+ "segments":{
17
+ "zsh":{
18
+ "pre_install_script":"filename",
19
+ "post_install_script":"filename",
20
+ "routes":{
21
+ ".zshrc":"/",
22
+ "zsh":"/"
23
+ }
24
+ }
25
+ }
26
+ }
data/dotfu.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'dotfu/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "dotfu"
8
+ spec.version = Dotfu::VERSION
9
+ spec.authors = ["Ernie Brodeur"]
10
+ spec.email = ["ebrodeur@ujami.net"]
11
+ spec.description = "Manage/sync/share dotfiles via github."
12
+ spec.summary = "More to come . . . "
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_runtime_dependency "bini"
22
+ spec.add_runtime_dependency "git"
23
+ spec.add_runtime_dependency "github_api"
24
+ spec.add_runtime_dependency "slop"
25
+ spec.add_runtime_dependency "yajl-ruby"
26
+ end
@@ -0,0 +1,9 @@
1
+ module Dotfu
2
+ module ConfigParser
3
+ def load(filename)
4
+
5
+ end
6
+
7
+ def
8
+ end
9
+ end
@@ -0,0 +1,199 @@
1
+ module Dotfu
2
+ class Repos
3
+ attr_accessor :repo
4
+ attr_accessor :branch
5
+ attr_accessor :user
6
+ attr_accessor :config_file
7
+ attr_accessor :working_dir
8
+ attr_accessor :target_dir
9
+
10
+ # r can be either a repo, or a user:repo pair.
11
+ def initialize(arg = nil)
12
+ parse_arg arg if arg
13
+ parse_config
14
+ end
15
+
16
+ ### Specialized attribute methods
17
+ def config_file
18
+ return @config_file ? @config_file : "dotfu.json"
19
+ end
20
+
21
+ # prepend repo with dotfiles- if it doesn't exist as it is set.
22
+ def repo=(word)
23
+ return @repo = word.start_with?('dotfiles-') ? word : "dotfiles-#{word}"
24
+ end
25
+
26
+ # target_dir should always return something.
27
+ def target_dir
28
+ return @target_dir ? @target_dir : Dir.home
29
+ end
30
+
31
+ # return user or the user in the config file.
32
+ def user
33
+ return @user ? @user : Dotfu.config_user
34
+ end
35
+
36
+ # return the explicit directory this repo is cloned into.
37
+ def working_dir
38
+ return nil if !repo || !user
39
+ return "#{Bini.data_dir}/repos/#{user}/#{repo}"
40
+ end
41
+
42
+ ### Work methods
43
+
44
+ # initial clone
45
+ def clone
46
+ return nil if !repo || !user
47
+ return git_cmd "clone git://github.com/#{user}/#{repo}.git #{working_dir}", false
48
+ end
49
+
50
+ # A wrapper method to clone or update a repo.
51
+ def fetch
52
+ return nil if !repo || !user
53
+ if fetched?
54
+ pull
55
+ else
56
+ clone
57
+ end
58
+ end
59
+
60
+
61
+ def install
62
+ result = git_cmd "checkout #{branch}" if branch
63
+
64
+ raise RuntimeError.new result unless result[:status].success?
65
+
66
+ # lets check if we have anything in the way, and abort instead of partially
67
+ # installing
68
+ existing_files = target_files.select {|f| File.exist? f}
69
+ raise NotImplementedError.new "File(s) exist: #{existing_files}" if existing_files.any?
70
+
71
+ # And now that we are ok with everything, lets make some fucking files.
72
+ # TODO: add deep linking (mkdir + ln_s for each target) or shallow (just the first directory)
73
+ FileUtils.mkdir_p target_dir
74
+ files.each {|file| FileUtils.ln_s working_file(file), target_file(file)}
75
+
76
+ return true
77
+ end
78
+
79
+ def pull
80
+ return nil if !repo || !user
81
+ return git_cmd "pull"
82
+ end
83
+
84
+ def uninstall
85
+ raise RuntimeError.new "Not installed." unless installed?
86
+
87
+ # ok we are not installed, lets clear the links. Later, this will restore
88
+ # the backups (or something similiar).
89
+ target_files.each {|f| FileUtils.rm f}
90
+ return true
91
+ end
92
+
93
+ ### Helper methods
94
+
95
+ # I need to make this more sophisticated. Or make a like updated? method.
96
+ def fetched?
97
+ return nil if !repo
98
+ return false if !working_dir
99
+ files = Dir.glob("#{working_dir}/**/*")
100
+
101
+ return true if files.any?
102
+ return false
103
+ end
104
+
105
+ # does it have a config file? Must be fetched or will return nil.
106
+ def config_file?
107
+ return false unless fetched?
108
+
109
+ return File.exist? "#{working_dir}/#{config_file}"
110
+ end
111
+
112
+ # returns true if every file is installed. I need to make this more indepth,
113
+ # but it will at lease ensure all files are linked to something in the working dir.
114
+ def installed?
115
+ results = target_files.map {|f| is_my_file?(f) }
116
+ return false if results.include? false
117
+ return true
118
+ end
119
+
120
+ # Return true if this file was installed from our repo.
121
+ def is_my_file?(file)
122
+ return true if File.exist?(file) && File.symlink?(file) && File.readlink(file).start_with?(working_dir)
123
+ return false
124
+ end
125
+ # return an [Array] of base filenames.
126
+ def files
127
+ if !@files
128
+ files = Dir.glob("#{working_dir}/*").map {|f| f.split(working_dir)[1][1..-1]}
129
+ files.delete config_file
130
+ @files = files
131
+ end
132
+ return @files
133
+ end
134
+
135
+ # Return the target file.
136
+ # Takes a [String] (explicit conversion) or [Array] for index lookup.
137
+ def target_file(file)
138
+ "#{target_dir}/.#{file_string(file)}"
139
+ end
140
+
141
+ # Return an [Array] of target files.
142
+ def target_files
143
+ files.map {|f| target_file f}
144
+ end
145
+
146
+ # return the working file.
147
+ # Takes a [String] (explicit conversion) or [Array] for index lookup.
148
+ def working_file(file)
149
+ "#{working_dir}/#{file_string(file)}"
150
+ end
151
+
152
+ # Return an [Array] of working files.
153
+ def working_files
154
+ files.map {|f| working_file f}
155
+ end
156
+
157
+
158
+ private
159
+ # So our input is now username@repo:branch
160
+ # Repo is the only one required.
161
+ def parse_arg(word)
162
+ if word.include? "@"
163
+ self.name,word = word.split("@")
164
+ end
165
+
166
+ if word.include? ":"
167
+ self.repo, self.branch = word.split(":")
168
+ end
169
+
170
+ self.repo = word if !self.repo
171
+ end
172
+
173
+ def parse_config
174
+ return nil unless config_file?
175
+
176
+ content = Yajl.load open("#{working_dir}/#{config_file}")
177
+ @target_dir = content["target_directory"] if content["target_directory"]
178
+ end
179
+
180
+ # Accepts a string or fixnum. Returns either the string or the files[fixnum]
181
+ # TODO: figure out a better fucking name for this.
182
+ def file_string(file)
183
+ return file.class == Fixnum ? files[file] : file
184
+ end
185
+ def git_cmd(cmd, cd = true)
186
+ if cd
187
+ pwd = Dir.pwd
188
+ Dir.chdir working_dir
189
+ end
190
+
191
+ out, err, status = Open3.capture3 "git #{cmd}"
192
+
193
+ Dir.chdir pwd if cd
194
+
195
+ return {status:status, out:out, err:err}
196
+ end
197
+
198
+ end
199
+ end
@@ -0,0 +1,3 @@
1
+ module Dotfu
2
+ VERSION = "0.1.0"
3
+ end
data/lib/dotfu.rb ADDED
@@ -0,0 +1,49 @@
1
+ require 'open3'
2
+ require 'yajl'
3
+ require 'bini'
4
+ require 'bini/config'
5
+ require 'bini/optparser'
6
+ require 'bini/sash'
7
+ require 'github_api'
8
+
9
+ require "dotfu/version"
10
+ require "dotfu/repos"
11
+
12
+ module Dotfu
13
+ extend self
14
+
15
+ # what does the config say our user is?
16
+ def config_user
17
+ if !instance_variables.include? "@config_user"
18
+ if installed?
19
+ output = `git config github.user`.chomp!
20
+ if output.empty?
21
+ @config_user = nil
22
+ else
23
+ @config_user = output
24
+ end
25
+ end
26
+ end
27
+
28
+ return @config_user
29
+ end
30
+
31
+ # Is git installed in the system?
32
+ def installed?
33
+ if !@git_installed
34
+ results = ENV["PATH"].split(":").map do |path|
35
+ File.exist?("#{path}/git")
36
+ end
37
+
38
+ @git_installed = results.include? true
39
+ end
40
+ return @git_installed
41
+ end
42
+
43
+ Bini.long_name = "dotfu"
44
+ GITHUB ||= Github.new user_agent:"Dotfu: #{Dotfu::VERSION}", auto_pagination:true
45
+ end
46
+
47
+
48
+
49
+
data/roadmap.md ADDED
@@ -0,0 +1,3 @@
1
+ ## 0.1.0
2
+
3
+ *
@@ -0,0 +1,99 @@
1
+ # Ripped without remorse from: https://github.com/cucumber/aruba/blob/master/lib/aruba/process.rb
2
+ # slightly modified for my purposes.
3
+ require 'childprocess'
4
+ require 'tempfile'
5
+ require 'shellwords'
6
+
7
+ class CLIProcess
8
+ include Shellwords
9
+
10
+ def initialize(cmd, exit_timeout = 0, io_wait = 1)
11
+ @exit_timeout = exit_timeout
12
+ @io_wait = io_wait
13
+ @io_waited = false
14
+
15
+ @cmd = cmd
16
+ @process = nil
17
+ @exit_code = nil
18
+ end
19
+
20
+ def run!(&block)
21
+ @process = ChildProcess.build(*shellwords(@cmd))
22
+ @out = Tempfile.new("binary-out")
23
+ @err = Tempfile.new("binary-err")
24
+ @process.io.stdout = @out
25
+ @process.io.stderr = @err
26
+ @process.duplex = true
27
+ @exit_code = nil
28
+ begin
29
+ @process.start
30
+ rescue ChildProcess::LaunchError => e
31
+ raise LaunchError.new(e.message)
32
+ end
33
+ yield self if block_given?
34
+ end
35
+
36
+ def stdin
37
+ @process.io.stdin
38
+ end
39
+
40
+ def output(keep_ansi = false)
41
+ stdout(keep_ansi) + stderr(keep_ansi)
42
+ end
43
+
44
+ def stdout(keep_ansi = false)
45
+ wait_for_io do
46
+ @out.rewind
47
+ filter_ansi(@out.read, keep_ansi)
48
+ end
49
+ end
50
+
51
+ def stderr(keep_ansi = false)
52
+ wait_for_io do
53
+ @err.rewind
54
+ filter_ansi(@err.read, keep_ansi)
55
+ end
56
+ end
57
+
58
+ def read_stdout(keep_ansi = false)
59
+ wait_for_io do
60
+ @process.io.stdout.flush
61
+ content = filter_ansi(open(@out.path).read, keep_ansi)
62
+ end
63
+ end
64
+
65
+ def stop(reader, keep_ansi)
66
+ return @exit_code unless @process
67
+ unless @process.exited?
68
+ @process.poll_for_exit(@exit_timeout)
69
+ end
70
+ reader.stdout stdout(keep_ansi)
71
+ reader.stderr stderr(keep_ansi)
72
+ @exit_code = @process.exit_code
73
+ @process = nil
74
+ @exit_code
75
+ end
76
+
77
+ def terminate(keep_ansi = false)
78
+ if @process
79
+ stdout(keep_ansi = false) && stderr(keep_ansi) # flush output
80
+ @process.stop
81
+ stdout(keep_ansi) && stderr(keep_ansi) # flush output
82
+ end
83
+ end
84
+
85
+ private
86
+
87
+ def wait_for_io(&block)
88
+ if @process && !@io_waited
89
+ sleep @io_wait
90
+ @io_waited = true
91
+ end
92
+ yield
93
+ end
94
+
95
+ def filter_ansi(string, keep_ansi)
96
+ keep_ansi ? string : string.gsub(/\e\[\d+(?>(;\d+)*)m/, '')
97
+ end
98
+
99
+ end
@@ -0,0 +1,8 @@
1
+ require 'spec_helper'
2
+
3
+ describe Dotfu do
4
+ describe 'Backup' do
5
+ it "will backup a file"
6
+ it "will restore a file"
7
+ end
8
+ end
@@ -0,0 +1,13 @@
1
+ if ENV["COVERAGE"] == 'true'
2
+ require 'simplecov'
3
+ require 'simplecov-rcov'
4
+
5
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
6
+ SimpleCov::Formatter::HTMLFormatter,
7
+ SimpleCov::Formatter::RcovFormatter
8
+ ]
9
+ SimpleCov.start do
10
+ add_filter "/spec/"
11
+ end
12
+ end
13
+
metadata ADDED
@@ -0,0 +1,144 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dotfu
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Ernie Brodeur
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-05-31 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bini
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: git
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: github_api
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: slop
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: yajl-ruby
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: Manage/sync/share dotfiles via github.
84
+ email:
85
+ - ebrodeur@ujami.net
86
+ executables:
87
+ - dotfu
88
+ extensions: []
89
+ extra_rdoc_files: []
90
+ files:
91
+ - .gitignore
92
+ - .rspec
93
+ - .travis.yml
94
+ - .yardopts
95
+ - CHANGELOG.md
96
+ - Gemfile
97
+ - Guardfile
98
+ - LICENSE.txt
99
+ - README.md
100
+ - Rakefile
101
+ - bin/dotfu
102
+ - commands/fetch.rb
103
+ - commands/install.rb
104
+ - commands/list.rb
105
+ - commands/uninstall.rb
106
+ - config_example.json
107
+ - dotfu.gemspec
108
+ - lib/dotfu.rb
109
+ - lib/dotfu/config_parser.rb
110
+ - lib/dotfu/repos.rb
111
+ - lib/dotfu/version.rb
112
+ - roadmap.md
113
+ - spec/cli_spec_helper.rb
114
+ - spec/dotfu_spec.rb
115
+ - spec/spec_helper.rb
116
+ homepage: ''
117
+ licenses:
118
+ - MIT
119
+ metadata: {}
120
+ post_install_message:
121
+ rdoc_options: []
122
+ require_paths:
123
+ - lib
124
+ required_ruby_version: !ruby/object:Gem::Requirement
125
+ requirements:
126
+ - - '>='
127
+ - !ruby/object:Gem::Version
128
+ version: '0'
129
+ required_rubygems_version: !ruby/object:Gem::Requirement
130
+ requirements:
131
+ - - '>='
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ requirements: []
135
+ rubyforge_project:
136
+ rubygems_version: 2.0.0
137
+ signing_key:
138
+ specification_version: 4
139
+ summary: More to come . . .
140
+ test_files:
141
+ - spec/cli_spec_helper.rb
142
+ - spec/dotfu_spec.rb
143
+ - spec/spec_helper.rb
144
+ has_rdoc: