puppet-modulebuilder 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 1d54453c16cf48b673985f7c0212fd2487d346938da71e4406a05837474c9018
4
+ data.tar.gz: 91164b968b1d6886f5e254b651cea4555c6a3ecdeddfb1fbabb8b22ef8ec8a67
5
+ SHA512:
6
+ metadata.gz: a2896cd01a49b78b35bcfbca9b5d3dc47e45eed641d60386c351ba0eeb90ef502f453139fc108b54d1e7b8344ef794aeb43fa56da3a1418050751887cab1fb1f
7
+ data.tar.gz: aba5ef82de309bc9b3da471ad43d920632ee19b75fd338b70f1252f199c3e46dad279356496179d05b80a1e5387a6f9d67723dfe955fe6fbd6916c56517a8098
@@ -0,0 +1,2 @@
1
+ # Setting ownership to the modules team
2
+ * @puppetlabs/modules @puppetlabs/pdk
data/.gitignore ADDED
@@ -0,0 +1,15 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ Gemfile.local
11
+
12
+ # rspec failure tracking
13
+ .rspec_status
14
+
15
+ Gemfile.lock
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,173 @@
1
+ require: rubocop-rspec
2
+
3
+ AllCops:
4
+ TargetRubyVersion: 2.3
5
+ Exclude:
6
+ # binstubs, and other utilities
7
+ - bin/**/*
8
+ - vendor/**/*
9
+ - vendor/**/.*
10
+ # package testing gems
11
+ - package-testing/vendor/**/*
12
+ - package-testing/vendor/**/.*
13
+ # Any thing in test fixtures
14
+ - spec/fixtures/**/*
15
+
16
+ # Metrics, excludes complexity and sizing metrics for now, as ruby's defaults are very strict
17
+ Metrics/AbcSize:
18
+ Enabled: False
19
+
20
+ Metrics/BlockLength:
21
+ Description: rspec uses long describe blocks, so allow long blocks under spec/
22
+ Enabled: False
23
+ Exclude:
24
+ - 'spec/**/*.rb'
25
+
26
+ Metrics/ClassLength:
27
+ Enabled: False
28
+
29
+ Metrics/CyclomaticComplexity:
30
+ Enabled: False
31
+
32
+ Layout/LineLength:
33
+ Description: People have wide screens, use them.
34
+ Max: 200
35
+
36
+ Metrics/MethodLength:
37
+ Enabled: False
38
+
39
+ Metrics/ModuleLength:
40
+ Enabled: False
41
+
42
+ Metrics/ParameterLists:
43
+ Enabled: False
44
+
45
+ Metrics/PerceivedComplexity:
46
+ Enabled: False
47
+
48
+ # RSpec cops
49
+ RSpec/BeforeAfterAll:
50
+ Description: Beware of using after(:all) as it may cause state to leak between tests. A necessary evil in acceptance testing.
51
+ Exclude:
52
+ - 'spec/acceptance/**/*.rb'
53
+ - 'package-testing/spec/package/**/*.rb'
54
+
55
+ RSpec/DescribeClass:
56
+ Description: This cop does not account for rspec-puppet, and beaker-rspec usage.
57
+ Enabled: False
58
+
59
+ RSpec/HookArgument:
60
+ Description: Prefer explicit :each argument, matching existing module's style
61
+ EnforcedStyle: each
62
+
63
+ RSpec/NestedGroups:
64
+ Description: Nested groups can lead to cleaner tests with less duplication
65
+ Max: 10
66
+
67
+ RSpec/ExampleLength:
68
+ Description: Forcing short examples leads to the creation of one-time use let() helpers
69
+ Enabled: False
70
+
71
+ RSpec/MessageSpies:
72
+ EnforcedStyle: receive
73
+
74
+ RSpec/ScatteredSetup:
75
+ Enabled: False
76
+
77
+ # This is fine
78
+ RSpec/ImplicitSubject:
79
+ Enabled: false
80
+
81
+ # Set a reasonble max level
82
+ RSpec/MultipleExpectations:
83
+ Max: 10
84
+
85
+ # Nested contexts don't comply
86
+ RSpec/ContextWording:
87
+ Enabled: false
88
+
89
+ # This is allowed
90
+ RSpec/SubjectStub:
91
+ Enabled: false
92
+
93
+ # Doesn't help readability
94
+ RSpec/PredicateMatcher:
95
+ Enabled: false
96
+
97
+ # Style Cops
98
+ Style/AsciiComments:
99
+ Description: Names, non-english speaking communities.
100
+ Enabled: False
101
+
102
+ Style/BlockDelimiters:
103
+ Description: Prefer braces for chaining. Mostly an aesthetical choice. Better to be consistent then.
104
+ EnforcedStyle: braces_for_chaining
105
+
106
+ Style/ClassAndModuleChildren:
107
+ Description: Compact style reduces the required amount of indentation.
108
+ EnforcedStyle: compact
109
+
110
+ Style/EmptyElse:
111
+ Description: Enforce against empty else clauses, but allow `nil` for clarity.
112
+ EnforcedStyle: empty
113
+
114
+ Style/FormatString:
115
+ Description: Following the main puppet project's style, prefer the % format format.
116
+ EnforcedStyle: percent
117
+
118
+ Style/FormatStringToken:
119
+ Description: Following the main puppet project's style, prefer the simpler template tokens over annotated ones.
120
+ EnforcedStyle: template
121
+
122
+ Style/IfUnlessModifier:
123
+ Description: Post-fix `if` modifiers are hard to parse for newcomers. We don't want to encourage them. Post-fix `unless` modifiers could be nice in some cases, but the Cop doesn't differentiate.
124
+ Enabled: false
125
+
126
+ Style/Lambda:
127
+ Description: Prefer the keyword for easier discoverability.
128
+ EnforcedStyle: literal
129
+
130
+ Style/MethodCalledOnDoEndBlock:
131
+ Enabled: true
132
+
133
+ Style/RegexpLiteral:
134
+ Description: Community preference. See https://github.com/voxpupuli/modulesync_config/issues/168
135
+ EnforcedStyle: percent_r
136
+
137
+ Style/SymbolProc:
138
+ Description: SymbolProc notation is not discoverable
139
+ Enabled: false
140
+
141
+ Style/TernaryParentheses:
142
+ Description: Checks for use of parentheses around ternary conditions. Enforce parentheses on complex expressions for better readability, but seriously consider breaking it up.
143
+ EnforcedStyle: require_parentheses_when_complex
144
+
145
+ Style/TrailingCommaInArguments:
146
+ Description: Prefer always trailing comma on multiline argument lists. This makes diffs, and re-ordering nicer.
147
+ EnforcedStyleForMultiline: comma
148
+
149
+ Style/TrailingCommaInArrayLiteral:
150
+ Description: Prefer always trailing comma on multiline literals. This makes diffs, and re-ordering nicer.
151
+ EnforcedStyleForMultiline: comma
152
+
153
+ Style/TrailingCommaInHashLiteral:
154
+ Description: Prefer always trailing comma on multiline literals. This makes diffs, and re-ordering nicer.
155
+ EnforcedStyleForMultiline: comma
156
+
157
+ Style/SymbolArray:
158
+ Description: Using percent style obscures symbolic intent of array's contents.
159
+ Enabled: true
160
+ EnforcedStyle: brackets
161
+
162
+ Style/HashEachMethods:
163
+ Enabled: true
164
+
165
+ Style/HashTransformKeys:
166
+ Enabled: true
167
+
168
+ Style/HashTransformValues:
169
+ Enabled: true
170
+
171
+ # Enforce LF line endings, even when on Windows
172
+ Layout/EndOfLine:
173
+ EnforcedStyle: lf
data/.travis.yml ADDED
@@ -0,0 +1,14 @@
1
+ ---
2
+ os: linux
3
+ language: ruby
4
+ cache: bundler
5
+ jobs:
6
+ include:
7
+ - rvm: 2.5.7
8
+ env:
9
+ - SIMPLECOV=yes
10
+ before_install: gem install bundler -v 2.1.4
11
+ - rvm: 2.4.9
12
+ before_install: gem install bundler -v 2.1.4
13
+ - rvm: 2.1.9
14
+ script: "bundle exec rake spec # don't try to run rubocop on ancient ruby"
data/CHANGELOG.md ADDED
@@ -0,0 +1,3 @@
1
+ ## 0.1.0
2
+
3
+ This is the initial release of the project.
data/Gemfile ADDED
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ # Specify your gem's dependencies in puppet-modulebuilder.gemspec
6
+ gemspec
7
+
8
+ group :development do
9
+ gem 'rake', '~> 12.0'
10
+ gem 'rspec', '~> 3.0'
11
+ if Gem::Version.new(RUBY_VERSION.dup) >= Gem::Version.new('2.3.0')
12
+ gem 'rubocop', '~> 0.68'
13
+ gem 'rubocop-rspec', '~> 1.38'
14
+
15
+ gem 'codecov', '~> 0.1'
16
+ gem 'simplecov', '~> 0.18'
17
+ gem 'simplecov-console', '~> 0.6'
18
+ end
19
+
20
+ if Gem::Version.new(RUBY_VERSION.dup) >= Gem::Version.new('2.5.0')
21
+ gem 'puppet', '~> 6.0'
22
+ elsif Gem::Version.new(RUBY_VERSION.dup) >= Gem::Version.new('2.4.0')
23
+ gem 'puppet', '~> 5.0' # rubocop:disable Bundler/DuplicatedGem Nope, not a duplicate!
24
+ else
25
+ gem 'puppet', '~> 4.0' # rubocop:disable Bundler/DuplicatedGem Nope, not a duplicate!
26
+ end
27
+ end
28
+
29
+ # Evaluate Gemfile.local and ~/.gemfile if they exist
30
+ extra_gemfiles = [
31
+ "#{__FILE__}.local",
32
+ File.join(Dir.home, '.gemfile'),
33
+ ]
34
+
35
+ extra_gemfiles.each do |gemfile|
36
+ if File.file?(gemfile) && File.readable?(gemfile)
37
+ eval(File.read(gemfile), binding) # rubocop:disable Security/Eval
38
+ end
39
+ end
data/LICENSE ADDED
@@ -0,0 +1,201 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "[]"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright [yyyy] [name of copyright owner]
190
+
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
194
+
195
+ http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
data/README.md ADDED
@@ -0,0 +1,38 @@
1
+ [![Build Status](https://travis-ci.com/puppetlabs/puppet-modulebuilder.svg?branch=master)](https://travis-ci.com/puppetlabs/puppet-modulebuilder) [![Build status](https://ci.appveyor.com/api/projects/status/j9tosvq4a09iw0bx/branch/master?svg=true)](https://ci.appveyor.com/project/puppetlabs/puppet-modulebuilder/branch/master)
2
+
3
+ # Puppet::Modulebuilder
4
+
5
+ Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/puppet/modulebuilder`. To experiment with that code, run `bin/console` for an interactive prompt.
6
+
7
+ TODO: Delete this and the text above, and describe your gem
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ ```ruby
14
+ gem 'puppet-modulebuilder'
15
+ ```
16
+
17
+ And then execute:
18
+
19
+ $ bundle install
20
+
21
+ Or install it yourself as:
22
+
23
+ $ gem install puppet-modulebuilder
24
+
25
+ ## Usage
26
+
27
+ TODO: Write usage instructions here
28
+
29
+ ## Development
30
+
31
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
32
+
33
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
34
+
35
+ ## Contributing
36
+
37
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/puppet-modulebuilder.
38
+
data/Rakefile ADDED
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+ begin
6
+ # make rubocop optional to deal with ruby 2.1
7
+ require 'rubocop/rake_task'
8
+
9
+ RuboCop::RakeTask.new(:rubocop) do |task|
10
+ task.options = %w[-D -S -E]
11
+ end
12
+
13
+ task default: [:rubocop]
14
+ rescue LoadError => e
15
+ puts "Can't load 'rubocop/rake_task': #{e.inspect}"
16
+ end
17
+
18
+ RSpec::Core::RakeTask.new(:spec) do |t|
19
+ t.pattern = 'spec/unit/**/*_spec.rb'
20
+ end
21
+
22
+ RSpec::Core::RakeTask.new(:acceptance) do |t|
23
+ t.pattern = 'spec/acceptance/**/*_spec.rb'
24
+ end
25
+
26
+ task default: [:spec, :acceptance]
data/appveyor.yml ADDED
@@ -0,0 +1,25 @@
1
+ ---
2
+ install:
3
+ - set PATH=C:\Ruby25-x64\bin;%PATH%
4
+ - set SIMPLECOV=yes
5
+ - gem install bundler -v 2.1.4
6
+ - bundle -v
7
+ - bundle install --retry 2
8
+
9
+ build: off
10
+
11
+ branches:
12
+ only:
13
+ - master
14
+
15
+ before_test:
16
+ - bundle env
17
+ - type Gemfile.lock
18
+
19
+ test_script:
20
+ - bundle exec rake
21
+
22
+ # Uncomment this block to enable RDP access to the AppVeyor test instance for
23
+ # debugging purposes.
24
+ # on_finish:
25
+ # - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'puppet/modulebuilder/version'
4
+ require 'puppet/modulebuilder/builder'
@@ -0,0 +1,414 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'logger'
4
+
5
+ module Puppet::Modulebuilder
6
+ # Class to build Puppet Modules from source
7
+ class Builder
8
+ DEFAULT_IGNORED = [
9
+ '/pkg/',
10
+ '~*',
11
+ '/coverage',
12
+ '/checksums.json',
13
+ '/REVISION',
14
+ '/spec/fixtures/modules/',
15
+ '/vendor/',
16
+ ].freeze
17
+
18
+ attr_reader :destination
19
+
20
+ attr_reader :logger
21
+
22
+ def initialize(source, destination, logger)
23
+ raise ArgumentError, 'logger is expected to be nil or a Logger. Got %{klass}' % { klass: logger.class } unless logger.nil? || logger.is_a?(Logger)
24
+
25
+ @source_validated = false
26
+ @source = source
27
+ @destination = destination.nil? ? File.join(source, 'pkg') : destination
28
+ @logger = logger.nil? ? ::Logger.new(File.open(File::NULL, 'w')) : logger
29
+ end
30
+
31
+ # The source to build the module from
32
+ # @return [String]
33
+ def source
34
+ return @source if @source_validated
35
+
36
+ validate_source!
37
+ @source
38
+ end
39
+
40
+ # Build a module package from a module directory.
41
+ #
42
+ # @return [String] The path to the built package file.
43
+ def build
44
+ create_build_dir
45
+
46
+ stage_module_in_build_dir
47
+ build_package
48
+
49
+ package_file
50
+ ensure
51
+ cleanup_build_dir
52
+ end
53
+
54
+ # Return the path to the temporary build directory, which will be placed
55
+ # inside the target directory and match the release name (see #release_name).
56
+ def build_dir
57
+ @build_dir ||= File.join(build_context[:parent_dir], build_context[:build_dir_name])
58
+ end
59
+
60
+ def build_context
61
+ {
62
+ parent_dir: destination,
63
+ build_dir_name: release_name,
64
+ }.freeze
65
+ end
66
+
67
+ # Iterate through all the files and directories in the module and stage
68
+ # them into the temporary build directory (unless ignored).
69
+ #
70
+ # @return nil
71
+ def stage_module_in_build_dir
72
+ require 'find'
73
+
74
+ Find.find(source) do |path|
75
+ next if path == source
76
+
77
+ if ignored_path?(path)
78
+ logger.debug("Ignoring #{path} from the build")
79
+ Find.prune
80
+ else
81
+ logger.debug("Staging #{path} for the build")
82
+ stage_path(path)
83
+ end
84
+ end
85
+ end
86
+
87
+ # Stage a file or directory from the module into the build directory.
88
+ #
89
+ # @param path [String] The path to the file or directory.
90
+ #
91
+ # @return nil.
92
+ def stage_path(path)
93
+ require 'pathname'
94
+
95
+ relative_path = Pathname.new(path).relative_path_from(Pathname.new(source))
96
+ dest_path = File.join(build_dir, relative_path)
97
+
98
+ validate_path_encoding!(relative_path.to_path)
99
+
100
+ begin
101
+ if file_directory?(path)
102
+ fileutils_mkdir_p(dest_path, mode: file_stat(path).mode)
103
+ elsif file_symlink?(path)
104
+ warn_symlink(path)
105
+ else
106
+ validate_ustar_path!(relative_path.to_path)
107
+ fileutils_cp(path, dest_path, preserve: true)
108
+ end
109
+ rescue ArgumentError => e
110
+ raise '%{message} Rename the file or exclude it from the package by adding it to the .pdkignore file in your module.' % { message: e.message }
111
+ end
112
+ end
113
+
114
+ # Check if the given path matches one of the patterns listed in the
115
+ # ignore file.
116
+ #
117
+ # @param path [String] The path to be checked.
118
+ #
119
+ # @return [Boolean] true if the path matches and should be ignored.
120
+ def ignored_path?(path)
121
+ path = path.to_s + '/' if File.directory?(path)
122
+
123
+ !ignored_files.match_paths([path], source).empty?
124
+ end
125
+
126
+ # Warn the user about a symlink that would have been included in the
127
+ # built package.
128
+ #
129
+ # @param path [String] The relative or absolute path to the symlink.
130
+ #
131
+ # @return nil.
132
+ def warn_symlink(path)
133
+ require 'pathname'
134
+
135
+ symlink_path = Pathname.new(path)
136
+ module_path = Pathname.new(module_dir)
137
+
138
+ logger.warn _('Symlinks in modules are not supported and will not be included in the package. Please investigate symlink %{from} -> %{to}.') % {
139
+ from: symlink_path.relative_path_from(module_path),
140
+ to: symlink_path.realpath.relative_path_from(module_path),
141
+ }
142
+ end
143
+
144
+ # Select the most appropriate ignore file in the module directory.
145
+ #
146
+ # In order of preference, we first try `.pdkignore`, then `.pmtignore`
147
+ # and finally `.gitignore`.
148
+ #
149
+ # @return [String] The path to the file containing the patterns of file
150
+ # paths to ignore.
151
+ def ignore_file
152
+ @ignore_file ||= [
153
+ File.join(source, '.pdkignore'),
154
+ File.join(source, '.pmtignore'),
155
+ File.join(source, '.gitignore'),
156
+ ].find { |file| file_exists?(file) && file_readable?(file) }
157
+ end
158
+
159
+ # Checks if the path contains any non-ASCII characters.
160
+ #
161
+ # Java will throw an error when it encounters a path containing
162
+ # characters that are not supported by the hosts locale. In order to
163
+ # maximise compatibility we limit the paths to contain only ASCII
164
+ # characters, which should be part of any locale character set.
165
+ #
166
+ # @param path [String] the relative path to be added to the tar file.
167
+ #
168
+ # @raise [ArgumentError] if the path contains non-ASCII characters.
169
+ #
170
+ # @return [nil]
171
+ def validate_path_encoding!(path)
172
+ return unless path =~ %r{[^\x00-\x7F]}
173
+
174
+ raise ArgumentError, "'%{path}' can only include ASCII characters in its path or " \
175
+ 'filename in order to be compatible with a wide range of hosts.' % { path: path }
176
+ end
177
+
178
+ # Creates a gzip compressed tarball of the build directory.
179
+ #
180
+ # If the destination package already exists, it will be removed before
181
+ # creating the new tarball.
182
+ #
183
+ # @return nil.
184
+ def build_package
185
+ require 'zlib'
186
+ require 'minitar'
187
+ require 'find'
188
+
189
+ FileUtils.rm_f(package_file)
190
+
191
+ # The chdir necessary us due to Minitar entry not be able to separate the filename
192
+ # within the TAR versus the source filename to pack
193
+ Dir.chdir(build_context[:parent_dir]) do
194
+ gz = Zlib::GzipWriter.new(File.open(package_file, 'wb'))
195
+ begin
196
+ tar = Minitar::Output.new(gz)
197
+ Find.find(build_context[:build_dir_name]) do |entry|
198
+ entry_meta = {
199
+ name: entry,
200
+ }
201
+
202
+ orig_mode = File.stat(entry).mode
203
+ min_mode = Minitar.dir?(entry) ? 0o755 : 0o644
204
+
205
+ entry_meta[:mode] = orig_mode | min_mode
206
+
207
+ if entry_meta[:mode] != orig_mode
208
+ logger.debug('Updated permissions of packaged \'%{entry}\' to %{new_mode}' % {
209
+ entry: entry,
210
+ new_mode: (entry_meta[:mode] & 0o7777).to_s(8),
211
+ })
212
+ end
213
+
214
+ Minitar.pack_file(entry_meta, tar)
215
+ end
216
+ ensure
217
+ tar.close
218
+ end
219
+ end
220
+ end
221
+
222
+ # Instantiate a new PathSpec class and populate it with the pattern(s) of
223
+ # files to be ignored.
224
+ #
225
+ # @return [PathSpec] The populated ignore path matcher.
226
+ def ignored_files
227
+ require 'pathspec'
228
+
229
+ @ignored_files ||=
230
+ begin
231
+ ignored = if ignore_file.nil?
232
+ PathSpec.new
233
+ else
234
+ PathSpec.new(read_file(ignore_file, open_args: 'rb:UTF-8'))
235
+ end
236
+
237
+ if File.realdirpath(destination).start_with?(File.realdirpath(source))
238
+ ignored = ignored.add("\/#{File.basename(destination)}\/")
239
+ end
240
+
241
+ DEFAULT_IGNORED.each { |r| ignored.add(r) }
242
+
243
+ ignored
244
+ end
245
+ end
246
+
247
+ # Create a temporary build directory where the files to be included in
248
+ # the package will be staged before building the tarball.
249
+ #
250
+ # If the directory already exists, remove it first.
251
+ def create_build_dir
252
+ cleanup_build_dir
253
+
254
+ fileutils_mkdir_p(build_dir)
255
+ end
256
+
257
+ # Remove the temporary build directory and all its contents from disk.
258
+ #
259
+ # @return nil.
260
+ def cleanup_build_dir
261
+ FileUtils.rm_rf(build_dir, secure: true)
262
+ end
263
+
264
+ # Read and parse the values from metadata.json for the module that is
265
+ # being built.
266
+ #
267
+ # @return [Hash{String => Object}] The hash of metadata values.
268
+ def metadata
269
+ return @metadata unless @metadata.nil?
270
+
271
+ metadata_json_path = File.join(source, 'metadata.json')
272
+
273
+ unless file_exists?(metadata_json_path)
274
+ raise ArgumentError, "'%{file}' does not exist or is not a file." % { file: metadata_json_path }
275
+ end
276
+
277
+ unless file_readable?(metadata_json_path)
278
+ raise ArgumentError, "Unable to open '%{file}' for reading." % { file: metadata_json_path }
279
+ end
280
+
281
+ require 'json'
282
+ begin
283
+ @metadata = JSON.parse(read_file(metadata_json_path))
284
+ rescue JSON::JSONError => e
285
+ raise ArgumentError, 'Invalid JSON in metadata.json: %{msg}' % { msg: e.message }
286
+ end
287
+ @metadata.freeze
288
+ end
289
+
290
+ # Return the path where the built package file will be written to.
291
+ def package_file
292
+ @package_file ||= File.join(destination, "#{release_name}.tar.gz")
293
+ end
294
+
295
+ # Verify if there is an existing package in the target directory and prompts
296
+ # the user if they want to overwrite it.
297
+ def package_already_exists?
298
+ file_exists?(package_file)
299
+ end
300
+
301
+ # Combine the module name and version into a Forge-compatible dash
302
+ # separated string.
303
+ #
304
+ # @return [String] The module name and version, joined by a dash.
305
+ def release_name
306
+ @release_name ||= [
307
+ metadata['name'],
308
+ metadata['version'],
309
+ ].join('-')
310
+ end
311
+
312
+ # Checks if the path length will fit into the POSIX.1-1998 (ustar) tar
313
+ # header format.
314
+ #
315
+ # POSIX.1-2001 (which allows paths of infinite length) was adopted by GNU
316
+ # tar in 2004 and is supported by minitar 0.7 and above. Unfortunately
317
+ # much of the Puppet ecosystem still uses minitar 0.6.1.
318
+ #
319
+ # POSIX.1-1998 tar format does not allow for paths greater than 256 bytes,
320
+ # or paths that can't be split into a prefix of 155 bytes (max) and
321
+ # a suffix of 100 bytes (max).
322
+ #
323
+ # This logic was pretty much copied from the private method
324
+ # {Archive::Tar::Minitar::Writer#split_name}.
325
+ #
326
+ # @param path [String] the relative path to be added to the tar file.
327
+ #
328
+ # @raise [ArgumentError] if the path is too long or could not be split.
329
+ #
330
+ # @return [nil]
331
+ def validate_ustar_path!(path)
332
+ if path.bytesize > 256
333
+ raise ArgumentError, "The path '%{path}' is longer than 256 bytes." % {
334
+ path: path,
335
+ }
336
+ end
337
+
338
+ if path.bytesize <= 100
339
+ prefix = ''
340
+ else
341
+ parts = path.split(File::SEPARATOR)
342
+ newpath = parts.pop
343
+ nxt = ''
344
+
345
+ loop do
346
+ nxt = parts.pop || ''
347
+ break if newpath.bytesize + 1 + nxt.bytesize >= 100
348
+
349
+ newpath = File.join(nxt, newpath)
350
+ end
351
+
352
+ prefix = File.join(*parts, nxt)
353
+ path = newpath
354
+ end
355
+
356
+ return unless path.bytesize > 100 || prefix.bytesize > 155
357
+
358
+ raise ArgumentError, \
359
+ "'%{path}' could not be split at a directory separator into two " \
360
+ 'parts, the first having a maximum length of 155 bytes and the ' \
361
+ 'second having a maximum length of 100 bytes.' % { path: path }
362
+ end
363
+
364
+ private
365
+
366
+ # Validates that source is able to be built
367
+ def validate_source!
368
+ unless file_directory?(@source) && file_readable?(@source)
369
+ raise ArgumentError, "Module source '%{source}' does not exist as a directory is or is not readable" % { source: @source }
370
+ end
371
+
372
+ @source_validated = true
373
+ end
374
+
375
+ # @return [String] The file contents
376
+ def read_file(file, nil_on_error: false, open_args: 'r')
377
+ File.read(file, open_args: Array(open_args))
378
+ rescue StandardError => e
379
+ raise e unless nil_on_error
380
+
381
+ nil
382
+ end
383
+
384
+ # Filesystem wrapper methods.
385
+ # These are mocked in spec tests.
386
+ def file_exists?(*args)
387
+ File.file?(*args)
388
+ end
389
+
390
+ def file_readable?(*args)
391
+ File.readable?(*args)
392
+ end
393
+
394
+ def file_directory?(*args)
395
+ File.directory?(*args)
396
+ end
397
+
398
+ def file_symlink?(*args)
399
+ File.symlink?(*args)
400
+ end
401
+
402
+ def fileutils_cp(*args)
403
+ FileUtils.cp(*args)
404
+ end
405
+
406
+ def fileutils_mkdir_p(*args)
407
+ FileUtils.mkdir_p(*args)
408
+ end
409
+
410
+ def file_stat(*args)
411
+ File.stat(*args)
412
+ end
413
+ end
414
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Puppet # rubocop:disable Style/ClassAndModuleChildren
4
+ module Modulebuilder
5
+ VERSION = '0.1.0'
6
+ end
7
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/puppet/modulebuilder/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'puppet-modulebuilder'
7
+ spec.version = Puppet::Modulebuilder::VERSION
8
+ spec.authors = ['Sheena', 'Team IAC']
9
+ spec.email = ['sheena@puppet.com', 'https://puppetlabs.github.io/iac/']
10
+ spec.summary = 'A gem to set up puppet-modulebuilder'
11
+ spec.homepage = 'https://github.com/puppetlabs/puppet-modulebuilder'
12
+ spec.required_ruby_version = Gem::Requirement.new('>= 2.1.0')
13
+
14
+ spec.metadata['homepage_uri'] = spec.homepage
15
+ spec.metadata['source_code_uri'] = 'https://github.com/puppetlabs/puppet-modulebuilder'
16
+ spec.metadata['changelog_uri'] = 'https://github.com/puppetlabs/puppet-modulebuilder/blob/master/CHANGELOG.md'
17
+
18
+ # Specify which files should be added to the gem when it is released.
19
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
20
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
21
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
22
+ end
23
+ spec.bindir = 'exe'
24
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
25
+ spec.require_paths = ['lib']
26
+ # minitar and pathspec is required for building Puppet modules
27
+ spec.add_runtime_dependency 'minitar', '~> 0.6'
28
+ spec.add_runtime_dependency 'pathspec', '~> 0.2.1'
29
+ end
metadata ADDED
@@ -0,0 +1,90 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: puppet-modulebuilder
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Sheena
8
+ - Team IAC
9
+ autorequire:
10
+ bindir: exe
11
+ cert_chain: []
12
+ date: 2020-02-27 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: minitar
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '0.6'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '0.6'
28
+ - !ruby/object:Gem::Dependency
29
+ name: pathspec
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: 0.2.1
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: 0.2.1
42
+ description:
43
+ email:
44
+ - sheena@puppet.com
45
+ - https://puppetlabs.github.io/iac/
46
+ executables: []
47
+ extensions: []
48
+ extra_rdoc_files: []
49
+ files:
50
+ - ".github/CODEOWNERS"
51
+ - ".gitignore"
52
+ - ".rspec"
53
+ - ".rubocop.yml"
54
+ - ".travis.yml"
55
+ - CHANGELOG.md
56
+ - Gemfile
57
+ - LICENSE
58
+ - README.md
59
+ - Rakefile
60
+ - appveyor.yml
61
+ - lib/puppet/modulebuilder.rb
62
+ - lib/puppet/modulebuilder/builder.rb
63
+ - lib/puppet/modulebuilder/version.rb
64
+ - puppet-modulebuilder.gemspec
65
+ homepage: https://github.com/puppetlabs/puppet-modulebuilder
66
+ licenses: []
67
+ metadata:
68
+ homepage_uri: https://github.com/puppetlabs/puppet-modulebuilder
69
+ source_code_uri: https://github.com/puppetlabs/puppet-modulebuilder
70
+ changelog_uri: https://github.com/puppetlabs/puppet-modulebuilder/blob/master/CHANGELOG.md
71
+ post_install_message:
72
+ rdoc_options: []
73
+ require_paths:
74
+ - lib
75
+ required_ruby_version: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: 2.1.0
80
+ required_rubygems_version: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ version: '0'
85
+ requirements: []
86
+ rubygems_version: 3.0.6
87
+ signing_key:
88
+ specification_version: 4
89
+ summary: A gem to set up puppet-modulebuilder
90
+ test_files: []