cheflow 0.2.0 → 0.3.0

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: 2898d4933067abfd83277a8f5ba628af3e6c1671
4
- data.tar.gz: 1570eefa27ab411e2929a9350f6dae56de9267a8
3
+ metadata.gz: 2cad60057a95b60c4ca9da1c46c06cc495c8bbf8
4
+ data.tar.gz: d9649b15a0084e3bfaa4e22daf979a09e114dbe7
5
5
  SHA512:
6
- metadata.gz: 0fa772f45a569263c28e71d3cbddc88861c6d294f088449700e8216272d1bc6e2e17fea926488a06c9aeb5fe1ede7d7bb09a57684be4bc3eb6d75ef192815a0f
7
- data.tar.gz: 986cce94c7a062bbc643a61573a7509c9042be705ec54d64499070a0adc6c6e0784125f7da1b1be395cab2c207ce00dc501e1486109762dbfd2fee4769c83466
6
+ metadata.gz: b36da2d89b9150759ef417a2766f11139911c24cd0e263703ec6c0d493f58dc90431009bc976229663cb62deabfb7aacfd665932bc1f716d824706276742efb3
7
+ data.tar.gz: 1aa106c0d68610f1e638f80238257113a1d04d3cfc8006c063f8ca77a507bf4f07f6f800d024e308dbbba604d8936396bb1c0a70c6fc92d524421a95b97f85c0
@@ -1,3 +1,11 @@
1
+ ## 0.3.0 (UNRELEASED)
2
+
3
+ - [Improvement] Don't return an environment that has no versions of the cookbook in it.
4
+ - [Feature] Added `bump` command which will bump the version of the current cookbook.
5
+ - [Feature] Added `upload` command which will upload the current cookbook.
6
+ - [Feature] Added straight up copies of the `outdated` and `update` Berkshelf commands.
7
+ - [Feature] Added `apply` command to apply locked versions to the given environment.
8
+
1
9
  ## 0.2.0
2
10
 
3
11
  - [Improvement] Only showing the 15 most recent version in the info command.
@@ -1,63 +1,8 @@
1
1
  ## Cookbooks
2
2
 
3
- Each cookbook has a pattern applied to it, which is one of Library, Application, or Node, and every
4
- cookbook should reside in its own Git repository.
5
-
6
-
7
- ### Library Cookbooks
8
-
9
- This is the most basic building block of Cookbooks. These types of cookbooks are reusable and are
10
- mixed into other cookbooks to enhance them by:
11
-
12
- - Adding LWRPs that abstract common functionality
13
- - Including Libraries that add Ruby modules/classes for any depending cookbooks
14
-
15
- The goal of these cookbooks is to abstract common things into re-usable building blocks. They often
16
- do not include a single recipe because their job is to solely enhance the Chef primitives. It is
17
- also very common for these cookbooks to not include attributes since there's nothing to configure.
18
-
19
- Library cookbooks may depend on other library cookbooks or application cookbooks. They never depend
20
- on a Node Cookbook and they never depend on a Wrapper cookbook.
21
-
22
- Since the name of an LWRP is derived from the cookbook it is defined in, these cookbooks are named
23
- with that in mind. Pick names that make sense for the way you want LWRPs to appear in recipes.
24
-
25
- Library cookbooks are usually public, and you are strongly encouraged to open source your Library
26
- cookbooks.
27
-
28
-
29
- ### Application Cookbooks
30
-
31
- These describe a single application or a single piece of software, that share the same name as the
32
- cookbook itself. If the application the cookbook manages contains multiple components then each one
33
- is broken up into it's own recipe and the recipe is named after the component it will install.
34
- Things are broken up in this way so you could install various components spread across a number of
35
- nodes within an environment.
36
-
37
- These cookbooks almost always contain a set of attributes which act as the runtime configuration for
38
- the cookbook. These attributes can do something like setting a port number or even describing the
39
- desired state of a service.
40
-
41
- Application cookbooks may depend on Library Cookbooks and other Application Cookbooks. They never
42
- depend on Node Cookbooks. They never depend on a Wrapper or Base Cookbook unless they are intended
43
- to be internal to your organization and will never be distributed to the Chef Community Site.
44
-
45
- These cookbooks are always named after the application they manage, and are usually name-spaced
46
- with your organization name as a prefix `{organization}_{application_cookbook}`.
47
-
48
-
49
- ### Wrapper Cookbooks
50
-
51
- This is the lightest Cookbook out of all the known Cookbook patterns. It does a very simple job of
52
- depending on an Application Cookbook and then exposing a recipe for each recipe found in the
53
- Application Cookbook that it is wrapping.
54
-
55
- Wrapper cookbooks depend on Application Cookbooks only. They do not depend on other Wrapper
56
- Cookbooks, Library Cookbooks, or Environment Cookbooks.
57
-
58
- These cookbooks follow the naming convention `{organization}_{wrapped_cookbook}` or even sometimes
59
- `{application}_{wrapped_cookbook}`. So the RabbitMQ cookbook for Codio would be called
60
- `codio_rabbitmq`.
3
+ Each and every cookbook is either a Node or a Non-Node cookbook, and every cookbook should reside in
4
+ its very own Git repository. Additionally, there is a Base cookbook that must be the very first
5
+ dependency of each Node cookbook.
61
6
 
62
7
 
63
8
  ### Node Cookbooks
@@ -76,16 +21,37 @@ achieved using Chef Environments, where a Node cookbook directly corresponds to
76
21
  of the same name. The Berkshelf lock file is converted into the environment's `cookbook_versions`
77
22
  using the Berkshelf `apply` command.
78
23
 
24
+ Node cookbooks should never be a dependency for any other cookbook.
25
+
79
26
  Node cookbook names are prefixed with `node_`. For example `node_webserver`
80
27
 
81
28
 
29
+ ### Non-Node Cookbook
30
+
31
+ Anything that is not specific to a node, and is required to be reusable, should appear in a Non-Node
32
+ cookbook.
33
+
34
+ Non-Node cookbooks should be consumed and depended on by one or more Node cookbooks, and should
35
+ never be used directly. Easy peasy!
36
+
37
+
82
38
  ### Base Cookbook
83
39
 
84
- All Node Cookbooks require at least one dependency, and that is the Base cookbook. The Base Cookbook
85
- is very similar in nature to a Library Cookbook. It contains several recipes that are common to all
86
- nodes. Examples include configuring NTP and creating system users and SSH access.
40
+ All Node Cookbooks require at least one dependency, and that is the Base cookbook.
41
+
42
+ This is the most basic building block of Cookbooks. These types of cookbooks are reusable and are
43
+ mixed into other Node cookbooks to enhance them by:
44
+
45
+ - Adding LWRPs that abstract common functionality
46
+ - Including Libraries that add Ruby modules/classes for any depending cookbooks
47
+
48
+ The goal of these cookbooks is to abstract common things into re-usable building blocks. It contains
49
+ several recipes that are common to all nodes. Examples include configuring NTP, creating system
50
+ users and SSH access.
51
+
52
+ The default recipe of your Node cookbooks should usually begin with your Base cookbook being
53
+ included first.
87
54
 
88
- The default recipe of your Node cookbooks would be the best place to include your Base cookbook.
89
55
 
90
56
 
91
57
  ## Cookbook Development
@@ -103,12 +69,11 @@ made in that release.
103
69
  [Berkshelf](http://berkshelf.com/) is used to manage Cookbook dependencies in all Cookbooks,
104
70
  especially Node Cookbooks.
105
71
 
106
- Every Cookbook, no matter what type should alwsy contain a Berksfile with at least the following
72
+ Every Cookbook, no matter what type should always containa a Berksfile with at least the following
107
73
  content:
108
74
 
109
75
  ```ruby
110
76
  source "http://berkshelf-api.int.codio.com"
111
-
112
77
  metadata
113
78
  ```
114
79
 
data/README.md CHANGED
@@ -20,15 +20,57 @@ For the full details, read COOKBOOKS.md.
20
20
 
21
21
  ## Installation
22
22
 
23
- Install Cheflow into the ChefDK
23
+ Install Cheflow into the ChefDK:
24
24
 
25
25
  $ chef gem install cheflow
26
26
 
27
27
 
28
+
29
+ ## Commands
30
+
31
+ ### Info
32
+
33
+ cheflow info|i
34
+
35
+ Displays useful information about the current cookbook, including the environments this cookbook is
36
+ being used in, and the list of versions.
37
+
38
+ This is also the default command that will be executed if you run Cheflow without any command.
39
+
40
+ cheflow
41
+
42
+ ### Bump
43
+
44
+ cheflow bump|b
45
+
46
+ Bumps the version of the current cookbook. By default, this will be the dev version (patch). To bump
47
+ the major or minor version, just pass that as the first argument:
48
+
49
+ cheflow bump minor
50
+
51
+ ### Upload
52
+
53
+ cheflow upload|up
54
+
55
+ Upload the current cookbook - the current cookbook being that which is in the current working
56
+ directory. The current version of the cookbook will determine if this upload should be frozen or
57
+ not. If it is a dev version (ie. a patch release), then the uploaded cookbook is not frozen.
58
+
59
+ ### Apply
60
+
61
+ cheflow apply|a
62
+
63
+ Apply the current Berksfile.lock file to the current Node cookbook's environment.
64
+
65
+
66
+
28
67
  ## Workflow
29
68
 
30
69
  The Cheflow workflow is predominantly based on the current Cookbook version, which determines
31
70
  whether to apply version locks from Berksfile.lock to the production environment or not.
71
+ Additionally, it will determine if the current cookbook is a node cookbook or not, and behave
72
+ accordingly.
73
+
32
74
 
33
75
  ### Development Releases
34
76
 
@@ -36,6 +78,17 @@ If the version's patch number is an odd one, ie. a dev release, then the environ
36
78
  specified. If no environment is specified, then it will default to `development`. Cheflow will not
37
79
  allow you to lock a development release to production, even if you specify it.
38
80
 
81
+
82
+ #### Publish a development version of a Non-Node cookbook
83
+
84
+ - Bump the dev version of the non-node cookbook. (eg. 1.0.0 to 1.0.1)
85
+ - Upload the cookbook without freezing: `berks up COOKBOOK --no-freeze`
86
+
87
+ Equivalent Cheflow command:
88
+
89
+ cheflow upload
90
+
91
+
39
92
  #### Create a new development version of a Node cookbook
40
93
 
41
94
  - Bump the dev version. (eg. 1.0.0 to 1.0.1)
@@ -49,6 +102,7 @@ Equivalent Cheflow command:
49
102
 
50
103
  Default environment is `development`.
51
104
 
105
+
52
106
  #### Update development version of a Node cookbook
53
107
 
54
108
  - Upload new unfrozen version: `berks upload COOKBOOK --no-freeze`.
@@ -59,6 +113,7 @@ Cheflow command:
59
113
 
60
114
  Default environment is `development`.
61
115
 
116
+
62
117
  ### Production Releases
63
118
 
64
119
  If the version's patch number is an even one. ie. a production release, then the environment is
@@ -18,12 +18,12 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ["lib"]
20
20
 
21
- spec.add_dependency "semverse", "~> 1.1"
22
- spec.add_dependency "berkshelf", "~> 3.0"
23
- spec.add_dependency "ridley", "~> 4.0"
21
+ spec.add_dependency "semverse", "~> 1.2"
22
+ spec.add_dependency "berkshelf", "~> 3.3"
23
+ spec.add_dependency "ridley", "~> 4.2"
24
24
  spec.add_dependency "ridley-connectors", "~> 2.3"
25
- spec.add_dependency "thor", "~> 0.18"
25
+ spec.add_dependency "thor", "~> 0.19"
26
26
 
27
- spec.add_development_dependency "bundler", "~> 1.5"
27
+ spec.add_development_dependency "bundler", "~> 1.10"
28
28
  spec.add_development_dependency "rake"
29
29
  end
@@ -21,20 +21,14 @@ module Cheflow
21
21
 
22
22
  namespace 'cheflow'
23
23
 
24
- map 'up' => :upgrade
24
+ map 'up' => :upload
25
+ map 'ud' => :update
26
+ map 'a' => :apply
27
+ map 'o' => :outdated
25
28
  map 'i' => :info
29
+ map 'b' => :bump
26
30
  map ["ver", "-v", "--version"] => :version
27
31
 
28
- class_option :verbose,
29
- type: :boolean,
30
- desc: "Output verbose information",
31
- aliases: "-v",
32
- default: false
33
- class_option :debug,
34
- type: :boolean,
35
- desc: "Output debug information",
36
- aliases: "-d",
37
- default: false
38
32
  class_option :berksfile,
39
33
  type: :string,
40
34
  default: nil,
@@ -57,35 +51,131 @@ module Cheflow
57
51
  aliases: "-P",
58
52
  default: nil
59
53
 
60
- # desc "upgrade [environment]", "Upload and apply the current cookbook version to the specified"
61
- # def upgrade(env = 'development')
62
- # end
54
+ desc 'upload', 'Upload the current cookbook'
55
+ long_desc <<-LONGDESC
56
+ Upload the current cookbook - the current cookbook being that which is in the current working
57
+ directory. The current version of the cookbook will determine if this upload should be frozen
58
+ or not. If it is a dev version (ie. a patch release), then the uploaded cookbook is not
59
+ frozen.
60
+ LONGDESC
61
+ def upload
62
+ say "Uploading #{cookbook.type} Cookbook: #{cookbook}"
63
+ begin
64
+ cookbook.upload
65
+ rescue Ridley::Errors::FrozenCookbook => e
66
+ say e, :red
67
+ exit(1)
68
+ end
69
+ end
70
+
71
+ desc 'update [COOKBOOKS]', 'Update the cookbooks (and dependencies) specified in the Berksfile'
72
+ def update(*cookbook_names)
73
+ cookbook.berksfile.update *cookbook_names
74
+ end
75
+
76
+ desc 'outdated [COOKBOOKS]', 'List dependencies that have new versions available that satisfy their constraints'
77
+ def outdated(*names)
78
+ outdated = cookbook.berksfile.outdated(*names)
79
+ Berkshelf.formatter.outdated(outdated)
80
+ end
81
+
82
+ desc 'apply ENVIRONMENT', 'Apply version locks from Berksfile.lock to a Chef environment'
83
+ def apply(environment_name)
84
+ unless cookbook.node_cookbook?
85
+ say "Cannot apply Non-Node Cookbooks. Run this again for a Node Cookbook", :red
86
+ exit(1)
87
+ end
88
+
89
+ lockfile = File.join(cookbook.path, Berkshelf::Lockfile::DEFAULT_FILENAME)
90
+ unless File.exist?(lockfile)
91
+ say "No lockfile found at #{lockfile}", :red
92
+ exit(1)
93
+ end
94
+
95
+ full_environment_name = "node_#{cookbook.node_name}"
96
+ full_environment_name += "_#{environment_name}" if environment_name != 'production'
97
+
98
+ say "Applying Node Cookbook #{cookbook} and its dependencies to #{environment_name} (#{full_environment_name})", :bold
99
+
100
+ if environment_name == 'production'
101
+ exit(1) unless yes?('Are you sure you wish to apply locked versions to production?', :red)
102
+ end
103
+
104
+ lockfile = Berkshelf::Lockfile.from_file(lockfile)
105
+ begin
106
+ unless lockfile.apply(full_environment_name)
107
+ error "Failed to apply locked versions"
108
+ exit(1)
109
+ end
110
+ rescue Berkshelf::EnvironmentNotFound => e
111
+ say "Unable to apply locked versions. #{e}", :red
112
+ exit(1)
113
+ end
114
+ end
63
115
 
64
116
  desc 'version', 'Display version information'
65
117
  def version
66
118
  say "Cheflow v#{Cheflow::VERSION}"
67
119
  end
68
120
 
121
+ desc 'bump', 'Bump the version, which is by default the patch version.'
122
+ long_desc <<-LONGDESC
123
+ Pass in the Semver level as the first argument to specify which level to bump.
124
+ Available levels are `major`, `minor` or `patch`.
125
+
126
+ Bump the patch version:
127
+ \x5> $ cheflow bump
128
+
129
+ Bump the major version:
130
+ \x5> $ cheflow bump major
131
+ LONGDESC
132
+ def bump(level='patch')
133
+ from = cookbook.version
134
+ major, minor, patch = from.major, from.minor, from.patch
135
+ binding.eval("#{level} = #{from.send(level)+1}")
136
+ to = "#{major}.#{minor}.#{patch}"
137
+
138
+ version_file = File.join(cookbook.path, 'VERSION')
139
+ if File.exist? version_file
140
+ File.open(version_file, 'w') { |file| file.puts to }
141
+ say "Bumped #{level} version from #{from} to #{to}", :green
142
+ else
143
+ say "The 'VERSION' file does not exist, so cannot bump the #{level} version", :red
144
+ exit(1)
145
+ end
146
+ end
147
+
69
148
  desc 'info', 'Display information about the cookbook'
70
149
  def info
71
- say "#{cookbook.type.capitalize} Cookbook: #{cookbook}"
150
+ say "#{cookbook.type.capitalize} Cookbook: #{cookbook}", :bold
72
151
  say cookbook.path
73
152
  say
74
- say "Environments: #{cookbook.node_environments.join("\n ")}"
75
- say
76
-
77
- say 'Versions: (most recent)'
78
-
79
- if (pv = cookbook.prod_versions).count > 15
80
- say " Production: #{pv[0,15].join(', ')} (...)"
81
- else
82
- say " Production: #{pv.join(', ')}"
83
- end
84
-
85
- if (dv = cookbook.dev_versions).count > 15
86
- say " Development: #{dv[0,15].join(', ')} (...)"
87
- else
88
- say " Development: #{dv.join(', ')}"
153
+ say "Environments: #{cookbook.node_environments(true).join("\n ")}"
154
+
155
+ pv = cookbook.prod_versions
156
+ dv = cookbook.dev_versions
157
+
158
+ if dv.count > 0 || pv.count > 0
159
+ say
160
+ say 'Versions: (most recent)'
161
+
162
+ if pv.count > 0
163
+ if pv.count > 10
164
+ say " Production: #{pv[0,10].join(', ')} (...)"
165
+ else
166
+ say " Production: #{pv.join(', ')}"
167
+ end
168
+ end
169
+
170
+ if dv.count > 0
171
+ if dv.count > 10
172
+ say " Development: #{dv[0,10].join(', ')} (...)"
173
+ else
174
+ say " Development: #{dv.join(', ')}"
175
+ end
176
+ end
177
+
178
+ say
89
179
  end
90
180
  end
91
181
 
@@ -24,6 +24,11 @@ module Cheflow
24
24
  @name ||= metadata.name
25
25
  end
26
26
 
27
+ # The name of the Node cookbook without the "node_" prefix.
28
+ def node_name
29
+ @node_name ||= node_cookbook? ? name.sub(/^node_/, '') : name
30
+ end
31
+
27
32
  def path
28
33
  @path ||= File.dirname(berksfile.filepath)
29
34
  end
@@ -32,7 +37,7 @@ module Cheflow
32
37
  @type ||= if name.start_with? 'node_'
33
38
  :node
34
39
  else
35
- :application
40
+ :non_node
36
41
  end
37
42
  end
38
43
 
@@ -40,10 +45,6 @@ module Cheflow
40
45
  type == :node
41
46
  end
42
47
 
43
- def application_cookbook?
44
- type == :application
45
- end
46
-
47
48
  def node_environment_objects
48
49
  @node_environment_objects ||= begin
49
50
  if node_cookbook?
@@ -54,12 +55,14 @@ module Cheflow
54
55
  end
55
56
  end
56
57
 
57
- def node_environments
58
+ def node_environments(with_versions=false)
58
59
  @node_environments ||= node_environment_objects.map do |e|
59
60
  env = e.name.gsub /^#{name}_/, ''
60
61
  env = env == name ? 'production' : env
61
- "#{env.ljust(12)} (#{e.cookbook_versions[name]})"
62
- end.sort
62
+ out = with_versions ? env.ljust(12) : env
63
+ out += " (#{e.cookbook_versions[name]})" if with_versions
64
+ out
65
+ end.compact.sort
63
66
  end
64
67
 
65
68
  def versions
@@ -82,6 +85,10 @@ module Cheflow
82
85
  ridley.cookbook.find(name, version).frozen?
83
86
  end
84
87
 
88
+ def upload
89
+ ridley.cookbook.upload path, freeze: !version.patch.odd?
90
+ end
91
+
85
92
 
86
93
  private
87
94
 
@@ -1,3 +1,3 @@
1
1
  module Cheflow
2
- VERSION = "0.2.0"
2
+ VERSION = "0.3.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cheflow
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joel Moss
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-02-17 00:00:00.000000000 Z
11
+ date: 2015-07-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: semverse
@@ -16,42 +16,42 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.1'
19
+ version: '1.2'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '1.1'
26
+ version: '1.2'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: berkshelf
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '3.0'
33
+ version: '3.3'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '3.0'
40
+ version: '3.3'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: ridley
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '4.0'
47
+ version: '4.2'
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '4.0'
54
+ version: '4.2'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: ridley-connectors
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -72,28 +72,28 @@ dependencies:
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: '0.18'
75
+ version: '0.19'
76
76
  type: :runtime
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: '0.18'
82
+ version: '0.19'
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: bundler
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: '1.5'
89
+ version: '1.10'
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: '1.5'
96
+ version: '1.10'
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: rake
99
99
  requirement: !ruby/object:Gem::Requirement
@@ -150,8 +150,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
150
150
  version: '0'
151
151
  requirements: []
152
152
  rubyforge_project:
153
- rubygems_version: 2.4.5
153
+ rubygems_version: 2.4.4
154
154
  signing_key:
155
155
  specification_version: 4
156
156
  summary: A Cookbook-Centric workflow tool
157
157
  test_files: []
158
+ has_rdoc: