chef-taste 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. data/.gitignore +17 -0
  2. data/Gemfile +4 -0
  3. data/LICENSE.txt +22 -0
  4. data/README.md +108 -0
  5. data/Rakefile +1 -0
  6. data/bin/taste +5 -0
  7. data/chef-taste.gemspec +31 -0
  8. data/lib/chef/taste.rb +41 -0
  9. data/lib/chef/taste/changelog.rb +107 -0
  10. data/lib/chef/taste/cli.rb +46 -0
  11. data/lib/chef/taste/dependency.rb +72 -0
  12. data/lib/chef/taste/dependency_checker.rb +95 -0
  13. data/lib/chef/taste/display.rb +84 -0
  14. data/lib/chef/taste/errors.rb +32 -0
  15. data/lib/chef/taste/symbols.rb +30 -0
  16. data/lib/chef/taste/version.rb +35 -0
  17. data/test/cookbooks/curry/.gitignore +17 -0
  18. data/test/cookbooks/curry/.kitchen.yml +27 -0
  19. data/test/cookbooks/curry/Berksfile +3 -0
  20. data/test/cookbooks/curry/Gemfile +7 -0
  21. data/test/cookbooks/curry/LICENSE +3 -0
  22. data/test/cookbooks/curry/README.md +13 -0
  23. data/test/cookbooks/curry/Thorfile +12 -0
  24. data/test/cookbooks/curry/Vagrantfile +86 -0
  25. data/test/cookbooks/curry/chefignore +96 -0
  26. data/test/cookbooks/curry/metadata.rb +12 -0
  27. data/test/cookbooks/curry/recipes/default.rb +8 -0
  28. data/test/cookbooks/fried_rice/.gitignore +17 -0
  29. data/test/cookbooks/fried_rice/.kitchen.yml +27 -0
  30. data/test/cookbooks/fried_rice/Berksfile +3 -0
  31. data/test/cookbooks/fried_rice/Gemfile +7 -0
  32. data/test/cookbooks/fried_rice/LICENSE +3 -0
  33. data/test/cookbooks/fried_rice/README.md +13 -0
  34. data/test/cookbooks/fried_rice/Thorfile +12 -0
  35. data/test/cookbooks/fried_rice/Vagrantfile +86 -0
  36. data/test/cookbooks/fried_rice/chefignore +96 -0
  37. data/test/cookbooks/fried_rice/metadata.rb +12 -0
  38. data/test/cookbooks/fried_rice/recipes/default.rb +8 -0
  39. data/test/cookbooks/noodles/.gitignore +17 -0
  40. data/test/cookbooks/noodles/.kitchen.yml +27 -0
  41. data/test/cookbooks/noodles/Berksfile +3 -0
  42. data/test/cookbooks/noodles/Gemfile +7 -0
  43. data/test/cookbooks/noodles/LICENSE +3 -0
  44. data/test/cookbooks/noodles/README.md +13 -0
  45. data/test/cookbooks/noodles/Thorfile +12 -0
  46. data/test/cookbooks/noodles/Vagrantfile +86 -0
  47. data/test/cookbooks/noodles/chefignore +96 -0
  48. data/test/cookbooks/noodles/metadata.rb +11 -0
  49. data/test/cookbooks/noodles/recipes/default.rb +8 -0
  50. data/test/cookbooks/water/.gitignore +17 -0
  51. data/test/cookbooks/water/.kitchen.yml +27 -0
  52. data/test/cookbooks/water/Berksfile +3 -0
  53. data/test/cookbooks/water/Gemfile +7 -0
  54. data/test/cookbooks/water/LICENSE +3 -0
  55. data/test/cookbooks/water/README.md +13 -0
  56. data/test/cookbooks/water/Thorfile +12 -0
  57. data/test/cookbooks/water/Vagrantfile +86 -0
  58. data/test/cookbooks/water/chefignore +96 -0
  59. data/test/cookbooks/water/metadata.rb +8 -0
  60. data/test/cookbooks/water/recipes/default.rb +8 -0
  61. metadata +297 -0
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in chef-taste.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Kannan Manickam <me@arangamani.net>
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,108 @@
1
+ # Chef::Taste
2
+
3
+ Chef Taste is a simple command line utility to check a cookbook's dependency status.
4
+ It will list the dependent cookbooks in a tabular format with the version information,
5
+ status, and the changelog (if possible) for out-of-date cookbooks.
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ gem 'chef-taste'
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install chef-taste
20
+
21
+ ## Usage
22
+
23
+ When you are inside the cookbooks directory, simply type `taste` to taste the cookbook.
24
+ The `metadata.rb` of the cookbook is parsed to obtain the dependencies. It will display
25
+ a table that contains the following rows:
26
+
27
+ * `Name` - The name of the cookbook
28
+ * `Requirement` - The version requirement specified in the metadata
29
+ * `Used` - The final version used based on the requirement constraint
30
+ * `Latest` - The latest version available in the community site
31
+ * `Status` - The status of the cookbook: up-to-date (a green tick mark) or out-of-date (a red x mark)
32
+ * `Changelog` - The changelog of out-of-date cookbooks if available.
33
+
34
+ The overall status will also be displayed in the bottom of the table.
35
+
36
+ ### Changelog
37
+ Most of the cookbooks are hosted in Github and are tagged for every release.
38
+ The changelog is computed by obtaining the source URL provided in the community site and
39
+ finding the tags being used and the latest tag and displaying a compare view that
40
+ compares these two tags. This URL is then shortened using goo.gl URL shortener to fit the table.
41
+
42
+ The details are obtained only for cookbooks available in the community site. Other cookbooks are
43
+ displayed but will simply have `N/A` in their details.
44
+
45
+ ### Examples
46
+
47
+ These examples are based on the cookbooks available in the `test/cookbooks` directory
48
+ in this repository.
49
+
50
+ #### 1. fried_rice cookbook
51
+
52
+ ```bash
53
+ kannanmanickam@mac fried_rice$ taste
54
+ +------------+-------------+--------+--------+--------+----------------------+
55
+ | Name | Requirement | Used | Latest | Status | Changelog |
56
+ +------------+-------------+--------+--------+--------+----------------------+
57
+ | ntp | ~> 1.4.0 | 1.4.0 | 1.5.0 | ✖ | http://goo.gl/qsfgwA |
58
+ | swap | = 0.3.5 | 0.3.5 | 0.3.6 | ✖ | http://goo.gl/vZtUQJ |
59
+ | windows | >= 0.0.0 | 1.11.0 | 1.11.0 | ✔ | |
60
+ | awesome_cb | >= 0.0.0 | N/A | N/A | N/A | |
61
+ +------------+-------------+--------+--------+--------+----------------------+
62
+ Status: out-of-date ( ✖ )
63
+ ```
64
+
65
+ #### 2. noodles cookbook
66
+
67
+ ```bash
68
+ kannanmanickam@mac noodles$ taste
69
+ +---------+-------------+--------+--------+--------+----------------------+
70
+ | Name | Requirement | Used | Latest | Status | Changelog |
71
+ +---------+-------------+--------+--------+--------+----------------------+
72
+ | mysql | ~> 3.0.12 | 3.0.12 | 3.0.12 | ✔ | |
73
+ | apache2 | ~> 1.7.0 | 1.7.0 | 1.8.4 | ✖ | http://goo.gl/9ejcpi |
74
+ | windows | >= 0.0.0 | 1.11.0 | 1.11.0 | ✔ | |
75
+ +---------+-------------+--------+--------+--------+----------------------+
76
+ Status: out-of-date ( ✖ )
77
+ ```
78
+
79
+ #### 3. curry cookbook
80
+
81
+ ```bash
82
+ kannanmanickam@mac curry$ taste
83
+ +-----------------+-------------+-------+--------+--------+-----------+
84
+ | Name | Requirement | Used | Latest | Status | Changelog |
85
+ +-----------------+-------------+-------+--------+--------+-----------+
86
+ | ntp | >= 0.0.0 | 1.5.0 | 1.5.0 | ✔ | |
87
+ | lvm | >= 0.0.0 | 1.0.0 | 1.0.0 | ✔ | |
88
+ | application | >= 0.0.0 | 4.1.0 | 4.1.0 | ✔ | |
89
+ | application_php | >= 0.0.0 | 2.0.0 | 2.0.0 | ✔ | |
90
+ +-----------------+-------------+-------+--------+--------+-----------+
91
+ Status: up-to-date ( ✔ )
92
+ ```
93
+
94
+ #### 4. water cookbook
95
+
96
+ ```bash
97
+ kannanmanickam@mac water$ taste
98
+ No dependent cookbooks
99
+ ```
100
+
101
+
102
+ ## Contributing
103
+
104
+ 1. Fork it
105
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
106
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
107
+ 4. Push to the branch (`git push origin my-new-feature`)
108
+ 5. Create new Pull Request
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+ $:.push File.expand_path("../../lib", __FILE__)
3
+ require 'chef/taste'
4
+
5
+ Chef::Taste::Cli.start
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'chef/taste/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "chef-taste"
8
+ spec.version = Chef::Taste::VERSION
9
+ spec.authors = ["Kannan Manickam"]
10
+ spec.email = ["me@arangamani.net"]
11
+ spec.description = %q{This gem checks if updated versions of dependent cookbooks are available for the cookbook}
12
+ spec.summary = %q{Chef cookbook dependency update checker}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+
24
+ spec.add_dependency "solve", "~> 0.8.0"
25
+ spec.add_dependency "berkshelf", "~> 2.0.10"
26
+ spec.add_dependency "thor", "~> 0.18.0"
27
+ spec.add_dependency "terminal-table", "~> 1.4.5"
28
+ spec.add_dependency "googl", "~> 0.6.3"
29
+ spec.add_dependency "octokit", "~> 2.5.0"
30
+ spec.add_dependency "colorize", "~> 0.5.8"
31
+ end
@@ -0,0 +1,41 @@
1
+ #
2
+ # Copyright (c) 2013 Kannan Manickam <me@arangamani.net>
3
+ #
4
+ # MIT License
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person obtaining
7
+ # a copy of this software and associated documentation files (the
8
+ # "Software"), to deal in the Software without restriction, including
9
+ # without limitation the rights to use, copy, modify, merge, publish,
10
+ # distribute, sublicense, and/or sell copies of the Software, and to
11
+ # permit persons to whom the Software is furnished to do so, subject to
12
+ # the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be
15
+ # included in all copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
+ #
25
+
26
+ require 'berkshelf'
27
+ require 'colorize'
28
+ require 'googl'
29
+ require 'octokit'
30
+ require 'ridley'
31
+ require 'terminal-table'
32
+ require 'thor'
33
+
34
+ require_relative 'taste/cli'
35
+ require_relative 'taste/changelog'
36
+ require_relative 'taste/dependency'
37
+ require_relative 'taste/dependency_checker'
38
+ require_relative 'taste/display'
39
+ require_relative 'taste/errors'
40
+ require_relative 'taste/symbols'
41
+ require_relative 'taste/version'
@@ -0,0 +1,107 @@
1
+ #
2
+ # Copyright (c) 2013 Kannan Manickam <me@arangamani.net>
3
+ #
4
+ # MIT License
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person obtaining
7
+ # a copy of this software and associated documentation files (the
8
+ # "Software"), to deal in the Software without restriction, including
9
+ # without limitation the rights to use, copy, modify, merge, publish,
10
+ # distribute, sublicense, and/or sell copies of the Software, and to
11
+ # permit persons to whom the Software is furnished to do so, subject to
12
+ # the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be
15
+ # included in all copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
+ #
25
+
26
+ require 'chef/taste'
27
+
28
+ module Chef
29
+ module Taste
30
+ # The Changelog class that computes the changelog if the current version
31
+ # being used and the latest version are different and the changelog is computable.
32
+ #
33
+ class Changelog
34
+ class << self
35
+ # Compute the changelog for the dependent cookbook if available
36
+ #
37
+ # @param dep [Dependency] the dependent cookbook
38
+ #
39
+ # @return [String] the goo.gl shortened URL for the changelog
40
+ #
41
+ def compute(dep)
42
+ changelog_url =
43
+ if dep.source_url =~ /^(https?:\/\/)?github.com\/(.*)\/(.*)$/
44
+ GithubChangelog.new("#{$2}/#{$3}", dep.version_used, dep.latest).compute
45
+ else
46
+ nil
47
+ end
48
+ Googl.shorten(changelog_url).short_url unless changelog_url.nil?
49
+ end
50
+ end
51
+
52
+ # The class for computing the changelog for cookbooks hosted in Github.
53
+ #
54
+ class GithubChangelog
55
+ # The Github repository
56
+ attr_reader :repo
57
+
58
+ # The version to compare from
59
+ attr_reader :from_version
60
+
61
+ # The version to compare to
62
+ attr_reader :to_version
63
+
64
+ # Constructor
65
+ #
66
+ # @param repo [String] the Github repo
67
+ # @param from_version [String] the version to compare from
68
+ # @param to_version [String] the version to compare to
69
+ #
70
+ # @return [GithubChangelog] the Github changelog object
71
+ #
72
+ def initialize(repo, from_version, to_version)
73
+ @repo = repo
74
+ @from_version = from_version
75
+ @to_version = to_version
76
+ end
77
+
78
+ # Computes the changelog URL for Github repositories
79
+ #
80
+ # @return [String] the computed changelog URL
81
+ #
82
+ def compute
83
+ tags = Octokit.tags(repo)
84
+ from_tag = nil
85
+ to_tag = nil
86
+ tags.each do |tag|
87
+ tag_name = tag.name
88
+ from_tag = tag_name if tag_name =~ /v?#{from_version}/
89
+ to_tag = tag_name if tag_name =~ /v?#{to_version}/
90
+ end
91
+ compare_url(from_tag, to_tag) if from_tag && to_tag
92
+ end
93
+
94
+ # Returns the compare URL for comparing two tags on Github
95
+ #
96
+ # @param from_tag [String] the tag to compare from
97
+ # @param to_tag [String] the tag to compare to
98
+ #
99
+ # @return [String] the Github URL to compare given two tags
100
+ #
101
+ def compare_url(from_tag, to_tag)
102
+ "https://github.com/#{repo}/compare/#{from_tag}...#{to_tag}"
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,46 @@
1
+ #
2
+ # Copyright (c) 2013 Kannan Manickam <me@arangamani.net>
3
+ #
4
+ # MIT License
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person obtaining
7
+ # a copy of this software and associated documentation files (the
8
+ # "Software"), to deal in the Software without restriction, including
9
+ # without limitation the rights to use, copy, modify, merge, publish,
10
+ # distribute, sublicense, and/or sell copies of the Software, and to
11
+ # permit persons to whom the Software is furnished to do so, subject to
12
+ # the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be
15
+ # included in all copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
+ #
25
+
26
+ require 'chef/taste'
27
+
28
+ module Chef
29
+ module Taste
30
+ # The CLI for chef-taste
31
+ #
32
+ class Cli < Thor
33
+ # The default task
34
+ default_task :check
35
+
36
+ desc 'check', 'Check status of dependent cookbooks'
37
+ # The check command
38
+ def check
39
+ dependencies = DependencyChecker.check
40
+ Display.print(dependencies)
41
+ rescue NotACookbookError
42
+ puts "The path is not a cookbook path".red
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,72 @@
1
+ #
2
+ # Copyright (c) 2013 Kannan Manickam <me@arangamani.net>
3
+ #
4
+ # MIT License
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person obtaining
7
+ # a copy of this software and associated documentation files (the
8
+ # "Software"), to deal in the Software without restriction, including
9
+ # without limitation the rights to use, copy, modify, merge, publish,
10
+ # distribute, sublicense, and/or sell copies of the Software, and to
11
+ # permit persons to whom the Software is furnished to do so, subject to
12
+ # the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be
15
+ # included in all copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
+ #
25
+
26
+ require 'chef/taste'
27
+
28
+ module Chef
29
+ module Taste
30
+ # The class that contains information about a dependent cookbook
31
+ #
32
+ class Dependency
33
+ # The name of the dependency
34
+ attr_reader :name
35
+
36
+ # The requirement for the dependency
37
+ attr_reader :requirement
38
+
39
+ # The version of cookbook used after applying the version constraint
40
+ attr_accessor :version_used
41
+
42
+ # The latest version available for the dependency
43
+ attr_accessor :latest
44
+
45
+ # The status of the dependency
46
+ attr_accessor :status
47
+
48
+ # The source URL for a cookbook
49
+ attr_accessor :source_url
50
+
51
+ # The changelog link for the dependency if available
52
+ attr_accessor :changelog
53
+
54
+ # Constructor
55
+ #
56
+ # @param name [String] the name of the dependent cookbook
57
+ # @param requirement [String] the version requirement for dependent cookbook
58
+ #
59
+ # @return [Dependency] the Dependency object
60
+ #
61
+ def initialize(name, requirement)
62
+ @name = name
63
+ @requirement = requirement
64
+ @version_used = 'N/A'
65
+ @latest = 'N/A'
66
+ @status = 'N/A'
67
+ @source_url = nil
68
+ @changelog = nil
69
+ end
70
+ end
71
+ end
72
+ end