dotfu 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.rspec +2 -0
- data/.travis.yml +11 -0
- data/.yardopts +1 -0
- data/CHANGELOG.md +0 -0
- data/Gemfile +29 -0
- data/Guardfile +35 -0
- data/LICENSE.txt +36 -0
- data/README.md +73 -0
- data/Rakefile +7 -0
- data/bin/dotfu +38 -0
- data/commands/fetch.rb +16 -0
- data/commands/install.rb +20 -0
- data/commands/list.rb +26 -0
- data/commands/uninstall.rb +23 -0
- data/config_example.json +26 -0
- data/dotfu.gemspec +26 -0
- data/lib/dotfu/config_parser.rb +9 -0
- data/lib/dotfu/repos.rb +199 -0
- data/lib/dotfu/version.rb +3 -0
- data/lib/dotfu.rb +49 -0
- data/roadmap.md +3 -0
- data/spec/cli_spec_helper.rb +99 -0
- data/spec/dotfu_spec.rb +8 -0
- data/spec/spec_helper.rb +13 -0
- metadata +144 -0
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
data/.rspec
ADDED
data/.travis.yml
ADDED
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
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
|
+
|
data/commands/install.rb
ADDED
@@ -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
|
+
|
data/config_example.json
ADDED
@@ -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
|
data/lib/dotfu/repos.rb
ADDED
@@ -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
|
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,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
|
data/spec/dotfu_spec.rb
ADDED
data/spec/spec_helper.rb
ADDED
@@ -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:
|