pdk 1.9.1 → 1.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 27fc827b1a70d71d9cce1b0c392f75b8e90f2732
4
- data.tar.gz: ad2eab7e2e51368f1407ba9828b9646369e472ee
3
+ metadata.gz: c1a405daa77845c20f2b65516c1a43532f4589c0
4
+ data.tar.gz: 155e5410885b024cbfb942c7b35c281c89a30402
5
5
  SHA512:
6
- metadata.gz: 35f7aaff79bd0600fe54706aa1ddd8509e33dc328df96f3d24bde51b2321fa885ef4e4d5438ed9dfd4129966ae3ea864746a07dab7a58ea4d4db0f08f8568548
7
- data.tar.gz: 922b5d8512cf3a2954de87f95c3cc03a67980901ff8a82ab909be9589d550f4ba8400460a63232da54fcba11757f7acaeada7afb53d32c21cf92af6140bfb410
6
+ metadata.gz: 253daf9ea17ec338176dc232ec049abc6fdd88facfe5995fbaba73cb804a30b4d082fa56973fea9b841ad936f86223111939ff474ff810dfddf6ab5beb3ed869
7
+ data.tar.gz: 45985a21c5d8989d8c7214a0e7fe90df74f022850d9fe1fb429d92ff01642a755a4379adda5eb2f8bf5ee71508b9668e7b164abd17e34b268a7acbcc024c3409
@@ -4,6 +4,30 @@ All changes to this repo will be documented in this file.
4
4
  See the [release notes](https://puppet.com/docs/pdk/latest/release_notes.html) for a high-level summary.
5
5
 
6
6
 
7
+ ## [v1.10.0](https://github.com/puppetlabs/pdk/tree/v1.10.0) (2019-04-02)
8
+ [Full Changelog](https://github.com/puppetlabs/pdk/compare/v1.9.1...v1.10.0)
9
+
10
+ **Implemented enhancements:**
11
+
12
+ - \(PDK-1086\) Change `pdk build --force` to warn if missing module metadata and continue [\#643](https://github.com/puppetlabs/pdk/pull/643) ([rodjek](https://github.com/rodjek))
13
+ - \(PDK-1308\) Ensure PDK-written non-templated files have trailing newline [\#640](https://github.com/puppetlabs/pdk/pull/640) ([scotje](https://github.com/scotje))
14
+ - \(PDK-718\) Add --template-ref argument for upstream template repo tags [\#434](https://github.com/puppetlabs/pdk/pull/434) ([hunner](https://github.com/hunner))
15
+
16
+ **Closed issues:**
17
+
18
+ - `pdk update` is not idempotent for deletion of CI config files [\#593](https://github.com/puppetlabs/pdk/issues/593)
19
+ - Rspec tests for CRLF line endings fail [\#587](https://github.com/puppetlabs/pdk/issues/587)
20
+ - class params feature in class object template not documented or useable [\#557](https://github.com/puppetlabs/pdk/issues/557)
21
+ - Disabling puppet-lint checks in PDK [\#538](https://github.com/puppetlabs/pdk/issues/538)
22
+
23
+ **Merged pull requests:**
24
+
25
+ - \(maint\) Fix package specs for template-ref changes [\#647](https://github.com/puppetlabs/pdk/pull/647) ([rodjek](https://github.com/rodjek))
26
+ - \(maint\) Enforce LF line endings in Rubocop [\#645](https://github.com/puppetlabs/pdk/pull/645) ([glennsarti](https://github.com/glennsarti))
27
+ - \(FM-7579, PDK-1236\) bump the version of CRI used [\#638](https://github.com/puppetlabs/pdk/pull/638) ([tphoney](https://github.com/tphoney))
28
+ - \(PDK-1294\) Update version post-release [\#637](https://github.com/puppetlabs/pdk/pull/637) ([bmjen](https://github.com/bmjen))
29
+ - \(PDK-1298\) acceptance:local test suite optimisation [\#633](https://github.com/puppetlabs/pdk/pull/633) ([rodjek](https://github.com/rodjek))
30
+
7
31
  ## [v1.9.1](https://github.com/puppetlabs/pdk/tree/v1.9.1) (2019-03-01)
8
32
  [Full Changelog](https://github.com/puppetlabs/pdk/compare/v1.9.0...v1.9.1)
9
33
 
@@ -17,6 +41,7 @@ See the [release notes](https://puppet.com/docs/pdk/latest/release_notes.html) f
17
41
 
18
42
  **Merged pull requests:**
19
43
 
44
+ - \(PDK-1289\) Release 1.9.1 [\#632](https://github.com/puppetlabs/pdk/pull/632) ([bmjen](https://github.com/bmjen))
20
45
  - \(maint\) Pin parallel gem to 1.13.0 [\#631](https://github.com/puppetlabs/pdk/pull/631) ([rodjek](https://github.com/rodjek))
21
46
  - \(PDK-1260\) Bump to 1.10.0.pre for new dev work [\#621](https://github.com/puppetlabs/pdk/pull/621) ([bmjen](https://github.com/bmjen))
22
47
 
@@ -39,11 +39,15 @@ module PDK::CLI
39
39
  end
40
40
 
41
41
  def self.template_url_option(dsl)
42
- desc = _('Specifies the URL to the template to use when creating new modules or classes. (default: %{default_url})') % { default_url: PDK::Util.default_template_url }
42
+ desc = _('Specifies the URL to the template to use when creating new modules or classes. (default: %{default_url})') % { default_url: PDK::Util::TemplateURI.default_template_uri }
43
43
 
44
44
  dsl.option nil, 'template-url', desc, argument: :required
45
45
  end
46
46
 
47
+ def self.template_ref_option(dsl)
48
+ dsl.option nil, 'template-ref', _('Specifies the template git branch or tag to use when creating new modules or classes.'), argument: :required
49
+ end
50
+
47
51
  def self.skip_interview_option(dsl)
48
52
  dsl.option nil, 'skip-interview', _('When specified, skips interactive querying of metadata.')
49
53
  end
@@ -28,8 +28,12 @@ module PDK::CLI
28
28
  #
29
29
  unless module_metadata.forge_ready?
30
30
  if opts[:force]
31
- PDK.logger.error _('This module is missing required fields in the metadata.json. Re-run the build command without --force to add this information.')
32
- exit 1
31
+ PDK.logger.warn _(
32
+ 'This module is missing the following fields in the metadata.json: %{fields}. ' \
33
+ 'These missing fields may affect the visibility of the module on the Forge.',
34
+ ) % {
35
+ fields: module_metadata.missing_fields.join(', '),
36
+ }
33
37
  else
34
38
  module_metadata.interview_for_forge!
35
39
  module_metadata.write!('metadata.json')
@@ -7,6 +7,7 @@ module PDK::CLI
7
7
  summary _('Convert an existing module to be compatible with the PDK.')
8
8
 
9
9
  PDK::CLI.template_url_option(self)
10
+ PDK::CLI.template_ref_option(self)
10
11
  PDK::CLI.skip_interview_option(self)
11
12
  PDK::CLI.full_interview_option(self)
12
13
  flag nil, :noop, _('Do not convert the module, just output what would be done.')
@@ -21,6 +22,8 @@ module PDK::CLI
21
22
  log_level: :info,
22
23
  )
23
24
 
25
+ PDK::CLI::Util.validate_template_opts(opts)
26
+
24
27
  if opts[:noop] && opts[:force]
25
28
  raise PDK::CLI::ExitWithError, _('You can not specify --noop and --force when converting a module')
26
29
  end
@@ -7,6 +7,7 @@ module PDK::CLI
7
7
  summary _('This command is now \'pdk new module\'.')
8
8
 
9
9
  PDK::CLI.template_url_option(self)
10
+ PDK::CLI.template_ref_option(self)
10
11
  PDK::CLI.skip_interview_option(self)
11
12
 
12
13
  run do |opts, args, _cmd|
@@ -19,6 +20,8 @@ module PDK::CLI
19
20
  exit 1
20
21
  end
21
22
 
23
+ PDK::CLI::Util.validate_template_opts(opts)
24
+
22
25
  PDK.logger.info(_('New modules are created using the ‘pdk new module’ command.'))
23
26
  prompt = TTY::Prompt.new(help_color: :cyan)
24
27
  redirect = PDK::CLI::Util::CommandRedirector.new(prompt)
@@ -4,8 +4,6 @@ module PDK::CLI
4
4
  usage _('class [options] <class_name>')
5
5
  summary _('Create a new class named <class_name> using given options')
6
6
 
7
- PDK::CLI.template_url_option(self)
8
-
9
7
  run do |opts, args, _cmd|
10
8
  require 'pdk/generate/puppet_class'
11
9
 
@@ -4,8 +4,6 @@ module PDK::CLI
4
4
  usage _('defined_type [options] <name>')
5
5
  summary _('Create a new defined type named <name> using given options')
6
6
 
7
- PDK::CLI.template_url_option(self)
8
-
9
7
  run do |opts, args, _cmd|
10
8
  PDK::CLI::Util.ensure_in_module!(
11
9
  message: _('Defined types can only be created from inside a valid module directory.'),
@@ -5,6 +5,7 @@ module PDK::CLI
5
5
  summary _('Create a new module named [module_name] using given options')
6
6
 
7
7
  PDK::CLI.template_url_option(self)
8
+ PDK::CLI.template_ref_option(self)
8
9
  PDK::CLI.skip_interview_option(self)
9
10
  PDK::CLI.full_interview_option(self)
10
11
 
@@ -18,6 +19,8 @@ module PDK::CLI
18
19
  module_name = args[0]
19
20
  target_dir = args[1]
20
21
 
22
+ PDK::CLI::Util.validate_template_opts(opts)
23
+
21
24
  if opts[:'skip-interview'] && opts[:'full-interview']
22
25
  PDK.logger.info _('Ignoring --full-interview and continuing with --skip-interview.')
23
26
  opts[:'full-interview'] = false
@@ -4,8 +4,6 @@ module PDK::CLI
4
4
  usage _('provider [options] <name>')
5
5
  summary _('[experimental] Create a new ruby provider named <name> using given options')
6
6
 
7
- PDK::CLI.template_url_option(self)
8
-
9
7
  run do |opts, args, _cmd|
10
8
  PDK::CLI::Util.ensure_in_module!
11
9
 
@@ -4,7 +4,6 @@ module PDK::CLI
4
4
  usage _('task [options] <name>')
5
5
  summary _('Create a new task named <name> using given options')
6
6
 
7
- PDK::CLI.template_url_option(self)
8
7
  option nil, :description, _('A short description of the purpose of the task'), argument: :required
9
8
 
10
9
  run do |opts, args, _cmd|
@@ -10,6 +10,8 @@ module PDK::CLI
10
10
  flag nil, :noop, _('Do not update the module, just output what would be done.')
11
11
  flag nil, :force, _('Update the module automatically, with no prompts.')
12
12
 
13
+ PDK::CLI.template_ref_option(self)
14
+
13
15
  run do |opts, _args, _cmd|
14
16
  require 'pdk/module/update'
15
17
 
@@ -188,6 +188,16 @@ module PDK
188
188
  end
189
189
  end
190
190
  module_function :validate_puppet_version_opts
191
+
192
+ def validate_template_opts(opts)
193
+ if opts[:'template-ref'] && opts[:'template-url'].nil?
194
+ raise PDK::CLI::ExitWithError, _('--template-ref requires --template-url to also be specified.')
195
+ end
196
+
197
+ return unless opts[:'template-url'] && opts[:'template-url'].include?('#')
198
+ raise PDK::CLI::ExitWithError, _('--template-url may not be used to specify paths containing #\'s.')
199
+ end
200
+ module_function :validate_template_opts
191
201
  end
192
202
  end
193
203
  end
@@ -72,7 +72,7 @@ module PDK::CLI
72
72
  end
73
73
 
74
74
  # Subsequent arguments are targets.
75
- targets.concat(args[1..-1]) if args.length > 1
75
+ targets.concat(args.to_a[1..-1]) if args.length > 1
76
76
 
77
77
  report = PDK::Report.new
78
78
  report_formats = if opts[:format]
@@ -54,10 +54,10 @@ module PDK
54
54
 
55
55
  prepare_module_directory(temp_target_dir)
56
56
 
57
- template_url = opts.fetch(:'template-url', PDK::Util.default_template_url)
57
+ template_uri = PDK::Util::TemplateURI.new(opts)
58
58
 
59
59
  begin
60
- PDK::Module::TemplateDir.new(template_url, metadata.data, true) do |templates|
60
+ PDK::Module::TemplateDir.new(template_uri, metadata.data, true) do |templates|
61
61
  templates.render do |file_path, file_content|
62
62
  file = Pathname.new(temp_target_dir) + file_path
63
63
  file.dirname.mkpath
@@ -74,21 +74,23 @@ module PDK
74
74
  raise PDK::CLI::ExitWithError, e
75
75
  end
76
76
 
77
- if template_url == PDK::Util.puppetlabs_template_url
78
- # If the user specifies our template via the command line, remove the
79
- # saved template-url answer.
77
+ # Only update the answers files after metadata has been written.
78
+ if template_uri.default?
79
+ # If the user specifies our default template url via the command
80
+ # line, remove the saved template-url answer so that the template_uri
81
+ # resolution can find new default URLs in the future.
80
82
  PDK.answers.update!('template-url' => nil) if opts.key?(:'template-url')
81
83
  else
82
- # Save the template-url answer if the module was generated using
83
- # a template other than ours.
84
- PDK.answers.update!('template-url' => template_url)
84
+ # Save the template-url answers if the module was generated using a
85
+ # template/reference other than ours.
86
+ PDK.answers.update!('template-url' => template_uri.metadata_format)
85
87
  end
86
88
 
87
89
  begin
88
90
  if FileUtils.mv(temp_target_dir, target_dir)
89
91
  Dir.chdir(target_dir) { PDK::Util::Bundler.ensure_bundle! } unless opts[:'skip-bundle-install']
90
92
 
91
- PDK.logger.info(_('Module \'%{name}\' generated at path \'%{path}\', from template \'%{template_url}\'.') % { name: opts[:module_name], path: target_dir, template_url: template_url })
93
+ PDK.logger.info _('Module \'%{name}\' generated at path \'%{path}\', from template \'%{url}\'.') % { name: opts[:module_name], path: target_dir, url: template_uri.git_remote }
92
94
  PDK.logger.info(_('In your module directory, add classes with the \'pdk new class\' command.'))
93
95
  end
94
96
  rescue Errno::EACCES => e
@@ -29,7 +29,7 @@ module PDK
29
29
  # @param options [Hash{Symbol => Object}]
30
30
  #
31
31
  # @api public
32
- def initialize(module_dir, object_name, options = {})
32
+ def initialize(module_dir, object_name, options)
33
33
  @module_dir = module_dir
34
34
  @options = options
35
35
  @object_name = object_name
@@ -205,12 +205,12 @@ module PDK
205
205
  # @api private
206
206
  def with_templates
207
207
  templates.each do |template|
208
- if template[:url].nil?
209
- PDK.logger.debug(_('No %{dir_type} template specified; trying next template directory.') % { dir_type: template[:type] })
208
+ if template[:uri].nil?
209
+ PDK.logger.debug(_('No %{dir_type} template found; trying next template directory.') % { dir_type: template[:type] })
210
210
  next
211
211
  end
212
212
 
213
- PDK::Module::TemplateDir.new(template[:url]) do |template_dir|
213
+ PDK::Module::TemplateDir.new(PDK::Util::TemplateURI.new(template[:uri])) do |template_dir|
214
214
  template_paths = template_dir.object_template_for(object_type)
215
215
 
216
216
  if template_paths
@@ -250,11 +250,7 @@ module PDK
250
250
  #
251
251
  # @api private
252
252
  def templates
253
- @templates ||= [
254
- { type: 'CLI', url: @options[:'template-url'], allow_fallback: false },
255
- { type: 'metadata', url: module_metadata.data['template-url'], allow_fallback: true },
256
- { type: 'default', url: PDK::Util.default_template_url, allow_fallback: false },
257
- ]
253
+ @templates ||= PDK::Util::TemplateURI.templates(@options)
258
254
  end
259
255
 
260
256
  # Retrieves the name of the module (without the forge username) from the
@@ -68,7 +68,7 @@ module PDK
68
68
  def stage_changes!
69
69
  metadata_path = 'metadata.json'
70
70
 
71
- PDK::Module::TemplateDir.new(template_url, nil, false) do |templates|
71
+ PDK::Module::TemplateDir.new(template_uri, nil, false) do |templates|
72
72
  new_metadata = update_metadata(metadata_path, templates.metadata)
73
73
  templates.module_metadata = new_metadata.data unless new_metadata.nil?
74
74
 
@@ -102,8 +102,8 @@ module PDK
102
102
  @update_manager ||= PDK::Module::UpdateManager.new
103
103
  end
104
104
 
105
- def template_url
106
- @template_url ||= options.fetch(:'template-url', PDK::Util.default_template_url)
105
+ def template_uri
106
+ @template_uri ||= PDK::Util::TemplateURI.new(options)
107
107
  end
108
108
 
109
109
  def update_metadata(metadata_path, template_metadata)
@@ -153,13 +153,13 @@ module PDK
153
153
  end
154
154
  end
155
155
 
156
- private
157
-
158
156
  def missing_fields
159
157
  fields = DEFAULTS.keys - %w[data_provider requirements dependencies]
160
158
  fields.select { |key| @data[key].nil? || @data[key].empty? }
161
159
  end
162
160
 
161
+ private
162
+
163
163
  # Do basic validation and parsing of the name parameter.
164
164
  def process_name(data)
165
165
  validate_name(data['name'])
@@ -16,8 +16,8 @@ module PDK
16
16
  # The template directory is only guaranteed to be available on disk
17
17
  # within the scope of the block passed to this method.
18
18
  #
19
- # @param path_or_url [String] The path to a directory to use as the
20
- # template or a URL to a git repository.
19
+ # @param uri [PDK::Util::TemplateURI] The path to a directory to use as the
20
+ # template or a URI to a git repository.
21
21
  # @param module_metadata [Hash] A Hash containing the module metadata.
22
22
  # Defaults to an empty Hash.
23
23
  # @yieldparam self [PDK::Module::TemplateDir] The initialised object with
@@ -37,17 +37,31 @@ module PDK
37
37
  # @raise [ArgumentError] (see #validate_module_template!)
38
38
  #
39
39
  # @api public
40
- def initialize(path_or_url, module_metadata = {}, init = false)
40
+ def initialize(uri, module_metadata = {}, init = false)
41
41
  unless block_given?
42
42
  raise ArgumentError, _('%{class_name} must be initialized with a block.') % { class_name: self.class.name }
43
43
  end
44
+ unless uri.is_a? PDK::Util::TemplateURI
45
+ raise ArgumentError, _('PDK::Module::TemplateDir.new must be initialized with a PDK::Util::TemplateURI, got a %{uri_type}') % { uri_type: uri.class }
46
+ end
44
47
 
45
- if PDK::Util::Git.repo?(path_or_url)
46
- @path = self.class.clone_template_repo(path_or_url)
47
- @repo = path_or_url
48
+ if PDK::Util::Git.repo?(uri.git_remote)
49
+ # This is either a bare local repo or a remote. either way it needs cloning.
50
+ @path = clone_template_repo(uri)
51
+ temp_dir_clone = true
48
52
  else
49
- @path = path_or_url
53
+ # if it is a local path & non-bare repo then we can use it directly.
54
+ # Still have to check the branch.
55
+ @path = uri.shell_path
56
+ # We don't do a checkout of local-path repos. There are lots of edge
57
+ # cases or user un-expectations.
58
+ if PDK::Util::Git.work_tree?(@path)
59
+ PDK.logger.warn _("Repository '%{repo}' has a work-tree; skipping git reset.") % {
60
+ repo: @path,
61
+ }
62
+ end
50
63
  end
64
+ @cloned_from = uri.metadata_format
51
65
 
52
66
  @init = init
53
67
  @moduleroot_dir = File.join(@path, 'moduleroot')
@@ -64,7 +78,7 @@ module PDK
64
78
  ensure
65
79
  # If we cloned a git repo to get the template, remove the clone once
66
80
  # we're done with it.
67
- if @repo
81
+ if temp_dir_clone
68
82
  FileUtils.remove_dir(@path)
69
83
  end
70
84
  end
@@ -78,16 +92,11 @@ module PDK
78
92
  #
79
93
  # @api public
80
94
  def metadata
81
- result = {
82
- 'pdk-version' => PDK::Util::Version.version_string,
95
+ {
96
+ 'pdk-version' => PDK::Util::Version.version_string,
97
+ 'template-url' => @cloned_from,
98
+ 'template-ref' => cache_template_ref(@path),
83
99
  }
84
-
85
- result['template-url'] = @repo ? @repo : @path
86
-
87
- ref_result = PDK::Util::Git.git('--git-dir', File.join(@path, '.git'), 'describe', '--all', '--long', '--always')
88
- result['template-ref'] = ref_result[:stdout].strip if ref_result[:exit_code].zero?
89
-
90
- result
91
100
  end
92
101
 
93
102
  # Loop through the files in the template, yielding each rendered file to
@@ -118,11 +127,11 @@ module PDK
118
127
  else
119
128
  begin
120
129
  dest_content = PDK::TemplateFile.new(File.join(template_loc, template_file), configs: config, template_dir: self).render
121
- rescue => e
130
+ rescue => error
122
131
  error_msg = _(
123
132
  "Failed to render template '%{template}'\n" \
124
133
  '%{exception}: %{message}',
125
- ) % { template: template_file, exception: e.class, message: e.message }
134
+ ) % { template: template_file, exception: error.class, message: error.message }
126
135
  raise PDK::CLI::FatalError, error_msg
127
136
  end
128
137
  end
@@ -290,24 +299,18 @@ module PDK
290
299
  # @raise [PDK::CLI::FatalError] If reset HEAD of the cloned repo to desired ref.
291
300
  #
292
301
  # @api private
293
- def self.clone_template_repo(origin_repo)
302
+ def clone_template_repo(uri)
294
303
  # @todo When switching this over to using rugged, cache the cloned
295
304
  # template repo in `%AppData%` or `$XDG_CACHE_DIR` and update before
296
305
  # use.
297
306
  temp_dir = PDK::Util.make_tmpdir_name('pdk-templates')
298
- git_ref = (origin_repo == PDK::Util.default_template_url) ? PDK::Util.default_template_ref : 'origin/master'
307
+ origin_repo = uri.git_remote
308
+ git_ref = uri.git_ref
299
309
 
300
310
  clone_result = PDK::Util::Git.git('clone', origin_repo, temp_dir)
301
311
 
302
312
  if clone_result[:exit_code].zero?
303
- Dir.chdir(temp_dir) do
304
- reset_result = PDK::Util::Git.git('reset', '--hard', git_ref)
305
- unless reset_result[:exit_code].zero?
306
- PDK.logger.error reset_result[:stdout]
307
- PDK.logger.error reset_result[:stderr]
308
- raise PDK::CLI::FatalError, _("Unable to set HEAD of git repository at '%{repo}' to ref:'%{ref}'.") % { repo: temp_dir, ref: git_ref }
309
- end
310
- end
313
+ checkout_template_ref(temp_dir, git_ref)
311
314
  else
312
315
  PDK.logger.error clone_result[:stdout]
313
316
  PDK.logger.error clone_result[:stderr]
@@ -316,6 +319,28 @@ module PDK
316
319
 
317
320
  PDK::Util.canonical_path(temp_dir)
318
321
  end
322
+
323
+ # @api private
324
+ def checkout_template_ref(path, ref)
325
+ if PDK::Util::Git.work_dir_clean?(path)
326
+ Dir.chdir(path) do
327
+ full_ref = PDK::Util::Git.ls_remote(path, ref)
328
+ cache_template_ref(path, full_ref)
329
+ reset_result = PDK::Util::Git.git('reset', '--hard', full_ref)
330
+ return if reset_result[:exit_code].zero?
331
+
332
+ PDK.logger.error reset_result[:stdout]
333
+ PDK.logger.error reset_result[:stderr]
334
+ raise PDK::CLI::FatalError, _("Unable to set HEAD of git repository at '%{repo}' to ref:'%{ref}'.") % { repo: path, ref: ref }
335
+ end
336
+ else
337
+ PDK.logger.warn _("Uncommitted changes found when attempting to set HEAD of git repository at '%{repo}' to ref '%{ref}'; skipping git reset.") % { repo: path, ref: ref }
338
+ end
339
+ end
340
+
341
+ def cache_template_ref(path, ref = nil)
342
+ @template_ref ||= PDK::Util::Git.describe(File.join(path, '.git'), ref)
343
+ end
319
344
  end
320
345
  end
321
346
  end