jeni 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. data/Bugs.rdoc +6 -0
  2. data/Gemfile +14 -0
  3. data/History.txt +9 -0
  4. data/Intro.txt +3 -0
  5. data/LICENCE.rdoc +159 -0
  6. data/README.md +188 -0
  7. data/lib/jeni.rb +374 -0
  8. data/lib/jeni/actions.rb +400 -0
  9. data/lib/jeni/errors.rb +22 -0
  10. data/lib/jeni/io.rb +71 -0
  11. data/lib/jeni/options.rb +68 -0
  12. data/lib/jeni/optparse.rb +84 -0
  13. data/lib/jeni/utils.rb +181 -0
  14. data/lib/jeni/version.rb +13 -0
  15. data/spec/jeni_spec.rb +85 -0
  16. data/spec/jeni_utils_spec.rb +297 -0
  17. data/spec/spec_helper.rb +26 -0
  18. data/test/examples/source/coati.haml.conf +37 -0
  19. data/test/examples/source/executable +3 -0
  20. data/test/examples/source/jenny-diff.rb +64 -0
  21. data/test/examples/source/jenny.rb +63 -0
  22. data/test/examples/source/shebang.rb +3 -0
  23. data/test/examples/source/subfiles/subfile_1.rb +63 -0
  24. data/test/examples/source/template.haml.rb +10 -0
  25. data/test/examples/target/archive/coati.haml.conf +37 -0
  26. data/test/examples/target/archive/executable +3 -0
  27. data/test/examples/target/archive/jenny-diff.rb +64 -0
  28. data/test/examples/target/archive/jenny.rb +63 -0
  29. data/test/examples/target/archive/shebang.rb +3 -0
  30. data/test/examples/target/archive/subfiles/subfile_1.rb +63 -0
  31. data/test/examples/target/archive/template.haml.rb +10 -0
  32. data/test/examples/target/jenny.rb +63 -0
  33. data/test/examples/target/jenny_link.rb +63 -0
  34. data/test/examples/target2/coati.conf +36 -0
  35. data/test/examples/target2/coati.haml.conf +37 -0
  36. data/test/examples/target2/executable +3 -0
  37. data/test/examples/target2/jenny-diff.rb +64 -0
  38. data/test/examples/target2/jenny.rb +63 -0
  39. data/test/examples/target2/jenny_link.rb +63 -0
  40. data/test/examples/target2/jenny_template.rb +10 -0
  41. data/test/examples/target2/jenny_test.rb +63 -0
  42. data/test/examples/target2/shebang.rb +3 -0
  43. data/test/examples/target2/std_template.rb +12 -0
  44. data/test/examples/target2/template.haml.rb +10 -0
  45. data/test/examples/test1.rb +30 -0
  46. data/test/examples/test2.rb +27 -0
  47. data/test/examples/test_args +24 -0
  48. data/test/examples/test_users +16 -0
  49. metadata +162 -0
@@ -0,0 +1,6 @@
1
+ == Outstanding Bugs and Change Requests for Jeni
2
+
3
+ [14-Sep-2012]
4
+
5
+ * Crashes if it tries to read a link that is a file
6
+ * chown and chgrp actions etc are blind to whether they are needed or not!
data/Gemfile ADDED
@@ -0,0 +1,14 @@
1
+ # @markup ruby
2
+ # Jeni Gemfile
3
+
4
+ # enable simple colours for terminal output
5
+ gem 'colored'
6
+
7
+ # ruby interface to diff
8
+ gem 'diffy'
9
+
10
+ # to support testing:
11
+ #gem 'rspec-mocks'
12
+
13
+ # to support templating
14
+ gem 'haml'
@@ -0,0 +1,9 @@
1
+ # @markup rdoc
2
+ # @title Change History
3
+
4
+ == History
5
+
6
+
7
+ [jeni-0.2.0 04-Oct-2012]
8
+
9
+ Application forked from local 'jeni' due to Gam-space name clash.
@@ -0,0 +1,3 @@
1
+ A simple alternative to rubigen and thor that can be used to create a post-install script
2
+ for a gem needing more than the standard file ops covered by rubygems. It can also be
3
+ used for straight directories instead of gems, if required.
@@ -0,0 +1,159 @@
1
+ Copyright (c) 2012 Robert Sharp
2
+
3
+ This software is licensed for use under the Open Software Licence v. 3.0
4
+ The terms of this licence can be found at http://www.opensource.org/licenses/osl-3.0.php
5
+ and below. Under the terms of this licence, all derivative works
6
+ must themselves be licensed under the Open Software Licence v. 3.0
7
+
8
+ Open Source Initiative OSI - The Open Software Licence 3.0:Licensing
9
+ [OSI Approved Licence]
10
+ Open Software Licence ("OSL") v. 3.0
11
+
12
+ This Open Software Licence (the "Licence") applies to any original work of authorship
13
+ (the "Original Work") whose owner (the "Licensor") has placed the following licensing
14
+ notice adjacent to the copyright notice for the Original Work:
15
+
16
+ Licensed under the Open Software Licence version 3.0
17
+
18
+ 1) Grant of Copyright Licence. Licensor grants You a worldwide, royalty-free, non-exclusive,
19
+ sublicensable license, for the duration of the copyright, to do the following:
20
+
21
+ a) to reproduce the Original Work in copies, either alone or as part of a collective work;
22
+
23
+ b) to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby
24
+ creating derivative works ("Derivative Works") based upon the Original Work;
25
+
26
+ c) to distribute or communicate copies of the Original Work and Derivative Works to
27
+ the public, with the proviso that copies of Original Work or Derivative Works that
28
+ You distribute or communicate shall be licensed under this Open Software Licence;
29
+
30
+ d) to perform the Original Work publicly; and
31
+
32
+ e) to display the Original Work publicly.
33
+
34
+ 2) Grant of Patent Licence. Licensor grants You a worldwide, royalty-free, non-exclusive,
35
+ sublicensable license, under patent claims owned or controlled by the Licensor that are
36
+ embodied in the Original Work as furnished by the Licensor, for the duration of the
37
+ patents, to make, use, sell, offer for sale, have made, and import the Original Work
38
+ and Derivative Works.
39
+
40
+ 3) Grant of Source Code Licence. The term "Source Code" means the preferred form of
41
+ the Original Work for making modifications to it and all available documentation
42
+ describing how to modify the Original Work. Licensor agrees to provide a machine-readable
43
+ copy of the Source Code of the Original Work along with each copy of the Original Work
44
+ that Licensor distributes. Licensor reserves the right to satisfy this obligation by
45
+ placing a machine-readable copy of the Source Code in an information repository reasonably
46
+ calculated to permit inexpensive and convenient access by You for as long as Licensor
47
+ continues to distribute the Original Work.
48
+
49
+ 4) Exclusions From Licence Grant. Neither the names of Licensor, nor the names of
50
+ any contributors to the Original Work, nor any of their trademarks or service marks,
51
+ may be used to endorse or promote products derived from this Original Work without
52
+ express prior permission of the Licensor. Except as expressly stated herein, nothing
53
+ in this Licence grants any license to Licensor's trademarks, copyrights, patents,
54
+ trade secrets or any other intellectual property. No patent license is granted to
55
+ make, use, sell, offer for sale, have made, or import embodiments of any patent claims
56
+ other than the licensed claims defined in Section 2. No license is granted to the
57
+ trademarks of Licensor even if such marks are included in the Original Work. Nothing
58
+ in this Licence shall be interpreted to prohibit Licensor from licensing under terms
59
+ different from this Licence any Original Work that Licensor otherwise would have a
60
+ right to license.
61
+
62
+ 5) External Deployment. The term "External Deployment" means the use, distribution,
63
+ or communication of the Original Work or Derivative Works in any way such that the
64
+ Original Work or Derivative Works may be used by anyone other than You, whether those
65
+ works are distributed or communicated to those persons or made available as an application
66
+ intended for use over a network. As an express condition for the grants of license hereunder,
67
+ You must treat any External Deployment by You of the Original Work or a Derivative Work
68
+ as a distribution under section 1(c).
69
+
70
+ 6) Attribution Rights. You must retain, in the Source Code of any Derivative Works
71
+ that You create, all copyright, patent, or trademark notices from the Source Code of
72
+ the Original Work, as well as any notices of licensing and any descriptive text identified
73
+ therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works
74
+ that You create to carry a prominent Attribution Notice reasonably calculated to inform
75
+ recipients that You have modified the Original Work.
76
+
77
+ 7) Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright
78
+ in and to the Original Work and the patent rights granted herein by Licensor are owned by
79
+ the Licensor or are sublicensed to You under the terms of this Licence with the permission
80
+ of the contributor(s) of those copyrights and patent rights. Except as expressly stated in
81
+ the immediately preceding sentence, the Original Work is provided under this Licence on an
82
+ "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation,
83
+ the warranties of non-infringement, merchantability or fitness for a particular purpose.
84
+ THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY
85
+ constitutes an essential part of this Licence. No license to the Original Work is granted
86
+ by this Licence except under this disclaimer.
87
+
88
+ 8) Limitation of Liability. Under no circumstances and under no legal theory, whether
89
+ in tort (including negligence), contract, or otherwise, shall the Licensor be liable
90
+ to anyone for any indirect, special, incidental, or consequential damages of any character
91
+ arising as a result of this Licence or the use of the Original Work including, without
92
+ limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction,
93
+ or any and all other commercial damages or losses. This limitation of liability shall not
94
+ apply to the extent applicable law prohibits such limitation.
95
+
96
+ 9) Acceptance and Termination. If, at any time, You expressly assented to this Licence,
97
+ that assent indicates your clear and irrevocable acceptance of this Licence and all of its
98
+ terms and conditions. If You distribute or communicate copies of the Original Work or a
99
+ Derivative Work, You must make a reasonable effort under the circumstances to obtain the
100
+ express assent of recipients to the terms of this Licence. This Licence conditions your
101
+ rights to undertake the activities listed in Section 1, including your right to create
102
+ Derivative Works based upon the Original Work, and doing so without honoring these
103
+ terms and conditions is prohibited by copyright law and international treaty. Nothing
104
+ in this Licence is intended to affect copyright exceptions and limitations (including
105
+ "fair use" or "fair dealing"). This Licence shall terminate immediately and You may no
106
+ longer exercise any of the rights granted to You by this Licence upon your failure to
107
+ honor the conditions in Section 1(c).
108
+
109
+ 10) Termination for Patent Action. This Licence shall terminate automatically and
110
+ You may no longer exercise any of the rights granted to You by this Licence as of
111
+ the date You commence an action, including a cross-claim or counterclaim, against
112
+ Licensor or any licensee alleging that the Original Work infringes a patent. This
113
+ termination provision shall not apply for an action alleging patent infringement
114
+ by combinations of the Original Work with other software or hardware.
115
+
116
+ 11) Jurisdiction, Venue and Governing Law. Any action or suit relating to this Licence
117
+ may be brought only in the courts of a jurisdiction wherein the Licensor resides or
118
+ in which Licensor conducts its primary business, and under the laws of that jurisdiction
119
+ excluding its conflict-of-law provisions. The application of the United Nations Convention
120
+ on Contracts for the International Sale of Goods is expressly excluded. Any use of
121
+ the Original Work outside the scope of this Licence or after its termination shall be
122
+ subject to the requirements and penalties of copyright or patent law in the appropriate
123
+ jurisdiction. This section shall survive the termination of this Licence.
124
+
125
+ 12) Attorneys' Fees. In any action to enforce the terms of this Licence or seeking
126
+ damages relating thereto, the prevailing party shall be entitled to recover its costs
127
+ and expenses, including, without limitation, reasonable attorneys' fees and costs
128
+ incurred in connection with such action, including any appeal of such action. This
129
+ section shall survive the termination of this Licence.
130
+
131
+ 13) Miscellaneous. If any provision of this Licence is held to be unenforceable,
132
+ such provision shall be reformed only to the extent necessary to make it enforceable.
133
+
134
+ 14) Definition of "You" in This Licence. "You" throughout this Licence, whether in
135
+ upper or lower case, means an individual or a legal entity exercising rights under,
136
+ and complying with all of the terms of, this Licence. For legal entities, "You" includes
137
+ any entity that controls, is controlled by, or is under common control with you. For
138
+ purposes of this definition, "control" means (i) the power, direct or indirect, to cause
139
+ the direction or management of such entity, whether by contract or otherwise, or (ii)
140
+ ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial
141
+ ownership of such entity.
142
+
143
+ 15) Right to Use. You may use the Original Work in all ways not otherwise restricted or
144
+ conditioned by this Licence or by law, and Licensor promises not to interfere with or be
145
+ responsible for such uses by You.
146
+
147
+ 16) Modification of This Licence. This Licence is Copyright © 2005 Lawrence Rosen.
148
+ Permission is granted to copy, distribute, or communicate this Licence without modification.
149
+ Nothing in this Licence permits You to modify this Licence as applied to the Original Work
150
+ or to Derivative Works. However, You may modify the text of this Licence and copy, distribute
151
+ or communicate your modified version (the "Modified Licence") and apply it to other original
152
+ works of authorship subject to the following conditions: (i) You may not indicate in any way
153
+ that your Modified Licence is the "Open Software Licence" or "OSL" and you may not use those
154
+ names in the name of your Modified Licence; (ii) You must replace the notice specified in the
155
+ first paragraph above with the notice "Licensed under <insert your license name here>" or
156
+ with a notice of your own that is not confusingly similar to the notice in this Licence;
157
+ and (iii) You may not claim that your original works are open source software unless your
158
+ Modified Licence has been approved by Open Source Initiative (OSI) and You comply with
159
+ its license review and certification process.
@@ -0,0 +1,188 @@
1
+ # JENI
2
+
3
+ _(a.k.a Jumpin Ermin's Nifty Installer)_
4
+
5
+ A simple alternative to rubigen and thor that can be used to create a post-install script
6
+ for a gem needing more than the standard file ops covered by rubygems. It can also be used
7
+ for straight directories instead of gems, if required.
8
+
9
+ **GitHub:** [https://github.com/osburn-sharp/jeni](https://github.com/osburn-sharp/jeni)
10
+
11
+ **RubyDoc:** [http://rdoc.info/github/osburn-sharp/jeni/frames](http://rdoc.info/github/osburn-sharp/jeni/frames)
12
+
13
+ **RubyGems:** [https://rubygems.org/gems/jeni](https://rubygems.org/gems/jeni)
14
+
15
+ ## Usage
16
+
17
+ To install,
18
+
19
+ gem install jeni.
20
+
21
+ It is a plain library with no binaries or the like. Documentation is available from [RDoc]()
22
+
23
+ ## Getting Started
24
+
25
+ To use, create an executable, require jeni, create an instance of `Jeni::Installer`
26
+ using the new or new_from_gem method and a block, call whatever methods you need to install files
27
+ etc. and then `run!` the block to do the real work. See documentation for details of available methods
28
+ and options.
29
+
30
+ ### Example
31
+
32
+ This is a simple example:
33
+
34
+ # start the installation block
35
+ Jeni::Installer.new_from_gem('my_gem') do |jeni|
36
+ jeni.pretend(opt[:pretend])
37
+ # copy a file
38
+ jeni.file('source.rb', '/etc/target.rb')
39
+ # create a wrapper
40
+ jeni.wrapper('sbin/jenerate.rb', '/usr/sbin/jenerate')
41
+ end.run!
42
+ # and run jeni
43
+
44
+ ### Actions
45
+
46
+ Jeni has the following actions, which for a gem (new_from_gem) will look for the source relative to the gem,
47
+ and for a directory (new) will look relative to that directory:
48
+
49
+ + *{Jeni::Installer#file file}* -
50
+ copy a file from the source to the filesystem
51
+
52
+ + *{Jeni::Installer#directory directory}* -
53
+ copy all the files in a directory to the filesystem
54
+
55
+ + *{Jeni::Installer#empty_directory empty_directory}* -
56
+ create an empty directory with no contents.
57
+
58
+ + *{Jeni::Installer#template template}* -
59
+ generate a file from a template
60
+
61
+ + *{Jeni::Installer#standard_template standard_template}* -
62
+ as for template, but looks in standard locations for the template file
63
+
64
+ + *{Jeni::Installer#wrapper wrapper}* -
65
+ create a wrapper script that calls a gem binary/script - limited to gems only
66
+
67
+ + *{Jeni::Installer#link link}* -
68
+ create a link to a file
69
+
70
+ + *{Jeni::Installer#message message}* -
71
+ output a message to the user
72
+
73
+ + *{Jeni::Installer#user user}* -
74
+ add a new user to the system
75
+
76
+ + *{Jeni::Installer#group group}* -
77
+ add a new group to the system
78
+
79
+ + *{Jeni::Installer#file_exists? file_exists?}* -
80
+ check that a given file exists, and optionally that it is executable
81
+
82
+ The file, directory and template methods take an options hash, which accepts the following options:
83
+
84
+ + *:chown* - change the owner of the copied file(s)
85
+ + *:chgrp* - change the group of the copied file(s)
86
+ + *:chmod* - change the mode of the copied file(s) which should be given in octal (e.g. 0755 and not 755 or '755')
87
+
88
+ For example:
89
+
90
+ jeni.file('source.rb', '/etc/target.rb', :chown=>'robert')
91
+
92
+ If the source is a relative path then it will be looked for relative to the Installer.
93
+ So, for a gem {Jeni::Installer.new_from_gem} it will be relative to the gem's directory and for a directory
94
+ {Jeni::Installer.new} it will be relative to that. If the source is an absolute path (starts with '/')
95
+ then it will be used as given.
96
+
97
+ Global options can also be set, as defined in #{Jeni::Options}. The same options can be set using {Jeni::Optparse#optparse}
98
+ to automatically process command line options. Call it with ARGV instead of manually setting Jeni's options.
99
+
100
+ Jeni::Installer.new_from_gem('jeni') do |jeni|
101
+ jeni.optparse(ARGV)
102
+ jeni.file('source/jeni.rb', File.join(target_dir, 'jeni_test.rb'), :chown=>'robert')
103
+ jeni.file('source/jeni.rb', File.join(target_dir, 'jeni.rb'), :chown=>'robert')
104
+ jeni.directory('source', target_dir)
105
+ jeni.wrapper('source/executable', File.join(target_dir, 'executable'), :chmod=>true)
106
+ jeni.link('source/jeni.rb', File.join(target_dir, 'jeni_link.rb'))
107
+ end.run!
108
+
109
+
110
+ ## Code Walkthrough
111
+
112
+ The main class is {Jeni::Installer} and the instance methods provide the actions that the installer can carry out.
113
+ Each method makes relevant checks (e.g. target directory is writeable) and queues one of more action requests,
114
+ e.g. to copy a file and then change the owner. The {Jeni::Installer#run!} method checks if any errors were
115
+ raised by the checks and aborts with error messages if they were. Otherwise it dispatches each action method with the
116
+ saved parameters.
117
+
118
+ The hard work is all done in {Jeni::Actions} and these methods are not intended to be directly accessible to the user.
119
+ Each action method controls messages and any interaction with the user and then carries out the intended action if
120
+ required. User IO is achieved through {Jeni::IO}. If interaction is required the user is prompted with a list of choices
121
+ and the appropriate action carried out as a result. This could include, for example, printing a diff listing between
122
+ an existing file and a new file intended to replace it.
123
+
124
+ Jeni has a variety of options that can be set and are separately defined in {Jeni::Options}. Alternatively, use
125
+ {Jeni::Optparse#optparse} mixin to set up these options from the command line (recommended).
126
+
127
+ The code is available from [GitHub](https://github.com/osburn-sharp/jeni)
128
+
129
+ ## Dependencies
130
+
131
+ A ruby compiler - works with 1.8.7.
132
+
133
+ Check the {file:Gemfile} for other dependencies.
134
+
135
+ ### Documentation
136
+
137
+ Documentation is best viewed using Yard. Documentation is available from [Rubydoc](http://rdoc.info/github/osburn-sharp/jeni/frames)
138
+
139
+ ## Testing/Modifying
140
+
141
+ Testing can be carried out with the GitHub sources and uses rspec. There is a complete rspec test suite
142
+ for the Utils module (spec/jeni_utils_spec.rb).
143
+
144
+ There is also a manual test that is an example that shows a range of possible results for a mock gem. Run this with
145
+
146
+ $ test/examples/test1.rb
147
+
148
+ The same tests are used for a source directory instead of a gem:
149
+
150
+ $ test/examples/test2.rb
151
+
152
+ There is also a variant on the above that uses optparse and can therefore accept any of the proposed
153
+ options:
154
+
155
+ $ test/examples/test_args -p
156
+
157
+ Will pretend. For more details:
158
+
159
+ $ test/examples/test_args --help
160
+
161
+ To test users and groups there is the following, which will fail if not run as root:
162
+
163
+ $ test/examples/test_users
164
+
165
+ ## Bugs
166
+
167
+ Details of any unresolved bugs and change requests are in {file:Bugs.rdoc Bugs}. Issues can be logged and tracked through
168
+ [GitHub](https://github.com/osburn-sharp/jeni/issues).
169
+
170
+ ## Changelog
171
+
172
+ See {file:History.txt} for a summary change history.
173
+
174
+ ## Author and Contact
175
+
176
+ The author may be contacted by via [GitHub](http://github.com/osburn-sharp)
177
+
178
+ ## Copyright and Licence
179
+
180
+ Copyright (c) 2012 Robert Sharp
181
+
182
+ This software is licensed under the terms defined in {file:LICENCE.rdoc}
183
+
184
+ ## Warranty
185
+
186
+ This software is provided "as is" and without any express or implied
187
+ warranties, including, without limitation, the implied warranties of
188
+ merchantibility and fitness for a particular purpose.
@@ -0,0 +1,374 @@
1
+ #
2
+ # Author:: R.J.Sharp
3
+ # Email:: robert(a)osburn-sharp.ath.cx
4
+ # Copyright:: Copyright (c) 2012
5
+ # License:: Open Software Licence v3.0
6
+ #
7
+ # This software is licensed for use under the Open Software Licence v. 3.0
8
+ # The terms of this licence can be found at http://www.opensource.org/licenses/osl-3.0.php
9
+ # and in the file LICENCE. Under the terms of this licence, all derivative works
10
+ # must themselves be licensed under the Open Software Licence v. 3.0
11
+ #
12
+ #
13
+
14
+ require 'rubygems'
15
+ require 'jeni/utils'
16
+ require 'jeni/actions'
17
+ require 'jeni/io'
18
+ require 'jeni/options'
19
+ require 'jeni/errors'
20
+ require 'jeni/optparse'
21
+
22
+ require 'etc'
23
+
24
+ # = Jeni
25
+ #
26
+ # A simple installer designed to support gems by installing the things
27
+ # that gem cannot install. For example: sbin files, /etc files.
28
+ #
29
+ # To use Jeni, you need to create an instance in a block, use the various methods
30
+ # to copy files etc, and run the resulting block:
31
+ #
32
+ # Jeni::Installer.construct('my_gem) do |jeni|
33
+ # jeni.pretend
34
+ # jeni.file('source.rb', '/usr/sbin/target')
35
+ # jeni.file('etc/config.rb', '/etc/my_gem.rb', :chown=>'root')
36
+ # end.run!
37
+ #
38
+ module Jeni
39
+
40
+ # The main class to be used by callers to construct an installation script.
41
+ # See {file:README.md Readme} for full details.
42
+ #
43
+ # Included modules are:
44
+ #
45
+ # * Options - methods to allow the direct assignment of defaults etc
46
+ # * Optparse - process command line options using Optparse
47
+ # * Utils, Actions and IO - methods under the bonnet
48
+ class Installer
49
+
50
+ include Jeni::Utils
51
+ include Jeni::Actions
52
+ include Jeni::IO
53
+ include Jeni::Options
54
+ include Jeni::Optparse
55
+
56
+ extend Jeni::IO # get say for the class as well!
57
+
58
+ # create a jeni installer instance
59
+ #
60
+ # @param [String] source_root is the path to the source to copy from etc
61
+ # @param [String] app_name is the name of the app being installed
62
+ # @yield [self] returns self
63
+ def initialize(source_root, app_name)
64
+ @app_name = app_name
65
+ #@gem_spec = Gem::Specification.find_by_name(@gem_name)
66
+ #@gem_dir = @gem_spec.gem_dir
67
+ @source_root = source_root
68
+ @commands = []
69
+ @errors = {}
70
+ @owner = nil
71
+ @gem_dir = nil
72
+ if block_given? then
73
+ yield self
74
+ else
75
+ return self
76
+ end
77
+ end
78
+
79
+ # @private
80
+ def set_gem(dir)
81
+ @gem_dir = dir
82
+ @gem = true
83
+ end
84
+
85
+ # allow scripters to know where the gem directory is
86
+ attr_reader :gem_dir
87
+
88
+ #protected :set_gem
89
+
90
+
91
+ # construct an installer to install files etc after a gem install.
92
+ #
93
+ # @param [String] gem_name is the name of the gem to install from
94
+ # @yield [Jeni::Installer] an instance through which options and actions can
95
+ # be taken
96
+ #
97
+ def self.new_from_gem(gem_name)
98
+ gem_spec = Gem::Specification.find_by_name(gem_name)
99
+ installer = self.new(gem_spec.gem_dir, gem_name)
100
+ installer.set_gem(gem_spec.gem_dir)
101
+ if block_given? then
102
+ yield(installer)
103
+ end
104
+ return installer
105
+ rescue Gem::LoadError
106
+ say(:fatal, "Gem name #{gem_name} could not be found", :error)
107
+ return nil
108
+ end
109
+
110
+ # action the commands, or not, depending on the options selected
111
+ def run!
112
+ if self.errors? then
113
+ unless @quiet
114
+ puts "There are errors in the installation, which has been cancelled:"
115
+ puts ""
116
+ end
117
+
118
+ self.each_error do |error, target|
119
+
120
+ say(error, target, :error)
121
+ end
122
+ return false
123
+ end
124
+ # no errors, so do something
125
+ @commands.each do |command|
126
+ verb = command.keys.first
127
+ args = command[verb]
128
+ case verb
129
+ when :mkdir
130
+ mkdir(args)
131
+ when :file
132
+ copy(args[0], args[1])
133
+ when :chown
134
+ chown(args[:file], args[:owner])
135
+ when :chgrp
136
+ chgrp(args[:file], args[:group])
137
+ when :chmod
138
+ chmod(args[:file], args[:mode])
139
+ when :generate
140
+ generate(args[0], args[1], args[2])
141
+ when :wrap
142
+ wrap(args[0], args[1], args[2])
143
+ when :link
144
+ link_it(args[0], args[1])
145
+ when :user
146
+ add_user(args[:name], args[:options])
147
+ when :group
148
+ add_group(args[:name], args[:options])
149
+ when :say
150
+ say(args[0], args[1], args[2])
151
+ end
152
+ end
153
+
154
+ return true
155
+
156
+ rescue JeniError => err
157
+ puts "An error has occurred. Aborting."
158
+ puts err.inspect
159
+ err.each do |el|
160
+ puts el
161
+ end
162
+ end
163
+
164
+ # check that a given file exists and report an error otherwise
165
+ #
166
+ # @param [String] path to the file that must exist
167
+ #
168
+ def exists?(path)
169
+ check_file(path)
170
+ end
171
+
172
+ # copy a file from the source, relative to the gem home to the target
173
+ # which is absolute
174
+ #
175
+ # @param [String] source is the gem-relative path to the file to copy
176
+ # @param [String] target is an absolute path to copy to
177
+ # @params [Hash] opts options for copying the file
178
+ # @option opts [String] :chown the name of the owner
179
+ # @option opts [String] :chgrp the name of the group
180
+ # @option opts [Octal] :chmod octal bit settings for chmod
181
+ #
182
+ # Note :chown and :chgrp override the global options e.g. {Jeni::Options#owner owner}
183
+ def file(source, target, opts={})
184
+ #gsource = File.join(@source_root, source)
185
+ #target_dir = File.dirname(target)
186
+ owner = opts[:chown] || @owner
187
+ chmod = opts[:chmod]
188
+
189
+ gsource = check_file(source)
190
+ check_target(target, owner)
191
+ check_chmod(chmod)
192
+
193
+ @commands << {:file => [gsource, target]}
194
+ process_options(opts, target)
195
+ end
196
+
197
+ # copy all of the files in the source directory to the target directory
198
+ #
199
+ # @param (see #file)
200
+ # @option (see #file)
201
+ #
202
+ def directory(source, target, opts={})
203
+ #gsource = File.join(@source_root, source)
204
+ gsource = check_file(source)
205
+ Dir["#{gsource}/*"].each do |sourcefile|
206
+ src_path = sourcefile.sub(@source_root + '/', '')
207
+ tgt_path = sourcefile.sub(gsource + '/', '')
208
+ targetfile = File.join(target, tgt_path)
209
+ if FileTest.directory?(sourcefile) then
210
+ self.directory(sourcefile, targetfile)
211
+ else
212
+ self.file(sourcefile, targetfile, opts)
213
+ end
214
+
215
+ end
216
+ end
217
+
218
+ # create an empty directory and change the owner if specified. This ignores
219
+ # any nomkdir setting. Do not use this method together with directory as
220
+ # jeni will get confused about whether the directory exists or not!
221
+ #
222
+ # @param [String] target is the absolute path of the empty directory to create
223
+ # @param [Hash] opts to customise the directory
224
+ # @option (see #file)
225
+ #
226
+ def empty_directory(target, opts={})
227
+ @commands << {:mkdir => target}
228
+ owner = opts[:chown] || @owner
229
+ chmod = opts[:chmod]
230
+ process_options(opts, target)
231
+ end
232
+
233
+ # check that a file exists
234
+ #
235
+ # @param [String] file to test, relative to the source (e.g. gem)
236
+ # @param [Hash] options
237
+ # @option opts [Symbol] :executable to check if it is also executable
238
+ #
239
+ def file_exists?(file, opts={})
240
+ check_file(file)
241
+ check_executable(file) if opts.has_key?(:executable)
242
+ @commands << {:say => [:exists, file, :ok]}
243
+ end
244
+
245
+ # create a new user
246
+ #
247
+ # @param [String] name of the new user to create
248
+ # @param [Hash] options for creating the user
249
+ # @option opts [Integer] :uid user id instead of next available
250
+ # @option opts [Integer] :gid group id instead of next available
251
+ # @option opts [String] :home path to home directory for user, defaults to /home/$user
252
+ # @option opts [String] :shell path to shell for user, defaults to /bin/bash
253
+ # @option opts [Boolean] :skip set true to skip if user exists else fail
254
+ #
255
+ def user(name, opts={})
256
+ skip = opts[:skip]
257
+ skip = check_new_user(name, opts, skip)
258
+ check_root(skip)
259
+ opts[:skip] = skip
260
+ @commands << {:user => {:name => name, :options => opts}}
261
+ end
262
+
263
+ # create a new group
264
+ #
265
+ # @param [String] name of the new group to create
266
+ # @param [Hash] options for creating the user
267
+ # @option opts [Integer] :gid group id instead of next available
268
+ # @option opts [Boolean] :skip set true to skip if user exists else fail
269
+ #
270
+ def group(name, opts={})
271
+ skip = opts[:skip]
272
+ skip = check_new_group(name, opts, skip)
273
+ check_root(skip)
274
+ opts[:skip] = skip
275
+ @commands << {:group => {:name => name, :options => opts}}
276
+ end
277
+
278
+ # generate a file from a template using the Haml engine for plain text input.
279
+ # The template is prefixed with the ':plain' directive. Variables can be
280
+ # passed to the template using the locals hash (see below). See {file: README.md Readme}
281
+ # for more details.
282
+ #
283
+ # the file can be used for anything, with ruby code inserting as #\{code\}.
284
+ #
285
+ # @param [String] source is the path to the template to render
286
+ # @param [String] target is an absolute path to the generated file
287
+ # @param [Hash] locals is an optional hash that will be converted to local params within the template
288
+ #
289
+ # include :chown=>'user' in the locals to change the owner as well. It will be stripped from the locals
290
+ # passed to the template. Similarly for :chmod and :chgrp.
291
+ #
292
+ def template(source, target, locals={})
293
+ #gsource = File.join(@source_root, source)
294
+ #target_dir = File.dirname(target)
295
+ opts = Hash.new
296
+ opts[:chown] = locals.delete(:chown)
297
+ opts[:chmod] = locals.delete(:chmod)
298
+ opts[:chgrp] = locals.delete(:chgrp)
299
+
300
+ gsource = check_file(source)
301
+ check_target(target, opts[:owner])
302
+ check_chmod(opts[:chmod])
303
+
304
+ @commands << {:generate => [gsource, target, locals]}
305
+ process_options(opts, target)
306
+ end
307
+
308
+ # as for {Jeni::Installer#template} but searches for the source template from a list of predefined
309
+ # directories. These are ~/.jermine/templates and /usr/local/share/templates
310
+ #
311
+ # @param (see #template)
312
+ #
313
+ def standard_template(source, target, locals={})
314
+ # search for the template until it is found
315
+ gsource = get_template(source)
316
+ opts = Hash.new
317
+ opts[:chown] = locals.delete(:chown)
318
+ opts[:chmod] = locals.delete(:chmod)
319
+ opts[:chgrp] = locals.delete(:chgrp)
320
+
321
+ check_target(target, opts[:owner])
322
+ check_chmod(opts[:chmod])
323
+
324
+ @commands << {:generate => [gsource, target, locals]}
325
+ process_options(opts, target)
326
+ end
327
+
328
+ # create a wrapper script at target to call source in a similar manner to Gem's wrapper
329
+ #
330
+ # The wrapper follows the same rules as for a gem, so its shebang can be set by the original
331
+ # file being wrapped, or by :custom_shebang in your .gemrc
332
+ #
333
+ # @param [String] source is the relative path to the file to wrap
334
+ # @param [String] target is an absolute path for the wrapper
335
+ # @params [Hash] opts options for wrapping the file
336
+ # @option opts [String] :chown the name of the owner
337
+ #
338
+ def wrapper(source, target, opts={})
339
+ #gsource = File.join(@source_root, source)
340
+ owner = opts[:chown] || @owner
341
+
342
+ check_gem(:wrapper)
343
+ gsource = check_file(source)
344
+ check_target(target, owner)
345
+
346
+ @commands << {:wrap => [source, target, gsource]}
347
+ @commands << {:chmod => {:file => target, :mode => 0755}} if opts.has_key?(:chmod)
348
+ end
349
+
350
+ # create a link at target to source
351
+ #
352
+ # @param [String] source is the gem-relative path to the file to link
353
+ # @param [String] target is an absolute path for the link
354
+ #
355
+ def link(source, target)
356
+ #gsource = File.expand_path(File.join(@source_root, source))
357
+ gsource = check_file(source)
358
+ check_target(target)
359
+ @commands << {:link => [gsource, target]}
360
+ end
361
+
362
+ # output a message in the same format as other messages
363
+ #
364
+ # @param [String] action a single word describing the action taking place
365
+ # @param [String] message a short message concerning the action
366
+ # @param [Symbol] status can be :ok, :no_change, :warning or :error
367
+ #
368
+ def message(action, message, status)
369
+ @commands << {:say => [action, message, status]}
370
+ end
371
+
372
+
373
+ end
374
+ end