config_skeleton 0.3.1 → 2.1.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
  SHA256:
3
- metadata.gz: 6a58d74e2627a2f4f0ac79fa05c9ce07d2b17b8500af7e38d59fd7a33ac22fe7
4
- data.tar.gz: 8b1d8531cc3fcc1183d14c465b886833b97130326d891b9da00ef05780b36a2c
3
+ metadata.gz: dadb672b628b814c3962711ce001733dacda07fa75c1b9b418f389719af745b1
4
+ data.tar.gz: 32f6f9d54602208c9a8c7402b1e6a12d8f9e2045307b929537d69f09c56adda7
5
5
  SHA512:
6
- metadata.gz: 351a846e41802b033bb217d3a574a5a8a18633cc51845313d15ef41220c036a92d068a6959fde5eea4fb25b8586974baf6e02ea62369342d911de5023bcc0863
7
- data.tar.gz: ebbf67d572a03b463b40e0e359446fd1e76d64ad4a0200556ffb78a425130aebf0876c23b0090e9770c9b6921a0e370d22d9e11520ad875b12b59d95671d4e2b
6
+ metadata.gz: 0cc9b1b46bbdeaa6c07850cad5dbe60b05d7bfadaf8cfa9d963a794e1320051463c74c94cef78822b7bd115567fed8622b1370068fdba079e3926cebd70b3dba
7
+ data.tar.gz: 64fc6ba379d78027b99f575eaa256b305e5dac656e9504d35b697d07c0d3fb5ac89793668d62046f1e500f8c59e3f4cdf47052a4ac62717f856206dd06a9f183
@@ -0,0 +1,50 @@
1
+ name: CI
2
+
3
+ on:
4
+ pull_request:
5
+ push:
6
+ branches:
7
+ - master
8
+ - main
9
+
10
+ jobs:
11
+ build:
12
+ runs-on: ubuntu-latest
13
+
14
+ strategy:
15
+ matrix:
16
+ ruby:
17
+ - 2.5
18
+ - 2.6
19
+ - 2.7
20
+ - 3.0
21
+
22
+ steps:
23
+ - uses: actions/checkout@v2
24
+
25
+ - name: Setup ruby
26
+ uses: ruby/setup-ruby@v1
27
+ with:
28
+ ruby-version: ${{ matrix.ruby }}
29
+ bundler-cache: true
30
+
31
+ - name: Lint
32
+ run: bundle exec rubocop
33
+
34
+ - name: Tests
35
+ run: bundle exec rake test
36
+
37
+ publish:
38
+ if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master')
39
+ needs: build
40
+ runs-on: ubuntu-latest
41
+
42
+ steps:
43
+ - uses: actions/checkout@v2
44
+
45
+ - name: Release Gem
46
+ uses: discourse/publish-rubygems-action@v2-beta
47
+ env:
48
+ RUBYGEMS_API_KEY: ${{ secrets.RUBYGEMS_API_KEY }}
49
+ GIT_EMAIL: team@discourse.org
50
+ GIT_NAME: discoursebot
data/.gitignore CHANGED
@@ -1,9 +1,2 @@
1
1
  /Gemfile.lock
2
- /pkg
3
- /doc
4
- /.yardoc
5
- /coverage
6
- /.bundle
7
2
  /cfg.txt
8
-
9
- .rubocop-https---raw-githubusercontent-com-discourse-discourse-master--rubocop-yml
data/.rubocop.yml CHANGED
@@ -1 +1,2 @@
1
- inherit_from: https://raw.githubusercontent.com/discourse/discourse/master/.rubocop.yml
1
+ inherit_gem:
2
+ rubocop-discourse: default.yml
@@ -1,30 +1,28 @@
1
+ # frozen_string_literal: true
2
+
1
3
  Gem::Specification.new do |s|
2
4
  s.name = "config_skeleton"
3
-
4
- s.version = "0.3.1"
5
+ s.version = "2.1.0"
5
6
 
6
7
  s.platform = Gem::Platform::RUBY
7
-
8
8
  s.summary = "Dynamically generate configs and reload servers"
9
-
10
- s.authors = ["Matt Palmer"]
11
- s.email = ["matt.palmer@discourse.org"]
9
+ s.authors = ["Matt Palmer", "Discourse Team"]
10
+ s.email = ["matt.palmer@discourse.org", "team@discourse.org"]
12
11
  s.homepage = "https://github.com/discourse/config_skeleton"
13
12
 
14
13
  s.files = `git ls-files -z`.split("\0").reject { |f| f =~ /^(G|spec|Rakefile)/ }
15
14
 
16
- s.required_ruby_version = ">= 2.3.0"
15
+ s.required_ruby_version = ">= 2.5.0"
17
16
 
18
17
  s.add_runtime_dependency 'diffy', '~> 3.0'
19
- s.add_runtime_dependency 'frankenstein', '~> 1.0'
20
18
  s.add_runtime_dependency 'rb-inotify', '~> 0.9'
21
- s.add_runtime_dependency 'service_skeleton', '> 0.a'
19
+ s.add_runtime_dependency 'service_skeleton', "~> 2.0"
20
+ s.add_runtime_dependency 'webrick'
22
21
 
23
22
  s.add_development_dependency 'bundler'
24
- s.add_development_dependency 'github-release'
25
- s.add_development_dependency 'rake', "~> 12.0"
23
+ s.add_development_dependency 'rake', "~> 13.0"
26
24
  s.add_development_dependency 'redcarpet'
27
- s.add_development_dependency 'rubocop'
25
+ s.add_development_dependency 'rubocop-discourse', '~> 2.4.1'
28
26
  s.add_development_dependency 'yard'
29
27
  s.add_development_dependency 'rspec'
30
28
  s.add_development_dependency 'pry'
@@ -1,10 +1,19 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'diffy'
2
4
  require 'fileutils'
3
5
  require 'frankenstein'
4
6
  require 'logger'
5
- require 'rb-inotify'
6
7
  require 'service_skeleton'
7
8
  require 'tempfile'
9
+ require 'digest/md5'
10
+
11
+ begin
12
+ require 'rb-inotify' unless ENV["DISABLE_INOTIFY"]
13
+ rescue FFI::NotFoundError => e
14
+ STDERR.puts "ERROR: Unable to initialize rb-inotify. To disable, set DISABLE_INOTIFY=1"
15
+ raise
16
+ end
8
17
 
9
18
  # Framework for creating config generation systems.
10
19
  #
@@ -23,20 +32,18 @@ require 'tempfile'
23
32
  #
24
33
  # 1. Implement service-specific config generation and reloading code, by
25
34
  # overriding the private methods #config_file, #config_data, and #reload_server
26
- # (and also potentially #config_ok?, #sleep_duration, #before_regenerate_config, and
27
- # #after_regenerate_config).
35
+ # (and also potentially #config_ok?, #sleep_duration, #before_regenerate_config, and #after_regenerate_config).
28
36
  # See the documentation for those methods for what they need to do.
29
37
  #
30
38
  # 1. Setup any file watchers you want with .watch and #watch.
31
39
  #
32
- # 1. Instantiate your new class, passing in an environment hash, and then call
33
- # #start. Something like this should do the trick:
40
+ # 1. Use the ServiceSkeleton Runner to start the service. Something like this should do the trick:
34
41
  #
35
42
  # class MyConfigGenerator < ConfigSkeleton
36
43
  # # Implement all the necessary methods
37
44
  # end
38
45
  #
39
- # MyConfigGenerator.new(ENV).start if __FILE__ == $0
46
+ # ServiceSkeleton::Runner.new(MyConfigGenerator, ENV).run if __FILE__ == $0
40
47
  #
41
48
  # 1. Sit back and relax.
42
49
  #
@@ -136,7 +143,8 @@ require 'tempfile'
136
143
  # method, passing one or more strings containing the full path to files or
137
144
  # directories to watch.
138
145
  #
139
- class ConfigSkeleton < ServiceSkeleton
146
+ class ConfigSkeleton
147
+ include ServiceSkeleton
140
148
  # All ConfigSkeleton-related errors will be subclasses of this.
141
149
  class Error < StandardError; end
142
150
 
@@ -155,6 +163,19 @@ class ConfigSkeleton < ServiceSkeleton
155
163
  end
156
164
  end
157
165
 
166
+ def self.inherited(klass)
167
+ klass.gauge :"#{klass.service_name}_last_generation_timestamp", docstring: "When the last config generation run was made"
168
+ klass.gauge :"#{klass.service_name}_last_change_timestamp", docstring: "When the config file was last written to"
169
+ klass.counter :"#{klass.service_name}_reload_total", docstring: "How many times we've asked the server to reload", labels: [:status]
170
+ klass.counter :"#{klass.service_name}_signals_total", docstring: "How many signals have been received (and handled)"
171
+ klass.gauge :"#{klass.service_name}_config_ok", docstring: "Whether the last config change was accepted by the server"
172
+
173
+ klass.hook_signal("HUP") do
174
+ logger.info("SIGHUP") { "received SIGHUP, triggering config regeneration" }
175
+ @trigger_regen_w << "."
176
+ end
177
+ end
178
+
158
179
  # Declare a file watch on all instances of the config generator.
159
180
  #
160
181
  # When you're looking to watch a file whose path is well-known and never-changing, you
@@ -186,23 +207,15 @@ class ConfigSkeleton < ServiceSkeleton
186
207
  @watches || []
187
208
  end
188
209
 
189
- # Create a new config generator.
190
- #
191
- # @param env [Hash<String, String>] the environment in which this config
192
- # generator runs. Typically you'll just pass `ENV` in here, but you can
193
- # pass in any hash you like, for testing purposes.
194
- #
195
- def initialize(env)
210
+ def initialize(*_, metrics:, config:)
196
211
  super
197
-
198
- hook_signal(:HUP) do
199
- logger.info("SIGHUP") { "received SIGHUP, triggering config regeneration" }
200
- @trigger_regen_w << "."
201
- end
202
-
203
212
  initialize_config_skeleton_metrics
204
213
  @trigger_regen_r, @trigger_regen_w = IO.pipe
205
214
  @terminate_r, @terminate_w = IO.pipe
215
+
216
+ raise "cooldown_duration invalid" if cooldown_duration < 0
217
+ raise "sleep_duration invalid" if sleep_duration < 0
218
+ raise "sleep_duration must not be less than cooldown_duration" if sleep_duration < cooldown_duration
206
219
  end
207
220
 
208
221
  # Expose the write pipe which can be written to to trigger a config
@@ -233,11 +246,16 @@ class ConfigSkeleton < ServiceSkeleton
233
246
  logger.debug(logloc) { "notifier fd is #{notifier.to_io.inspect}" }
234
247
 
235
248
  loop do
236
- if ios = IO.select(
237
- [notifier.to_io, @terminate_r, @trigger_regen_r],
238
- [], [],
239
- sleep_duration.tap { |d| logger.debug(logloc) { "Sleeping for #{d} seconds" } }
240
- )
249
+ if cooldown_duration > 0
250
+ logger.debug(logloc) { "Sleeping for #{cooldown_duration} seconds (cooldown)" }
251
+ IO.select([@terminate_r], [], [], cooldown_duration)
252
+ end
253
+
254
+ timeout = sleep_duration - cooldown_duration
255
+ logger.debug(logloc) { "Sleeping for #{timeout} seconds unless interrupted" }
256
+ ios = IO.select([notifier.to_io, @terminate_r, @trigger_regen_r], [], [], timeout)
257
+
258
+ if ios
241
259
  if ios.first.include?(notifier.to_io)
242
260
  logger.debug(logloc) { "inotify triggered" }
243
261
  notifier.process
@@ -294,6 +312,7 @@ class ConfigSkeleton < ServiceSkeleton
294
312
  # @see .watch for watching files and directories whose path never changes.
295
313
  #
296
314
  def watch(*files)
315
+ return if ENV["DISABLE_INOTIFY"]
297
316
  files.each do |f|
298
317
  if File.directory?(f)
299
318
  notifier.watch(f, :recursive, :create, :modify, :delete, :move) { |ev| logger.info("#{logloc} watcher") { "detected #{ev.flags.join(", ")} on #{ev.watcher.path}/#{ev.name}; regenerating config" } }
@@ -305,22 +324,11 @@ class ConfigSkeleton < ServiceSkeleton
305
324
 
306
325
  private
307
326
 
308
- # Register metrics in the ServiceSkeleton metrics registry
309
- #
310
- # @return [void]
311
- #
312
327
  def initialize_config_skeleton_metrics
313
- @config_generation = Frankenstein::Request.new("#{service_name}_generation", outgoing: false, description: "config generation", registry: metrics)
314
-
315
- metrics.gauge(:"#{service_name}_last_generation_timestamp", "When the last config generation run was made")
316
- metrics.gauge(:"#{service_name}_last_change_timestamp", "When the config file was last written to")
317
- metrics.counter(:"#{service_name}_reload_total", "How many times we've asked the server to reload")
318
- metrics.counter(:"#{service_name}_signals_total", "How many signals have been received (and handled)")
319
- metrics.gauge(:"#{service_name}_config_ok", "Whether the last config change was accepted by the server")
320
-
321
- metrics.last_generation_timestamp.set({}, 0)
322
- metrics.last_change_timestamp.set({}, 0)
323
- metrics.config_ok.set({}, 0)
328
+ @config_generation = Frankenstein::Request.new("#{self.class.service_name}_generation", outgoing: false, description: "config generation", registry: metrics)
329
+ metrics.last_generation_timestamp.set(0)
330
+ metrics.last_change_timestamp.set(0)
331
+ metrics.config_ok.set(0)
324
332
  end
325
333
 
326
334
  # Write out a config file if one doesn't exist, or do an initial regen run
@@ -335,7 +343,7 @@ class ConfigSkeleton < ServiceSkeleton
335
343
  else
336
344
  logger.info(logloc) { "No existing config file #{config_file} found; writing one" }
337
345
  File.write(config_file, instrumented_config_data)
338
- metrics.last_change_timestamp.set({}, Time.now.to_f)
346
+ metrics.last_change_timestamp.set(Time.now.to_f)
339
347
  end
340
348
  end
341
349
 
@@ -366,16 +374,23 @@ class ConfigSkeleton < ServiceSkeleton
366
374
  # Run code before the config is regenerated and the config_file
367
375
  # is written.
368
376
  #
377
+ # @param force_reload [Boolean] Whether the regenerate_config was called with force_reload
378
+ # @param existing_config_hash [String] MD5 hash of the config file before regeneration.
379
+ #
369
380
  # @note this can optionally be implemented by subclasses.
370
381
  #
371
- def before_regenerate_config(force_reload); end
382
+ def before_regenerate_config(force_reload:, existing_config_hash:, existing_config_data:); end
372
383
 
373
- # Run code after the config is regenerated and if the regeneration
374
- # was forced the new config has been cycled in.
384
+ # Run code after the config is regenerated and potentially a new file is written.
385
+ #
386
+ # @param force_reload [Boolean] Whether the regenerate_config was called with force_reload
387
+ # @param config_was_different [Boolean] Whether the diff of the old and new config was different.
388
+ # @param config_was_cycled [Boolean] Whether a new config file was cycled in.
389
+ # @param new_config_hash [String] MD5 hash of the new config file after write.
375
390
  #
376
391
  # @note this can optionally be implemented by subclasses.
377
392
  #
378
- def after_regenerate_config(force_reload); end
393
+ def after_regenerate_config(force_reload:, config_was_different:, config_was_cycled:, new_config_hash:); end
379
394
 
380
395
  # Verify that the currently running config is acceptable.
381
396
  #
@@ -416,7 +431,7 @@ class ConfigSkeleton < ServiceSkeleton
416
431
  #
417
432
  def instrumented_config_data
418
433
  begin
419
- @config_generation.measure { config_data.tap { metrics.last_generation_timestamp.set({}, Time.now.to_f) } }
434
+ @config_generation.measure { config_data.tap { metrics.last_generation_timestamp.set(Time.now.to_f) } }
420
435
  rescue => ex
421
436
  log_exception(ex, logloc) { "Call to config_data raised exception" }
422
437
  nil
@@ -440,12 +455,34 @@ class ConfigSkeleton < ServiceSkeleton
440
455
  60
441
456
  end
442
457
 
458
+ # How long to ignore signals/notifications after a config regeneration
459
+ #
460
+ # Hammering a downstream service with reload requests is often a bad idea.
461
+ # This method exists to allow subclasses to define a 'cooldown' duration.
462
+ # After each config regeneration, the config generator will sleep for this
463
+ # duration, regardless of any CONT signals or inotify events. Those events
464
+ # will be queued up, and processed at the end of the cooldown.
465
+ #
466
+ # The cooldown_duration is counted as part of the sleep_duration. So for
467
+ # the default values of 60 and 5, the service will cooldown for 5s, then wait
468
+ # for 55s.
469
+ #
470
+ # @return [Integer] the number of seconds to 'cooldown' for. This *must* be
471
+ # greater than zero, and less than sleep_duration
472
+ #
473
+ def cooldown_duration
474
+ 5
475
+ end
476
+
443
477
  # The instance of INotify::Notifier that is holding our file watches.
444
478
  #
445
479
  # @return [INotify::Notifier]
446
480
  #
447
481
  def notifier
448
482
  @notifier ||= INotify::Notifier.new
483
+ rescue NameError
484
+ raise if !ENV["DISABLE_INOTIFY"]
485
+ @notifier ||= Struct.new(:to_io).new(IO.pipe[1]) # Stub for macOS development
449
486
  end
450
487
 
451
488
  # Do the hard yards of actually regenerating the config and performing the reload.
@@ -458,35 +495,54 @@ class ConfigSkeleton < ServiceSkeleton
458
495
  # @return [void]
459
496
  #
460
497
  def regenerate_config(force_reload: false)
461
- before_regenerate_config(force_reload)
498
+ data = File.read(config_file)
499
+ existing_config_hash = Digest::MD5.hexdigest(data)
500
+ before_regenerate_config(
501
+ force_reload: force_reload,
502
+ existing_config_hash: existing_config_hash,
503
+ existing_config_data: data
504
+ )
462
505
 
463
506
  logger.debug(logloc) { "force? #{force_reload.inspect}" }
464
- tmpfile = Tempfile.new(service_name, File.dirname(config_file))
507
+ tmpfile = Tempfile.new(self.class.service_name, File.dirname(config_file))
465
508
  logger.debug(logloc) { "Tempfile is #{tmpfile.path}" }
466
509
  unless (new_config = instrumented_config_data).nil?
467
510
  File.write(tmpfile.path, new_config)
468
511
  tmpfile.close
469
- logger.debug(logloc) { require 'digest/md5'; "Existing config hash: #{Digest::MD5.hexdigest(File.read(config_file))}, new config hash: #{Digest::MD5.hexdigest(File.read(tmpfile.path))}" }
512
+
513
+ new_config_hash = Digest::MD5.hexdigest(File.read(tmpfile.path))
514
+ logger.debug(logloc) do
515
+ "Existing config hash: #{existing_config_hash}, new config hash: #{new_config_hash}"
516
+ end
470
517
 
471
518
  match_perms(config_file, tmpfile.path)
472
519
 
473
- diff = Diffy::Diff.new(config_file, tmpfile.path, source: 'files', context: 3, include_diff_info: true)
474
- if diff.to_s != ""
475
- logger.info(logloc) { "Config has changed. Diff:\n#{diff.to_s}" }
520
+ diff = Diffy::Diff.new(config_file, tmpfile.path, source: 'files', context: 3, include_diff_info: true).to_s
521
+ config_was_different = diff != ""
522
+
523
+ if config_was_different
524
+ logger.info(logloc) { "Config has changed. Diff:\n#{diff}" }
476
525
  end
477
526
 
478
527
  if force_reload
479
528
  logger.debug(logloc) { "Forcing config reload because force_reload == true" }
480
529
  end
481
530
 
482
- if force_reload || diff.to_s != ""
531
+ config_was_cycled = false
532
+ if force_reload || config_was_different
483
533
  cycle_config(tmpfile.path)
534
+ config_was_cycled = true
484
535
  end
485
536
  end
486
537
 
487
- after_regenerate_config(force_reload)
538
+ after_regenerate_config(
539
+ force_reload: force_reload,
540
+ config_was_different: config_was_different,
541
+ config_was_cycled: config_was_cycled,
542
+ new_config_hash: new_config_hash
543
+ )
488
544
  ensure
489
- metrics.last_change_timestamp.set({}, File.stat(config_file).mtime.to_f)
545
+ metrics.last_change_timestamp.set(File.stat(config_file).mtime.to_f)
490
546
  tmpfile.close rescue nil
491
547
  tmpfile.unlink rescue nil
492
548
  end
@@ -536,7 +592,7 @@ class ConfigSkeleton < ServiceSkeleton
536
592
  logger.debug(logloc) { "Restored previous config file" }
537
593
  File.rename(old_copy, config_file)
538
594
  end
539
- metrics.reload_total.increment(status: "failure")
595
+ metrics.reload_total.increment(labels: { status: "failure" })
540
596
 
541
597
  return
542
598
  end
@@ -544,21 +600,21 @@ class ConfigSkeleton < ServiceSkeleton
544
600
  logger.debug(logloc) { "Server reloaded successfully" }
545
601
 
546
602
  if config_ok?
547
- metrics.config_ok.set({}, 1)
603
+ metrics.config_ok.set(1)
548
604
  logger.debug(logloc) { "Configuration successfully updated." }
549
- metrics.reload_total.increment(status: "success")
550
- metrics.last_change_timestamp.set({}, Time.now.to_f)
605
+ metrics.reload_total.increment(labels: { status: "success" })
606
+ metrics.last_change_timestamp.set(Time.now.to_f)
551
607
  else
552
- metrics.config_ok.set({}, 0)
608
+ metrics.config_ok.set(0)
553
609
  if config_was_ok
554
610
  logger.warn(logloc) { "New config file failed config_ok? test; rolling back to previous known-good config" }
555
611
  File.rename(old_copy, config_file)
556
612
  reload_server
557
- metrics.reload_total.increment(status: "bad-config")
613
+ metrics.reload_total.increment(labels: { status: "bad-config" })
558
614
  else
559
615
  logger.warn(logloc) { "New config file failed config_ok? test; leaving new config in place because old config is broken too" }
560
- metrics.reload_total.increment(status: "everything-is-awful")
561
- metrics.last_change_timestamp.set({}, Time.now.to_f)
616
+ metrics.reload_total.increment(labels: { status: "everything-is-awful" })
617
+ metrics.last_change_timestamp.set(Time.now.to_f)
562
618
  end
563
619
  end
564
620
  ensure
metadata CHANGED
@@ -1,14 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: config_skeleton
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matt Palmer
8
+ - Discourse Team
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2020-11-27 00:00:00.000000000 Z
12
+ date: 2021-03-11 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: diffy
@@ -24,20 +25,6 @@ dependencies:
24
25
  - - "~>"
25
26
  - !ruby/object:Gem::Version
26
27
  version: '3.0'
27
- - !ruby/object:Gem::Dependency
28
- name: frankenstein
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - "~>"
32
- - !ruby/object:Gem::Version
33
- version: '1.0'
34
- type: :runtime
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - "~>"
39
- - !ruby/object:Gem::Version
40
- version: '1.0'
41
28
  - !ruby/object:Gem::Dependency
42
29
  name: rb-inotify
43
30
  requirement: !ruby/object:Gem::Requirement
@@ -56,24 +43,24 @@ dependencies:
56
43
  name: service_skeleton
57
44
  requirement: !ruby/object:Gem::Requirement
58
45
  requirements:
59
- - - ">"
46
+ - - "~>"
60
47
  - !ruby/object:Gem::Version
61
- version: 0.a
48
+ version: '2.0'
62
49
  type: :runtime
63
50
  prerelease: false
64
51
  version_requirements: !ruby/object:Gem::Requirement
65
52
  requirements:
66
- - - ">"
53
+ - - "~>"
67
54
  - !ruby/object:Gem::Version
68
- version: 0.a
55
+ version: '2.0'
69
56
  - !ruby/object:Gem::Dependency
70
- name: bundler
57
+ name: webrick
71
58
  requirement: !ruby/object:Gem::Requirement
72
59
  requirements:
73
60
  - - ">="
74
61
  - !ruby/object:Gem::Version
75
62
  version: '0'
76
- type: :development
63
+ type: :runtime
77
64
  prerelease: false
78
65
  version_requirements: !ruby/object:Gem::Requirement
79
66
  requirements:
@@ -81,7 +68,7 @@ dependencies:
81
68
  - !ruby/object:Gem::Version
82
69
  version: '0'
83
70
  - !ruby/object:Gem::Dependency
84
- name: github-release
71
+ name: bundler
85
72
  requirement: !ruby/object:Gem::Requirement
86
73
  requirements:
87
74
  - - ">="
@@ -100,14 +87,14 @@ dependencies:
100
87
  requirements:
101
88
  - - "~>"
102
89
  - !ruby/object:Gem::Version
103
- version: '12.0'
90
+ version: '13.0'
104
91
  type: :development
105
92
  prerelease: false
106
93
  version_requirements: !ruby/object:Gem::Requirement
107
94
  requirements:
108
95
  - - "~>"
109
96
  - !ruby/object:Gem::Version
110
- version: '12.0'
97
+ version: '13.0'
111
98
  - !ruby/object:Gem::Dependency
112
99
  name: redcarpet
113
100
  requirement: !ruby/object:Gem::Requirement
@@ -123,19 +110,19 @@ dependencies:
123
110
  - !ruby/object:Gem::Version
124
111
  version: '0'
125
112
  - !ruby/object:Gem::Dependency
126
- name: rubocop
113
+ name: rubocop-discourse
127
114
  requirement: !ruby/object:Gem::Requirement
128
115
  requirements:
129
- - - ">="
116
+ - - "~>"
130
117
  - !ruby/object:Gem::Version
131
- version: '0'
118
+ version: 2.4.1
132
119
  type: :development
133
120
  prerelease: false
134
121
  version_requirements: !ruby/object:Gem::Requirement
135
122
  requirements:
136
- - - ">="
123
+ - - "~>"
137
124
  - !ruby/object:Gem::Version
138
- version: '0'
125
+ version: 2.4.1
139
126
  - !ruby/object:Gem::Dependency
140
127
  name: yard
141
128
  requirement: !ruby/object:Gem::Requirement
@@ -195,11 +182,12 @@ dependencies:
195
182
  description:
196
183
  email:
197
184
  - matt.palmer@discourse.org
185
+ - team@discourse.org
198
186
  executables: []
199
187
  extensions: []
200
188
  extra_rdoc_files: []
201
189
  files:
202
- - ".github/workflows/ruby.yml"
190
+ - ".github/workflows/ci.yml"
203
191
  - ".gitignore"
204
192
  - ".rspec"
205
193
  - ".rubocop.yml"
@@ -221,14 +209,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
221
209
  requirements:
222
210
  - - ">="
223
211
  - !ruby/object:Gem::Version
224
- version: 2.3.0
212
+ version: 2.5.0
225
213
  required_rubygems_version: !ruby/object:Gem::Requirement
226
214
  requirements:
227
215
  - - ">="
228
216
  - !ruby/object:Gem::Version
229
217
  version: '0'
230
218
  requirements: []
231
- rubygems_version: 3.0.3
219
+ rubygems_version: 3.1.4
232
220
  signing_key:
233
221
  specification_version: 4
234
222
  summary: Dynamically generate configs and reload servers
@@ -1,48 +0,0 @@
1
- # This workflow uses actions that are not certified by GitHub.
2
- # They are provided by a third-party and are governed by
3
- # separate terms of service, privacy policy, and support
4
- # documentation.
5
- # This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake
6
- # For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby
7
-
8
- name: Ruby
9
-
10
- on:
11
- push:
12
- branches:
13
- - master
14
- tags:
15
- - v*
16
- pull_request:
17
- branches:
18
- - master
19
- workflow_dispatch:
20
-
21
- jobs:
22
- build:
23
- runs-on: ubuntu-latest
24
-
25
- steps:
26
- - uses: actions/checkout@v2
27
-
28
- - name: Set up Ruby
29
- uses: ruby/setup-ruby@v1
30
- with:
31
- ruby-version: 2.6
32
- - name: Install dependencies
33
- run: bundle install
34
- - name: Run specs
35
- run: bundle exec rspec
36
-
37
- publish:
38
- if: contains(github.ref, 'refs/tags/v')
39
- needs: build
40
- runs-on: ubuntu-latest
41
-
42
- steps:
43
- - uses: actions/checkout@v2
44
-
45
- - name: Release Gem
46
- uses: CvX/publish-rubygems-action@master
47
- env:
48
- RUBYGEMS_API_KEY: ${{secrets.RUBYGEMS_API_KEY}}