postfix_admin 0.3.1 → 0.3.2

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
  SHA256:
3
- metadata.gz: a9877c43c00661c60c1fee5e200211c97cd4cdc8d53593985c3e49f8296d27b9
4
- data.tar.gz: 9d9744f09e0a9e3ccdc72af138a4879824f3cd00bc3a1a111d0320a30007ee84
3
+ metadata.gz: 79176db25ca525f4d2031a41bf66f35895ae7d96d944af8e607c75f7654a1916
4
+ data.tar.gz: 5d3f6f207505ff525e4e192d6d03747bb5c7cf08e90dad5d313d93893bbb8601
5
5
  SHA512:
6
- metadata.gz: 86dfd30f6793fd6c13d09e532d7499f2e040dbe654f1bf86299d79b518a96906b6e208fd87c9bbda631d577ccfe39fb08f910e9cc41617be00425c84860d03f2
7
- data.tar.gz: f553b648aefc912877d3bcc149258a1d529caa457827f24b04bc4c5d4b45d7fc1397c4189c56ba0e800360320b6295b92abbde0ff344dc56d764995d8d88c448
6
+ metadata.gz: be19b8e778bf216deb66d9cc922d75022fb32425036def3a8b0642086a56b81b899bf3ec5070f667926b085354a3803da82f9d9b1e09075bae181876e67ac59f
7
+ data.tar.gz: 6af8ea674a934b5c522880a8c7f5a0c2d1a9c41959e626280f781ff6e6b62065b870830ee288582d49cafe91c83f8c42e535b61d9c62809c510fbdf396f72174
data/CHANGELOG.md CHANGED
@@ -1,7 +1,10 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 0.3.2
4
+ * Added `-d` (description) option for the `setup` subcommand
5
+
3
6
  ## 0.3.1
4
- + Added `admins`, `domains`, `accounts`, `aliases` and `forwards` subcommands
7
+ * Added `admins`, `domains`, `accounts`, `aliases` and `forwards` subcommands
5
8
  * Added `teardown` subcommand for the opposite operation of `setup`
6
9
  * `delete_domain` subcommand removes logs associated with the domain
7
10
  * Added `-s` (scheme) and `-r` (rounds) options for subcommands that require password arguments
data/README.md CHANGED
@@ -35,6 +35,20 @@ Execute the `postfix_admin` command to generate your config file at `~/.postfix_
35
35
  Edit the file for your environment:
36
36
 
37
37
  $ vi ~/.postfix_admin.conf
38
+ ---
39
+ database: mysql2://postfix:password@localhost/postfix
40
+ aliases: 30
41
+ mailboxes: 30
42
+ maxquota: 100
43
+ scheme: CRAM-MD5
44
+
45
+ The configuration file format is as follows:
46
+
47
+ database: mysql2://<username>:<password>@<host>/<database>
48
+ aliases: Default maximum number of aliases allowed per domain
49
+ mailboxes: Default maximum number of mailboxes allowed per domain
50
+ maxquota: Default maximum quota (in MB) allowed per mailbox for a domain
51
+ scheme: Default password scheme
38
52
 
39
53
  You can see the domains on your host if the `database` parameter is set properly:
40
54
 
data/docker-compose.yml CHANGED
@@ -3,7 +3,6 @@
3
3
  # Run tests on service app
4
4
  # You may need to run tests on Docker because of its requirement of `doveadm` command.
5
5
  # docker compose exec app bundle exec rake setup_test_db
6
- # docker compose exec app bundle exec rake test
7
6
  # docker compose exec app bundle exec rake spec
8
7
  #
9
8
  # rspec spec/runner_spec.rb
@@ -122,12 +122,13 @@ module PostfixAdmin
122
122
 
123
123
  attributes = {
124
124
  address: address,
125
- goto: goto
125
+ goto: goto,
126
+ domain: domain_name
126
127
  }
127
128
 
128
- domain.rel_aliases << Alias.new(attributes)
129
+ new_alias = Alias.new(attributes)
129
130
 
130
- raise_save_error(domain) unless domain.save
131
+ raise_save_error(new_alias) unless new_alias.save
131
132
  end
132
133
 
133
134
  def delete_alias(address)
@@ -153,7 +154,7 @@ module PostfixAdmin
153
154
  raise_error "Domain has already been registered: #{domain_name}"
154
155
  end
155
156
 
156
- new_description = description || domain_name
157
+ new_description = description || ""
157
158
 
158
159
  domain = Domain.new
159
160
  domain.attributes = {
@@ -206,7 +207,8 @@ module PostfixAdmin
206
207
  end
207
208
 
208
209
  def raise_save_error(obj)
209
- raise_error "Failed to save #{obj.class}: #{obj.errors.full_messages.join(', ')}"
210
+ massage = obj.errors.full_messages.join(', ')
211
+ raise_error "Failed to save #{obj.class}: #{massage}"
210
212
  end
211
213
 
212
214
  def address_split(address)
@@ -60,6 +60,8 @@ module PostfixAdmin
60
60
  show_domains
61
61
  puts
62
62
  show_admins
63
+ puts
64
+ show_recent_logs
63
65
  end
64
66
  end
65
67
 
@@ -71,11 +73,23 @@ module PostfixAdmin
71
73
  end
72
74
  end
73
75
 
76
+ def show_recent_logs
77
+ if Log.count.zero?
78
+ puts "No logs"
79
+ return
80
+ end
81
+
82
+ logs = Log.last(10)
83
+ puts_title("Recent Logs")
84
+ puts_log_table(logs)
85
+ end
86
+
74
87
  # Set up a domain
75
88
  # Add a domain, add an admin, and grant the admin access to the domain
76
- def setup_domain(domain_name, password, scheme: nil, rounds: nil)
89
+ def setup_domain(domain_name, password, description: nil,
90
+ scheme: nil, rounds: nil)
77
91
  admin = "admin@#{domain_name}"
78
- add_domain(domain_name)
92
+ add_domain(domain_name, description: description)
79
93
  add_admin(admin, password, scheme: scheme, rounds: rounds)
80
94
  add_admin_domain(admin, domain_name)
81
95
  end
@@ -139,6 +153,7 @@ module PostfixAdmin
139
153
  "Active", "Description"]
140
154
 
141
155
  puts_title("Domains")
156
+
142
157
  if Domain.without_all.empty?
143
158
  puts "No domains"
144
159
  return
@@ -146,9 +161,7 @@ module PostfixAdmin
146
161
 
147
162
  Domain.without_all.each_with_index do |d, i|
148
163
  no = i + 1
149
- aliases_str = "%4d / %4s" % [d.pure_aliases.count, d.aliases_str]
150
- mailboxes_str = "%4d / %4s" % [d.rel_mailboxes.count, d.mailboxes_str]
151
- rows << [no.to_s, d.domain, aliases_str, mailboxes_str,
164
+ rows << [no.to_s, d.domain, d.alias_usage_display_str, d.mailbox_usage_display_str,
152
165
  d.maxquota_str, d.active_str, d.description]
153
166
  end
154
167
 
@@ -207,6 +220,7 @@ module PostfixAdmin
207
220
  headings = ["No.", "Admin", "Domains", "Active", "Scheme Prefix"]
208
221
 
209
222
  puts_title("Admins")
223
+
210
224
  if admins.empty?
211
225
  puts "No admins"
212
226
  return
@@ -235,6 +249,7 @@ module PostfixAdmin
235
249
  "Scheme Prefix", "Maildir"]
236
250
 
237
251
  puts_title("Accounts")
252
+
238
253
  if mailboxes.empty?
239
254
  puts "No accounts"
240
255
  return
@@ -276,6 +291,7 @@ module PostfixAdmin
276
291
  def show_admin_domain(user_name)
277
292
  admin = Admin.find(user_name)
278
293
  puts_title("Admin Domains (#{user_name})")
294
+
279
295
  if admin.rel_domains.empty?
280
296
  puts "\nNo domains for #{user_name}"
281
297
  return
@@ -286,6 +302,7 @@ module PostfixAdmin
286
302
  no = i + 1
287
303
  rows << [no.to_s, d.domain]
288
304
  end
305
+
289
306
  puts_table(rows: rows, headings: %w[No. Domain])
290
307
  end
291
308
 
@@ -331,7 +348,7 @@ module PostfixAdmin
331
348
 
332
349
  def edit_account(address, options)
333
350
  quota = options[:quota]
334
- raise "Invalid Quota value: #{quota}" if quota && quota <= 0
351
+ raise "Invalid Quota value: #{quota}" if quota && quota < 0
335
352
 
336
353
  mailbox_check(address)
337
354
  mailbox = Mailbox.find(address)
@@ -377,7 +394,7 @@ module PostfixAdmin
377
394
  end
378
395
 
379
396
  def log(domain: nil, last: nil)
380
- headings = %w[Timestamp Admin Domain Action Data]
397
+ headings = %w[No. Timestamp Admin Domain Action Data]
381
398
  rows = []
382
399
 
383
400
  logs = if domain
@@ -388,10 +405,17 @@ module PostfixAdmin
388
405
 
389
406
  logs = logs.last(last) if last
390
407
 
391
- logs.each do |l|
408
+ puts_log_table(logs)
409
+ end
410
+
411
+ def puts_log_table(logs)
412
+ headings = %w[No. Timestamp Admin Domain Action Data]
413
+ rows = []
414
+ logs.each_with_index do |l, i|
415
+ no = i + 1
392
416
  # TODO: Consider if zone should be included ('%Z').
393
417
  time = l.timestamp.strftime("%Y-%m-%d %X")
394
- rows << [time, l.username, l.domain, l.action, l.data]
418
+ rows << [no, time, l.username, l.domain, l.action, l.data]
395
419
  end
396
420
 
397
421
  puts_table(headings: headings, rows: rows)
@@ -404,24 +428,28 @@ module PostfixAdmin
404
428
  puts [a.username, %Q!"#{a.password}"!, a.super_admin?, a.active].join(',')
405
429
  end
406
430
  puts
431
+
407
432
  puts "Domains"
408
433
  puts "Domain Name,Max Quota,Active"
409
434
  Domain.without_all.each do |d|
410
435
  puts [d.domain, d.maxquota, d.active].join(',')
411
436
  end
412
437
  puts
438
+
413
439
  puts "Mailboxes"
414
440
  puts "User Name,Name,Password,Quota,Maildir,Active"
415
441
  Mailbox.all.each do |m|
416
442
  puts [m.username, %Q!"#{m.name}"!, %Q!"#{m.password}"!, m.quota, %Q!"#{m.maildir}"!, m.active].join(',')
417
443
  end
418
444
  puts
445
+
419
446
  puts "Aliases"
420
447
  puts "Address,Go to,Active"
421
448
  Alias.all.select { |a| !a.mailbox? }.each do |a|
422
449
  puts [a.address, %Q!"#{a.goto}"!, a.active].join(',')
423
450
  end
424
451
  puts
452
+
425
453
  puts "Forwards"
426
454
  puts "Address,Go to,Active"
427
455
  Alias.all.select { |a| a.mailbox? && a.goto != a.address }.each do |a|
@@ -438,6 +466,7 @@ module PostfixAdmin
438
466
  rows << ["Admins", Admin.count]
439
467
  rows << ["Mailboxes", Mailbox.count]
440
468
  rows << ["Aliases", Alias.pure.count]
469
+ rows << ["Logs", Log.count]
441
470
 
442
471
  puts_title(title)
443
472
  puts_table(rows: rows)
@@ -449,8 +478,8 @@ module PostfixAdmin
449
478
 
450
479
  rows = []
451
480
  domain = Domain.find(domain_name)
452
- rows << ["Mailboxes", "%4d / %4s" % [domain.rel_mailboxes.count, domain.mailboxes_str]]
453
- rows << ["Aliases", "%4d / %4s" % [domain.pure_aliases.count, domain.aliases_str]]
481
+ rows << ["Mailboxes", domain.mailbox_usage_display_str]
482
+ rows << ["Aliases", domain.alias_usage_display_str]
454
483
  rows << ["Max Quota (MB)", domain.maxquota_str]
455
484
  rows << ["Active", domain.active_str]
456
485
  rows << ["Description", domain.description]
@@ -505,6 +534,7 @@ module PostfixAdmin
505
534
  puts "configure file: #{config_file} was generated.\nPlease execute after edit it."
506
535
  exit
507
536
  end
537
+
508
538
  open(config_file) do |f|
509
539
  YAML.load(f.read)
510
540
  end
@@ -1,4 +1,5 @@
1
- require 'postfix_admin/models/concerns/has_password'
1
+ require "postfix_admin/models/application_record"
2
+ require "postfix_admin/models/concerns/has_password"
2
3
 
3
4
  module PostfixAdmin
4
5
  class Admin < ApplicationRecord
@@ -1,3 +1,5 @@
1
+ require "postfix_admin/models/application_record"
2
+
1
3
  module PostfixAdmin
2
4
  class Alias < ApplicationRecord
3
5
  # version: 1841
@@ -18,10 +20,16 @@ module PostfixAdmin
18
20
 
19
21
  validate on: :create do |a|
20
22
  domain = a.rel_domain
21
- if domain.aliases.zero? || a.mailbox
22
- elsif domain.rel_aliases.pure.count >= domain.aliases
23
- message = "already has the maximum number of aliases " \
24
- "(maximum is #{domain.aliases} aliases)"
23
+
24
+ if domain.alias_unlimited?
25
+ # unlimited: do nothing
26
+ elsif domain.alias_disabled?
27
+ # disabled
28
+ a.errors.add(:domain, "has a disabled status for aliases")
29
+ elsif domain.pure_alias_count >= domain.aliases
30
+ # exceeding alias limit
31
+ message = "has already reached the maximum number of aliases " \
32
+ "(maximum: #{domain.aliases})"
25
33
  a.errors.add(:domain, message)
26
34
  end
27
35
  end
@@ -1,5 +1,5 @@
1
- require 'active_record'
2
- require 'postfix_admin/models/concerns/existing_timestamp'
1
+ require "active_record"
2
+ require "postfix_admin/models/concerns/existing_timestamp"
3
3
 
4
4
  module PostfixAdmin
5
5
  class ApplicationRecord < ActiveRecord::Base
@@ -1,3 +1,5 @@
1
+ require "postfix_admin/models/application_record"
2
+
1
3
  module PostfixAdmin
2
4
  class Domain < ApplicationRecord
3
5
  # version: 1841
@@ -18,6 +20,9 @@ module PostfixAdmin
18
20
  # | active | tinyint(1) | NO | | 1 | |
19
21
  # +-------------+--------------+------+-----+---------------------+-------+
20
22
 
23
+ UNLIMITED = 0
24
+ DISABLED = -1
25
+
21
26
  self.table_name = :domain
22
27
  self.primary_key = :domain
23
28
 
@@ -84,6 +89,22 @@ module PostfixAdmin
84
89
  rel_aliases.pure
85
90
  end
86
91
 
92
+ def mailbox_count
93
+ rel_mailboxes.count
94
+ end
95
+
96
+ def pure_alias_count
97
+ pure_aliases.count
98
+ end
99
+
100
+ def mailbox_usage_display_str
101
+ "%4d / %4s" % [mailbox_count, mailboxes_str]
102
+ end
103
+
104
+ def alias_usage_display_str
105
+ "%4d / %4s" % [pure_alias_count, aliases_str]
106
+ end
107
+
87
108
  def aliases_str
88
109
  max_num_str(aliases)
89
110
  end
@@ -96,13 +117,33 @@ module PostfixAdmin
96
117
  max_num_str(maxquota)
97
118
  end
98
119
 
120
+ def mailbox_unlimited?
121
+ mailboxes == UNLIMITED
122
+ end
123
+
124
+ def alias_unlimited?
125
+ aliases == UNLIMITED
126
+ end
127
+
128
+ def maxquota_unlimited?
129
+ maxquota.zero?
130
+ end
131
+
132
+ def mailbox_disabled?
133
+ mailboxes == DISABLED
134
+ end
135
+
136
+ def alias_disabled?
137
+ aliases == DISABLED
138
+ end
139
+
99
140
  private
100
141
 
101
142
  def max_num_str(num)
102
143
  case num
103
- when -1
144
+ when DISABLED
104
145
  "Disabled"
105
- when 0
146
+ when UNLIMITED
106
147
  "Unlimited"
107
148
  else
108
149
  num.to_s
@@ -1,3 +1,5 @@
1
+ require "postfix_admin/models/application_record"
2
+
1
3
  module PostfixAdmin
2
4
  class DomainAdmin < ApplicationRecord
3
5
  # version: 1841
@@ -1,3 +1,5 @@
1
+ require "postfix_admin/models/application_record"
2
+
1
3
  module PostfixAdmin
2
4
  class Log < ApplicationRecord
3
5
  # version: 1841
@@ -1,4 +1,5 @@
1
- require 'postfix_admin/models/concerns/has_password'
1
+ require "postfix_admin/models/application_record"
2
+ require "postfix_admin/models/concerns/has_password"
2
3
 
3
4
  module PostfixAdmin
4
5
  class Mailbox < ApplicationRecord
@@ -23,13 +24,14 @@ module PostfixAdmin
23
24
  # | token_validity | datetime | NO | | 2000-01-01 00:00:00 | |
24
25
  # +----------------+--------------+------+-----+---------------------+-------+
25
26
 
27
+ UNLIMITED_QUOTA = 0
28
+ DISABLED_QUOTA = -1
29
+
26
30
  self.table_name = :mailbox
27
31
  self.primary_key = :username
28
32
 
29
33
  include HasPassword
30
34
 
31
- # attribute :quota_mb, :integer
32
-
33
35
  validates :username, presence: true, uniqueness: { case_sensitive: false },
34
36
  format: { with: RE_EMAIL_LIKE_WITH_ANCHORS,
35
37
  message: "must be a valid email address" }
@@ -48,9 +50,16 @@ module PostfixAdmin
48
50
 
49
51
  validate on: :create do |mailbox|
50
52
  domain = mailbox.rel_domain
51
- if !domain.mailboxes.zero? && domain.rel_mailboxes.count >= domain.mailboxes
52
- message = "already has the maximum number of mailboxes " \
53
- "(maximum is #{domain.mailboxes} mailboxes)"
53
+
54
+ if domain.mailbox_unlimited?
55
+ # unlimited: do nothing
56
+ elsif domain.mailbox_disabled?
57
+ # disabled
58
+ mailbox.errors.add(:domain, "has a disabled status for mailboxes")
59
+ elsif domain.mailbox_count >= domain.mailboxes
60
+ # exceeding mailbox limit
61
+ message = "has already reached the maximum number of mailboxes " \
62
+ "(maximum: #{domain.mailboxes})"
54
63
  mailbox.errors.add(:domain, message)
55
64
  end
56
65
  end
@@ -62,17 +71,21 @@ module PostfixAdmin
62
71
  end
63
72
 
64
73
  validate do |mailbox|
65
- next if mailbox.quota == -1
74
+ next if mailbox.quota == DISABLED_QUOTA
66
75
 
67
76
  domain = mailbox.rel_domain
68
77
 
69
- unless domain.maxquota.zero?
70
- if mailbox.quota.zero?
71
- mailbox.errors.add(:quota, "cannot be 0")
72
- elsif mailbox.quota_mb > domain.maxquota
73
- message = "must be less than or equal to #{domain.maxquota} (MB)"
74
- mailbox.errors.add(:quota, message)
75
- end
78
+ next if domain.maxquota_unlimited?
79
+
80
+ # Quota limit
81
+ message = "must be less than or equal to #{domain.maxquota} MB"
82
+ if mailbox.quota_unlimited?
83
+ # trying to set quota to 0 (unlimited) even when domain's maxquota is not unlimited
84
+ mailbox.errors.add(:quota, "cannot be set to 0 (unlimited)")
85
+ mailbox.errors.add(:quota, message)
86
+ elsif mailbox.quota_mb > domain.maxquota
87
+ # trying to set quota to value greater than domain's maxquota
88
+ mailbox.errors.add(:quota, message)
76
89
  end
77
90
  end
78
91
 
@@ -84,6 +97,10 @@ module PostfixAdmin
84
97
  domain: mailbox.domain)
85
98
  end
86
99
 
100
+ def quota_unlimited?
101
+ quota.zero?
102
+ end
103
+
87
104
  def quota_mb
88
105
  raise Error, "quota is out of range: #{quota}" if quota < 0
89
106
 
@@ -108,10 +125,10 @@ module PostfixAdmin
108
125
 
109
126
  def quota_mb_str(format: "%6.1f")
110
127
  case quota
111
- when -1
128
+ when DISABLED_QUOTA
112
129
  # It's not sure what 'disabled' means for quota.
113
130
  "Disabled"
114
- when 0
131
+ when UNLIMITED_QUOTA
115
132
  "Unlimited"
116
133
  else
117
134
  quota_mb = quota / KB_TO_MB.to_f
@@ -1,3 +1,5 @@
1
+ require "postfix_admin/models/application_record"
2
+
1
3
  module PostfixAdmin
2
4
  class Quota2 < ApplicationRecord
3
5
  # version: 1841
@@ -55,17 +55,21 @@ module PostfixAdmin
55
55
  runner { @cli.show_forwards }
56
56
  end
57
57
 
58
- desc "setup example.com password", "Set up a domain (add a domain and an admin user for it)"
58
+ desc "setup example.com password",
59
+ "Set up a domain (add a domain and an admin user for it)"
60
+ method_option :description, type: :string, aliases: "-d", desc: "description"
59
61
  method_option :scheme, type: :string, aliases: "-s", desc: "password scheme"
60
62
  method_option :rounds, type: :string, aliases: "-r", desc: "encryption rounds for BLF-CRYPT, SHA256-CRYPT and SHA512-CRYPT schemes"
61
63
  def setup(domain_name, password)
62
64
  runner do
63
65
  @cli.setup_domain(domain_name, password,
66
+ description: options[:description],
64
67
  scheme: options[:scheme], rounds: options[:rounds])
65
68
  end
66
69
  end
67
70
 
68
- desc "teardown example.com", "Tear down a domain (delete a domain and an admin user for it)"
71
+ desc "teardown example.com",
72
+ "Tear down a domain (delete a domain and an admin user for it)"
69
73
  def teardown(domain_name)
70
74
  runner { @cli.teardown_domain(domain_name) }
71
75
  end
@@ -99,11 +103,11 @@ module PostfixAdmin
99
103
  end
100
104
 
101
105
  desc "edit_domain example.com", "Edit a domain"
102
- method_option :aliases, type: :numeric, aliases: "-a", desc: "Edit aliases limitation"
103
- method_option :mailboxes, type: :numeric, aliases: "-m", desc: "Edit mailboxes limitation"
104
- method_option :maxquota, type: :numeric, aliases: "-q", desc: "Edit max quota limitation"
106
+ method_option :aliases, type: :numeric, aliases: "-a", desc: "Update aliase limit"
107
+ method_option :mailboxes, type: :numeric, aliases: "-m", desc: "Update mailboxe limit"
108
+ method_option :maxquota, type: :numeric, aliases: "-q", desc: "Update maximum quota limit (MB)"
105
109
  method_option :active, type: :boolean, desc: "Update active status"
106
- method_option :description, type: :string, aliases: "-d", desc: "Edit description"
110
+ method_option :description, type: :string, aliases: "-d", desc: "Update description"
107
111
  def edit_domain(domain_name)
108
112
  runner do
109
113
  if options.size == 0
@@ -153,11 +157,11 @@ module PostfixAdmin
153
157
 
154
158
  desc "edit_account user@example.com", "Edit an account"
155
159
  method_option :goto, type: :string, aliases: "-g",
156
- desc: "mailboxes, addresses e-mails are delivered to"
160
+ desc: "Update mailboxes, addresses emails are delivered to"
157
161
  method_option :quota, type: :numeric, aliases: "-q",
158
- desc: "quota limitation (MB)"
162
+ desc: "Update quota limit (MB)"
159
163
  method_option :name, type: :string, aliases: "-n",
160
- desc: "full name"
164
+ desc: "Update full name"
161
165
  method_option :active, type: :boolean,
162
166
  desc: "Update active status"
163
167
  def edit_account(address)
@@ -1,3 +1,3 @@
1
1
  module PostfixAdmin
2
- VERSION = "0.3.1"
2
+ VERSION = "0.3.2"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: postfix_admin
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Hitoshi Kurokawa
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-07-10 00:00:00.000000000 Z
11
+ date: 2024-07-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor