265 Commits

Author SHA1 Message Date
Jeremy Cook
cc83a4e1fa Preparing for 1.1.3 release. 2015-10-31 09:48:10 -04:00
Jeremy Cook
dcc5cb0bc1 Merge pull request #146 from JCook21/issue134
Fix for issue134
2015-10-25 15:21:26 -04:00
Jeremy Cook
978416d1e4 Fixing diff problems by providing source block and checking for
directories in diff
2015-10-20 21:47:43 -04:00
Jeremy Cook
1c12c73e4b Merge branch 'rweng-named_castles' 2015-10-14 22:31:20 -04:00
Jeremy Cook
1016002638 Merge branch 'named_castles' of https://github.com/rweng/homesick into rweng-named_castles 2015-10-14 22:30:46 -04:00
Jeremy Cook
6431a864ad Merge pull request #145 from JCook21/master
Maintenance fixes
2015-10-13 22:43:07 -04:00
Jeremy Cook
42f661cfbf Update to use new travis container infrastructure (see
http://docs.travis-ci.com/user/migrating-from-legacy/?utm_source=legacy-notice&utm_medium=banner&utm_campaign=legacy-upgrade)
2015-10-12 19:30:29 -04:00
Jeremy Cook
7632591681 Coding standards fixes based off of Rubocop and minor edits to make
logic flow easier to understand.
2015-10-12 19:30:29 -04:00
Jeremy Cook
a9a5b81dc5 Adding system notifications to development gems. 2015-10-12 19:30:29 -04:00
Jeremy Cook
721c10cffd Merge pull request #143 from rweng/fix-open
Fix homesick open
2015-10-12 11:06:10 -04:00
Robin Wenglewski
332aad8ad0 change 'homesick open' to run '$EDITOR .' instead of '$EDITOR' in castle_dir #142 2015-10-12 16:39:52 +02:00
Robin Wenglewski
171b4c1fb8 add option to pass in destination to homesick clone 2015-10-11 18:21:22 +02:00
Jeremy Cook
60d4458bbc Merge pull request #124 from shioyama/clone_destination
Pass destination when cloning url.
2015-03-22 14:12:41 -04:00
Chris Salzberg
9ad171ab78 Pass destination when cloning url. 2015-03-05 22:26:41 +09:00
Jeremy Cook
5918746059 Merge pull request #137 from gerasiov/master
Thanks!
2015-02-23 23:00:22 -05:00
Alexander GQ Gerasiov
4641843ffd Add "requite 'pathname'" to lib/homesick/utils.rb
Since Pathname is used in lib/homesick/utils.rb, it should require this module
itself.

Signed-off-by: Alexander GQ Gerasiov <gq@cs.msu.su>
2015-02-22 23:09:18 +03:00
Jeremy Cook
1a181b907c Merge pull request #136 from williamboman/patch-1
Thanks for fixing this!
2015-02-20 09:19:15 -05:00
William Boman
fb7595d254 Escape message correctly on git_commit_all. 2015-02-19 16:51:53 +01:00
Jeremy Cook
c8f0999035 Preparing for new release. 2015-01-02 00:06:20 -05:00
Jeremy Cook
46faec7857 Bug fix to make sure git check works properly. 2015-01-01 21:21:27 -05:00
Jeremy Cook
e35d3fe6ba Merge pull request #128 from wireframe/force-rc 2014-12-13 08:12:04 -05:00
Jeremy Cook
ba620e0f7f Merge pull request #127 from JCook21/CheckGit
In lieu of other comments I'm going to go ahead and merge this.
2014-12-02 20:53:42 -05:00
Ryan Sonnek
5700f55dc3 Add --force option to homesick rc command
Support automatically eval-ing .homesickrc file without prompting for user input.
This is particularly useful for headless scripts that do not support
user input.
2014-12-01 13:38:43 -06:00
Jeremy Cook
2c92010093 Fix to make tests pass in Ruby 1.9.3 2014-11-25 21:18:13 -05:00
Jeremy Cook
03490531d8 Changed name of git check method to be more descriptive. 2014-11-24 08:49:17 -05:00
Jeremy Cook
7bd9759e81 Added tests for a minimumGit version of 1.8.0. 2014-11-23 22:22:44 -05:00
Jeremy Cook
a808f56caf Tightened up git checking to check for a minimum installed version of
Git.
2014-11-23 14:32:47 -05:00
Jeremy Cook
b7e2b45e69 Added simple implementation to check if git is installed before
executing commands.
2014-11-20 21:25:47 -05:00
Jeremy Cook
63c45d7c3a Removed unneeded config line. 2014-11-09 11:44:09 -05:00
Jeremy Cook
096067ac62 Merge pull request #125 from JCook21/master
Updated to use Rspec 3.1
2014-10-01 21:37:44 -04:00
Jeremy Cook
8d6bf4c0c5 Updated to use Rspec 3.1 2014-09-30 19:39:16 -04:00
Jeremy Cook
882b862780 Merge pull request #123 from JCook21/SymlinkedHomes
I'm merging this as it seems to be harmless and hasn't elicited any responses.
2014-09-27 17:37:21 -04:00
Jeremy Cook
e06a5d6300 Merge pull request #122 from JCook21/SmokeTest
Added basic smoke test
2014-09-19 14:38:44 -04:00
Jeremy Cook
7451e8c739 Bug fix to cover cases where homes are symlinked. 2014-09-19 14:36:32 -04:00
Jeremy Cook
f034f773c5 Added a smoke test to ensure that calling bin/homesick outputs some text 2014-09-19 14:19:09 -04:00
Jeremy Cook
681fd98dc3 Merge pull request #121 from PeterDaveHello/patch-1
Looks good to me, thanks.
2014-09-19 12:10:34 -04:00
Jeremy Cook
e57b139e32 Merge pull request #117 from JCook21/Shell
Merging as there doesn't seem to be any comments or issues with this.
2014-09-19 12:08:54 -04:00
Peter Dave Hello
b64bfe2bb6 Use svg instead of png to get better image quality 2014-09-08 15:42:54 +08:00
Jeremy Cook
ee04b5788a Removed the homesick shell module and folded its code in elsewhere. 2014-06-14 15:03:57 -04:00
Jeremy Cook
2e8d431ab5 Regenerate gemspec for version 1.1.1 2014-05-21 20:56:20 -04:00
Jeremy Cook
3465c37c0e Updated version to 1.1.1 2014-05-21 20:56:07 -04:00
Jeremy Cook
bf6894e313 Regenerate gemspec for version 1.1.0 2014-05-21 20:55:35 -04:00
Jeremy Cook
77e3f7f479 Merge pull request #116 from JCook21/master
Minimum Ruby Version
2014-05-21 20:54:15 -04:00
Jeremy Cook
753f5027b0 Added minimum ruby version to the gemspec. 2014-05-08 22:07:37 -04:00
Jeremy Cook
23c012a527 Regenerate gemspec for version 1.1.0 2014-04-28 20:48:43 -04:00
Jeremy Cook
895543641b Added details of 1.1.0 release. 2014-04-28 20:45:52 -04:00
Jeremy Cook
72bfc5a2fd Regenerate gemspec for version 1.1.0 2014-04-28 19:20:20 -04:00
Jeremy Cook
f5054cf41d Bumped version number. 2014-04-28 19:20:02 -04:00
Jeremy Cook
b60703d496 Bumped version number in preparation for a release. 2014-04-26 18:06:54 -04:00
Jeremy Cook
9a8788fb80 Removed uneeded config lines since the pretend and quiet options are set
in global config.
2014-04-26 18:04:34 -04:00
Jeremy Cook
1a44edcde1 Merge pull request #111 from JCook21/Pretend
Edits to keep code DRY
2014-04-26 17:45:08 -04:00
Nicolas McCurdy
383f2a9f32 Merge pull request #113 from JCook21/BugFix
Bug fix
2014-04-23 01:17:08 -04:00
Jeremy Cook
f55828f1d4 Fixing a bug that breaks the handling of collisions. 2014-04-22 20:54:05 -04:00
Jeremy Cook
d4f9633a0c Added ability for methods to be overrode, through the pretend and quiet
options, skipping their default behaviour if so.
2014-04-22 19:43:40 -04:00
Jeremy Cook
b5138bcdd1 Merge pull request #110 from nicolasmccurdy/refactor
Refactor
2014-04-22 12:46:49 -04:00
Nicolas McCurdy
d9ee74bf14 Move GitActions and FileActions into a new Actions module 2014-04-16 20:54:26 -04:00
Nicolas McCurdy
2148697864 Separate Actions into two new modules: FileActions and GitActions 2014-04-16 18:05:02 -04:00
Nicolas McCurdy
1c3403064e Don't refer to Homesick as a class 2014-04-16 17:57:00 -04:00
Nicolas McCurdy
03d87807e0 Use require instead of autoload (fix #108) 2014-04-16 17:48:36 -04:00
Nicolas McCurdy
f6c4e5e42e Require Thor in Homesick::Shell 2014-04-16 17:29:17 -04:00
Nicolas McCurdy
705a416d74 Extract the CLI into a new Homesick::CLI class, while making Homesick a module 2014-04-16 17:20:05 -04:00
Nicolas McCurdy
41f3f9d374 Merge pull request #107 from JCook21/Utils
Small refactoring to keep main homesick class smaller.
2014-04-16 13:38:56 -04:00
Jeremy Cook
74cfd29272 Small refactoring to keep main homesick class smaller. 2014-04-15 22:20:12 -04:00
Jeremy Cook
9a3268b7c3 Merge pull request #100 from nicolasmccurdy/use-coveralls
Use coveralls
2014-04-15 21:15:46 -04:00
Nicolas McCurdy
70c1666606 Add a Gitter badge to the readme 2014-04-13 17:20:28 -04:00
Jeremy Cook
53ac09a5e9 Merge pull request #101 from nicolasmccurdy/use-libraries
Use stdlib methods to replace most non-git shell calls. Looks good.
2014-04-13 17:17:30 -04:00
Jeremy Cook
c790e34b39 Added documentation for the exec and exec_all commands. 2014-04-13 14:58:54 -04:00
Nicolas McCurdy
8428ad1c9c Merge pull request #105 from JCook21/ExecCommands
Further work on exec command
2014-04-13 14:29:59 -04:00
Jeremy Cook
efea18327b Added options for exec command and a new exec_all command. 2014-04-13 10:59:40 -04:00
Nicolas McCurdy
84fb1d1462 Replace a system call in the spec helper 2014-04-06 02:01:15 -04:00
Nicolas McCurdy
5dc7b5068d Replace any system calls in lib that don't use git with calls to FileUtils 2014-04-06 02:01:15 -04:00
Nicolas McCurdy
ab603240e4 Merge pull request #97 from JCook21/exec
Added Exec command
2014-04-06 00:48:20 -04:00
Nicolas McCurdy
2e5c2ec018 Add coverage status to the readme 2014-04-05 23:44:27 -04:00
Nicolas McCurdy
349e75584f Switch from simplecov to coveralls for code coverage (in the cloud!!!) 2014-04-05 23:27:29 -04:00
Jeremy Cook
bea3a0b680 Added exec command to homesick. 2014-04-05 09:21:16 -04:00
Jeremy Cook
8b6bf92e9a Applied fixes that allow tests to pass again. 2014-04-05 09:18:56 -04:00
Nicolas McCurdy
133c3613e9 Merge pull request #96 from nicolasmccurdy/fix-simplecov
Fix simplecov
2014-04-05 00:47:31 -04:00
Nicolas McCurdy
ff2e5ee064 Merge pull request #95 from nicolasmccurdy/use-expect-syntax
Use expect syntax
2014-04-04 23:23:42 -04:00
Nicolas McCurdy
22aed48d4e Set up simplecov (it was in the Gemfile before, but it wasn't actually used) 2014-04-04 15:11:31 -04:00
Nicolas McCurdy
4c7e45a1d5 Use simplecov instead of rcov for all Rubies, since we have dropped 1.8 anyway 2014-04-04 15:08:36 -04:00
Nicolas McCurdy
ca41ae7f85 Only allow RSpec's new "expect" syntax 2014-04-04 02:10:19 -04:00
Nicolas McCurdy
fa61e7b10e Avoid using "should" in example descriptions 2014-04-04 01:53:23 -04:00
Nicolas McCurdy
8397dcacc5 Convert specs to RSpec 2.14.8 syntax with Transpec
This conversion is done by Transpec 1.10.4 with the following command:
    transpec

* 39 conversions
    from: obj.should
      to: expect(obj).to

* 22 conversions
    from: == expected
      to: eq(expected)

* 18 conversions
    from: obj.should_receive(:message)
      to: expect(obj).to receive(:message)

* 13 conversions
    from: obj.stub(:message)
      to: allow(obj).to receive(:message)

* 11 conversions
    from: obj.should_not
      to: expect(obj).not_to

* 5 conversions
    from: =~ /pattern/
      to: match(/pattern/)

* 2 conversions
    from: obj.should_not_receive(:message)
      to: expect(obj).not_to receive(:message)
2014-04-04 01:37:44 -04:00
Jeremy Cook
3f2d343161 Merge pull request #93 from JCook21/master
Made tests less reliant on file paths
2014-03-16 10:44:57 -04:00
Jeremy Cook
d91628f811 Made tests more generic since file paths may be different on various
systems.
2014-03-08 17:26:07 -05:00
Jeremy Cook
94bff3aa9d Small change to make symlink text a symbol in the map command. 2014-01-25 23:16:15 -05:00
Christian Bundy
7253bdd634 Merge pull request #82 from thenickperson/rubocop-fixes
Reduce Rubocop errors
2014-01-23 12:19:57 -08:00
Jeremy Cook
8c6a17404f Merge pull request #83 from thenickperson/remove-duplicate-methods
Remove duplicate methods rm and rm_link
2014-01-22 04:14:27 -08:00
Nicolas McCurdy
98edb54ca4 Merge remote-tracking branch 'upstream/master' into rubocop-fixes
Conflicts:
	lib/homesick.rb
2014-01-21 22:37:48 -05:00
Christian Bundy
9b780ffac6 Merge pull request #87 from JCook21/Consistent
Rename "symlink" command to "link" (with backward compatibility)
2014-01-21 16:17:12 -08:00
Jeremy Cook
59f6239ea0 Renamed symlink command to link. 2014-01-21 17:21:07 -05:00
Nicolas McCurdy
c2cb6081e1 Fix some more code style issues with block params being in the wrong place 2014-01-20 23:26:29 -05:00
Nicolas McCurdy
95943deb82 Revert changes that use "\" and disable the line length cop 2014-01-20 21:03:21 -05:00
Nicolas McCurdy
08a71f657f Fix a minor code style issue where "do" wasn't on the same line as its params 2014-01-20 16:03:18 -05:00
Nicolas McCurdy
2b48544e32 Merge pull request #81 from thenickperson/ruby-2.1
Test with Ruby 2.1.0 on Travis and add it to the supported Rubies
2014-01-20 12:56:20 -08:00
Nicolas McCurdy
f1191d4b3c Remove duplicate methods rm and rm_link 2014-01-16 02:44:04 -05:00
Nicolas McCurdy
8173429131 Ignore the remaining cyclomatic complexity issues 2014-01-16 00:31:00 -05:00
Nicolas McCurdy
82be04ad8a Fix rubocop issues for some recently merged code 2014-01-16 00:22:13 -05:00
Nicolas McCurdy
bb735763c6 Separate the action handling of ln_s into a new method to lower complexity 2014-01-15 23:48:56 -05:00
Nicolas McCurdy
0bbb82f9ba Wrap all lines of Ruby code to 79 characters (maximum) 2014-01-15 23:44:39 -05:00
Nicolas McCurdy
e42eff4e10 Refactor the ln_s method to decrease its cyclomatic complexity 2014-01-15 23:34:12 -05:00
Nicolas McCurdy
7e659f11fe Fix some broken spec expectations and move/rename a method 2014-01-15 23:29:40 -05:00
Nicolas McCurdy
c667cefd4c Refactor the clone method to decrease its cyclomatic complexity 2014-01-15 23:29:40 -05:00
Nicolas McCurdy
571c5799e9 Swap some if/else statements so the positive case is always first 2014-01-15 23:23:30 -05:00
Nicolas McCurdy
604a3b2a20 Add todo comments to the rubocop config 2014-01-15 23:23:30 -05:00
Nicolas McCurdy
7d36460851 Rename the todo config to .rubocop.yml, now that more rubocop issues are fixed 2014-01-15 23:23:30 -05:00
Nicolas McCurdy
8f634b9d07 Add brief documentation comments for Homesick and Homesick::Actions 2014-01-15 23:23:30 -05:00
Nicolas McCurdy
12244abb56 Fix a few more syntax-related rubocop issues manually 2014-01-15 23:23:29 -05:00
Nicolas McCurdy
fc2bbb1d6e Fix several rubocop issues with "rubocop -a" 2014-01-15 23:23:29 -05:00
Nicolas McCurdy
f03e7670cf Fix an issue reported by rubocop that wasn't ignored in the todo config
Issue text: Favor modifier if/unless usage when you have a single-line body.
2014-01-15 23:22:03 -05:00
Nicolas McCurdy
e202b7eae7 Add a rubocop todo-style config created by "--auto-gen-config" and inherit it 2014-01-15 23:22:03 -05:00
Nicolas McCurdy
3fcbd21104 Test with Ruby 2.1.0 on Travis and add it to the supported Rubies 2014-01-15 21:55:28 -05:00
Jeremy Cook
db0f604faf Regenerate gemspec for version 1.0.0 2014-01-15 20:24:36 -05:00
Jeremy Cook
e5a6e43333 Preparing for 1.0.0 release. 2014-01-15 20:23:25 -05:00
Jeremy Cook
faa5f0b9ed Merge pull request #79 from JCook21/Tests
Added tests for untested methods
2014-01-15 16:43:35 -08:00
Jeremy Cook
7c13727978 Merge branch 'master' into Tests 2014-01-15 19:41:06 -05:00
Jeremy Cook
2dba8b6496 Merge branch 'master' of github.com:technicalpickles/homesick 2014-01-15 19:38:29 -05:00
Jeremy Cook
2dadd4e064 Removed libnotify which seems to be causing build errors. 2014-01-15 19:38:02 -05:00
Jeremy Cook
a60ca62eba Merge pull request #71 from thenickperson/master
Merging this one in following discussions on #80.
2014-01-15 16:35:17 -08:00
Nicolas McCurdy
674ffb6bb2 Merge remote-tracking branch 'upstream/master' 2014-01-15 16:57:41 -05:00
Jeremy Cook
5fa0fc037c Updated README for .homesickrc. 2014-01-10 09:36:09 -05:00
Nicolas McCurdy
8a537b8204 Add gem version, dependency status, and code quality badges to the readme 2014-01-10 09:36:09 -05:00
Jeremy Cook
6e25f13e06 Added libnotify Gem on *nix systems in case people want to use it for
Guard notifications.
2014-01-10 09:36:09 -05:00
Jeremy Cook
df8f6b1cb0 Added command to display current version of homesick. 2014-01-10 09:36:09 -05:00
Jeremy Cook
6050a9a7ac Updated README for .homesickrc. 2014-01-09 22:38:47 -05:00
Jeremy Cook
0abd9436ad Merge branch 'master' of github.com:technicalpickles/homesick 2014-01-09 22:34:36 -05:00
Jeremy Cook
af159f5b97 Added libnotify Gem on *nix systems in case people want to use it for
Guard notifications.
2014-01-09 17:45:05 -05:00
Josh Nichols
a657c5622e Merge pull request #77 from thenickperson/readme-badges
Add gem version, dependency status, and code quality badges to the readme
2014-01-09 08:11:56 -08:00
Nicolas McCurdy
6f216cd916 Add gem version, dependency status, and code quality badges to the readme 2014-01-09 02:59:26 -05:00
Nicolas McCurdy
8bf1864335 Switch from the test-construct gem (deprecated) to test_construct 2014-01-09 02:49:52 -05:00
Nicolas McCurdy
8931739e97 Travis: Don't test on Ruby 1.8 (it's deprecated, and it breaks the build) 2014-01-09 02:49:52 -05:00
Jeremy Cook
ab46cf7b2f Merge branch 'master' of github.com:technicalpickles/homesick into Tests 2014-01-06 21:47:17 -05:00
Jeremy Cook
d115714a9f Merge branch 'master' of github.com:technicalpickles/homesick 2014-01-06 21:45:04 -05:00
Jeremy Cook
e787abd3f3 Merge pull request #78 from JCook21/Docs
Added docs for the rc command.
2014-01-06 18:43:06 -08:00
Jeremy Cook
1c0fe66944 Added docs for the rc command. 2014-01-06 21:42:24 -05:00
Jeremy Cook
148d18565f Added tests for untested methods. 2014-01-06 21:24:24 -05:00
Jeremy Cook
264d586863 Updated tests to remove shell output from test results. 2014-01-06 21:24:24 -05:00
Jeremy Cook
76bee65475 Updated gems to use test_construct, removing warning message. 2014-01-06 21:24:24 -05:00
Jeremy Cook
8dac49548c Added command to display current version of homesick. 2014-01-06 19:12:13 -05:00
Jeremy Cook
5c5d204d15 Removed notification settings from project so that user can set them in
~/.guard.rb instead.
2014-01-05 10:08:29 -05:00
Jeremy Cook
e1f85973c1 Added documentation for the new commands to the readme file. 2014-01-02 10:03:24 -05:00
Jeremy Cook
3554806741 Updated gemspec file for homesick. 2013-12-31 21:50:17 -05:00
Jeremy Cook
e4cc308d43 Merge pull request #69 from JCook21/master
Added cd and open commands and tests for commit and status
2013-12-31 18:23:23 -08:00
Jeremy Cook
78271a9ed4 Added commands to cd into a castle and to open a shell and to open the
default editor in the root of a given castle.
2013-12-31 21:20:13 -05:00
Jeremy Cook
8f67188c19 Added new tests for status and commit 2013-12-31 21:18:14 -05:00
Jeremy Cook
c432b27c92 Added guard to project to run tests automatically. 2013-12-30 20:08:17 -05:00
Josh Nichols
30a3bbb198 Merge pull request #67 from technicalpickles/reword-third-person-to-second-person
Reword README's language from third person to second person
2013-12-17 14:21:17 -08:00
Josh Nichols
e7f9358f96 Update jeweler's gemspec language too 2013-12-17 17:18:51 -05:00
Josh Nichols
750c7773ae Reword 'A person's home is their case' to 'Your home directory is your casetle' 2013-12-17 17:16:54 -05:00
Trae Robrock
900277f426 Merge pull request #64 from zacharyalexstern/master
Update README.markdown to remove gendered terms
2013-12-01 16:38:57 -08:00
Zachary Alex Stern
208adeef6c Update README.markdown
Replaced gendered terms/pronouns.

This is a great project, and it can be even greater if it doesn't perpetuate the assumption-of-maleness that seems to pervade all tech spaces.

I do realize that "a man's home is his castle" is a saying, and not just arbitrary, but I counter that with the age old argument: But Still.
2013-11-30 14:23:47 -08:00
Thilko Richter
086828b12f Merge pull request #24 from thilko/master
Destroy your castles
2013-11-25 11:26:29 -08:00
Christian Bundy
c73d556e6f Merge branch 'thilko' into destroy-castles
Conflicts:
	lib/homesick/actions.rb
	spec/homesick_spec.rb
2013-11-24 19:01:30 -08:00
thilko
357e2f60f2 delete repo dir 2013-11-23 22:05:07 +01:00
thilko
243ba70b33 using rm_rf to avoid confirmations on delete 2013-11-23 21:54:40 +01:00
thilko
640da07089 call unlink before removing the castle repo 2013-11-23 21:06:41 +01:00
thilko
69c38774fe ask user before start destroying 2013-11-23 21:01:53 +01:00
Thilko Richter
44527850f6 destroy action implemented 2013-11-23 19:42:39 +01:00
Trae Robrock
8d96b2c31f Regenerate gemspec for version 0.9.7 2013-11-23 19:38:53 +01:00
Trae Robrock
0019e8c61c Bump version 2013-11-23 19:38:53 +01:00
bcd
545f5fc3e9 Git clone now uses config push.default upstream 2013-11-23 19:38:53 +01:00
Chris Ball
5108de20c3 Remove symlink shorthand note in README
Seems that using `homesick symlink <username>/<reponame>` after a clone does not locate the directory properly. `homesick symlink <reponame>` works in both cases regardless if the shorthand or full git url was used to clone.
2013-11-23 19:38:53 +01:00
Trae Robrock
9656be1dde Regenerate gemspec for version 0.9.6 2013-11-23 19:38:53 +01:00
Trae Robrock
09890e8048 Bump version 2013-11-23 19:38:53 +01:00
Trae Robrock
b672b4c526 Regenerate gemspec for version 0.9.5 2013-11-23 19:38:53 +01:00
mingkai
6ca49327c3 Change github-repo pattern to allow numbers for usernames/repos 2013-11-23 19:38:53 +01:00
bcd87
3d47cc44af Rake now passes on Ruby 1.8.7 on my machine 2013-11-23 19:38:52 +01:00
bcd87
e8b471ac97 Changed the spec, bundle exec rake works 2013-11-23 19:38:52 +01:00
bcd87
0e6915b860 Using the homesick clone github shortcut now clones to ~/.homesick/repos/repo/, in stead of ~/.homesick/repos/username/repo/ 2013-11-23 19:38:52 +01:00
Trae Robrock
0d48e517f8 Regenerate gemspec for version 0.9.5 2013-11-23 19:38:52 +01:00
Trae Robrock
f2469ecaaf Bump version the right way 2013-11-23 19:38:52 +01:00
Trae Robrock
c8451c7d3f Regenerate gemspec for version 0.9.4 2013-11-23 19:38:52 +01:00
Trae Robrock
d3025a34ca Bump version 2013-11-23 19:38:52 +01:00
Trae Robrock
17426583e0 Add test for parens in filenames, and fixed 2013-11-23 19:38:52 +01:00
muratayusuke
04c4a4c059 Regenerate gemspec for version 0.9.4 2013-11-23 19:38:52 +01:00
muratayusuke
6ae0aaa6f9 bump up version 2013-11-23 19:38:51 +01:00
muratayusuke
d22361f2ac Regenerate gemspec for version 0.9.3 2013-11-23 19:38:51 +01:00
muratayusuke
e21e608cca bump up version 2013-11-23 19:38:51 +01:00
muratayusuke
238658cf69 remove unused variables 2013-11-23 19:38:51 +01:00
muratayusuke
2a361d86e0 fix deprecated method: stub! -> stub 2013-11-23 19:38:51 +01:00
Trae Robrock
294fb3d4ce Add tests for the rc command 2013-11-23 19:38:51 +01:00
Trae Robrock
342da7e250 Adding test for clone running homesickrc 2013-11-23 19:38:51 +01:00
John Bellone
9c52108035 Update specs to for change to https vs. git protocol. 2013-11-23 19:38:51 +01:00
Trae Robrock
5b954b93e3 Fix #19 homesickrc pathname needs a to_s to eval
Also, moved the file evaluation into a new function so the script can be
ran manually which should make testing these scripts easier.
2013-11-23 19:38:50 +01:00
Trae Robrock
b596e063f5 Add unlink functionality 2013-11-23 19:38:50 +01:00
Trae Robrock
965b35b78c Ignore vendor dir 2013-11-23 19:37:25 +01:00
muratayusuke
9551b3acb4 Regenerate gemspec for version 0.9.3 2013-11-23 19:37:25 +01:00
muratayusuke
27ac1c7782 bump up version 2013-11-23 19:37:25 +01:00
muratayusuke
f9d0b69bce add recursive option to 'homesick clone' 2013-11-23 19:37:25 +01:00
muratayusuke
a91ce82d77 Regenerate gemspec for version 0.9.2 2013-11-23 19:37:25 +01:00
muratayusuke
d8b5d8163b remove Gemfile.lock from repository 2013-11-23 19:37:25 +01:00
muratayusuke
6fca06d341 bump up version 2013-11-23 19:37:24 +01:00
Austin Lin
44080829e3 Update readme with correct file path for .homesick_subdir per technicalpickles/homesick@360e8185f7 2013-11-23 19:37:24 +01:00
David Simon
651e028d5b Whoops, fixed typo 2013-11-23 19:37:24 +01:00
David Simon
6f3186df2f Using DEFAULT_CASTLE_NAME in show_path, diff, status 2013-11-23 19:37:24 +01:00
David Simon
92c61f928e Added three commands: show_path, status, diff 2013-11-23 19:37:24 +01:00
akahige
d3cb45f879 default castle name to constant 2013-11-23 19:37:24 +01:00
John Bellone
314e2932fb Update homesick.rb to make https default for GitHub clones.
If we use HTTPS it is a lot easier for corporate worlds to manage proxies since its usually already done for us. Also HTTPS cloning is just as fast as the git protocol as of more recent versions.
2013-11-23 19:37:24 +01:00
akahige
8ee5165ccf set default castle name: 'dotfiles' for some commands 2013-11-23 19:37:24 +01:00
muratayusuke
8f2a9e6703 remove duplicate spec 2013-11-23 19:37:24 +01:00
muratayusuke
f8a6fb9ce2 follow Ruby Style Guide for some points 2013-11-23 19:37:23 +01:00
muratayusuke
bf248cd645 don't fail test even if rubocop detects some offence 2013-11-23 19:36:38 +01:00
muratayusuke
1563814cb0 use single-quate if don't need double-quate 2013-11-23 19:36:37 +01:00
muratayusuke
91770998a7 don't install rubocop under ruby 1.9.2 2013-11-23 19:36:37 +01:00
muratayusuke
d7aca1025f don't run rubocode on ruby 1.8.7 2013-11-23 19:36:37 +01:00
muratayusuke
b3298d18c8 fix coding style 2013-11-23 19:36:37 +01:00
muratayusuke
359147e7e8 use rubocop 2013-11-23 19:36:37 +01:00
muratayusuke
937f1912d7 Regenerate gemspec for version 0.9.1 2013-11-23 19:36:37 +01:00
muratayusuke
18c0e32309 bump up version 2013-11-23 19:36:37 +01:00
Fletcher Nichol
1518cb1155 Remove .git suffix on destination directory if URL ends with it.
For example, the following:

    homesick clone git://github.com/technicalpickles/pickled-vim.git

should produce a castle directory of:

    $HOME/.homesick/repos/pickled-vim
2013-11-23 19:36:37 +01:00
muratayusuke
c5b7dd2918 fix #35 2013-11-23 19:36:36 +01:00
muratayusuke
b668b7eda2 Regenerate gemspec for version 0.9.0 2013-11-23 19:36:36 +01:00
muratayusuke
a595ead2c6 bump up version and update changelog 2013-11-23 19:36:36 +01:00
muratayusuke
cc1ee544c3 move castle/home/.homesick_subdir to castle/.homesick_subdir 2013-11-23 19:36:36 +01:00
Yusuke Murata
a0862936e8 fix typo 2013-11-23 19:36:36 +01:00
Yusuke Murata
f91f5743b6 fix style of README 2013-11-23 19:36:36 +01:00
Yusuke Murata
eb74b90b42 fix style of README 2013-11-23 19:36:36 +01:00
muratayusuke
c3c108bd50 add .homesick_subdir explanation to README 2013-11-23 19:36:36 +01:00
muratayusuke
06975f79f5 deal with edge case: the parent and descendant are both listed in the manifest 2013-11-23 19:36:36 +01:00
muratayusuke
830106a168 refactor symlink 2013-11-23 19:36:35 +01:00
muratayusuke
aa2dfcc42f refactor given_castle 2013-11-23 19:36:35 +01:00
muratayusuke
ed71fd6227 add ruby-2.0.0 to travis 2013-11-23 19:36:35 +01:00
muratayusuke
fd60528567 fix spec for ruby-1.8.7 2013-11-23 19:36:35 +01:00
muratayusuke
01934d5b00 replace .manifest to .homesick_subdir 2013-11-23 19:36:35 +01:00
muratayusuke
bba0e3ed7d support nested dir in .homesick_subdir 2013-11-23 19:36:35 +01:00
Eric West
7db0b13d30 refactor, cleanup and tweak 2013-11-23 19:36:35 +01:00
Eric West
b1c6c8f835 Handling edge cases
Covers only edge cases related to tracking, not yet
handling linking or updating. Getting a bit hairy,
must be refactored.
2013-11-23 19:36:35 +01:00
muratayusuke
7620f40cb2 symlink subdirs with .homesick_subdir 2013-11-23 19:36:34 +01:00
Eric West
cf9058be04 Track makes entries in .manifest
When a user tracks a file or directory that is in
a nested folder, Homesick creates a .manifest in the
user's castle (if there isn't one already) and adds
an entry listing the file or directory's parent
directory (if it isn't already listed).
2013-11-23 19:36:34 +01:00
Eric West
234532ebef Specs for track 2013-11-23 19:36:34 +01:00
Eric West
ccddbb1316 Track now properly traverses folder structure 2013-11-23 19:36:34 +01:00
muratayusuke
4ef315d4e2 Regenerate gemspec for version 0.8.1 2013-11-23 19:36:34 +01:00
muratayusuke
9b7fe331f6 update changelog 2013-11-23 19:36:34 +01:00
muratayusuke
eeff0b40fb update rake version
"rake spec" showed following warning
/home/muratayusuke/.rvm/gems/ruby-1.9.3-p392/gems/rake-0.8.7/lib/rake/alt_system.rb:32: Use RbConfig instead of obsolete and deprecated Config.
(in /mnt/projects/homesick)
/home/muratayusuke/.rvm/rubies/ruby-1.9.3-p392/bin/ruby -S rspec spec/homesick_spec.rb
so update rake version to fix it.
2013-11-23 19:36:34 +01:00
Eric West
0c933c0085 Fixes glob to work with 2.0.0
homesick list fails on ruby 2.0.0-rc2, I think because they fixed this
bug: https://bugs.ruby-lang.org/issues/6977, changing the way recursive
globs work. Test case:

```ruby
require "homesick"
require "pathname"

repos = Homesick.new.send :repos_dir

Dir.glob("#{repos}/**/*/.git") # => []

Dir.glob("#{repos}/**/*/.git", File::FNM_DOTMATCH) # => ["/home/eric/.homesick/repos/dotfiles/.git"]

```
This change, however, then broke 1.9.3, but removing the extra "/*"
works on both 1.9 and 2.0.
2013-11-23 19:36:34 +01:00
muratayusuke
a3d94fcca6 add build status to README 2013-11-23 19:36:33 +01:00
muratayusuke
9fe1d190da prepare for release 0.8.0 2013-11-23 19:36:33 +01:00
Jacob Buys
73981c2e75 Build with Travis CI 2013-11-23 19:36:33 +01:00
Josh Nichols
107dec388e Fix git_clone to work with github URLs like https://github.com/technicalpickles/dotpickles 2013-11-23 19:36:33 +01:00
muratayusuke
1df44aea40 enable recursive submodule 2013-11-23 19:36:33 +01:00
Jason Buckner
3bc623be7c perform a git add when doing a homesick track 2013-11-23 19:36:33 +01:00
Jason Buckner
123e6cf82d splits up castle commit from castle push for more fine-grained control 2013-11-23 19:34:08 +01:00
Jason Buckner
5e9d134021 update documentation for naming consistency 2013-11-23 19:34:08 +01:00
Jason Buckner
0a022fddcc fix homesick pull documentation 2013-11-23 19:34:08 +01:00
Jason Buckner
f18a4dc16f add homesick push to readme, a placeholder test, and removed the all option from git push 2013-11-23 19:34:07 +01:00
Jason Buckner
9ac754fd40 start adding git push functionality 2013-11-23 19:34:07 +01:00
Jorge
2667053fde Making git repos uri non greedy so it works with uris with / 2013-11-23 19:34:07 +01:00
Trae Robrock
8874994feb Regenerate gemspec for version 0.9.7 2013-11-02 16:03:57 -07:00
Trae Robrock
5a8b92f556 Bump version 2013-11-02 16:03:51 -07:00
Trae Robrock
a65c2e6a1f Merge pull request #57 from boyvanduuren/issue_54
Git clone now uses config push.default upstream
2013-11-02 16:02:20 -07:00
bcd
2d0304feb1 Git clone now uses config push.default upstream 2013-11-02 21:15:51 +01:00
Trae Robrock
34fec63234 Merge pull request #56 from cball/update-readme
Remove symlink shorthand note in README
2013-10-30 15:20:33 -07:00
Chris Ball
1d5f27f567 Remove symlink shorthand note in README
Seems that using `homesick symlink <username>/<reponame>` after a clone does not locate the directory properly. `homesick symlink <reponame>` works in both cases regardless if the shorthand or full git url was used to clone.
2013-10-30 14:01:40 -04:00
Trae Robrock
093db8bdac Regenerate gemspec for version 0.9.6 2013-10-18 07:18:15 -07:00
Trae Robrock
04602efd6e Bump version 2013-10-18 07:18:07 -07:00
Trae Robrock
3265de0c1d Regenerate gemspec for version 0.9.5 2013-10-18 07:17:39 -07:00
Trae Robrock
e4a428e0c5 Merge pull request #55 from mingkai0812/master
Change github-repo pattern to allow numbers for usernames/repos
2013-10-18 07:15:05 -07:00
mingkai
6cc28450a4 Change github-repo pattern to allow numbers for usernames/repos 2013-10-18 14:35:56 +02:00
Yusuke Murata
a0c7fbacb7 Merge pull request #53 from boyvanduuren/issue52
Change the destination for the github clone shortcut
2013-10-07 09:39:40 -07:00
bcd87
b750094934 Rake now passes on Ruby 1.8.7 on my machine 2013-09-24 08:15:56 +02:00
bcd87
d953a964cd Changed the spec, bundle exec rake works 2013-09-23 20:58:16 +02:00
bcd87
aa95ffac82 Using the homesick clone github shortcut now clones to ~/.homesick/repos/repo/, in stead of ~/.homesick/repos/username/repo/ 2013-09-23 20:30:10 +02:00
Thilko Richter
54b2b9b339 correct typo 2012-09-06 21:23:45 +02:00
Thilko Richter
fede78c337 cleanup correctly 2012-09-06 21:12:31 +02:00
Thilko Richter
8a41dca46d destroy action implemented 2012-09-06 07:38:54 +02:00
21 changed files with 1754 additions and 1090 deletions

2
.gitignore vendored
View File

@@ -46,3 +46,5 @@ pkg
Gemfile.lock Gemfile.lock
vendor/ vendor/
homesick*.gem

19
.rubocop.yml Normal file
View File

@@ -0,0 +1,19 @@
# TODO: Eval is required for the .homesickrc feature. This should eventually be
# removed if the feature is implemented in a more secure way.
Eval:
Enabled: false
# TODO: The following settings disable reports about issues that can be fixed
# through refactoring. Remove these as offenses are removed from the code base.
ClassLength:
Enabled: false
CyclomaticComplexity:
Max: 13
LineLength:
Enabled: false
MethodLength:
Max: 36

View File

@@ -1,5 +1,6 @@
language: ruby language: ruby
rvm: rvm:
- 2.1.0
- 2.0.0 - 2.0.0
- 1.9.3 - 1.9.3
- 1.8.7 sudo: false

View File

@@ -1,3 +1,27 @@
#1.1.3
* Allow a destinaton to be passed when cloning a castle
* Make sure `homesick edit` opens default editor in the root of the given castle
* Fixed bug when diffing edited files
* Fixed crashing bug when attempting to diff directories
* Ensure that messages are escaped correctly on `git commit all`
#1.1.2
* Added '--force' option to the rc command to bypass confirmation checks when running a .homesickrc file
* Added a check to make sure that a minimum of Git 1.8.0 is installed. This stops Homesick failing silently if Git is not installed.
* Code refactoring and fixes.
#1.1.0
* Added exec and exec_all commands to run commands inside one or all clones castles.
* Code refactoring.
#1.0.0
* Removed support for Ruby 1.8.7
* Added a version command
# 0.9.8
* Introduce new commands
* `homesick cd`
* `homesick open`
# 0.9.4 # 0.9.4
* Use https protocol instead of git protocol * Use https protocol instead of git protocol
* Introduce new commands * Introduce new commands

18
Gemfile
View File

@@ -1,3 +1,4 @@
require 'rbconfig'
source 'https://rubygems.org' source 'https://rubygems.org'
# Add dependencies required to use your gem here. # Add dependencies required to use your gem here.
@@ -7,11 +8,20 @@ gem "thor", ">= 0.14.0"
# Include everything needed to run rake, tests, features, etc. # Include everything needed to run rake, tests, features, etc.
group :development do group :development do
gem "rake", ">= 0.8.7" gem "rake", ">= 0.8.7"
gem "rspec", "~> 2.10" gem "rspec", "~> 3.1.0"
gem "guard"
gem "guard-rspec"
gem "rb-readline", "~> 0.5.0"
gem "jeweler", ">= 1.6.2" gem "jeweler", ">= 1.6.2"
gem "rcov", :platforms => :mri_18 gem 'coveralls', require: false
gem "simplecov", :platforms => :mri_19 gem "test_construct"
gem "test-construct" gem "capture-output", "~> 1.0.0"
if RbConfig::CONFIG['host_os'] =~ /linux|freebsd|openbsd|sunos|solaris/
gem 'libnotify'
end
if RbConfig::CONFIG['host_os'] =~ /darwin|mac os/
gem 'terminal-notifier-guard', '~> 1.6.1'
end
if RUBY_VERSION >= '1.9.2' if RUBY_VERSION >= '1.9.2'
gem "rubocop" gem "rubocop"
end end

6
Guardfile Normal file
View File

@@ -0,0 +1,6 @@
guard :rspec, :cmd => 'bundle exec rspec' do
watch(%r{^spec/.+_spec\.rb$})
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
watch(%r{^lib/homesick/.*\.rb}) { "spec" }
watch('spec/spec_helper.rb') { "spec" }
end

View File

@@ -1,8 +1,13 @@
# homesick # homesick
[![Build Status](https://travis-ci.org/technicalpickles/homesick.png?branch=master)](https://travis-ci.org/technicalpickles/homesick) [![Gem Version](https://badge.fury.io/rb/homesick.svg)](http://badge.fury.io/rb/homesick)
[![Build Status](https://travis-ci.org/technicalpickles/homesick.svg?branch=master)](https://travis-ci.org/technicalpickles/homesick)
[![Dependency Status](https://gemnasium.com/technicalpickles/homesick.svg)](https://gemnasium.com/technicalpickles/homesick)
[![Coverage Status](https://coveralls.io/repos/technicalpickles/homesick/badge.png)](https://coveralls.io/r/technicalpickles/homesick)
[![Code Climate](https://codeclimate.com/github/technicalpickles/homesick.svg)](https://codeclimate.com/github/technicalpickles/homesick)
[![Gitter chat](https://badges.gitter.im/technicalpickles/homesick.svg)](https://gitter.im/technicalpickles/homesick)
A man's home (directory) is his castle, so don't leave home with out it. Your home directory is your castle. Don't leave your dotfiles behind.
Homesick is sorta like [rip](http://github.com/defunkt/rip), but for dotfiles. It uses git to clone a repository containing dotfiles, and saves them in `~/.homesick`. It then allows you to symlink all the dotfiles into place with a single command. Homesick is sorta like [rip](http://github.com/defunkt/rip), but for dotfiles. It uses git to clone a repository containing dotfiles, and saves them in `~/.homesick`. It then allows you to symlink all the dotfiles into place with a single command.
@@ -27,14 +32,16 @@ With the castle cloned, you can now link its contents into your home dir:
homesick symlink pickled-vim homesick symlink pickled-vim
If you use the shorthand syntax for GitHub repositories in your clone, please note you will instead need to run:
homesick symlink technicalpickles/pickled-vim
You can remove symlinks anytime when you don't need them anymore You can remove symlinks anytime when you don't need them anymore
homesick unlink pickled-vim homesick unlink pickled-vim
If you need to add further configuration steps you can add these in a file called '.homesickrc' in the root of a castle. Once you've cloned a castle with a .homesickrc run the configuration with:
homesick rc CASTLE
The contents of the .homesickrc file must be valid Ruby code as the file will be executed with Ruby's eval construct. The .homesickrc is also passed the current homesick object during its execution and this is available within the .homesickrc file as the 'self' variable. As the rc operation can be destructive the command normally asks for confirmation before proceeding. You can bypass this by passing the '--force' option, for example `homesick rc --force CASTLE`.
If you're not sure what castles you have around, you can easily list them: If you're not sure what castles you have around, you can easily list them:
homesick list homesick list
@@ -51,10 +58,30 @@ To push your castle:
homesick push CASTLE homesick push CASTLE
To open a terminal in the root of a castle:
homesick cd CASTLE
To open your default editor in the root of a castle (the $EDITOR environment variable must be set):
homesick open CASTLE
To execute a shell command inside the root directory of a given castle:
homesick exec CASTLE COMMAND
To execute a shell command inside the root directory of every cloned castle:
homesick exec_all COMMAND
Not sure what else homesick has up its sleeve? There's always the built in help: Not sure what else homesick has up its sleeve? There's always the built in help:
homesick help homesick help
If you ever want to see what version of homesick you have type:
homesick version|-v|--version
## .homesick_subdir ## .homesick_subdir
`homesick symlink` basically makes symlink to only first depth in `castle/home`. If you want to link nested files/directories, please use .homesick_subdir. `homesick symlink` basically makes symlink to only first depth in `castle/home`. If you want to link nested files/directories, please use .homesick_subdir.
@@ -133,6 +160,14 @@ and castle
`-- .emacs.d `-- .emacs.d
`-- elisp `-- elisp
## Supported Ruby Versions
Homesick is tested on the following Ruby versions:
* 1.9.3
* 2.0.0
* 2.1.0
## Note on Patches/Pull Requests ## Note on Patches/Pull Requests
* Fork the project. * Fork the project.

View File

@@ -1,5 +1,6 @@
require 'rubygems' require 'rubygems'
require 'bundler' require 'bundler'
require_relative 'lib/homesick/version'
begin begin
Bundler.setup(:default, :development) Bundler.setup(:default, :development)
rescue Bundler::BundlerError => e rescue Bundler::BundlerError => e
@@ -12,9 +13,10 @@ require 'rake'
require 'jeweler' require 'jeweler'
Jeweler::Tasks.new do |gem| Jeweler::Tasks.new do |gem|
gem.name = "homesick" gem.name = "homesick"
gem.summary = %Q{A man's home is his castle. Never leave your dotfiles behind.} gem.summary = %Q{Your home directory is your castle. Don't leave your dotfiles behind.}
gem.description = %Q{ gem.description = %Q{
A man's home (directory) is his castle, so don't leave home with out it. Your home directory is your castle. Don't leave your dotfiles behind.
Homesick is sorta like rip, but for dotfiles. It uses git to clone a repository containing dotfiles, and saves them in ~/.homesick. It then allows you to symlink all the dotfiles into place with a single command. Homesick is sorta like rip, but for dotfiles. It uses git to clone a repository containing dotfiles, and saves them in ~/.homesick. It then allows you to symlink all the dotfiles into place with a single command.
@@ -22,7 +24,7 @@ Jeweler::Tasks.new do |gem|
gem.email = ["josh@technicalpickles.com", "info@muratayusuke.com"] gem.email = ["josh@technicalpickles.com", "info@muratayusuke.com"]
gem.homepage = "http://github.com/technicalpickles/homesick" gem.homepage = "http://github.com/technicalpickles/homesick"
gem.authors = ["Joshua Nichols", "Yusuke Murata"] gem.authors = ["Joshua Nichols", "Yusuke Murata"]
gem.version = "0.9.5" gem.version = Homesick::Version::STRING
gem.license = "MIT" gem.license = "MIT"
# Have dependencies? Add them to Gemfile # Have dependencies? Add them to Gemfile

View File

@@ -6,4 +6,4 @@ $LOAD_PATH.unshift lib.to_s
require 'homesick' require 'homesick'
Homesick.start Homesick::CLI.start

View File

@@ -2,15 +2,17 @@
# DO NOT EDIT THIS FILE DIRECTLY # DO NOT EDIT THIS FILE DIRECTLY
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec' # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
# -*- encoding: utf-8 -*- # -*- encoding: utf-8 -*-
# stub: homesick 1.1.3 ruby lib
Gem::Specification.new do |s| Gem::Specification.new do |s|
s.name = "homesick" s.name = "homesick"
s.version = "0.9.5" s.version = "1.1.3"
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.require_paths = ["lib"]
s.authors = ["Joshua Nichols", "Yusuke Murata"] s.authors = ["Joshua Nichols", "Yusuke Murata"]
s.date = "2013-09-18" s.date = "2015-10-31"
s.description = "\n A man's home (directory) is his castle, so don't leave home with out it.\n\n Homesick is sorta like rip, but for dotfiles. It uses git to clone a repository containing dotfiles, and saves them in ~/.homesick. It then allows you to symlink all the dotfiles into place with a single command. \n\n " s.description = "\n Your home directory is your castle. Don't leave your dotfiles behind.\n \n\n Homesick is sorta like rip, but for dotfiles. It uses git to clone a repository containing dotfiles, and saves them in ~/.homesick. It then allows you to symlink all the dotfiles into place with a single command. \n\n "
s.email = ["josh@technicalpickles.com", "info@muratayusuke.com"] s.email = ["josh@technicalpickles.com", "info@muratayusuke.com"]
s.executables = ["homesick"] s.executables = ["homesick"]
s.extra_rdoc_files = [ s.extra_rdoc_files = [
@@ -21,57 +23,70 @@ Gem::Specification.new do |s|
s.files = [ s.files = [
".document", ".document",
".rspec", ".rspec",
".rubocop.yml",
".travis.yml", ".travis.yml",
"ChangeLog.markdown", "ChangeLog.markdown",
"Gemfile", "Gemfile",
"Guardfile",
"LICENSE", "LICENSE",
"README.markdown", "README.markdown",
"Rakefile", "Rakefile",
"bin/homesick", "bin/homesick",
"homesick.gemspec", "homesick.gemspec",
"lib/homesick.rb", "lib/homesick.rb",
"lib/homesick/actions.rb", "lib/homesick/actions/file_actions.rb",
"lib/homesick/shell.rb", "lib/homesick/actions/git_actions.rb",
"spec/homesick_spec.rb", "lib/homesick/cli.rb",
"lib/homesick/utils.rb",
"lib/homesick/version.rb",
"spec/homesick_cli_spec.rb",
"spec/spec.opts", "spec/spec.opts",
"spec/spec_helper.rb" "spec/spec_helper.rb"
] ]
s.homepage = "http://github.com/technicalpickles/homesick" s.homepage = "http://github.com/technicalpickles/homesick"
s.licenses = ["MIT"] s.licenses = ["MIT"]
s.require_paths = ["lib"] s.rubygems_version = "2.2.2"
s.rubygems_version = "1.8.23" s.summary = "Your home directory is your castle. Don't leave your dotfiles behind."
s.summary = "A man's home is his castle. Never leave your dotfiles behind."
if s.respond_to? :specification_version then if s.respond_to? :specification_version then
s.specification_version = 3 s.specification_version = 4
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
s.add_runtime_dependency(%q<thor>, [">= 0.14.0"]) s.add_runtime_dependency(%q<thor>, [">= 0.14.0"])
s.add_development_dependency(%q<rake>, [">= 0.8.7"]) s.add_development_dependency(%q<rake>, [">= 0.8.7"])
s.add_development_dependency(%q<rspec>, ["~> 2.10"]) s.add_development_dependency(%q<rspec>, ["~> 3.1.0"])
s.add_development_dependency(%q<guard>, [">= 0"])
s.add_development_dependency(%q<guard-rspec>, [">= 0"])
s.add_development_dependency(%q<rb-readline>, ["~> 0.5.0"])
s.add_development_dependency(%q<jeweler>, [">= 1.6.2"]) s.add_development_dependency(%q<jeweler>, [">= 1.6.2"])
s.add_development_dependency(%q<rcov>, [">= 0"]) s.add_development_dependency(%q<coveralls>, [">= 0"])
s.add_development_dependency(%q<simplecov>, [">= 0"]) s.add_development_dependency(%q<test_construct>, [">= 0"])
s.add_development_dependency(%q<test-construct>, [">= 0"]) s.add_development_dependency(%q<capture-output>, ["~> 1.0.0"])
s.add_development_dependency(%q<rubocop>, [">= 0"]) s.add_development_dependency(%q<rubocop>, [">= 0"])
else else
s.add_dependency(%q<thor>, [">= 0.14.0"]) s.add_dependency(%q<thor>, [">= 0.14.0"])
s.add_dependency(%q<rake>, [">= 0.8.7"]) s.add_dependency(%q<rake>, [">= 0.8.7"])
s.add_dependency(%q<rspec>, ["~> 2.10"]) s.add_dependency(%q<rspec>, ["~> 3.1.0"])
s.add_dependency(%q<guard>, [">= 0"])
s.add_dependency(%q<guard-rspec>, [">= 0"])
s.add_dependency(%q<rb-readline>, ["~> 0.5.0"])
s.add_dependency(%q<jeweler>, [">= 1.6.2"]) s.add_dependency(%q<jeweler>, [">= 1.6.2"])
s.add_dependency(%q<rcov>, [">= 0"]) s.add_dependency(%q<coveralls>, [">= 0"])
s.add_dependency(%q<simplecov>, [">= 0"]) s.add_dependency(%q<test_construct>, [">= 0"])
s.add_dependency(%q<test-construct>, [">= 0"]) s.add_dependency(%q<capture-output>, ["~> 1.0.0"])
s.add_dependency(%q<rubocop>, [">= 0"]) s.add_dependency(%q<rubocop>, [">= 0"])
end end
else else
s.add_dependency(%q<thor>, [">= 0.14.0"]) s.add_dependency(%q<thor>, [">= 0.14.0"])
s.add_dependency(%q<rake>, [">= 0.8.7"]) s.add_dependency(%q<rake>, [">= 0.8.7"])
s.add_dependency(%q<rspec>, ["~> 2.10"]) s.add_dependency(%q<rspec>, ["~> 3.1.0"])
s.add_dependency(%q<guard>, [">= 0"])
s.add_dependency(%q<guard-rspec>, [">= 0"])
s.add_dependency(%q<rb-readline>, ["~> 0.5.0"])
s.add_dependency(%q<jeweler>, [">= 1.6.2"]) s.add_dependency(%q<jeweler>, [">= 1.6.2"])
s.add_dependency(%q<rcov>, [">= 0"]) s.add_dependency(%q<coveralls>, [">= 0"])
s.add_dependency(%q<simplecov>, [">= 0"]) s.add_dependency(%q<test_construct>, [">= 0"])
s.add_dependency(%q<test-construct>, [">= 0"]) s.add_dependency(%q<capture-output>, ["~> 1.0.0"])
s.add_dependency(%q<rubocop>, [">= 0"]) s.add_dependency(%q<rubocop>, [">= 0"])
end end
end end

View File

@@ -1,404 +1,14 @@
require 'thor' # -*- encoding : utf-8 -*-
require 'homesick/actions/file_actions'
require 'homesick/actions/git_actions'
require 'homesick/version'
require 'homesick/utils'
require 'homesick/cli'
class Homesick < Thor # Homesick's top-level module
autoload :Shell, 'homesick/shell' module Homesick
autoload :Actions, 'homesick/actions' GITHUB_NAME_REPO_PATTERN = %r{\A([A-Za-z0-9_-]+/[A-Za-z0-9_-]+)\Z}
include Thor::Actions
include Homesick::Actions
add_runtime_options!
GITHUB_NAME_REPO_PATTERN = /\A([A-Za-z_-]+\/[A-Za-z_-]+)\Z/
SUBDIR_FILENAME = '.homesick_subdir' SUBDIR_FILENAME = '.homesick_subdir'
DEFAULT_CASTLE_NAME = 'dotfiles' DEFAULT_CASTLE_NAME = 'dotfiles'
def initialize(args = [], options = {}, config = {})
super
self.shell = Homesick::Shell.new
end
desc 'clone URI', 'Clone +uri+ as a castle for homesick'
def clone(uri)
inside repos_dir do
destination = nil
if File.exist?(uri)
uri = Pathname.new(uri).expand_path
if uri.to_s.start_with?(repos_dir.to_s)
raise "Castle already cloned to #{uri}"
end
destination = uri.basename
ln_s uri, destination
elsif uri =~ GITHUB_NAME_REPO_PATTERN
destination = Pathname.new($1)
git_clone "https://github.com/#{$1}.git", :destination => destination
elsif uri =~ /%r([^%r]*?)(\.git)?\Z/
destination = Pathname.new($1)
git_clone uri
elsif uri =~ /[^:]+:([^:]+)(\.git)?\Z/
destination = Pathname.new($1)
git_clone uri
else
raise "Unknown URI format: #{uri}"
end
if destination.join('.gitmodules').exist?
inside destination do
git_submodule_init
git_submodule_update
end
end
rc(destination)
end
end
desc 'rc CASTLE', 'Run the .homesickrc for the specified castle'
def rc(name = DEFAULT_CASTLE_NAME)
inside repos_dir do
destination = Pathname.new(name)
homesickrc = destination.join('.homesickrc').expand_path
if homesickrc.exist?
proceed = shell.yes?("#{name} has a .homesickrc. Proceed with evaling it? (This could be destructive)")
if proceed
shell.say_status 'eval', homesickrc
inside destination do
eval homesickrc.read, binding, homesickrc.expand_path.to_s
end
else
shell.say_status 'eval skip', "not evaling #{homesickrc}, #{destination} may need manual configuration", :blue
end
end
end
end
desc 'pull CASTLE', 'Update the specified castle'
method_option :all, :type => :boolean, :default => false, :required => false, :desc => 'Update all cloned castles'
def pull(name = DEFAULT_CASTLE_NAME)
if options[:all]
inside_each_castle do |castle|
shell.say castle.to_s.gsub(repos_dir.to_s + '/', '') + ':'
update_castle castle
end
else
update_castle name
end
end
desc 'commit CASTLE', "Commit the specified castle's changes"
def commit(name = DEFAULT_CASTLE_NAME)
commit_castle name
end
desc 'push CASTLE', 'Push the specified castle'
def push(name = DEFAULT_CASTLE_NAME)
push_castle name
end
desc 'unlink CASTLE', 'Unsymlinks all dotfiles from the specified castle'
def unlink(name = DEFAULT_CASTLE_NAME)
check_castle_existance(name, 'symlink')
inside castle_dir(name) do
subdirs = subdirs(name)
# unlink files
unsymlink_each(name, castle_dir(name), subdirs)
# unlink files in subdirs
subdirs.each do |subdir|
unsymlink_each(name, subdir, subdirs)
end
end
end
desc 'symlink CASTLE', 'Symlinks all dotfiles from the specified castle'
method_option :force, :default => false, :desc => 'Overwrite existing conflicting symlinks without prompting.'
def symlink(name = DEFAULT_CASTLE_NAME)
check_castle_existance(name, 'symlink')
inside castle_dir(name) do
subdirs = subdirs(name)
# link files
symlink_each(name, castle_dir(name), subdirs)
# link files in subdirs
subdirs.each do |subdir|
symlink_each(name, subdir, subdirs)
end
end
end
desc 'track FILE CASTLE', 'add a file to a castle'
def track(file, castle = DEFAULT_CASTLE_NAME)
castle = Pathname.new(castle)
file = Pathname.new(file.chomp('/'))
check_castle_existance(castle, 'track')
absolute_path = file.expand_path
relative_dir = absolute_path.relative_path_from(home_dir).dirname
castle_path = Pathname.new(castle_dir(castle)).join(relative_dir)
FileUtils.mkdir_p castle_path
# Are we already tracking this or anything inside it?
target = Pathname.new(castle_path.join(file.basename))
if target.exist?
if absolute_path.directory?
move_dir_contents(target, absolute_path)
absolute_path.rmtree
subdir_remove(castle, relative_dir + file.basename)
elsif more_recent? absolute_path, target
target.delete
mv absolute_path, castle_path
else
shell.say_status(:track, "#{target} already exists, and is more recent than #{file}. Run 'homesick SYMLINK CASTLE' to create symlinks.", :blue) unless options[:quiet]
end
else
mv absolute_path, castle_path
end
inside home_dir do
absolute_path = castle_path + file.basename
home_path = home_dir + relative_dir + file.basename
ln_s absolute_path, home_path
end
inside castle_path do
git_add absolute_path
end
# are we tracking something nested? Add the parent dir to the manifest
subdir_add(castle, relative_dir) unless relative_dir.eql?(Pathname.new('.'))
end
desc 'list', 'List cloned castles'
def list
inside_each_castle do |castle|
say_status castle.relative_path_from(repos_dir).to_s, `git config remote.origin.url`.chomp, :cyan
end
end
desc 'status CASTLE', 'Shows the git status of a castle'
def status(castle = DEFAULT_CASTLE_NAME)
check_castle_existance(castle, 'status')
inside repos_dir.join(castle) do
git_status
end
end
desc 'diff CASTLE', 'Shows the git diff of uncommitted changes in a castle'
def diff(castle = DEFAULT_CASTLE_NAME)
check_castle_existance(castle, 'diff')
inside repos_dir.join(castle) do
git_diff
end
end
desc 'show_path CASTLE', 'Prints the path of a castle'
def show_path(castle = DEFAULT_CASTLE_NAME)
check_castle_existance(castle, 'show_path')
say repos_dir.join(castle)
end
desc 'generate PATH', 'generate a homesick-ready git repo at PATH'
def generate(castle)
castle = Pathname.new(castle).expand_path
github_user = `git config github.user`.chomp
github_user = nil if github_user == ''
github_repo = castle.basename
empty_directory castle
inside castle do
git_init
if github_user
url = "git@github.com:#{github_user}/#{github_repo}.git"
git_remote_add 'origin', url
end
empty_directory 'home'
end
end
protected
def home_dir
@home_dir ||= Pathname.new(ENV['HOME'] || '~').expand_path
end
def repos_dir
@repos_dir ||= home_dir.join('.homesick', 'repos').expand_path
end
def castle_dir(name)
repos_dir.join(name, 'home')
end
def check_castle_existance(name, action)
unless castle_dir(name).exist?
say_status :error, "Could not #{action} #{name}, expected #{castle_dir(name)} exist and contain dotfiles", :red
exit(1)
end
end
def all_castles
dirs = Pathname.glob("#{repos_dir}/**/.git", File::FNM_DOTMATCH)
# reject paths that lie inside another castle, like git submodules
return dirs.reject do |dir|
dirs.any? do |other|
dir != other && dir.fnmatch(other.parent.join('*').to_s)
end
end
end
def inside_each_castle(&block)
all_castles.each do |git_dir|
castle = git_dir.dirname
Dir.chdir castle do # so we can call git config from the right contxt
yield castle
end
end
end
def update_castle(castle)
check_castle_existance(castle, 'pull')
inside repos_dir.join(castle) do
git_pull
git_submodule_init
git_submodule_update
end
end
def commit_castle(castle)
check_castle_existance(castle, 'commit')
inside repos_dir.join(castle) do
git_commit_all
end
end
def push_castle(castle)
check_castle_existance(castle, 'push')
inside repos_dir.join(castle) do
git_push
end
end
def subdir_file(castle)
repos_dir.join(castle, SUBDIR_FILENAME)
end
def subdirs(castle)
subdir_filepath = subdir_file(castle)
subdirs = []
if subdir_filepath.exist?
subdir_filepath.readlines.each do |subdir|
subdirs.push(subdir.chomp)
end
end
subdirs
end
def subdir_add(castle, path)
subdir_filepath = subdir_file(castle)
File.open(subdir_filepath, 'a+') do |subdir|
subdir.puts path unless subdir.readlines.reduce(false) do |memo, line|
line.eql?("#{path.to_s}\n") || memo
end
end
inside castle_dir(castle) do
git_add subdir_filepath
end
end
def subdir_remove(castle, path)
subdir_filepath = subdir_file(castle)
if subdir_filepath.exist?
lines = IO.readlines(subdir_filepath).delete_if { |line| line == "#{path}\n" }
File.open(subdir_filepath, 'w') { |manfile| manfile.puts lines }
end
inside castle_dir(castle) do
git_add subdir_filepath
end
end
def move_dir_contents(target, dir_path)
child_files = dir_path.children
child_files.each do |child|
target_path = target.join(child.basename)
if target_path.exist?
if more_recent?(child, target_path) && target.file?
target_path.delete
mv child, target
end
next
end
mv child, target
end
end
def more_recent?(first, second)
first_p = Pathname.new(first)
second_p = Pathname.new(second)
first_p.mtime > second_p.mtime && !first_p.symlink?
end
def each_file(castle, basedir, subdirs)
absolute_basedir = Pathname.new(basedir).expand_path
inside basedir do
files = Pathname.glob('{.*,*}').reject{ |a| ['.', '..'].include?(a.to_s) }
files.each do |path|
absolute_path = path.expand_path
castle_home = castle_dir(castle)
# make ignore dirs
ignore_dirs = []
subdirs.each do |subdir|
# ignore all parent of each line in subdir file
Pathname.new(subdir).ascend do |p|
ignore_dirs.push(p)
end
end
# ignore dirs written in subdir file
matched = false
ignore_dirs.uniq.each do |ignore_dir|
if absolute_path == castle_home.join(ignore_dir)
matched = true
break
end
end
next if matched
relative_dir = absolute_basedir.relative_path_from(castle_home)
home_path = home_dir.join(relative_dir).join(path)
yield(absolute_path, home_path)
end
end
end
def unsymlink_each(castle, basedir, subdirs)
each_file(castle, basedir, subdirs) do |absolute_path, home_path|
rm_link home_path
end
end
def symlink_each(castle, basedir, subdirs)
each_file(castle, basedir, subdirs) do |absolute_path, home_path|
ln_s absolute_path, home_path
end
end
end end

View File

@@ -1,139 +0,0 @@
class Homesick
module Actions
# TODO move this to be more like thor's template, empty_directory, etc
def git_clone(repo, config = {})
config ||= {}
destination = config[:destination] || File.basename(repo, '.git')
destination = Pathname.new(destination) unless destination.kind_of?(Pathname)
FileUtils.mkdir_p destination.dirname
if ! destination.directory?
say_status 'git clone', "#{repo} to #{destination.expand_path}", :green unless options[:quiet]
system "git clone -q --recursive #{repo} #{destination}" unless options[:pretend]
else
say_status :exist, destination.expand_path, :blue unless options[:quiet]
end
end
def git_init(path = '.')
path = Pathname.new(path)
inside path do
if !path.join('.git').exist?
say_status 'git init', '' unless options[:quiet]
system 'git init >/dev/null' unless options[:pretend]
else
say_status 'git init', 'already initialized', :blue unless options[:quiet]
end
end
end
def git_remote_add(name, url)
existing_remote = `git config remote.#{name}.url`.chomp
existing_remote = nil if existing_remote == ''
if !existing_remote
say_status 'git remote', "add #{name} #{url}" unless options[:quiet]
system "git remote add #{name} #{url}" unless options[:pretend]
else
say_status 'git remote', "#{name} already exists", :blue unless options[:quiet]
end
end
def git_submodule_init(config = {})
say_status 'git submodule', 'init', :green unless options[:quiet]
system 'git submodule --quiet init' unless options[:pretend]
end
def git_submodule_update(config = {})
say_status 'git submodule', 'update', :green unless options[:quiet]
system 'git submodule --quiet update --init --recursive >/dev/null 2>&1' unless options[:pretend]
end
def git_pull(config = {})
say_status 'git pull', '', :green unless options[:quiet]
system 'git pull --quiet' unless options[:pretend]
end
def git_push(config = {})
say_status 'git push', '', :green unless options[:quiet]
system 'git push' unless options[:pretend]
end
def git_commit_all(config = {})
say_status 'git commit all', '', :green unless options[:quiet]
system 'git commit -v -a' unless options[:pretend]
end
def git_add(file, config = {})
say_status 'git add file', '', :green unless options[:quiet]
system "git add '#{file}'" unless options[:pretend]
end
def git_status(config = {})
say_status 'git status', '', :green unless options[:quiet]
system "git status" unless options[:pretend]
end
def git_diff(config = {})
say_status 'git diff', '', :green unless options[:quiet]
system "git diff" unless options[:pretend]
end
def mv(source, destination, config = {})
source = Pathname.new(source)
destination = Pathname.new(destination + source.basename)
if destination.exist?
say_status :conflict, "#{destination} exists", :red unless options[:quiet]
if options[:force] || shell.file_collision(destination) { source }
system "mv '#{source}' '#{destination}'" unless options[:pretend]
end
else
# this needs some sort of message here.
system "mv '#{source}' '#{destination}'" unless options[:pretend]
end
end
def rm_link(target)
target = Pathname.new(target)
if target.symlink?
say_status :unlink, "#{target.expand_path}", :green unless options[:quiet]
FileUtils.rm_rf target
else
say_status :conflict, "#{target} is not a symlink", :red unless options[:quiet]
end
end
def ln_s(source, destination, config = {})
source = Pathname.new(source)
destination = Pathname.new(destination)
FileUtils.mkdir_p destination.dirname
if destination.symlink?
if destination.readlink == source
say_status :identical, destination.expand_path, :blue unless options[:quiet]
else
say_status :conflict, "#{destination} exists and points to #{destination.readlink}", :red unless options[:quiet]
if options[:force] || shell.file_collision(destination) { source }
system "ln -nsf '#{source}' '#{destination}'" unless options[:pretend]
end
end
elsif destination.exist?
say_status :conflict, "#{destination} exists", :red unless options[:quiet]
if options[:force] || shell.file_collision(destination) { source }
system "rm -rf '#{destination}'" unless options[:pretend]
system "ln -sf '#{source}' '#{destination}'" unless options[:pretend]
end
else
say_status :symlink, "#{source.expand_path} to #{destination.expand_path}", :green unless options[:quiet]
system "ln -s '#{source}' '#{destination}'" unless options[:pretend]
end
end
end
end

View File

@@ -0,0 +1,89 @@
# -*- encoding : utf-8 -*-
module Homesick
module Actions
# File-related helper methods for Homesick
module FileActions
def mv(source, destination)
source = Pathname.new(source)
destination = Pathname.new(destination + source.basename)
case
when destination.exist? && (options[:force] || shell.file_collision(destination) { source })
say_status :conflict, "#{destination} exists", :red
FileUtils.mv source, destination unless options[:pretend]
else
FileUtils.mv source, destination unless options[:pretend]
end
end
def rm_rf(dir)
say_status "rm -rf #{dir}", '', :green
FileUtils.rm_r dir, force: true
end
def rm_link(target)
target = Pathname.new(target)
if target.symlink?
say_status :unlink, "#{target.expand_path}", :green
FileUtils.rm_rf target
else
say_status :conflict, "#{target} is not a symlink", :red
end
end
def rm(file)
say_status "rm #{file}", '', :green
FileUtils.rm file, force: true
end
def rm_r(dir)
say_status "rm -r #{dir}", '', :green
FileUtils.rm_r dir
end
def ln_s(source, destination)
source = Pathname.new(source)
destination = Pathname.new(destination)
FileUtils.mkdir_p destination.dirname
action = if destination.symlink? && destination.readlink == source
:identical
elsif destination.symlink?
:symlink_conflict
elsif destination.exist?
:conflict
else
:success
end
handle_symlink_action action, source, destination
end
def handle_symlink_action(action, source, destination)
case action
when :identical
say_status :identical, destination.expand_path, :blue
when :symlink_conflict
say_status :conflict,
"#{destination} exists and points to #{destination.readlink}",
:red
FileUtils.rm destination
FileUtils.ln_s source, destination, force: true unless options[:pretend]
when :conflict
say_status :conflict, "#{destination} exists", :red
if collision_accepted?(destination, source)
FileUtils.rm_r destination, force: true unless options[:pretend]
FileUtils.ln_s source, destination, force: true unless options[:pretend]
end
else
say_status :symlink,
"#{source.expand_path} to #{destination.expand_path}",
:green
FileUtils.ln_s source, destination unless options[:pretend]
end
end
end
end
end

View File

@@ -0,0 +1,113 @@
# -*- encoding : utf-8 -*-
module Homesick
module Actions
# Git-related helper methods for Homesick
module GitActions
# Information on the minimum git version required for Homesick
MIN_VERSION = {
major: 1,
minor: 8,
patch: 0
}
STRING = MIN_VERSION.values.join('.')
def git_version_correct?
info = `git --version`.scan(/(\d+)\.(\d+)\.(\d+)/).flatten.map(&:to_i)
return false unless info.count == 3
current_version = Hash[[:major, :minor, :patch].zip(info)]
return true if current_version.eql?(MIN_VERSION)
return true if current_version[:major] > MIN_VERSION[:major]
return true if current_version[:major] == MIN_VERSION[:major] && current_version[:minor] > MIN_VERSION[:minor]
return true if current_version[:major] == MIN_VERSION[:major] && current_version[:minor] == MIN_VERSION[:minor] && current_version[:patch] >= MIN_VERSION[:patch]
false
end
# TODO: move this to be more like thor's template, empty_directory, etc
def git_clone(repo, config = {})
config ||= {}
destination = config[:destination] || File.basename(repo, '.git')
destination = Pathname.new(destination) unless destination.is_a?(Pathname)
FileUtils.mkdir_p destination.dirname
if destination.directory?
say_status :exist, destination.expand_path, :blue
else
say_status 'git clone',
"#{repo} to #{destination.expand_path}",
:green
system "git clone -q --config push.default=upstream --recursive #{repo} #{destination}"
end
end
def git_init(path = '.')
path = Pathname.new(path)
inside path do
if path.join('.git').exist?
say_status 'git init', 'already initialized', :blue
else
say_status 'git init', ''
system 'git init >/dev/null'
end
end
end
def git_remote_add(name, url)
existing_remote = `git config remote.#{name}.url`.chomp
existing_remote = nil if existing_remote == ''
if existing_remote
say_status 'git remote', "#{name} already exists", :blue
else
say_status 'git remote', "add #{name} #{url}"
system "git remote add #{name} #{url}"
end
end
def git_submodule_init
say_status 'git submodule', 'init', :green
system 'git submodule --quiet init'
end
def git_submodule_update
say_status 'git submodule', 'update', :green
system 'git submodule --quiet update --init --recursive >/dev/null 2>&1'
end
def git_pull
say_status 'git pull', '', :green
system 'git pull --quiet'
end
def git_push
say_status 'git push', '', :green
system 'git push'
end
def git_commit_all(config = {})
say_status 'git commit all', '', :green
if config[:message]
system %(git commit -a -m "#{config[:message]}")
else
system 'git commit -v -a'
end
end
def git_add(file)
say_status 'git add file', '', :green
system "git add '#{file}'"
end
def git_status
say_status 'git status', '', :green
system 'git status'
end
def git_diff
say_status 'git diff', '', :green
system 'git diff'
end
end
end
end

328
lib/homesick/cli.rb Normal file
View File

@@ -0,0 +1,328 @@
# -*- encoding : utf-8 -*-
require 'thor'
module Homesick
# Homesick's command line interface
class CLI < Thor
include Thor::Actions
include Homesick::Actions::FileActions
include Homesick::Actions::GitActions
include Homesick::Version
include Homesick::Utils
add_runtime_options!
map '-v' => :version
map '--version' => :version
# Retain a mapped version of the symlink command for compatibility.
map symlink: :link
def initialize(args = [], options = {}, config = {})
super
# Check if git is installed
unless git_version_correct?
say_status :error, "Git version >= #{Homesick::Actions::GitActions::STRING} must be installed to use Homesick", :red
exit(1)
end
# Hack in support for diffing symlinks
# Also adds support for checking if destination or content is a directory
shell_metaclass = class << shell; self; end
shell_metaclass.send(:define_method, :show_diff) do |destination, content|
destination = Pathname.new(destination)
content = Pathname.new(content)
return 'Unable to create diff: destination or content is a directory' if destination.directory? || content.directory?
return super(destination, content) unless destination.symlink?
say "- #{destination.readlink}", :red, true
say "+ #{content.expand_path}", :green, true
end
end
desc 'clone URI CASTLE_NAME', 'Clone +uri+ as a castle with name CASTLE_NAME for homesick'
def clone(uri, destination=nil)
destination = Pathname.new(destination) unless destination.nil?
inside repos_dir do
if File.exist?(uri)
uri = Pathname.new(uri).expand_path
fail "Castle already cloned to #{uri}" if uri.to_s.start_with?(repos_dir.to_s)
destination = uri.basename if destination.nil?
ln_s uri, destination
elsif uri =~ GITHUB_NAME_REPO_PATTERN
destination = Pathname.new(uri).basename if destination.nil?
git_clone "https://github.com/#{Regexp.last_match[1]}.git",
destination: destination
elsif uri =~ /%r([^%r]*?)(\.git)?\Z/ || uri =~ /[^:]+:([^:]+)(\.git)?\Z/
destination = Pathname.new(Regexp.last_match[1].gsub(/\.git$/, '')).basename if destination.nil?
git_clone uri, destination: destination
else
fail "Unknown URI format: #{uri}"
end
setup_castle(destination)
end
end
desc 'rc CASTLE', 'Run the .homesickrc for the specified castle'
method_option :force,
default: false,
desc: 'Evaluate .homesickrc without prompting.'
def rc(name = DEFAULT_CASTLE_NAME)
inside repos_dir do
destination = Pathname.new(name)
homesickrc = destination.join('.homesickrc').expand_path
return unless homesickrc.exist?
proceed = options[:force] || shell.yes?("#{name} has a .homesickrc. Proceed with evaling it? (This could be destructive)")
return say_status 'eval skip', "not evaling #{homesickrc}, #{destination} may need manual configuration", :blue unless proceed
say_status 'eval', homesickrc
inside destination do
eval homesickrc.read, binding, homesickrc.expand_path.to_s
end
end
end
desc 'pull CASTLE', 'Update the specified castle'
method_option :all,
type: :boolean,
default: false,
required: false,
desc: 'Update all cloned castles'
def pull(name = DEFAULT_CASTLE_NAME)
if options[:all]
inside_each_castle do |castle|
say castle.to_s.gsub(repos_dir.to_s + '/', '') + ':'
update_castle castle
end
else
update_castle name
end
end
desc 'commit CASTLE MESSAGE', "Commit the specified castle's changes"
def commit(name = DEFAULT_CASTLE_NAME, message = nil)
commit_castle name, message
end
desc 'push CASTLE', 'Push the specified castle'
def push(name = DEFAULT_CASTLE_NAME)
push_castle name
end
desc 'unlink CASTLE', 'Unsymlinks all dotfiles from the specified castle'
def unlink(name = DEFAULT_CASTLE_NAME)
check_castle_existance(name, 'symlink')
inside castle_dir(name) do
subdirs = subdirs(name)
# unlink files
unsymlink_each(name, castle_dir(name), subdirs)
# unlink files in subdirs
subdirs.each do |subdir|
unsymlink_each(name, subdir, subdirs)
end
end
end
desc 'link CASTLE', 'Symlinks all dotfiles from the specified castle'
method_option :force,
default: false,
desc: 'Overwrite existing conflicting symlinks without prompting.'
def link(name = DEFAULT_CASTLE_NAME)
check_castle_existance(name, 'symlink')
inside castle_dir(name) do
subdirs = subdirs(name)
# link files
symlink_each(name, castle_dir(name), subdirs)
# link files in subdirs
subdirs.each do |subdir|
symlink_each(name, subdir, subdirs)
end
end
end
desc 'track FILE CASTLE', 'add a file to a castle'
def track(file, castle = DEFAULT_CASTLE_NAME)
castle = Pathname.new(castle)
file = Pathname.new(file.chomp('/'))
check_castle_existance(castle, 'track')
absolute_path = file.expand_path
relative_dir = absolute_path.relative_path_from(home_dir).dirname
castle_path = Pathname.new(castle_dir(castle)).join(relative_dir)
FileUtils.mkdir_p castle_path
# Are we already tracking this or anything inside it?
target = Pathname.new(castle_path.join(file.basename))
if target.exist?
if absolute_path.directory?
move_dir_contents(target, absolute_path)
absolute_path.rmtree
subdir_remove(castle, relative_dir + file.basename)
elsif more_recent? absolute_path, target
target.delete
mv absolute_path, castle_path
else
say_status(:track,
"#{target} already exists, and is more recent than #{file}. Run 'homesick SYMLINK CASTLE' to create symlinks.",
:blue)
end
else
mv absolute_path, castle_path
end
inside home_dir do
absolute_path = castle_path + file.basename
home_path = home_dir + relative_dir + file.basename
ln_s absolute_path, home_path
end
inside castle_path do
git_add absolute_path
end
# are we tracking something nested? Add the parent dir to the manifest
subdir_add(castle, relative_dir) unless relative_dir.eql?(Pathname.new('.'))
end
desc 'list', 'List cloned castles'
def list
inside_each_castle do |castle|
say_status castle.relative_path_from(repos_dir).to_s,
`git config remote.origin.url`.chomp,
:cyan
end
end
desc 'status CASTLE', 'Shows the git status of a castle'
def status(castle = DEFAULT_CASTLE_NAME)
check_castle_existance(castle, 'status')
inside repos_dir.join(castle) do
git_status
end
end
desc 'diff CASTLE', 'Shows the git diff of uncommitted changes in a castle'
def diff(castle = DEFAULT_CASTLE_NAME)
check_castle_existance(castle, 'diff')
inside repos_dir.join(castle) do
git_diff
end
end
desc 'show_path CASTLE', 'Prints the path of a castle'
def show_path(castle = DEFAULT_CASTLE_NAME)
check_castle_existance(castle, 'show_path')
say repos_dir.join(castle)
end
desc 'generate PATH', 'generate a homesick-ready git repo at PATH'
def generate(castle)
castle = Pathname.new(castle).expand_path
github_user = `git config github.user`.chomp
github_user = nil if github_user == ''
github_repo = castle.basename
empty_directory castle
inside castle do
git_init
if github_user
url = "git@github.com:#{github_user}/#{github_repo}.git"
git_remote_add 'origin', url
end
empty_directory 'home'
end
end
desc 'destroy CASTLE', 'Delete all symlinks and remove the cloned repository'
def destroy(name)
check_castle_existance name, 'destroy'
return unless shell.yes?('This will destroy your castle irreversible! Are you sure?')
unlink(name)
rm_rf repos_dir.join(name)
end
desc 'cd CASTLE', 'Open a new shell in the root of the given castle'
def cd(castle = DEFAULT_CASTLE_NAME)
check_castle_existance castle, 'cd'
castle_dir = repos_dir.join(castle)
say_status "cd #{castle_dir.realpath}",
"Opening a new shell in castle '#{castle}'. To return to the original one exit from the new shell.",
:green
inside castle_dir do
system(ENV['SHELL'])
end
end
desc 'open CASTLE',
'Open your default editor in the root of the given castle'
def open(castle = DEFAULT_CASTLE_NAME)
unless ENV['EDITOR']
say_status :error,
'The $EDITOR environment variable must be set to use this command',
:red
exit(1)
end
check_castle_existance castle, 'open'
castle_dir = repos_dir.join(castle)
say_status "#{castle_dir.realpath}: #{ENV['EDITOR']} .",
"Opening the root directory of castle '#{castle}' in editor '#{ENV['EDITOR']}'.",
:green
inside castle_dir do
system("#{ENV['EDITOR']} .")
end
end
desc 'exec CASTLE COMMAND',
'Execute a single shell command inside the root of a castle'
def exec(castle, *args)
check_castle_existance castle, 'exec'
unless args.count > 0
say_status :error,
'You must pass a shell command to execute',
:red
exit(1)
end
full_command = args.join(' ')
say_status "exec '#{full_command}'",
"#{options[:pretend] ? 'Would execute' : 'Executing command'} '#{full_command}' in castle '#{castle}'",
:green
inside repos_dir.join(castle) do
system(full_command)
end
end
desc 'exec_all COMMAND',
'Execute a single shell command inside the root of every cloned castle'
def exec_all(*args)
unless args.count > 0
say_status :error,
'You must pass a shell command to execute',
:red
exit(1)
end
full_command = args.join(' ')
inside_each_castle do |castle|
say_status "exec '#{full_command}'",
"#{options[:pretend] ? 'Would execute' : 'Executing command'} '#{full_command}' in castle '#{castle}'",
:green
system(full_command)
end
end
desc 'version', 'Display the current version of homesick'
def version
say Homesick::Version::STRING
end
end
end

View File

@@ -1,17 +0,0 @@
class Homesick
# Hack in support for diffing symlinks
class Shell < Thor::Shell::Color
def show_diff(destination, content)
destination = Pathname.new(destination)
if destination.symlink?
say "- #{destination.readlink}", :red, true
say "+ #{content.expand_path}", :green, true
else
super
end
end
end
end

216
lib/homesick/utils.rb Normal file
View File

@@ -0,0 +1,216 @@
# -*- encoding : utf-8 -*-
require 'pathname'
module Homesick
# Various utility methods that are used by Homesick
module Utils
QUIETABLE = ['say_status']
PRETENDABLE = ['system']
QUIETABLE.each do |method_name|
define_method(method_name) do |*args|
super(*args) unless options[:quiet]
end
end
PRETENDABLE.each do |method_name|
define_method(method_name) do |*args|
super(*args) unless options[:pretend]
end
end
protected
def home_dir
@home_dir ||= Pathname.new(ENV['HOME'] || '~').realpath
end
def repos_dir
@repos_dir ||= home_dir.join('.homesick', 'repos').expand_path
end
def castle_dir(name)
repos_dir.join(name, 'home')
end
def check_castle_existance(name, action)
return if castle_dir(name).exist?
say_status :error,
"Could not #{action} #{name}, expected #{castle_dir(name)} exist and contain dotfiles",
:red
exit(1)
end
def all_castles
dirs = Pathname.glob("#{repos_dir}/**/.git", File::FNM_DOTMATCH)
# reject paths that lie inside another castle, like git submodules
dirs.reject do |dir|
dirs.any? do |other|
dir != other && dir.fnmatch(other.parent.join('*').to_s)
end
end
end
def inside_each_castle
all_castles.each do |git_dir|
castle = git_dir.dirname
Dir.chdir castle do # so we can call git config from the right contxt
yield castle
end
end
end
def update_castle(castle)
check_castle_existance(castle, 'pull')
inside repos_dir.join(castle) do
git_pull
git_submodule_init
git_submodule_update
end
end
def commit_castle(castle, message)
check_castle_existance(castle, 'commit')
inside repos_dir.join(castle) do
git_commit_all message: message
end
end
def push_castle(castle)
check_castle_existance(castle, 'push')
inside repos_dir.join(castle) do
git_push
end
end
def subdir_file(castle)
repos_dir.join(castle, SUBDIR_FILENAME)
end
def subdirs(castle)
subdir_filepath = subdir_file(castle)
subdirs = []
if subdir_filepath.exist?
subdir_filepath.readlines.each do |subdir|
subdirs.push(subdir.chomp)
end
end
subdirs
end
def subdir_add(castle, path)
subdir_filepath = subdir_file(castle)
File.open(subdir_filepath, 'a+') do |subdir|
subdir.puts path unless subdir.readlines.reduce(false) do |memo, line|
line.eql?("#{path}\n") || memo
end
end
inside castle_dir(castle) do
git_add subdir_filepath
end
end
def subdir_remove(castle, path)
subdir_filepath = subdir_file(castle)
if subdir_filepath.exist?
lines = IO.readlines(subdir_filepath).delete_if do |line|
line == "#{path}\n"
end
File.open(subdir_filepath, 'w') { |manfile| manfile.puts lines }
end
inside castle_dir(castle) do
git_add subdir_filepath
end
end
def move_dir_contents(target, dir_path)
child_files = dir_path.children
child_files.each do |child|
target_path = target.join(child.basename)
if target_path.exist?
if more_recent?(child, target_path) && target.file?
target_path.delete
mv child, target
end
next
end
mv child, target
end
end
def more_recent?(first, second)
first_p = Pathname.new(first)
second_p = Pathname.new(second)
first_p.mtime > second_p.mtime && !first_p.symlink?
end
def collision_accepted?(destination, source)
fail "Arguments must be instances of Pathname, #{destination.class.name} and #{source.class.name} given" unless destination.instance_of?(Pathname) && source.instance_of?(Pathname)
options[:force] || shell.file_collision(destination) { source }
end
def each_file(castle, basedir, subdirs)
absolute_basedir = Pathname.new(basedir).expand_path
inside basedir do
files = Pathname.glob('{.*,*}').reject do |a|
['.', '..'].include?(a.to_s)
end
files.each do |path|
absolute_path = path.expand_path
castle_home = castle_dir(castle)
# make ignore dirs
ignore_dirs = []
subdirs.each do |subdir|
# ignore all parent of each line in subdir file
Pathname.new(subdir).ascend do |p|
ignore_dirs.push(p)
end
end
# ignore dirs written in subdir file
matched = false
ignore_dirs.uniq.each do |ignore_dir|
if absolute_path == castle_home.join(ignore_dir)
matched = true
break
end
end
next if matched
relative_dir = absolute_basedir.relative_path_from(castle_home)
home_path = home_dir.join(relative_dir).join(path)
yield(absolute_path, home_path)
end
end
end
def unsymlink_each(castle, basedir, subdirs)
each_file(castle, basedir, subdirs) do |_absolute_path, home_path|
rm_link home_path
end
end
def symlink_each(castle, basedir, subdirs)
each_file(castle, basedir, subdirs) do |absolute_path, home_path|
ln_s absolute_path, home_path
end
end
def setup_castle(path)
if path.join('.gitmodules').exist?
inside path do
git_submodule_init
git_submodule_update
end
end
rc(path)
end
end
end

12
lib/homesick/version.rb Normal file
View File

@@ -0,0 +1,12 @@
# -*- encoding : utf-8 -*-
module Homesick
# A representation of Homesick's version number in constants, including a
# String of the entire version number
module Version
MAJOR = 1
MINOR = 1
PATCH = 3
STRING = [MAJOR, MINOR, PATCH].compact.join('.')
end
end

824
spec/homesick_cli_spec.rb Normal file
View File

@@ -0,0 +1,824 @@
# -*- encoding : utf-8 -*-
require 'spec_helper'
require 'capture-output'
require 'pathname'
describe Homesick::CLI do
let(:home) { create_construct }
after { home.destroy! }
let(:castles) { home.directory('.homesick/repos') }
let(:homesick) { Homesick::CLI.new }
before { allow(homesick).to receive(:repos_dir).and_return(castles) }
describe 'smoke tests' do
context 'when running bin/homesick' do
before do
bin_path = Pathname.new(__FILE__).parent.parent
@output = `#{bin_path.expand_path}/bin/homesick`
end
it 'should output some text when bin/homesick is called' do
expect(@output.length).to be > 0
end
end
context 'when a git version that doesn\'t meet the minimum required is installed' do
before do
expect_any_instance_of(Homesick::Actions::GitActions).to receive(:`).and_return('git version 1.7.6')
end
it 'should raise an exception' do
output = Capture.stdout { expect { Homesick::CLI.new }.to raise_error SystemExit }
expect(output.chomp).to include(Homesick::Actions::GitActions::STRING)
end
end
context 'when a git version that is the same as the minimum required is installed' do
before do
expect_any_instance_of(Homesick::Actions::GitActions).to receive(:`).at_least(:once).and_return("git version #{Homesick::Actions::GitActions::STRING}")
end
it 'should not raise an exception' do
output = Capture.stdout { expect { Homesick::CLI.new }.not_to raise_error }
expect(output.chomp).not_to include(Homesick::Actions::GitActions::STRING)
end
end
context 'when a git version that is greater than the minimum required is installed' do
before do
expect_any_instance_of(Homesick::Actions::GitActions).to receive(:`).at_least(:once).and_return('git version 3.9.8')
end
it 'should not raise an exception' do
output = Capture.stdout { expect { Homesick::CLI.new }.not_to raise_error }
expect(output.chomp).not_to include(Homesick::Actions::GitActions::STRING)
end
end
end
describe 'clone' do
context 'has a .homesickrc' do
it 'runs the .homesickrc' do
somewhere = create_construct
local_repo = somewhere.directory('some_repo')
local_repo.file('.homesickrc') do |file|
file << "File.open(Dir.pwd + '/testing', 'w') do |f|
f.print 'testing'
end"
end
expect_any_instance_of(Thor::Shell::Basic).to receive(:yes?).with(be_a(String)).and_return(true)
expect(homesick).to receive(:say_status).with('eval', kind_of(Pathname))
homesick.clone local_repo
expect(castles.join('some_repo').join('testing')).to exist
end
end
context 'of a file' do
it 'symlinks existing directories' do
somewhere = create_construct
local_repo = somewhere.directory('wtf')
homesick.clone local_repo
expect(castles.join('wtf').readlink).to eq(local_repo)
end
context 'when it exists in a repo directory' do
before do
existing_castle = given_castle('existing_castle')
@existing_dir = existing_castle.parent
end
it 'raises an error' do
expect(homesick).not_to receive(:git_clone)
expect { homesick.clone @existing_dir.to_s }.to raise_error(/already cloned/i)
end
end
end
it 'clones git repo like file:///path/to.git' do
bare_repo = File.join(create_construct.to_s, 'dotfiles.git')
system "git init --bare #{bare_repo} >/dev/null 2>&1"
# Capture stderr to suppress message about cloning an empty repo.
Capture.stderr do
homesick.clone "file://#{bare_repo}"
end
expect(File.directory?(File.join(home.to_s, '.homesick/repos/dotfiles')))
.to be_truthy
end
it 'clones git repo like git://host/path/to.git' do
expect(homesick).to receive(:git_clone)
.with('git://github.com/technicalpickles/pickled-vim.git', destination: Pathname.new('pickled-vim'))
homesick.clone 'git://github.com/technicalpickles/pickled-vim.git'
end
it 'clones git repo like git@host:path/to.git' do
expect(homesick).to receive(:git_clone)
.with('git@github.com:technicalpickles/pickled-vim.git', destination: Pathname.new('pickled-vim'))
homesick.clone 'git@github.com:technicalpickles/pickled-vim.git'
end
it 'clones git repo like http://host/path/to.git' do
expect(homesick).to receive(:git_clone)
.with('http://github.com/technicalpickles/pickled-vim.git', destination: Pathname.new('pickled-vim'))
homesick.clone 'http://github.com/technicalpickles/pickled-vim.git'
end
it 'clones git repo like http://host/path/to' do
expect(homesick).to receive(:git_clone)
.with('http://github.com/technicalpickles/pickled-vim', destination: Pathname.new('pickled-vim'))
homesick.clone 'http://github.com/technicalpickles/pickled-vim'
end
it 'clones git repo like host-alias:repos.git' do
expect(homesick).to receive(:git_clone).with('gitolite:pickled-vim.git',
destination: Pathname.new('pickled-vim'))
homesick.clone 'gitolite:pickled-vim.git'
end
it 'throws an exception when trying to clone a malformed uri like malformed' do
expect(homesick).not_to receive(:git_clone)
expect { homesick.clone 'malformed' }.to raise_error
end
it 'clones a github repo' do
expect(homesick).to receive(:git_clone)
.with('https://github.com/wfarr/dotfiles.git', destination: Pathname.new('dotfiles'))
homesick.clone 'wfarr/dotfiles'
end
it 'accepts a destination', :focus do
expect(homesick).to receive(:git_clone)
.with('https://github.com/wfarr/dotfiles.git',
destination: Pathname.new('other-name'))
homesick.clone 'wfarr/dotfiles', 'other-name'
end
end
describe 'rc' do
let(:castle) { given_castle('glencairn') }
context 'when told to do so' do
before do
expect_any_instance_of(Thor::Shell::Basic).to receive(:yes?).with(be_a(String)).and_return(true)
end
it 'executes the .homesickrc' do
castle.file('.homesickrc') do |file|
file << "File.open(Dir.pwd + '/testing', 'w') do |f|
f.print 'testing'
end"
end
expect(homesick).to receive(:say_status).with('eval', kind_of(Pathname))
homesick.rc castle
expect(castle.join('testing')).to exist
end
end
context 'when options[:force] == true' do
let(:homesick) { Homesick::CLI.new [], force: true }
before do
expect_any_instance_of(Thor::Shell::Basic).to_not receive(:yes?)
end
it 'executes the .homesickrc' do
castle.file('.homesickrc') do |file|
file << "File.open(Dir.pwd + '/testing', 'w') do |f|
f.print 'testing'
end"
end
expect(homesick).to receive(:say_status).with('eval', kind_of(Pathname))
homesick.rc castle
expect(castle.join('testing')).to exist
end
end
context 'when told not to do so' do
before do
expect_any_instance_of(Thor::Shell::Basic).to receive(:yes?).with(be_a(String)).and_return(false)
end
it 'does not execute the .homesickrc' do
castle.file('.homesickrc') do |file|
file << "File.open(Dir.pwd + '/testing', 'w') do |f|
f.print 'testing'
end"
end
expect(homesick).to receive(:say_status).with('eval skip', /not evaling.+/, :blue)
homesick.rc castle
expect(castle.join('testing')).not_to exist
end
end
end
describe 'link_castle' do
let(:castle) { given_castle('glencairn') }
it 'links dotfiles from a castle to the home folder' do
dotfile = castle.file('.some_dotfile')
homesick.link('glencairn')
expect(home.join('.some_dotfile').readlink).to eq(dotfile)
end
it 'links non-dotfiles from a castle to the home folder' do
dotfile = castle.file('bin')
homesick.link('glencairn')
expect(home.join('bin').readlink).to eq(dotfile)
end
context 'when forced' do
let(:homesick) { Homesick::CLI.new [], force: true }
it 'can override symlinks to directories' do
somewhere_else = create_construct
existing_dotdir_link = home.join('.vim')
FileUtils.ln_s somewhere_else, existing_dotdir_link
dotdir = castle.directory('.vim')
homesick.link('glencairn')
expect(existing_dotdir_link.readlink).to eq(dotdir)
end
it 'can override existing directory' do
existing_dotdir = home.directory('.vim')
dotdir = castle.directory('.vim')
homesick.link('glencairn')
expect(existing_dotdir.readlink).to eq(dotdir)
end
end
context "with '.config' in .homesick_subdir" do
let(:castle) { given_castle('glencairn', ['.config']) }
it 'can symlink in sub directory' do
dotdir = castle.directory('.config')
dotfile = dotdir.file('.some_dotfile')
homesick.link('glencairn')
home_dotdir = home.join('.config')
expect(home_dotdir.symlink?).to eq(false)
expect(home_dotdir.join('.some_dotfile').readlink).to eq(dotfile)
end
end
context "with '.config/appA' in .homesick_subdir" do
let(:castle) { given_castle('glencairn', ['.config/appA']) }
it 'can symlink in nested sub directory' do
dotdir = castle.directory('.config').directory('appA')
dotfile = dotdir.file('.some_dotfile')
homesick.link('glencairn')
home_dotdir = home.join('.config').join('appA')
expect(home_dotdir.symlink?).to eq(false)
expect(home_dotdir.join('.some_dotfile').readlink).to eq(dotfile)
end
end
context "with '.config' and '.config/someapp' in .homesick_subdir" do
let(:castle) do
given_castle('glencairn', ['.config', '.config/someapp'])
end
it 'can symlink under both of .config and .config/someapp' do
config_dir = castle.directory('.config')
config_dotfile = config_dir.file('.some_dotfile')
someapp_dir = config_dir.directory('someapp')
someapp_dotfile = someapp_dir.file('.some_appfile')
homesick.link('glencairn')
home_config_dir = home.join('.config')
home_someapp_dir = home_config_dir.join('someapp')
expect(home_config_dir.symlink?).to eq(false)
expect(home_config_dir.join('.some_dotfile').readlink).to eq(config_dotfile)
expect(home_someapp_dir.symlink?).to eq(false)
expect(home_someapp_dir.join('.some_appfile').readlink).to eq(someapp_dotfile)
end
end
context 'when call with no castle name' do
let(:castle) { given_castle('dotfiles') }
it 'using default castle name: "dotfiles"' do
dotfile = castle.file('.some_dotfile')
homesick.link
expect(home.join('.some_dotfile').readlink).to eq(dotfile)
end
end
end
describe 'unlink' do
let(:castle) { given_castle('glencairn') }
it 'unlinks dotfiles in the home folder' do
castle.file('.some_dotfile')
homesick.link('glencairn')
homesick.unlink('glencairn')
expect(home.join('.some_dotfile')).not_to exist
end
it 'unlinks non-dotfiles from the home folder' do
castle.file('bin')
homesick.link('glencairn')
homesick.unlink('glencairn')
expect(home.join('bin')).not_to exist
end
context "with '.config' in .homesick_subdir" do
let(:castle) { given_castle('glencairn', ['.config']) }
it 'can unlink sub directories' do
castle.directory('.config').file('.some_dotfile')
homesick.link('glencairn')
homesick.unlink('glencairn')
home_dotdir = home.join('.config')
expect(home_dotdir).to exist
expect(home_dotdir.join('.some_dotfile')).not_to exist
end
end
context "with '.config/appA' in .homesick_subdir" do
let(:castle) { given_castle('glencairn', ['.config/appA']) }
it 'can unsymlink in nested sub directory' do
castle.directory('.config').directory('appA').file('.some_dotfile')
homesick.link('glencairn')
homesick.unlink('glencairn')
home_dotdir = home.join('.config').join('appA')
expect(home_dotdir).to exist
expect(home_dotdir.join('.some_dotfile')).not_to exist
end
end
context "with '.config' and '.config/someapp' in .homesick_subdir" do
let(:castle) do
given_castle('glencairn', ['.config', '.config/someapp'])
end
it 'can unsymlink under both of .config and .config/someapp' do
config_dir = castle.directory('.config')
config_dir.file('.some_dotfile')
config_dir.directory('someapp').file('.some_appfile')
homesick.link('glencairn')
homesick.unlink('glencairn')
home_config_dir = home.join('.config')
home_someapp_dir = home_config_dir.join('someapp')
expect(home_config_dir).to exist
expect(home_config_dir.join('.some_dotfile')).not_to exist
expect(home_someapp_dir).to exist
expect(home_someapp_dir.join('.some_appfile')).not_to exist
end
end
context 'when call with no castle name' do
let(:castle) { given_castle('dotfiles') }
it 'using default castle name: "dotfiles"' do
castle.file('.some_dotfile')
homesick.link
homesick.unlink
expect(home.join('.some_dotfile')).not_to exist
end
end
end
describe 'list' do
it 'says each castle in the castle directory' do
given_castle('zomg')
given_castle('wtf/zomg')
expect(homesick).to receive(:say_status)
.with('zomg', 'git://github.com/technicalpickles/zomg.git', :cyan)
expect(homesick).to receive(:say_status)
.with('wtf/zomg', 'git://github.com/technicalpickles/zomg.git', :cyan)
homesick.list
end
end
describe 'status' do
it 'says "nothing to commit" when there are no changes' do
given_castle('castle_repo')
text = Capture.stdout { homesick.status('castle_repo') }
expect(text).to match(%r{nothing to commit \(create/copy files and use "git add" to track\)$})
end
it 'says "Changes to be committed" when there are changes' do
given_castle('castle_repo')
some_rc_file = home.file '.some_rc_file'
homesick.track(some_rc_file.to_s, 'castle_repo')
text = Capture.stdout { homesick.status('castle_repo') }
expect(text).to match(%r{Changes to be committed:.*new file:\s*home\/.some_rc_file}m)
end
end
describe 'diff' do
it 'outputs an empty message when there are no changes to commit' do
given_castle('castle_repo')
some_rc_file = home.file '.some_rc_file'
homesick.track(some_rc_file.to_s, 'castle_repo')
Capture.stdout do
homesick.commit 'castle_repo', 'Adding a file to the test'
end
text = Capture.stdout { homesick.diff('castle_repo') }
expect(text).to eq('')
end
it 'outputs a diff message when there are changes to commit' do
given_castle('castle_repo')
some_rc_file = home.file '.some_rc_file'
homesick.track(some_rc_file.to_s, 'castle_repo')
Capture.stdout do
homesick.commit 'castle_repo', 'Adding a file to the test'
end
File.open(some_rc_file.to_s, 'w') do |file|
file.puts 'Some test text'
end
text = Capture.stdout { homesick.diff('castle_repo') }
expect(text).to match(/diff --git.+Some test text$/m)
end
end
describe 'show_path' do
it 'says the path of a castle' do
castle = given_castle('castle_repo')
expect(homesick).to receive(:say).with(castle.dirname)
homesick.show_path('castle_repo')
end
end
describe 'pull' do
it 'performs a pull, submodule init and update when the given castle exists' do
given_castle('castle_repo')
allow(homesick).to receive(:system).once.with('git pull --quiet')
allow(homesick).to receive(:system).once.with('git submodule --quiet init')
allow(homesick).to receive(:system).once.with('git submodule --quiet update --init --recursive >/dev/null 2>&1')
homesick.pull 'castle_repo'
end
it 'prints an error message when trying to pull a non-existant castle' do
expect(homesick).to receive('say_status').once
.with(:error,
/Could not pull castle_repo, expected .* exist and contain dotfiles/,
:red)
expect { homesick.pull 'castle_repo' }.to raise_error(SystemExit)
end
describe '--all' do
it 'pulls each castle when invoked with --all' do
given_castle('castle_repo')
given_castle('glencairn')
allow(homesick).to receive(:system).exactly(2).times.with('git pull --quiet')
allow(homesick).to receive(:system).exactly(2).times
.with('git submodule --quiet init')
allow(homesick).to receive(:system).exactly(2).times
.with('git submodule --quiet update --init --recursive >/dev/null 2>&1')
Capture.stdout do
Capture.stderr { homesick.invoke 'pull', [], all: true }
end
end
end
end
describe 'push' do
it 'performs a git push on the given castle' do
given_castle('castle_repo')
allow(homesick).to receive(:system).once.with('git push')
homesick.push 'castle_repo'
end
it 'prints an error message when trying to push a non-existant castle' do
expect(homesick).to receive('say_status').once
.with(:error, /Could not push castle_repo, expected .* exist and contain dotfiles/, :red)
expect { homesick.push 'castle_repo' }.to raise_error(SystemExit)
end
end
describe 'track' do
it 'moves the tracked file into the castle' do
castle = given_castle('castle_repo')
some_rc_file = home.file '.some_rc_file'
homesick.track(some_rc_file.to_s, 'castle_repo')
tracked_file = castle.join('.some_rc_file')
expect(tracked_file).to exist
expect(some_rc_file.readlink).to eq(tracked_file)
end
it 'handles files with parens' do
castle = given_castle('castle_repo')
some_rc_file = home.file 'Default (Linux).sublime-keymap'
homesick.track(some_rc_file.to_s, 'castle_repo')
tracked_file = castle.join('Default (Linux).sublime-keymap')
expect(tracked_file).to exist
expect(some_rc_file.readlink).to eq(tracked_file)
end
it 'tracks a file in nested folder structure' do
castle = given_castle('castle_repo')
some_nested_file = home.file('some/nested/file.txt')
homesick.track(some_nested_file.to_s, 'castle_repo')
tracked_file = castle.join('some/nested/file.txt')
expect(tracked_file).to exist
expect(some_nested_file.readlink).to eq(tracked_file)
end
it 'tracks a nested directory' do
castle = given_castle('castle_repo')
some_nested_dir = home.directory('some/nested/directory/')
homesick.track(some_nested_dir.to_s, 'castle_repo')
tracked_file = castle.join('some/nested/directory/')
expect(tracked_file).to exist
expect(some_nested_dir.realpath).to eq(tracked_file.realpath)
end
context 'when call with no castle name' do
it 'using default castle name: "dotfiles"' do
castle = given_castle('dotfiles')
some_rc_file = home.file '.some_rc_file'
homesick.track(some_rc_file.to_s)
tracked_file = castle.join('.some_rc_file')
expect(tracked_file).to exist
expect(some_rc_file.readlink).to eq(tracked_file)
end
end
describe 'commit' do
it 'has a commit message when the commit succeeds' do
given_castle('castle_repo')
some_rc_file = home.file '.a_random_rc_file'
homesick.track(some_rc_file.to_s, 'castle_repo')
text = Capture.stdout do
homesick.commit('castle_repo', 'Test message')
end
expect(text).to match(/^\[master \(root-commit\) \w+\] Test message/)
end
end
# Note that this is a test for the subdir_file related feature of track,
# not for the subdir_file method itself.
describe 'subdir_file' do
it 'adds the nested files parent to the subdir_file' do
castle = given_castle('castle_repo')
some_nested_file = home.file('some/nested/file.txt')
homesick.track(some_nested_file.to_s, 'castle_repo')
subdir_file = castle.parent.join(Homesick::SUBDIR_FILENAME)
File.open(subdir_file, 'r') do |f|
expect(f.readline).to eq("some/nested\n")
end
end
it 'does NOT add anything if the files parent is already listed' do
castle = given_castle('castle_repo')
some_nested_file = home.file('some/nested/file.txt')
other_nested_file = home.file('some/nested/other.txt')
homesick.track(some_nested_file.to_s, 'castle_repo')
homesick.track(other_nested_file.to_s, 'castle_repo')
subdir_file = castle.parent.join(Homesick::SUBDIR_FILENAME)
File.open(subdir_file, 'r') do |f|
expect(f.readlines.size).to eq(1)
end
end
it 'removes the parent of a tracked file from the subdir_file if the parent itself is tracked' do
castle = given_castle('castle_repo')
some_nested_file = home.file('some/nested/file.txt')
nested_parent = home.directory('some/nested/')
homesick.track(some_nested_file.to_s, 'castle_repo')
homesick.track(nested_parent.to_s, 'castle_repo')
subdir_file = castle.parent.join(Homesick::SUBDIR_FILENAME)
File.open(subdir_file, 'r') do |f|
f.each_line { |line| expect(line).not_to eq("some/nested\n") }
end
end
end
end
describe 'destroy' do
it 'removes the symlink files' do
expect_any_instance_of(Thor::Shell::Basic).to receive(:yes?).and_return('y')
given_castle('stronghold')
some_rc_file = home.file '.some_rc_file'
homesick.track(some_rc_file.to_s, 'stronghold')
homesick.destroy('stronghold')
expect(some_rc_file).not_to be_exist
end
it 'deletes the cloned repository' do
expect_any_instance_of(Thor::Shell::Basic).to receive(:yes?).and_return('y')
castle = given_castle('stronghold')
some_rc_file = home.file '.some_rc_file'
homesick.track(some_rc_file.to_s, 'stronghold')
homesick.destroy('stronghold')
expect(castle).not_to be_exist
end
end
describe 'cd' do
it "cd's to the root directory of the given castle" do
given_castle('castle_repo')
expect(homesick).to receive('inside').once.with(kind_of(Pathname)).and_yield
expect(homesick).to receive('system').once.with(ENV['SHELL'])
Capture.stdout { homesick.cd 'castle_repo' }
end
it 'returns an error message when the given castle does not exist' do
expect(homesick).to receive('say_status').once
.with(:error, /Could not cd castle_repo, expected .* exist and contain dotfiles/, :red)
expect { homesick.cd 'castle_repo' }.to raise_error(SystemExit)
end
end
describe 'open' do
it 'opens the system default editor in the root of the given castle' do
# Make sure calls to ENV use default values for most things...
allow(ENV).to receive(:[]).and_call_original
# Set a default value for 'EDITOR' just in case none is set
allow(ENV).to receive(:[]).with('EDITOR').and_return('vim')
given_castle 'castle_repo'
expect(homesick).to receive('inside').once.with(kind_of(Pathname)).and_yield
expect(homesick).to receive('system').once.with('vim .')
Capture.stdout { homesick.open 'castle_repo' }
end
it 'returns an error message when the $EDITOR environment variable is not set' do
# Set the default editor to make sure it fails.
allow(ENV).to receive(:[]).with('EDITOR').and_return(nil)
expect(homesick).to receive('say_status').once
.with(:error, 'The $EDITOR environment variable must be set to use this command', :red)
expect { homesick.open 'castle_repo' }.to raise_error(SystemExit)
end
it 'returns an error message when the given castle does not exist' do
# Set a default just in case none is set
allow(ENV).to receive(:[]).with('EDITOR').and_return('vim')
allow(homesick).to receive('say_status').once
.with(:error, /Could not open castle_repo, expected .* exist and contain dotfiles/, :red)
expect { homesick.open 'castle_repo' }.to raise_error(SystemExit)
end
end
describe 'version' do
it 'prints the current version of homesick' do
text = Capture.stdout { homesick.version }
expect(text.chomp).to match(/#{Regexp.escape(Homesick::Version::STRING)}/)
end
end
describe 'exec' do
before do
given_castle 'castle_repo'
end
it 'executes a single command with no arguments inside a given castle' do
allow(homesick).to receive('inside').once.with(kind_of(Pathname)).and_yield
allow(homesick).to receive('say_status').once
.with(be_a(String), be_a(String), :green)
allow(homesick).to receive('system').once.with('ls')
Capture.stdout { homesick.exec 'castle_repo', 'ls' }
end
it 'executes a single command with arguments inside a given castle' do
allow(homesick).to receive('inside').once.with(kind_of(Pathname)).and_yield
allow(homesick).to receive('say_status').once
.with(be_a(String), be_a(String), :green)
allow(homesick).to receive('system').once.with('ls -la')
Capture.stdout { homesick.exec 'castle_repo', 'ls', '-la' }
end
it 'raises an error when the method is called without a command' do
allow(homesick).to receive('say_status').once
.with(:error, be_a(String), :red)
allow(homesick).to receive('exit').once.with(1)
Capture.stdout { homesick.exec 'castle_repo' }
end
context 'pretend' do
it 'does not execute a command when the pretend option is passed' do
allow(homesick).to receive('say_status').once
.with(be_a(String), match(/.*Would execute.*/), :green)
expect(homesick).to receive('system').never
Capture.stdout { homesick.invoke 'exec', %w(castle_repo ls -la), pretend: true }
end
end
context 'quiet' do
it 'does not print status information when quiet is passed' do
expect(homesick).to receive('say_status').never
allow(homesick).to receive('system').once
.with('ls -la')
Capture.stdout { homesick.invoke 'exec', %w(castle_repo ls -la), quiet: true }
end
end
end
describe 'exec_all' do
before do
given_castle 'castle_repo'
given_castle 'another_castle_repo'
end
it 'executes a command without arguments inside the root of each cloned castle' do
allow(homesick).to receive('inside_each_castle').exactly(:twice).and_yield('castle_repo').and_yield('another_castle_repo')
allow(homesick).to receive('say_status').at_least(:once)
.with(be_a(String), be_a(String), :green)
allow(homesick).to receive('system').at_least(:once).with('ls')
Capture.stdout { homesick.exec_all 'ls' }
end
it 'executes a command with arguments inside the root of each cloned castle' do
allow(homesick).to receive('inside_each_castle').exactly(:twice).and_yield('castle_repo').and_yield('another_castle_repo')
allow(homesick).to receive('say_status').at_least(:once)
.with(be_a(String), be_a(String), :green)
allow(homesick).to receive('system').at_least(:once).with('ls -la')
Capture.stdout { homesick.exec_all 'ls', '-la' }
end
it 'raises an error when the method is called without a command' do
allow(homesick).to receive('say_status').once
.with(:error, be_a(String), :red)
allow(homesick).to receive('exit').once.with(1)
Capture.stdout { homesick.exec_all }
end
context 'pretend' do
it 'does not execute a command when the pretend option is passed' do
allow(homesick).to receive('say_status').at_least(:once)
.with(be_a(String), match(/.*Would execute.*/), :green)
expect(homesick).to receive('system').never
Capture.stdout { homesick.invoke 'exec_all', %w(ls -la), pretend: true }
end
end
context 'quiet' do
it 'does not print status information when quiet is passed' do
expect(homesick).to receive('say_status').never
allow(homesick).to receive('system').at_least(:once)
.with('ls -la')
Capture.stdout { homesick.invoke 'exec_all', %w(ls -la), quiet: true }
end
end
end
end

View File

@@ -1,493 +0,0 @@
require 'spec_helper'
describe 'homesick' do
let(:home) { create_construct }
after { home.destroy! }
let(:castles) { home.directory('.homesick/repos') }
let(:homesick) { Homesick.new }
before { homesick.stub(:repos_dir).and_return(castles) }
describe 'clone' do
context 'has a .homesickrc' do
it 'should run the .homesickrc' do
somewhere = create_construct
local_repo = somewhere.directory('some_repo')
local_repo.file('.homesickrc') do |file|
file << "File.open(Dir.pwd + '/testing', 'w') { |f| f.print 'testing' }"
end
expect($stdout).to receive(:print)
expect($stdin).to receive(:gets).and_return('y')
homesick.clone local_repo
castles.join('some_repo').join('testing').should exist
end
end
context 'of a file' do
it 'should symlink existing directories' do
somewhere = create_construct
local_repo = somewhere.directory('wtf')
homesick.clone local_repo
castles.join('wtf').readlink.should == local_repo
end
context 'when it exists in a repo directory' do
before do
existing_castle = given_castle('existing_castle')
@existing_dir = existing_castle.parent
end
it 'should raise an error' do
homesick.should_not_receive(:git_clone)
expect { homesick.clone @existing_dir.to_s }.to raise_error(/already cloned/i)
end
end
end
it 'should clone git repo like file:///path/to.git' do
bare_repo = File.join(create_construct.to_s, 'dotfiles.git')
system "git init --bare #{bare_repo} >/dev/null 2>&1"
homesick.clone "file://#{bare_repo}"
File.directory?(File.join(home.to_s, '.homesick/repos/dotfiles')).should be_true
end
it 'should clone git repo like git://host/path/to.git' do
homesick.should_receive(:git_clone).with('git://github.com/technicalpickles/pickled-vim.git')
homesick.clone 'git://github.com/technicalpickles/pickled-vim.git'
end
it 'should clone git repo like git@host:path/to.git' do
homesick.should_receive(:git_clone).with('git@github.com:technicalpickles/pickled-vim.git')
homesick.clone 'git@github.com:technicalpickles/pickled-vim.git'
end
it 'should clone git repo like http://host/path/to.git' do
homesick.should_receive(:git_clone).with('http://github.com/technicalpickles/pickled-vim.git')
homesick.clone 'http://github.com/technicalpickles/pickled-vim.git'
end
it 'should clone git repo like http://host/path/to' do
homesick.should_receive(:git_clone).with('http://github.com/technicalpickles/pickled-vim')
homesick.clone 'http://github.com/technicalpickles/pickled-vim'
end
it 'should clone git repo like host-alias:repos.git' do
homesick.should_receive(:git_clone).with('gitolite:pickled-vim.git')
homesick.clone 'gitolite:pickled-vim.git'
end
it 'should throw an exception when trying to clone a malformed uri like malformed' do
homesick.should_not_receive(:git_clone)
expect { homesick.clone 'malformed' }.to raise_error
end
it 'should clone a github repo' do
homesick.should_receive(:git_clone).with('https://github.com/wfarr/dotfiles.git', :destination => Pathname.new('wfarr/dotfiles'))
homesick.clone 'wfarr/dotfiles'
end
end
describe 'rc' do
let(:castle) { given_castle('glencairn') }
context 'when told to do so' do
before do
expect($stdout).to receive(:print)
expect($stdin).to receive(:gets).and_return('y')
end
it 'executes the .homesickrc' do
castle.file('.homesickrc') do |file|
file << "File.open(Dir.pwd + '/testing', 'w') { |f| f.print 'testing' }"
end
homesick.rc castle
castle.join('testing').should exist
end
end
context 'when told not to do so' do
before do
expect($stdout).to receive(:print)
expect($stdin).to receive(:gets).and_return('n')
end
it 'does not execute the .homesickrc' do
castle.file('.homesickrc') do |file|
file << "File.open(Dir.pwd + '/testing', 'w') { |f| f.print 'testing' }"
end
homesick.rc castle
castle.join('testing').should_not exist
end
end
end
describe 'symlink' do
let(:castle) { given_castle('glencairn') }
it 'links dotfiles from a castle to the home folder' do
dotfile = castle.file('.some_dotfile')
homesick.symlink('glencairn')
home.join('.some_dotfile').readlink.should == dotfile
end
it 'links non-dotfiles from a castle to the home folder' do
dotfile = castle.file('bin')
homesick.symlink('glencairn')
home.join('bin').readlink.should == dotfile
end
context 'when forced' do
let(:homesick) { Homesick.new [], :force => true }
it 'can override symlinks to directories' do
somewhere_else = create_construct
existing_dotdir_link = home.join('.vim')
FileUtils.ln_s somewhere_else, existing_dotdir_link
dotdir = castle.directory('.vim')
homesick.symlink('glencairn')
existing_dotdir_link.readlink.should == dotdir
end
it 'can override existing directory' do
existing_dotdir = home.directory('.vim')
dotdir = castle.directory('.vim')
homesick.symlink('glencairn')
existing_dotdir.readlink.should == dotdir
end
end
context "with '.config' in .homesick_subdir" do
let(:castle) { given_castle('glencairn', ['.config']) }
it 'can symlink in sub directory' do
dotdir = castle.directory('.config')
dotfile = dotdir.file('.some_dotfile')
homesick.symlink('glencairn')
home_dotdir = home.join('.config')
home_dotdir.symlink?.should be == false
home_dotdir.join('.some_dotfile').readlink.should == dotfile
end
end
context "with '.config/appA' in .homesick_subdir" do
let(:castle) { given_castle('glencairn', ['.config/appA']) }
it 'can symlink in nested sub directory' do
dotdir = castle.directory('.config').directory('appA')
dotfile = dotdir.file('.some_dotfile')
homesick.symlink('glencairn')
home_dotdir = home.join('.config').join('appA')
home_dotdir.symlink?.should be == false
home_dotdir.join('.some_dotfile').readlink.should == dotfile
end
end
context "with '.config' and '.config/someapp' in .homesick_subdir" do
let(:castle) { given_castle('glencairn', ['.config', '.config/someapp']) }
it 'can symlink under both of .config and .config/someapp' do
config_dir = castle.directory('.config')
config_dotfile = config_dir.file('.some_dotfile')
someapp_dir = config_dir.directory('someapp')
someapp_dotfile = someapp_dir.file('.some_appfile')
homesick.symlink('glencairn')
home_config_dir = home.join('.config')
home_someapp_dir = home_config_dir.join('someapp')
home_config_dir.symlink?.should be == false
home_config_dir.join('.some_dotfile').readlink.should be == config_dotfile
home_someapp_dir.symlink?.should be == false
home_someapp_dir.join('.some_appfile').readlink.should == someapp_dotfile
end
end
context "when call with no castle name" do
let(:castle) { given_castle('dotfiles') }
it 'using default castle name: "dotfiles"' do
dotfile = castle.file('.some_dotfile')
homesick.symlink
home.join('.some_dotfile').readlink.should == dotfile
end
end
end
describe 'unlink' do
let(:castle) { given_castle('glencairn') }
it 'unlinks dotfiles in the home folder' do
castle.file('.some_dotfile')
homesick.symlink('glencairn')
homesick.unlink('glencairn')
home.join('.some_dotfile').should_not exist
end
it 'unlinks non-dotfiles from the home folder' do
castle.file('bin')
homesick.symlink('glencairn')
homesick.unlink('glencairn')
home.join('bin').should_not exist
end
context "with '.config' in .homesick_subdir" do
let(:castle) { given_castle('glencairn', ['.config']) }
it 'can unlink sub directories' do
castle.directory('.config').file('.some_dotfile')
homesick.symlink('glencairn')
homesick.unlink('glencairn')
home_dotdir = home.join('.config')
home_dotdir.should exist
home_dotdir.join('.some_dotfile').should_not exist
end
end
context "with '.config/appA' in .homesick_subdir" do
let(:castle) { given_castle('glencairn', ['.config/appA']) }
it 'can unsymlink in nested sub directory' do
castle.directory('.config').directory('appA').file('.some_dotfile')
homesick.symlink('glencairn')
homesick.unlink('glencairn')
home_dotdir = home.join('.config').join('appA')
home_dotdir.should exist
home_dotdir.join('.some_dotfile').should_not exist
end
end
context "with '.config' and '.config/someapp' in .homesick_subdir" do
let(:castle) { given_castle('glencairn', ['.config', '.config/someapp']) }
it 'can unsymlink under both of .config and .config/someapp' do
config_dir = castle.directory('.config')
config_dir.file('.some_dotfile')
config_dir.directory('someapp').file('.some_appfile')
homesick.symlink('glencairn')
homesick.unlink('glencairn')
home_config_dir = home.join('.config')
home_someapp_dir = home_config_dir.join('someapp')
home_config_dir.should exist
home_config_dir.join('.some_dotfile').should_not exist
home_someapp_dir.should exist
home_someapp_dir.join('.some_appfile').should_not exist
end
end
context "when call with no castle name" do
let(:castle) { given_castle('dotfiles') }
it 'using default castle name: "dotfiles"' do
castle.file('.some_dotfile')
homesick.symlink
homesick.unlink
home.join('.some_dotfile').should_not exist
end
end
end
describe 'list' do
it 'should say each castle in the castle directory' do
given_castle('zomg')
given_castle('wtf/zomg')
homesick.should_receive(:say_status).with('zomg', 'git://github.com/technicalpickles/zomg.git', :cyan)
homesick.should_receive(:say_status).with('wtf/zomg', 'git://github.com/technicalpickles/zomg.git', :cyan)
homesick.list
end
end
describe 'status' do
xit 'needs testing'
end
describe 'diff' do
xit 'needs testing'
end
describe 'show_path' do
it 'should say the path of a castle' do
castle = given_castle('castle_repo')
homesick.should_receive(:say).with(castle.dirname)
homesick.show_path('castle_repo')
end
end
describe 'pull' do
xit 'needs testing'
describe '--all' do
xit 'needs testing'
end
end
describe 'commit' do
xit 'needs testing'
end
describe 'push' do
xit 'needs testing'
end
describe 'track' do
it 'should move the tracked file into the castle' do
castle = given_castle('castle_repo')
some_rc_file = home.file '.some_rc_file'
homesick.track(some_rc_file.to_s, 'castle_repo')
tracked_file = castle.join('.some_rc_file')
tracked_file.should exist
some_rc_file.readlink.should == tracked_file
end
it 'should handle files with parens' do
castle = given_castle('castle_repo')
some_rc_file = home.file 'Default (Linux).sublime-keymap'
homesick.track(some_rc_file.to_s, 'castle_repo')
tracked_file = castle.join('Default (Linux).sublime-keymap')
tracked_file.should exist
some_rc_file.readlink.should == tracked_file
end
it 'should track a file in nested folder structure' do
castle = given_castle('castle_repo')
some_nested_file = home.file('some/nested/file.txt')
homesick.track(some_nested_file.to_s, 'castle_repo')
tracked_file = castle.join('some/nested/file.txt')
tracked_file.should exist
some_nested_file.readlink.should == tracked_file
end
it 'should track a nested directory' do
castle = given_castle('castle_repo')
some_nested_dir = home.directory('some/nested/directory/')
homesick.track(some_nested_dir.to_s, 'castle_repo')
tracked_file = castle.join('some/nested/directory/')
tracked_file.should exist
some_nested_dir.realpath.should == tracked_file.realpath
end
context "when call with no castle name" do
it 'using default castle name: "dotfiles"' do
castle = given_castle('dotfiles')
some_rc_file = home.file '.some_rc_file'
homesick.track(some_rc_file.to_s)
tracked_file = castle.join('.some_rc_file')
tracked_file.should exist
some_rc_file.readlink.should == tracked_file
end
end
describe 'subdir_file' do
it 'should add the nested files parent to the subdir_file' do
castle = given_castle('castle_repo')
some_nested_file = home.file('some/nested/file.txt')
homesick.track(some_nested_file.to_s, 'castle_repo')
subdir_file = castle.parent.join(Homesick::SUBDIR_FILENAME)
File.open(subdir_file, 'r') do |f|
f.readline.should == "some/nested\n"
end
end
it 'should NOT add anything if the files parent is already listed' do
castle = given_castle('castle_repo')
some_nested_file = home.file('some/nested/file.txt')
other_nested_file = home.file('some/nested/other.txt')
homesick.track(some_nested_file.to_s, 'castle_repo')
homesick.track(other_nested_file.to_s, 'castle_repo')
subdir_file = castle.parent.join(Homesick::SUBDIR_FILENAME)
File.open(subdir_file, 'r') do |f|
f.readlines.size.should == 1
end
end
it 'should remove the parent of a tracked file from the subdir_file if the parent itself is tracked' do
castle = given_castle('castle_repo')
some_nested_file = home.file('some/nested/file.txt')
nested_parent = home.directory('some/nested/')
homesick.track(some_nested_file.to_s, 'castle_repo')
homesick.track(nested_parent.to_s, 'castle_repo')
subdir_file = castle.parent.join(Homesick::SUBDIR_FILENAME)
File.open(subdir_file, 'r') do |f|
f.each_line { |line| line.should_not == "some/nested\n" }
end
end
end
end
end

View File

@@ -1,19 +1,24 @@
require 'coveralls'
Coveralls.wear!
$LOAD_PATH.unshift(File.dirname(__FILE__)) $LOAD_PATH.unshift(File.dirname(__FILE__))
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
require 'homesick' require 'homesick'
require 'rspec' require 'rspec'
require 'rspec/autorun' require 'test_construct'
require 'construct' require 'tempfile'
RSpec.configure do |config| RSpec.configure do |config|
config.include Construct::Helpers config.include TestConstruct::Helpers
config.expect_with(:rspec) { |c| c.syntax = :expect }
config.before { ENV['HOME'] = home.to_s } config.before { ENV['HOME'] = home.to_s }
config.before { silence! } config.before { silence! }
def silence! def silence!
homesick.stub(:say_status) allow(homesick).to receive(:say_status)
end end
def given_castle(path, subdirs = []) def given_castle(path, subdirs = [])
@@ -21,11 +26,13 @@ RSpec.configure do |config|
castles.directory(path) do |castle| castles.directory(path) do |castle|
Dir.chdir(castle) do Dir.chdir(castle) do
system 'git init >/dev/null 2>&1' system 'git init >/dev/null 2>&1'
system 'git config user.email "test@test.com"'
system 'git config user.name "Test Name"'
system "git remote add origin git://github.com/technicalpickles/#{name}.git >/dev/null 2>&1" system "git remote add origin git://github.com/technicalpickles/#{name}.git >/dev/null 2>&1"
if subdirs if subdirs
subdir_file = castle.join(Homesick::SUBDIR_FILENAME) subdir_file = castle.join(Homesick::SUBDIR_FILENAME)
subdirs.each do |subdir| subdirs.each do |subdir|
system "echo #{subdir} >> #{subdir_file}" File.open(subdir_file, 'a') { |file| file.write "\n#{subdir}\n" }
end end
end end
return castle.directory('home') return castle.directory('home')