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 +4 -4
- data/Cartage.yml.rdoc +51 -215
- data/Contributing.md +71 -0
- data/Extending.md +520 -0
- data/History.md +106 -0
- data/{Licence.rdoc → Licence.md} +5 -5
- data/Manifest.txt +19 -12
- data/README.rdoc +64 -109
- data/Rakefile +37 -32
- data/bin/cartage +2 -2
- data/cartage-cli.md +137 -0
- data/cartage.yml.sample +43 -153
- data/lib/cartage.rb +296 -366
- data/lib/cartage/backport.rb +66 -0
- data/lib/cartage/cli.rb +234 -0
- data/lib/cartage/commands/echo.rb +23 -0
- data/lib/cartage/commands/info.rb +57 -0
- data/lib/cartage/commands/manifest.rb +88 -0
- data/lib/cartage/commands/pack.rb +18 -0
- data/lib/cartage/config.rb +118 -125
- data/lib/cartage/core.rb +112 -0
- data/lib/cartage/gli_ext.rb +58 -0
- data/lib/cartage/minitest.rb +140 -0
- data/lib/cartage/plugin.rb +137 -25
- data/lib/cartage/plugins/build_tarball.rb +40 -0
- data/lib/cartage/{manifest.rb → plugins/manifest.rb} +47 -35
- data/test/minitest_config.rb +3 -65
- data/test/test_cartage.rb +253 -109
- data/test/test_cartage_build_tarball.rb +57 -0
- data/test/test_cartage_config.rb +100 -28
- data/test/test_cartage_core.rb +172 -0
- data/test/test_cartage_manifest.rb +234 -13
- data/test/test_cartage_plugin.rb +85 -0
- metadata +63 -79
- data/.autotest +0 -8
- data/.gemtest +0 -1
- data/.minitest.rb +0 -2
- data/.travis.yml +0 -36
- data/Contributing.rdoc +0 -66
- data/Gemfile +0 -9
- data/History.rdoc +0 -43
- data/lib/cartage/command.rb +0 -59
- data/lib/cartage/manifest/commands.rb +0 -106
- data/lib/cartage/pack_command.rb +0 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c43e24d4e0f7258cdbd39dcc488e76243f735167
|
4
|
+
data.tar.gz: f8cbe8898d7a8a7332b41415696c1cc206ddabe9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cc9bdac5b457d1954b97a7ef92b32a41e5b60c12993ab7b9d154723870991aa671b2f8dcda9ef1c70e708c4931ac65dc17d38299464a6f6189748f247dd19ecb
|
7
|
+
data.tar.gz: 1bc2c43ded9eed5e7e18b66c589e2879fa7cb6989c1a714bc9ba9ba2c6f258fb17d167be653b1c0be3650bd8f4cf7e7e81afef9e95ff2c75a84b9da73fe4a86c
|
data/Cartage.yml.rdoc
CHANGED
@@ -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
|
-
|
21
|
-
<%=
|
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
|
-
|
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
|
39
|
-
|
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
|
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
|
-
|
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
|
-
===
|
59
|
+
=== +compression+
|
61
60
|
|
62
|
-
The
|
63
|
-
|
64
|
-
|
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
|
-
|
65
|
+
This affects the compression and filenames of both the final package and the
|
66
|
+
dependency cache.
|
67
67
|
|
68
|
-
|
68
|
+
compression: gzip
|
69
69
|
|
70
|
-
|
71
|
-
"development", "test"]</tt>. Overridden with <tt>cartage --without
|
72
|
-
group1,group2</tt>.
|
70
|
+
=== +quiet+
|
73
71
|
|
74
|
-
|
75
|
-
|
76
|
-
- test
|
77
|
-
- other
|
72
|
+
Silence normal output. Optional, defaults false. Overridden with <tt>cartage
|
73
|
+
--quiet</tt>.
|
78
74
|
|
79
|
-
|
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
|
-
|
88
|
-
the selected storage provider.
|
77
|
+
=== +verbose+
|
89
78
|
|
90
|
-
|
79
|
+
Show verbose output. Optional, defaults false. Overridden with <tt>cartage
|
80
|
+
--verbose</tt>.
|
91
81
|
|
92
|
-
|
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
|
-
|
97
|
-
s3:
|
98
|
-
path: cartage-bucket
|
84
|
+
=== +disable_dependency_cache+
|
99
85
|
|
100
|
-
|
86
|
+
Disable dependency caching. Optional, defaults false.
|
101
87
|
|
102
|
-
|
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
|
-
|
90
|
+
=== +dependency_cache_path+
|
108
91
|
|
109
|
-
|
110
|
-
|
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
|
-
|
113
|
-
|
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
|
-
|
100
|
+
dependency_cache_path: <%= ENV['SEMAPHORE_CACHE'] %>
|
121
101
|
|
122
|
-
|
123
|
-
+rackspace_api_key+, and optionally +rackspace_auth_url+.
|
102
|
+
=== +commands+
|
124
103
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
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
|
-
|
110
|
+
commands: {}
|
146
111
|
|
147
|
-
|
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
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
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
|
-
|
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: {}
|
data/Contributing.md
ADDED
@@ -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
|
data/Extending.md
ADDED
@@ -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.
|