cartage 1.2 → 2.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 93cd6f86971a0f549be3d3926c0af79468217630
4
- data.tar.gz: acff341e0fccbdf7d6a96aab9faffbefcedb19a1
3
+ metadata.gz: c43e24d4e0f7258cdbd39dcc488e76243f735167
4
+ data.tar.gz: f8cbe8898d7a8a7332b41415696c1cc206ddabe9
5
5
  SHA512:
6
- metadata.gz: b49e371c9d7c42af2a8030f3356aeaa2063e5d4cc2835dbc43886bc076988d8cb09f6139568bfdcc57784bb06655d59ee17dde75bdda4a240d09130456cab4c9
7
- data.tar.gz: ecf1af90ef466bd692e0b671b270fa0b5cec37988bb7a5caf445126ebcb88896b339cd80cbbc4d8610fd75036fc521a1f81bd24c747d7a8a50b16a8cecd2e209
6
+ metadata.gz: cc9bdac5b457d1954b97a7ef92b32a41e5b60c12993ab7b9d154723870991aa671b2f8dcda9ef1c70e708c4931ac65dc17d38299464a6f6189748f247dd19ecb
7
+ data.tar.gz: 1bc2c43ded9eed5e7e18b66c589e2879fa7cb6989c1a714bc9ba9ba2c6f258fb17d167be653b1c0be3650bd8f4cf7e7e81afef9e95ff2c75a84b9da73fe4a86c
@@ -11,38 +11,37 @@ configuration file. The main value that would probably be set under most
11
11
  circumstances is the +name+ value.
12
12
 
13
13
  Because Cartage reads its configuration through ERB, the following structure is
14
- recommended for +config/cartage.yml+.
14
+ recommended for +config/cartage.yml+. Note that Cartage::Config.import is
15
+ shorthand for <tt>File.read(filename) if File.exist?(filename)</tt>. Both
16
+ +config/ansible/cartage.yml+ and +config/local/cartage.yml+ should be in your
17
+ +.gitignore+.
15
18
 
16
19
  ---
17
20
  name: my-application
18
21
 
19
22
  # This must not be indented for it to work.
20
- <% if File.exist?('config/ansible/cartage.yml') %>
21
- <%= File.read('config/ansible/cartage.yml') %>
22
- <% end %>
23
- <% if File.exist?('config/local/cartage.yml') %>
24
- <%= File.read('config/local/cartage.yml') %>
25
- <% end %>
23
+ <%= Cartage::Config.import 'config/ansible/cartage.yml' %>
24
+ <%= Cartage::Config.import 'config/local/cartage.yml' %>
26
25
 
27
26
  == Main Cartage Configuration Options
28
27
 
29
28
  === +name+
30
29
 
31
30
  The name of the application. Optional, defaults to the basename of the origin
32
- Git URL. Overridden with <tt>cartage --name NAME</tt>.
31
+ repo URL. Overridden with <tt>cartage --name NAME</tt>.
33
32
 
34
33
  name: my-application
35
34
 
36
35
  === +target+
37
36
 
38
- The target path for the Cartage package. Optional and defaults to
39
- <tt>./tmp</tt>. Overridden with <tt>cartage --target PATH</tt>.
37
+ The target path for the Cartage package. Optional, defaults to <tt>./tmp</tt>.
38
+ Overridden with <tt>cartage --target PATH</tt>.
40
39
 
41
40
  target: tmp/cartage
42
41
 
43
42
  === +root_path+
44
43
 
45
- The root path of the application. Optional, defaults to the top of the Git
44
+ The root path of the application. Optional, defaults to the top of the
46
45
  repository (<tt>git rev-parse --show-cdup</tt>). Overridden with <tt>cartage
47
46
  --root-path ROOT_PATH</tt>.
48
47
 
@@ -52,232 +51,69 @@ repository (<tt>git rev-parse --show-cdup</tt>). Overridden with <tt>cartage
52
51
 
53
52
  The timestamp for the final package (which is
54
53
  <tt><em>name</em>-<em>timestamp</em></tt>). Optional, defaults to the current
55
- time in UTC. Overridden with <tt>cartage --timestamp TIMESTAMP</tt>. This
56
- value is *not* validated to be a time value when supplied.
54
+ time in UTC. Overridden with <tt>cartage --timestamp TIMESTAMP</tt>. This value
55
+ is *not* validated to be a time value when supplied.
57
56
 
58
57
  timestamp: not-a-timestamp
59
58
 
60
- === +bundle_cache+
59
+ === +compression+
61
60
 
62
- The bundle cache path, where the package’s <tt>vendor-bundle.tar.bz2</tt>
63
- will be stored between builds. Optional, defaults to <tt>./tmp</tt>.
64
- Overridden with <tt>cartage --bundle-cache BUNDLE_CACHE</tt>.
61
+ The type of compression to be used. Optional, defaults to 'bzip2'. Must be one
62
+ of 'bzip2', 'gzip', or 'none'. Overridden with <tt>cartage --compression
63
+ TYPE</tt>.
65
64
 
66
- bundle_cache: tmp/cache
65
+ This affects the compression and filenames of both the final package and the
66
+ dependency cache.
67
67
 
68
- === +without+
68
+ compression: gzip
69
69
 
70
- The groups to exclude from <tt>bundle install</tt>. Optional, defaults to <tt>[
71
- "development", "test"]</tt>. Overridden with <tt>cartage --without
72
- group1,group2</tt>.
70
+ === +quiet+
73
71
 
74
- without:
75
- - development
76
- - test
77
- - other
72
+ Silence normal output. Optional, defaults false. Overridden with <tt>cartage
73
+ --quiet</tt>.
78
74
 
79
- === +plugins+
80
-
81
- A dictionary that contains all plug-in configuration, keyed by plug-in name.
82
-
83
- == Cartage Plug-In Configuration Options
84
-
85
- === cartage-s3
75
+ quiet: true
86
76
 
87
- Cartage-s3 needs to know where to put the completed package and how to log into
88
- the selected storage provider.
77
+ === +verbose+
89
78
 
90
- ==== +path+
79
+ Show verbose output. Optional, defaults false. Overridden with <tt>cartage
80
+ --verbose</tt>.
91
81
 
92
- The path to the cartage S3 bucket or directory (for another service that
93
- Fog::Storage supports). This has no default and is overridden with <tt>cartage
94
- s3 --path PATH</tt>.
82
+ verbose: true
95
83
 
96
- plugins:
97
- s3:
98
- path: cartage-bucket
84
+ === +disable_dependency_cache+
99
85
 
100
- ==== +credentials+
86
+ Disable dependency caching. Optional, defaults false.
101
87
 
102
- The credentials dictionary passed to Fog::Storage for uploads. Each provider
103
- has different keys. If present, this will dictionary be used in preference to
104
- <tt>cartage s3</tt> flags <tt>--key-id</tt>, <tt>--secret-key</tt>, and
105
- <tt>--region</tt> as those work *only* with Amazon AWS S3.
88
+ disable_dependency_cache: true
106
89
 
107
- ===== AWS
90
+ === +dependency_cache_path+
108
91
 
109
- AWS S3 storage connections need +provider+, +aws_access_key_id+,
110
- +aws_secret_access_key+, and +region+.
92
+ The path where the dependency cache will be written
93
+ (<tt>dependency-cache.tar.*</tt>) for use in successive builds. Optional,
94
+ defaults to <tt>./tmp</tt>. Overridden with <tt>cartage --dependency-cache-path
95
+ PATH</tt>.
111
96
 
112
- plugins:
113
- s3:
114
- credentials:
115
- provider: AWS
116
- aws_access_key_id: YOUR_AWS_ACCESS_KEY_ID
117
- aws_secret_access_key: YOUR_AWS_SECRET_ACCESS_KEY
118
- region: us-west-2
97
+ On a CI system, this should be written somewhere that the CI system uses for
98
+ build caching.
119
99
 
120
- ===== Rackspace
100
+ dependency_cache_path: <%= ENV['SEMAPHORE_CACHE'] %>
121
101
 
122
- Rackspace storage connections need +provider+, +rackspace_username+,
123
- +rackspace_api_key+, and optionally +rackspace_auth_url+.
102
+ === +commands+
124
103
 
125
- plugins:
126
- s3:
127
- credentials:
128
- provider: Rackspace
129
- rackspace_username: RACKSPACE_USERNAME
130
- rackspace_api_key: RACKSPACE_API_KEY
131
- rackspace_auth_url: lon.auth.api.rackspacecloud.com
132
-
133
- ===== Google
134
-
135
- Google storage connections need +provider+, +google_storage_access_key_id+, and
136
- +google_storage_secret_access_key+.
137
-
138
- plugins:
139
- s3:
140
- credentials:
141
- provider: Google
142
- google_storage_access_key_id: YOUR_SECRET_ACCESS_KEY_ID
143
- google_storage_secret_access_key: YOUR_SECRET_ACCESS_KEY
104
+ This dictionary is for command-specific configuration. As of 2.0, none of the
105
+ commands provided by default or in existing plug-ins have command-specific
106
+ configuration. The keys are freeform and should be based on the *primary* name
107
+ of the command (so the <tt>cartage pack</tt> command should use the key
108
+ <tt>pack</tt>.)
144
109
 
145
- === cartage-remote
110
+ commands: {}
146
111
 
147
- Cartage-remote needs to know where its remote build is going to run and how to
148
- authenticate. It may also be told *what* to run. For cartage-remote 1.0, the
149
- +remote+ configuration section is *required* (at least the +server+ key).
150
-
151
- ==== +server+
152
-
153
- The name of the build server. This field is required and can show up in two
154
- different formats. The first is as a string matching
155
- <tt>[user@]host[:port]</tt>.
156
-
157
- plugins:
158
- remote:
159
- server: build@my-build-machine:2222
160
-
161
- The second is as a dictionary with +user+, +host+, and +port+ keys.
112
+ === +plugins+
162
113
 
163
- plugins:
164
- remote:
165
- server:
166
- user: build
167
- host: my-build-machine
168
- port: 2222
114
+ This dictionary is for plug-in-specific configuration. See each plug-in for
115
+ configuration options. The keys to the plug-ins are based on the plug-in name.
116
+ cartage-bundler is available as Cartage::Bundler; the transformed plug-in name
117
+ will be <tt>bundler</tt>.
169
118
 
170
- ==== +keys+
171
-
172
- The SSH key(s) used to connect to the server. Optional, and cartage-remote will
173
- use <tt>~/.ssh/*id_[rd]sa</tt> to find keys if this is not provided. The first
174
- format is as a dictionary, embedding private keys directly in the configuration
175
- file.
176
-
177
- plugins:
178
- remote:
179
- keys:
180
- custom: |
181
- -----BEGIN RSA PRIVATE KEY-----
182
- ...
183
- -----END RSA PRIVATE KEY-----
184
-
185
- The second form is as an array of glob patterns to match key files.
186
-
187
- plugins:
188
- remote:
189
- keys:
190
- - "config/secrets/*id_[rd]sa"
191
- - "~/.ssh/*id_[rd]sa"
192
-
193
- The third form is as a single glob pattern to match key files or a specific key
194
- file.
195
-
196
- plugins:
197
- remote:
198
- keys: "config/secrets/*id_[rd]sa"
199
-
200
- ==== +build+
201
-
202
- The build script that will be run on the remote server. This is optional with a
203
- reasonable default, below.
204
-
205
- #!/bin/bash
206
- set -e
207
- if [ -f Gemfile ]; then
208
- bundle install --path %<remote_bundle>s
209
- bundle exec cartage build \
210
- --config-file %<config_file>s \
211
- --target %<project_path>s
212
- else
213
- cartage build --config-file %<config_file>s \
214
- --target %<project_path>s
215
- fi
216
-
217
- An example with an alternate build script that uses cartage-s3 to upload.
218
-
219
- plugins:
220
- remote:
221
- build: |
222
- #!/bin/bash
223
- set -e
224
- if [ -f Gemfile ]; then
225
- bundle install --path %<remote_bundle>s
226
- bundle exec cartage s3 \
227
- --config-file %<config_file>s \
228
- --target %<project_path>s \
229
- --verbose
230
- else
231
- cartage build \
232
- --config-file %<config_file>s \
233
- --target %<project_path>s \
234
- --verbose
235
- fi
236
-
237
- ==== +prebuild+
238
-
239
- The prebuild script that will be run on the local system. This is optional with
240
- a reasonable default, below:
241
-
242
- #!/bin/bash
243
- ssh-keyscan -H %<remote_host>s >> ~/.ssh/known_hosts
244
-
245
- An example with a slightly extended example is below. It is strongly
246
- recommended that the <tt>ssh-keyscan</tt> step be run in all prebuild scripts
247
- as cartage-remote runs SSH in a paranoid mode.
248
-
249
- plugins:
250
- remote:
251
- prebuild: |
252
- #!/bin/bash
253
- ssh-keyscan -H %<remote_host>s >> ~/.ssh/known_hosts
254
- echo 'Prebuild complete'
255
-
256
- ==== +postbuild+
257
-
258
- The postbuild script that will be run on the local system. This is optional
259
- with no default (no post-build actions).
260
-
261
- The postbuild script will be passed the stage identifier as <tt>$1</tt>
262
- (valid values are +config+, +ssh_config+, +prebuild+, +remote_clone+,
263
- +remote_build+, +cleanup+, or +finished+). If the build was interrupted with
264
- an error, the error message will be passed as <tt>$2</tt>.
265
-
266
- An example that posts a message to Slack is below.
267
-
268
- plugins:
269
- remote:
270
- postbuild: |
271
- #!/bin/bash
272
- case ${1:-undefined} in
273
- finished)
274
- t="token=SLACK_TOKEN"
275
- c="channel=%23ci"
276
- d=MYDOMAIN
277
- u="https://${d}.slack.com/services/hooks/slackbot?${t}&${c}"
278
- curl --data "Build %<name>s-%<timestamp>s complete." ${u}
279
- ;;
280
- *)
281
- : # ${1} is the stage, ${2} is the error message
282
- ;;
283
- esac
119
+ plugins: {}
@@ -0,0 +1,71 @@
1
+ ## Contributing
2
+
3
+ We value any contribution to Cartage you can provide: a bug report, a feature
4
+ request, or code contributions. Cartage is reasonably complex code, so there
5
+ are a few guidelines:
6
+
7
+ * Changes *will not* be accepted without tests. The test suite is written
8
+ with [Minitest][].
9
+ * Match our coding style.
10
+ * Use a thoughtfully-named topic branch that contains your change. Rebase
11
+ your commits into logical chunks as necessary.
12
+ * Use [quality commit messages][].
13
+ * Do not change the version number; when your patch is accepted and a release
14
+ is made, the version will be updated at that point.
15
+ * Submit a GitHub pull request with your changes.
16
+ * New behaviours and features are probably better created as cartage plugins.
17
+ If they are best suited for the core Cartage features, they require new or
18
+ updated documentation.
19
+
20
+ ### Test Dependencies
21
+
22
+ Cartage uses Ryan Davis’s [Hoe][] to manage the release process, which adds a
23
+ number of rake tasks. You will mostly be interested in:
24
+
25
+ $ rake
26
+
27
+ which runs the tests the same way that:
28
+
29
+ $ rake test
30
+ $ rake travis
31
+
32
+ will do.
33
+
34
+ To assist with the installation of the development dependencies for Cartage, I
35
+ have provided the simplest possible Gemfile pointing to the (generated)
36
+ `cartage.gemspec` file. This will permit you to do:
37
+
38
+ $ bundle install
39
+
40
+ to get the development dependencies. If you aleady have `hoe` installed, you
41
+ can accomplish the same thing with:
42
+
43
+ $ rake newb
44
+
45
+ This task will install any missing dependencies, run the tests/specs, and
46
+ generate the RDoc.
47
+
48
+ ### Workflow
49
+
50
+ Here's the most direct way to get your work merged into the project:
51
+
52
+ * Fork the project.
53
+ * Clone down your fork (`git clone
54
+ git://github.com/KineticCafe/cartage.git`).
55
+ * Create a topic branch to contain your change (`git checkout -b
56
+ my_awesome_feature`).
57
+ * Hack away, add tests. Not necessarily in that order.
58
+ * Make sure everything still passes by running `rake`.
59
+ * If necessary, rebase your commits into logical chunks, without errors.
60
+ * Push the branch up (`git push origin my_awesome_feature`).
61
+ * Create a pull request against KineticCafe/cartage and describe
62
+ what your change does and the why you think it should be merged.
63
+
64
+ ### Contributors
65
+
66
+ * Austin Ziegler created Cartage.
67
+
68
+ [Minitest]: https://github.com/seattlerb/minitest
69
+ [quality commit messages]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html
70
+ [Hoe]: https://github.com/seattlerb/hoe
71
+ [kccoc]: https://github.com/KineticCafe/code-of-conduct
@@ -0,0 +1,520 @@
1
+ # Extending Cartage
2
+
3
+ Cartage is designed for extension. There are two distinct *types* of extension
4
+ that can be written: *commands* and *plug-ins*. Most *command* extensions will
5
+ implement a *plug-in* extension, but Cartage can be extended with either
6
+ mechanism on its own. For both *commands* and *plug-ins* there is automatic
7
+ discovery and activation.
8
+
9
+ ## Command Extensions
10
+
11
+ Command extensions add new commands and subcommands to the Cartage CLI
12
+ (`bin/cartage`). The Cartage CLI is written with the [GLI][] DSL, so all of the
13
+ features present in GLI can be used to add commands to Cartage itself. Commands
14
+ are discovered by placing a command definition file in `lib/cartage/commands`
15
+ in the gem containing the command extension.
16
+
17
+ ### Defining a Command
18
+
19
+ New commands *extend* Cartage::CLI, opening a context where the GLI DSL can be
20
+ used to create a new command. Outside of the new command block, it is
21
+ recommended that only `desc`, `long_desc`, `arg`, and `command` be used since
22
+ there is no way of adding support for new global switches or flags.
23
+
24
+ ```ruby
25
+ Cartage::CLI.extend do
26
+ # Define the commands here.
27
+ desc 'A new command'
28
+ long_desc 'A long description for the new command'
29
+ arg :maybe, :optional
30
+ arg :yes # required by default
31
+ arg :items, :multiple
32
+ command 'newcommand' do |echo|
33
+ echo.desc 'Enable fuzz mode.'
34
+ echo.switch :fuzz
35
+
36
+ echo.desc 'Fuzz level.'
37
+ echo.flag 'fuzz-level', arg_name: :LEVEL, default_value: 0
38
+ echo.action do |global, options, args|
39
+ # Whatever the new command does should be done here.
40
+ end
41
+ end
42
+ end
43
+ ```
44
+
45
+ It is recommended that a command extension add only one new command with
46
+ subcommands to handle more complex operations.
47
+
48
+ ```ruby
49
+ Cartage::CLI.extend do
50
+ command 'newcommand' do |echo|
51
+ echo.command 'subcommand' do |sc|
52
+ sc.action do |global, options, args|
53
+ # subcommand actions
54
+ end
55
+ end
56
+
57
+ echo.default_command :subcommand
58
+ end
59
+ end
60
+ ```
61
+
62
+ ### Global Cartage Instance
63
+
64
+ Each defined command operates in the same global context and has access to a
65
+ method (`cartage`) that returns the operating instance of Cartage for the
66
+ execution of the `cartage` command.
67
+
68
+ ```ruby
69
+ Cartage::CLI.extend do
70
+ desc 'Echo the provided text'
71
+ arg :TEXT, :multiple
72
+ command 'echo' do |echo|
73
+ echo.desc 'Suppress newlines'
74
+ echo.switch [ :c, 'newlines' ], default: true, negatable: false
75
+ echo.action do |_g, options, args|
76
+ unless cartage.quiet
77
+ message = args.join(' ')
78
+ if options['no-newlines'] || cartage.config(for_command: 'echo').no_newlines
79
+ puts message
80
+ else
81
+ print message
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
87
+ ```
88
+
89
+ In the example above, the `echo` command checks the global configuration for
90
+ the value of the `quiet` switch, and the command options and configuration for
91
+ whether newlines should be included in the output. Note the use of
92
+ Cartage#config with the `for_command` keyword parameter.
93
+
94
+ ### Exiting the Command
95
+
96
+ If the command is successful, completing its action block without raising an
97
+ exception will be sufficient. If an exception is thrown, it will be caught, its
98
+ message displayed, and `cartage` will exit with a non-zero exit status.
99
+
100
+ Cartage::CLI provides three *special* exceptions:
101
+
102
+ Cartage::CLI::QuietExit
103
+ : This will abort or exit the command with the provided exit status. No message
104
+ will be displayed. This is used in `cartage manifest check`.
105
+
106
+ Cartage::CLI::CustomExit
107
+ : An alias for GLI::CustomExit. Raised with a message and an exit status. The
108
+ message will be displayed, and the exit status will be returned. This should be
109
+ used for *failure* of the command.
110
+
111
+ Cartage::CLI::CommandException
112
+ : An alias for GLI::CommandException. This exception is similar to CustomExit,
113
+ but should be used when there is a configuration issue, or a conflict in flags
114
+ or switches. It requires three parameters: the message to display, the command
115
+ name for help purposes, and the exit status.
116
+
117
+ ### Example (`info` Command)
118
+
119
+ Below is an example command and sub-commands, `cartage info`. This has
120
+ limited utility, but will illustrate how commands are created in Cartage. This
121
+ example is usable, as the `info` command is a hidden command on `cartage`.
122
+
123
+ ```ruby
124
+ # Call Cartage::CLI.extend to add the commands defined in the block.
125
+ Cartage::CLI.extend do
126
+ # Use +desc+ to provide a description about the next command.
127
+ desc 'Show information about Cartage itself'
128
+
129
+ # Use +long_desc+ to provide a long description about the next command that
130
+ # can # be included in a generated RDoc file.
131
+ long_desc <<-'DESC'
132
+ Shows various bits of information about Cartage based on the running instance.
133
+ Some plug-ins do run-time resolution of configuration values, so what is shown
134
+ through these subcommands will not represent that resolution.
135
+ DESC
136
+
137
+ # Declare a new command with +command+ and one or more names for the command.
138
+ # This command is available as both <tt>cartage info</tt> and <tt>cartage
139
+ # i</tt>. The block contains the definition of the switches, flags,
140
+ # subcommands, and actions for this command.
141
+ command %w(info i) do |info|
142
+ # The same GLI::DSL works on the command being created, Here, we are
143
+ # creating <tt>cartage info plugins</tt>.
144
+ info.desc 'Show the plug-ins that have been found.'
145
+ info.long_desc <<-'DESC'
146
+ Only plug-ins using the class plug-in protocol are found this way. Plug-ins
147
+ that just provide commands are not reported as plugins.
148
+ DESC
149
+ info.command 'plugins' do |plugins|
150
+ # Implement the #action as a block. It accepts three parameters: the
151
+ # global options hash, the local command options hash, and any arguments
152
+ # passed on the command-line.
153
+ #
154
+ # Within a command's action, a +cartage+ object is available that
155
+ # represents the Cartage instance that will be used for packaging.
156
+ plugins.action do |_global, _options, _args|
157
+ plugs = cartage.plugins.enabled
158
+ if plugs.empty?
159
+ puts 'No active plug-ins.'
160
+ else
161
+ plugs.map! { |plug| "* #{plug.plugin_name} (#{plug.version})" }
162
+ puts <<-plugins
163
+ Active Plug-ins:
164
+
165
+ #{plugs.join("\n")}
166
+ plugins
167
+ end
168
+ end
169
+ end
170
+ end
171
+ end
172
+ ```
173
+
174
+ ## Plug-In Extensions
175
+
176
+ Cartage plug-ins:
177
+
178
+ * are automatically discovered and loaded;
179
+ * are accessible from and initialized with an owning Cartage instance;
180
+ * have a namespace in the Cartage configuration file; and
181
+ * may implement feature requests or offers (see below).
182
+
183
+ When explaining how to build plug-ins, we will be examining three different
184
+ plug-ins:
185
+
186
+ * Cartage::Manifest manages the `Manifest.txt` file and the `.cartignore`
187
+ file that indicates what files will be included in the release package, and
188
+ is for use with the `cartage manifest` family of commands.
189
+
190
+ * No configuration
191
+ * Bundled with Cartage (`lib/cartage/plugins/manifest.rb`)
192
+
193
+ * Cartage::BuildTarball is responsible for building the final package after
194
+ Cartage prepares the work area.
195
+
196
+ * No configuration
197
+ * Implements feature requests
198
+ * Implements feature offers
199
+ * Bundled with Cartage (`lib/cartage/plugins/build_tarball.rb`)
200
+
201
+ * Cartage::Bundler is responsible for vendoring Bundler dependencies for Ruby
202
+ projects. It was originally part of Cartage 1.x, but has been extracted to
203
+ its own gem.
204
+
205
+ * Has configuration
206
+ * Implements feature offers
207
+ * Gem [cartage-bundler][] (`lib/cartage/plugins/bundler.rb`)
208
+
209
+ ### Plug-In Discovery and Initialization
210
+
211
+ Plug-ins are placed in files that are found in `lib/cartage/plugins` and are
212
+ found in the `$LOAD_PATH`[^1]. The code in these files should define a class
213
+ that inherits from Cartage::Plugin.
214
+
215
+ When a Cartage instance is created, the plug-in will be loaded and the
216
+ resulting plug-in class will be added to the instance as a method matching the
217
+ name of the class of the plug-in. Given an instance of Cartage called
218
+ `cartage`:
219
+
220
+ * Cartage::Manifest is available as `cartage.manifest`.
221
+ * Cartage::BuildTarball is available as `cartage.build_tarball`.
222
+ * Cartage::Bundler is available as `cartage.bundler`.
223
+
224
+ ### Plug-In Configuration
225
+
226
+ Each plug-in has its own configuration dictionary as part of the
227
+ Cartage::Config dictionary, under the `plugins` dictionary. When the Cartage
228
+ configuration file is loaded and resolved for Cartage, the relevant section of
229
+ the Cartage dictionary is provided to the plug-in.
230
+
231
+ Any plug-in's configuration dictionary can be requested through Cartage#config
232
+ with the `for_plugin` keyword parameter:
233
+
234
+ ```ruby
235
+ cartage.config(for_plugin: :bundler)
236
+ ```
237
+
238
+ Plug-ins should implement `#resolve_plugin_config!` to finalize configuration
239
+ as appropriate.
240
+
241
+ * Cartage::Manifest only uses core Cartage configuration.
242
+ * Cartage::BuildTarball only uses core Cartage configuration.
243
+ * Cartage::Bundler uses its configuration section. For full details, read the
244
+ documentation of [`cartage-bundler`][]. `#resolve_plugin_config!` resolves
245
+ the Gemfile and the gem group exclusions.
246
+
247
+ ```yaml
248
+ ---
249
+ commands: {}
250
+ plugins:
251
+ bundler:
252
+ without_groups:
253
+ - assets
254
+ - development
255
+ - test
256
+ ```
257
+
258
+ This is handled with:
259
+
260
+ ```ruby
261
+ # Cartage::Bundler is a +vendor_dependencies+ plug-in for Cartage.
262
+ class Cartage::Bundler < Cartage::Plugin
263
+ private
264
+
265
+ attr_reader :gemfile, :without_groups
266
+
267
+ def resolve_plugin_config!(config)
268
+ @gemfile = cartage.root_path.join(config.gemfile || 'Gemfile').expand_path
269
+ @without_groups = Array(config.without_groups || %w(development test))
270
+ end
271
+ end
272
+ ```
273
+
274
+ ### Disabling Plug-Ins
275
+
276
+ All discovered plug-ins are enabled by default and can be disabled in the
277
+ plug-in's configuration dictionary with the key `disabled`. Note that disabling
278
+ a plug-in only has meaning for plug-ins that implement feature offers, as
279
+ disabled plug-ins will be skipped when features are requested.
280
+
281
+ ```yaml
282
+ ---
283
+ plugins:
284
+ bundler:
285
+ disabled: true
286
+ ```
287
+
288
+ Plug-ins may have additional conditions that may cause them to be disabled.
289
+ Cartage::Bundler is disabled if the `Gemfile` does not exist.
290
+
291
+ ```ruby
292
+ class Cartage::Bundler < Cartage::Plugin
293
+ # Cartage::Bundler is only enabled if the Gemfile exists.
294
+ def disabled?
295
+ super || !gemfile.exist?
296
+ end
297
+ end
298
+ ```
299
+
300
+ ### Feature Requests and Offers
301
+
302
+ Cartage plug-ins can implement feature *requests* or *offers* to provide
303
+ functionality that is loosely-coupled and dynamically discovered. Cartage
304
+ commands or plug-ins will broadcast feature *requests* to all enabled plug-ins;
305
+ any plug-in which *offers* that feature will then have appropriate methods
306
+ called to perform the plug-in's implementation of that feature.
307
+
308
+ A *feature* is simply a label that plug-ins *request* or *offer*. An example is
309
+ useful, based on Cartage#build_package (run by `cartage pack`).
310
+
311
+ 1. Cartage *requests* `:vendor_dependencies`. Cartage::Bundler *offers*
312
+ `:vendor_dependencies`. In turn, `#vendor_dependencies` (which ultimately
313
+ runs `bundle install`) and `#path` will be called on Cartage::Bundler.
314
+
315
+ 2. Cartage *requests* `:pre_build_package`.
316
+
317
+ 3. Cartage *requests* `:build_package`. Cartage::BuildTarball *offers*
318
+ `:build_package`, so `#build_package` will be called.
319
+
320
+ 4. Cartage::BuildTarball *requests* `:pre_build_tarball`.
321
+
322
+ 5. Cartage::BuildTarball builds the tarball.
323
+
324
+ 6. Cartage::BuildTarball *requests* `:post_build_tarball`.
325
+
326
+ 7. Cartage *requests* `:post_build_package`.
327
+
328
+ There is no fixed set of features, so any plug-in can request any feature. When
329
+ feature `:build_package` is requested (see [Requesting Features][]), the
330
+ collection of plugins is filtered for plugins that `#offer?(:build_package)`.
331
+
332
+ #### Offering Features
333
+
334
+ Most plug-ins will *offer* features. The easiest way to offer a particular
335
+ feature is to implement a public method that matches the name of the requested
336
+ feature[^2]. This is visible in Cartage::Bundler with `#vendor_dependencies`
337
+ and Cartage::BuildTarball with `#build_package`.
338
+
339
+ This works because feature *requests* will generally want to call a method of
340
+ the same name as the feature. The implementation of the `#offer?` test on
341
+ Cartage::Plugin makes this explicit.
342
+
343
+ ```ruby
344
+ class Cartage::Plugin
345
+ def offer?(name)
346
+ enabled? && offer_feature?(name.to_sym)
347
+ end
348
+
349
+ private
350
+
351
+ def offer_feature?(name)
352
+ respond_to?(name)
353
+ end
354
+ end
355
+ ```
356
+
357
+ #### Requesting Features
358
+
359
+ Some plug-ins will *request* features. As shown in the outline above,
360
+ Cartage::BuildTarball *offers* the `:build_package` feature but *requests* two
361
+ additional features, `:pre_build_tarball` and `:post_build_tarball`. It does
362
+ this with Cartage::Plugins#request.
363
+
364
+ ```ruby
365
+ class Cartage::BuildTarball < Cartage::Plugin
366
+ def build_package
367
+ cartage.plugins.request(:pre_build_tarball)
368
+ # build the tarball here
369
+ cartage.plugins.request(:post_build_tarball)
370
+ end
371
+ end
372
+ ```
373
+
374
+ Cartage::Plugins#request selects plugins that *offer* the feature, and then
375
+ loops over the selected plug-ins calling the feature method against them. The
376
+ implementation of #request is flexible enough to allow for a feature to be
377
+ requested and a different method to be called. This makes
378
+ Cartage::BuildTarball#build_package implementation look like:
379
+
380
+ ```ruby
381
+ class Cartage::BuildTarball < Cartage::Plugin
382
+ def build_package
383
+ cartage.plugins.request(:pre_build_tarball, :pre_build_tarball)
384
+ # build the tarball here
385
+ cartage.plugins.request(:post_build_tarball, :pre_build_tarball)
386
+ end
387
+ end
388
+ ```
389
+
390
+ This works for a feature that requires multiple phases or steps, like
391
+ `:vendor_dependencies` does:
392
+
393
+ ```ruby
394
+ class Cartage
395
+ private
396
+
397
+ def vendor_dependencies
398
+ extract_dependency_cache
399
+
400
+ plugins.request(:vendor_dependencies)
401
+
402
+ create_dependency_cache(
403
+ plugins.request_map(:vendor_dependencies, :path).compact.flatten
404
+ )
405
+ end
406
+ end
407
+ ```
408
+
409
+ 1. Features can be requested using methods other than the name of the feature.
410
+ `:vendor_dependencies` requires both `#vendor_dependencies` and `#path`.
411
+ 2. Cartage::Plugins#request_map is used to return the collection of results
412
+ from the plug-ins *offering* the feature requested.
413
+
414
+ ### *Feature Requests* from Cartage
415
+
416
+ The following *feature requests* are made when running Cartage:
417
+
418
+ #### `:vendor_dependencies`
419
+
420
+ Used during the setup of the work area to install external dependencies in the
421
+ package directory. Since external dependencies are usually described by a file,
422
+ a plug-in that offers `:vendor_dependencies` should disable itself if the
423
+ descriptive file cannot be found (such as the `Gemfile` or `package.json`).
424
+
425
+ > [cartage-bundler][] offers this for Ruby Bundler-based packages.
426
+
427
+ __Methods Required__
428
+
429
+ * __`#vendor_dependencies`__: Invokes the tool that installs the external
430
+ dependencies. This might run `bundle install` (for Ruby Bundler) or `npm
431
+ install` (for Node.js NPM). It should be run in a way that only includes
432
+ production dependencies, not development dependencies.
433
+
434
+ * __`#path`__: Indicates the directory or directories, relative to the work
435
+ area, where the external dependencies are installed. This might be
436
+ `vendor/bundle` (for Ruby Bundler) or `node_modules` (for Node.js NPM).
437
+
438
+ #### `:pre_build_package`
439
+
440
+ Used to perform any other work that must be done for the contents of the
441
+ package to be ready for packaging. A plug-in could be written to perform asset
442
+ precompilation prior to packaging, for example.
443
+
444
+ Plug-ins offering `:pre_build_package` __should__ offer `:post_build_package`.
445
+
446
+ __Methods Required__
447
+
448
+ * __`#pre_build_package`__: Performs the actions required to finalize package
449
+ preparation.
450
+
451
+ #### `:post_build_package`
452
+
453
+ Used to perform any other work that must be done for the contents of the
454
+ package to be complete. A plug-in could be written to cryptographically sign
455
+ built packages, for example.
456
+
457
+ __Methods Required__
458
+
459
+ * __`#post_build_package`__: Performs the actions required after package
460
+ creation.
461
+
462
+ If actions should be performed against any created packages, this should
463
+ request `:build_package#package_name`.
464
+
465
+ ```ruby
466
+ cartage.plugins.request_map(:build_package, :package_name)
467
+ ```
468
+
469
+ #### `:build_package`
470
+
471
+ Used to create a release package.
472
+
473
+ > This offered by Cartage as Cartage::BuildTarball.
474
+
475
+ __Methods Required__
476
+
477
+ * __`#build_package`__: Performs the actions required to build a package for
478
+ a particular format.
479
+
480
+ * __`#package_name`__: Returns the name of the package that will be created.
481
+
482
+ #### `:pre_build_tarball`
483
+
484
+ Used to perform any other work that must be done for the contents of the
485
+ tarball to be ready for packaging.
486
+
487
+ Plug-ins offering `:pre_build_tarball` __should__ offer an implementation of
488
+ `:post_build_tarball` that reverses the effects of `:pre_build_tarball`. In
489
+ this way, side-effects between two `:build_package` plug-ins do not occur.
490
+
491
+ > Requested by Cartage::BuildTarball#build_package.
492
+
493
+ __Methods Required__
494
+
495
+ * __`#pre_build_tarball`__: Performs the actions required before building the
496
+ tarball.
497
+
498
+ #### `:post_build_tarball`
499
+
500
+ Used to perform any other work that must be after creating the tarball.
501
+ Recommended to revert actions performed in `:pre_build_tarball` plug-ins.
502
+
503
+ > Requested by Cartage::BuildTarball#build_package.
504
+
505
+ __Methods Required__
506
+
507
+ * __`#post_build_tarball`__: Performs the actions required after building the
508
+ tarball.
509
+
510
+ [GLI]: https://github.com/davetron5000/gli
511
+ [cartage-bundler]: https://github.com/KineticCafe/cartage-bundler
512
+ [Requesting Features]: #label-Requesting+Features
513
+
514
+ [^1]: Plug-ins are discovered using Rubygems APIs, where commands are
515
+ discovered using `$LOAD_PATH`. While there is a difference, it has little
516
+ practical impact.
517
+
518
+ [^2]: This may not be sufficient for all requested features. The
519
+ `:vendor_dependencies` feature requires both `#vendor_dependencies` and
520
+ `#path` be implemented.