dotter_dotfiles 0.2.0 → 0.3.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.
data/ChangeLog.md CHANGED
@@ -1,3 +1,8 @@
1
+ ### 0.3.0 / 2015-11-10
2
+ * Attempted (and failed) to implement a smart clone command.
3
+ * Implemented a dumb clone command that wipes away VCS history and package state after cloning.
4
+ * Sorry for this taking forever to release.
5
+
1
6
  ### 0.2.0 / 2015-11-10
2
7
  * Implemented the update_all command
3
8
  * Minor internal refactoring
data/README.md CHANGED
@@ -14,11 +14,36 @@ It is very new, unfinished software that likely has multiple bugs. However, due
14
14
 
15
15
  Due to the name 'dotter' already being used, the gem is available under the name 'dotter_dotfiles'. Install it with `gem install dotter_dotfiles`
16
16
 
17
- To see all available commands, use `dotter help`. Not all commands are currently implemented.
17
+ To see all available commands, use `dotter help` or look at the list of commands below. All commands are implemented, but the clone command is dumb due to the difficulties
18
+ involved in reconstructing the entire structure and state ` ~/dotfiles` from a single, flat git repository.
19
+
20
+ Commands:
21
+ ```
22
+ dotter add PACKAGE FILE # Add a file from a package to the next commit of that package.
23
+ dotter clone REPO_URL # Clones the dotfiles / packages of the specified repository into ~/dotfiles. Will overwrite any existing data.
24
+ dotter commit PACKAGE -m, --commit-message=COMMIT_MESSAGE # Commit your changes to a Git-tracked package.
25
+ dotter help [COMMAND] # Describe available commands or one specific command
26
+ dotter import PATH PACKAGE # Imports a file or directory into the specified package
27
+ dotter import_repo REPO_URL PACKAGE # Clones the specified git repository as the contents of the specified Package.
28
+ dotter init # Initialise the directory structure for ~/dotfiles
29
+ dotter list # List all packages present in ~/dotfiles
30
+ dotter log PACKAGE # View the commit log of a package.
31
+ dotter publish PACKAGE # Make a package available in your public dotfiles repository
32
+ dotter reset PACKAGE # Reset what will be commmitted in the next commit to the given package.
33
+ dotter status PACKAGE # Obtain the repository status of a Git-tracked package.
34
+ dotter stow PACKAGE # Stow the given package name.
35
+ dotter track PACKAGE # Begin tracking the given package with Git
36
+ dotter unpublish PACKAGE # Make a package private after publishing it.
37
+ dotter unstow PACKAGE # Unstow the given package name.
38
+ dotter update PACKAGE # Updates the specified package
39
+ dotter update_all # Updates all stowed packages.
40
+ dotter version # Print the dotter version
41
+ ```
18
42
  This project tries to follow [Semantic Versioning](http://semver.org/) but no guarantees are made in this regard.
19
43
  ## TODO
20
44
  1. Refactor and clean up the code.
21
- 2. Implement all unimplemented commands.
22
- 3. Implement error handling
23
- 4. Add any useful suggested features.
24
- 5. Port to Crystal so it can be a single executable.
45
+ 2. ~~Implement all unimplemented commands.~~
46
+ 3. Make the `clone` command smarter
47
+ 4. Implement error handling
48
+ 5. Add any useful suggested features.
49
+ 6. Port to Crystal so it can be a single executable.
data/bin/dotter CHANGED
@@ -8,7 +8,7 @@ if File.directory?(File.join(root,'.git'))
8
8
  rescue LoadError => e
9
9
  warn e.message
10
10
  warn "Run `gem install bundler` to install Bundler"
11
- exit -1
11
+ exit(-1)
12
12
  end
13
13
  end
14
14
  end
data/lib/dotter/cli.rb CHANGED
@@ -5,186 +5,201 @@ require 'dotter/version'
5
5
  require 'dotter/configuration'
6
6
  require 'dotter/package'
7
7
  require 'dotter/publicgitrepo'
8
+ require 'dotter/foreigngitrepo'
8
9
  require 'pathname'
10
+ require 'git'
9
11
  module Dotter
10
- class CLI < Thor
11
- include Utilities
12
- desc "version", "Print the dotter version"
13
- def version
14
- puts "This is dotter #{Dotter::VERSION}"
15
- end
16
- desc "init", "Initialise the directory structure for ~/dotfiles"
17
- def init
18
- puts "Initialising ~/dotfiles"
19
- puts "Creating the dotfiles directory."
20
- FileUtils.mkpath(dotfiles_path)
21
- go_to_dotfiles
22
- puts "Creating the directory for the combined public dotfiles."
23
- FileUtils.mkpath('public')
24
- puts "Creating an initial package for dotter."
25
- FileUtils.mkpath('dotter/.dotter/gitrepos')
26
- FileUtils.mkpath('dotter/.dotter/indexes/')
27
- # If we don't do this now, we'll get a nasty exception if we ever access the configuration.
28
- FIleUtils.touch('dotter/.dotter/Dotfile')
29
- end
30
- desc "list", "List all packages present in ~/dotfiles"
31
- def list
32
- puts "List of packages in ~/dotfiles"
33
- all_package_names.each do |package|
34
- puts package
35
- end
36
- end
37
- desc "stow PACKAGE", "Stow the given package name."
38
- def stow(package)
39
- package = Package.new(package)
40
- if package.stowed?
41
- error "Package #{package} is already stowed."
42
- exit(1)
43
- end
44
- puts "Stowing package #{package}"
45
- puts package.stow
46
- end
47
- desc "unstow PACKAGE", "Unstow the given package name."
48
- def unstow(package)
49
- package = Package.new(package)
50
- if package.unstowed?
51
- error "Package #{package} is not stowed."
52
- exit(1)
53
- end
54
- puts "Unstowing package #{package}"
55
- puts package.unstow
56
- end
57
- desc "track PACKAGE", "Begin tracking the given package with Git"
58
- def track(package)
59
- puts "Initialising Git repository for package #{package}"
60
- package = Package.new(package)
61
- package.track
62
- puts "Repository for package #{package} initialised. Git's metadata is stored in #{package.repo.metadata_path.to_s}"
63
- puts "Creating an initial snapshot to serve as a starting point."
64
- repo = package.repo
65
- repo.add('.')
66
- repo.commit_all("Initial snapshot of the package's contents")
67
- puts "Initial snapshot created."
68
- end
69
- desc "publish PACKAGE", "Make a package available in your public dotfiles repository"
70
- def publish(package)
71
- puts "Making package #{package} public"
72
- public_repo = PublicGitRepo.new
73
- puts public_repo.add_package(package)
74
- end
75
- desc "unpublish PACKAGE", "Make a package private after publishing it."
76
- def unpublish(package)
77
- puts "Making package #{package} private again"
78
- public_repo = PublicGitRepo.new
79
- public_repo.remove_package(package)
80
- end
81
- method_option :commit_message, :required => true, :aliases => "-m"
82
- method_option :all, :type => :boolean, :aliases => "-a"
83
- desc "commit PACKAGE", "Commit your changes to a Git-tracked package."
84
- def commit(package)
85
- package = Package.new(package)
86
- if package.untracked?
87
- error "Package #{package} is not tracked by Git."
88
- exit 1
89
- end
90
- puts "Committing the changes to package #{package} with commit message #{options.commit_message}."
91
- commit_message = options.commit_message
92
- repo = package.repo
93
- if options.all
94
- repo.commit_all(commit_message)
95
- else
96
- repo.commit(commit_message)
97
- end
98
- end
99
- desc "update PACKAGE", "Updates the specified package"
100
- def update(package)
101
- puts "Updating the contents / symlinks for package #{package}"
102
- package = Package.new(package)
103
- if package.unstowed?
104
- error "Package #{package} is not stowed and therefore cannot be updated."
105
- exit 1
106
- end
107
- package.update
108
- end
109
- desc "update_all", "Updates all stowed packages."
110
- def update_all()
111
- puts "Updating all stowed packages"
112
- all_packages = []
113
- all_package_names.each do |package|
114
- all_packages.push(Package.new(package.to_s))
115
- end
116
- stowed_packages = all_packages.select { |package| package.stowed? }
117
- stowed_packages.each do |package|
118
- puts "Updating #{package}"
119
- package.update
120
- end
121
- end
122
- desc "import PATH PACKAGE", "Imports a file or directory into the specified package"
123
- def import(path, package)
124
- puts "Importing #{path} into package #{package}"
125
- filepath = Pathname.new(File.expand_path(path))
126
- packagepath = package_path(package)
127
- if not Dir.exist?(packagepath.to_s) then FileUtils.mkpath(packagepath.to_s) end
128
- homepath = Pathname.new(File.expand_path('~'))
129
- relative_filepath = filepath.relative_path_from(homepath)
130
- complete_path = packagepath + relative_filepath
131
- FileUtils.copy(File.expand_path(path), complete_path.to_s)
132
- puts "File imported successfully. Update the package to make the symlink."
133
- end
134
- desc "import_repo REPO_URL PACKAGE", "Clones the specified git repository as the contents of the specified Package."
135
- def import_repo(repo_url, package)
136
- puts "Cloning repository #{repo_url} into package #{package}"
137
- end
138
- desc "clone REPO_URL", "Clones the dotfiles / packages of the specified repository into ~/dotfiles. Will overwrite any existing data."
139
- def clone(repo_url)
140
- puts "Cloning repository #{repo_url} directly into ~/dotfiles"
141
- end
142
- desc "status PACKAGE", "Obtain the repository status of a Git-tracked package."
143
- def status(package)
144
- package = Package.new(package)
145
- if package.untracked?
146
- error "Package #{package} is not tracked by Git."
147
- exit 1
148
- end
149
- metadata_path = repo_path(package.to_s)
150
- metadata_indexes_path = index_path(package.to_s)
151
- # Punt because it does this better than ruby-git.
152
- system({"GIT_DIR" => metadata_path.to_s, "GIT_INDEX_FILE" => metadata_indexes_path.to_s}, "git status")
153
- end
154
- desc "add PACKAGE FILE", "Add a file from a package to the next commit of that package."
155
- def add(package,file)
156
- package = Package.new(package)
157
- if package.untracked?
158
- error "Package #{package} is not tracked by Git."
159
- exit 1
160
- end
161
- puts "Marking #{file} to be committed for package #{package}"
162
- repo = package.repo
163
- repo.add(file)
164
- end
165
- desc "reset PACKAGE", "Reset what will be commmitted in the next commit to the given package."
166
- def reset(package)
167
- package = Package.new(package)
168
- if package.untracked?
169
- error "Package #{package} is not tracked by Git."
170
- exit 1
171
- end
172
- puts "Resetting what will be committed to package #{package}"
173
- repo = package.repo
174
- repo.reset()
175
- end
176
- desc "log PACKAGE", "View the commit log of a package."
177
- def log(package)
178
- package = Package.new(package)
179
- if package.untracked?
180
- error "Package #{package} is not tracked by Git."
181
- exit 1
182
- end
183
- puts "Obtaining the log of package #{package}"
184
- repo = package.repo
185
- repo.log.each do |commit|
186
- puts "[#{commit.date}] #{commit.message} (#{commit.author.name})"
187
- end
188
- end
189
- end
12
+ class CLI < Thor
13
+ include Utilities
14
+ desc 'version', 'Print the dotter version'
15
+ def version
16
+ puts "This is dotter #{Dotter::VERSION}"
17
+ end
18
+ desc 'init', 'Initialise the directory structure for ~/dotfiles'
19
+ def init
20
+ puts 'Initialising ~/dotfiles'
21
+ puts 'Creating the dotfiles directory.'
22
+ FileUtils.mkpath(dotfiles_path)
23
+ go_to_dotfiles
24
+ puts 'Creating the directory for the combined public dotfiles.'
25
+ FileUtils.mkpath('public')
26
+ puts 'Creating an initial package for dotter.'
27
+ FileUtils.mkpath('dotter/.dotter/gitrepos')
28
+ FileUtils.mkpath('dotter/.dotter/indexes/')
29
+ # If we don't do this now, we'll get a nasty exception if we ever access the configuration.
30
+ FileUtils.touch('dotter/.dotter/Dotfile')
31
+ end
32
+ desc 'list', 'List all packages present in ~/dotfiles'
33
+ def list
34
+ puts 'List of packages in ~/dotfiles'
35
+ all_package_names.each do |package|
36
+ puts package
37
+ end
38
+ end
39
+ desc 'stow PACKAGE', 'Stow the given package name.'
40
+ def stow(package)
41
+ package = Package.new(package)
42
+ if package.stowed?
43
+ error "Package #{package} is already stowed."
44
+ exit(1)
45
+ end
46
+ puts "Stowing package #{package}"
47
+ puts package.stow
48
+ end
49
+ desc 'unstow PACKAGE', 'Unstow the given package name.'
50
+ def unstow(package)
51
+ package = Package.new(package)
52
+ if package.unstowed?
53
+ error "Package #{package} is not stowed."
54
+ exit(1)
55
+ end
56
+ puts "Unstowing package #{package}"
57
+ puts package.unstow
58
+ end
59
+ desc 'track PACKAGE', 'Begin tracking the given package with Git'
60
+ def track(package)
61
+ puts "Initialising Git repository for package #{package}"
62
+ package = Package.new(package)
63
+ package.track
64
+ puts "Repository for package #{package} initialised. Git's metadata is stored in #{package.repo.metadata_path}"
65
+ puts 'Creating an initial snapshot to serve as a starting point.'
66
+ repo = package.repo
67
+ repo.add('.')
68
+ repo.commit_all("Initial snapshot of the package's contents")
69
+ puts 'Initial snapshot created.'
70
+ end
71
+ desc 'publish PACKAGE', 'Make a package available in your public dotfiles repository'
72
+ def publish(package)
73
+ puts "Making package #{package} public"
74
+ public_repo = PublicGitRepo.new
75
+ puts public_repo.add_package(package)
76
+ end
77
+ desc 'unpublish PACKAGE', 'Make a package private after publishing it.'
78
+ def unpublish(package)
79
+ puts "Making package #{package} private again"
80
+ public_repo = PublicGitRepo.new
81
+ public_repo.remove_package(package)
82
+ end
83
+ method_option :commit_message, required: true, aliases: '-m'
84
+ method_option :all, type: :boolean, aliases: '-a'
85
+ desc 'commit PACKAGE', 'Commit your changes to a Git-tracked package.'
86
+ def commit(package)
87
+ package = Package.new(package)
88
+ if package.untracked?
89
+ error "Package #{package} is not tracked by Git."
90
+ exit 1
91
+ end
92
+ puts "Committing the changes to package #{package} with commit message #{options.commit_message}."
93
+ commit_message = options.commit_message
94
+ repo = package.repo
95
+ if options.all
96
+ repo.commit_all(commit_message)
97
+ else
98
+ repo.commit(commit_message)
99
+ end
100
+ end
101
+ desc 'update PACKAGE', 'Updates the specified package. For packages imported from external repositories, also updates the repository.'
102
+ def update(package)
103
+ puts "Updating the contents / symlinks for package #{package}"
104
+ package = Package.new(package)
105
+ if package.unstowed?
106
+ error "Package #{package} is not stowed and therefore cannot be updated."
107
+ exit 1
108
+ end
109
+ package.update
110
+ end
111
+ desc 'update_all', 'Updates all stowed packages.'
112
+ def update_all
113
+ puts 'Updating all stowed packages'
114
+ all_packages = []
115
+ all_package_names.each do |package|
116
+ all_packages.push(Package.new(package.to_s))
117
+ end
118
+ stowed_packages = all_packages.select(&:stowed?)
119
+ stowed_packages.each do |package|
120
+ puts "Updating #{package}"
121
+ package.update
122
+ end
123
+ end
124
+ desc 'import PATH PACKAGE', 'Imports a file or directory into the specified package'
125
+ def import(path, package)
126
+ puts "Importing #{path} into package #{package}"
127
+ filepath = Pathname.new(File.expand_path(path))
128
+ packagepath = package_path(package)
129
+ FileUtils.mkpath(packagepath.to_s) unless Dir.exist?(packagepath.to_s)
130
+ homepath = Pathname.new(File.expand_path('~'))
131
+ relative_filepath = filepath.relative_path_from(homepath)
132
+ complete_path = packagepath + relative_filepath
133
+ FileUtils.copy(File.expand_path(path), complete_path.to_s)
134
+ puts 'File imported successfully. Update the package to make the symlink.'
135
+ end
136
+ desc 'import_repo REPO_URL PACKAGE', 'Clones the specified git repository as the contents of the specified Package.'
137
+ def import_repo(repo_url, package)
138
+ puts "Cloning repository #{repo_url} into package #{package}"
139
+ ForeignGitRepo.new(package, true, repo_url)
140
+ puts "Repository #{repo_url} successfully cloned into #{package}."
141
+ # We need to manually set the package as tracked to avoid calling init() again.
142
+ config = Configuration.new
143
+ config.track(package)
144
+ config.set_type(package, 'git_repo')
145
+ config.set_url(package, repo_url)
146
+ end
147
+ desc 'clone REPO_URL', 'Clones the dotfiles / packages of the specified repository into ~/dotfiles. Will overwrite any existing data.'
148
+ def clone(repo_url)
149
+ puts "Cloning repository #{repo_url} directly into ~/dotfiles"
150
+ repo = Git.clone(repo_url, @@dotfiles_path.to_s)
151
+ puts "Due to implementation difficulties, all the the commit history and state information will be lost."
152
+ go_to_dotfiles
153
+ `rm -rf .git`
154
+ `rm -rf dotter/*`
155
+ `touch dotter/Dotfile`
156
+ end
157
+ desc 'status PACKAGE', 'Obtain the repository status of a Git-tracked package.'
158
+ def status(package)
159
+ package = Package.new(package)
160
+ if package.untracked?
161
+ error "Package #{package} is not tracked by Git."
162
+ exit 1
163
+ end
164
+ metadata_path = repo_path(package.to_s)
165
+ metadata_indexes_path = index_path(package.to_s)
166
+ # Punt because it does this better than ruby-git.
167
+ system({ 'GIT_DIR' => metadata_path.to_s, 'GIT_INDEX_FILE' => metadata_indexes_path.to_s }, 'git status')
168
+ end
169
+ desc 'add PACKAGE FILE', 'Add a file from a package to the next commit of that package.'
170
+ def add(package, file)
171
+ package = Package.new(package)
172
+ if package.untracked?
173
+ error "Package #{package} is not tracked by Git."
174
+ exit 1
175
+ end
176
+ puts "Marking #{file} to be committed for package #{package}"
177
+ repo = package.repo
178
+ repo.add(file)
179
+ end
180
+ desc 'reset PACKAGE', 'Reset what will be commmitted in the next commit to the given package.'
181
+ def reset(package)
182
+ package = Package.new(package)
183
+ if package.untracked?
184
+ error "Package #{package} is not tracked by Git."
185
+ exit 1
186
+ end
187
+ puts "Resetting what will be committed to package #{package}"
188
+ repo = package.repo
189
+ repo.reset
190
+ end
191
+ desc 'log PACKAGE', 'View the commit log of a package.'
192
+ def log(package)
193
+ package = Package.new(package)
194
+ if package.untracked?
195
+ error "Package #{package} is not tracked by Git."
196
+ exit 1
197
+ end
198
+ puts "Obtaining the log of package #{package}"
199
+ repo = package.repo
200
+ repo.log.each do |commit|
201
+ puts "[#{commit.date}] #{commit.message} (#{commit.author.name})"
202
+ end
203
+ end
204
+ end
190
205
  end
@@ -1,39 +1,56 @@
1
1
  require 'dotter/utilities'
2
2
  require 'inifile'
3
3
  module Dotter
4
- class Configuration
5
- include Utilities
6
- attr_reader :config_file
7
- attr_accessor :config
8
- def initialize(config_file=package_path('dotter') + '.dotter/Dotfile')
9
- @config_file = config_file
10
- @config = IniFile.load(config_file)
11
- end
12
- def package_config(package)
13
- @config[package]
14
- end
15
- def save()
16
- @config.write()
17
- end
18
- def set_state(package, state)
19
- package_conf = self.package_config(package)
20
- package_conf['state'] = state
21
- self.save()
22
- end
23
- def track(package)
24
- package_conf = self.package_config(package)
25
- package_conf['tracked'] = true
26
- self.save()
27
- end
28
- def publish(package)
29
- package_conf = self.package_config(package)
30
- package_conf['public'] = true
31
- self.save()
32
- end
33
- def unpublish(package)
34
- package_conf = self.package_config(package)
35
- package_conf['public'] = false
36
- self.save()
37
- end
38
- end
4
+ class Configuration
5
+ include Utilities
6
+ attr_reader :config_file
7
+ attr_accessor :config
8
+ def initialize(config_file = package_path('dotter') + '.dotter/Dotfile')
9
+ @config_file = config_file
10
+ @config = IniFile.load(config_file)
11
+ end
12
+
13
+ def package_config(package)
14
+ @config[package]
15
+ end
16
+
17
+ def save
18
+ @config.write
19
+ end
20
+
21
+ def set_state(package, state)
22
+ package_conf = package_config(package)
23
+ package_conf['state'] = state
24
+ save
25
+ end
26
+
27
+ def track(package)
28
+ package_conf = package_config(package)
29
+ package_conf['tracked'] = true
30
+ save
31
+ end
32
+
33
+ def publish(package)
34
+ package_conf = package_config(package)
35
+ package_conf['public'] = true
36
+ save
37
+ end
38
+
39
+ def unpublish(package)
40
+ package_conf = package_config(package)
41
+ package_conf['public'] = false
42
+ save
43
+ end
44
+
45
+ def set_type(package, type)
46
+ package_conf = package_config(package)
47
+ package_conf['type'] = type
48
+ save
49
+ end
50
+ def set_url(package,url)
51
+ package_conf = package_config(package)
52
+ package_conf['url'] = url
53
+ save
54
+ end
55
+ end
39
56
  end