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 +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:
|