hako 2.17.0 → 2.18.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
  SHA256:
3
- metadata.gz: 39056ee36a91a2e24c9dc59cfe7e5a0fef62c55578c31cf6aa2243d29d89ca8e
4
- data.tar.gz: ca5ecea7f27470068cf6d69d925363fa43dfcde5bba28764e51df11e9e57cfb2
3
+ metadata.gz: bf69da97907f68236fdaeca00d74f4933d0c517afd44e072721a77b2d5ba9d0e
4
+ data.tar.gz: 1d3455be0daf4d3c0e61e5384fd82ccbb2a14b4a007d5115cf77b4495f63b115
5
5
  SHA512:
6
- metadata.gz: 99c17719f1f60db1bd9c5413f6cb02005464f90bda67f5f543b6537d71796f0fe8ea9e32a267bf1000bcc48e1b98ef03c03cf4a0878cc1d8b2107e9fb405b362
7
- data.tar.gz: 23f57698cb4d2cdf7fd59bed1c25977fefbea9868fcbd4179d860729ca0ebf372d177dd8d25d5205e6814b7edd77450837054da96d232f8844c604e743324264
6
+ metadata.gz: 6c59d28f95d1be5246c4454924f8dbada59e4ffea9222eb23e684a2996161a89bcb3a33d8af1779637f3248d824118f3b48b6745816c608ae756e8646654ffc1
7
+ data.tar.gz: 2039675e62e6f47b3a9e37b63b843c5ac099079df435cc9f2705758722745044319f135c2b7f35371f4b192ead601d4ec842e362943cd961d2b32308909ecba1
@@ -13,10 +13,10 @@ jobs:
13
13
  fail-fast: false
14
14
  matrix:
15
15
  ruby:
16
- - '2.6'
17
- - '2.7'
18
- - '3.0'
19
16
  - '3.1'
17
+ - '3.2'
18
+ - '3.3'
19
+ - '3.4'
20
20
  steps:
21
21
  - uses: actions/checkout@v2
22
22
  - uses: ruby/setup-ruby@v1
@@ -32,6 +32,6 @@ jobs:
32
32
  - uses: ruby/setup-ruby@v1
33
33
  with:
34
34
  # Use the same version with .rubocop.yml
35
- ruby-version: 2.6
35
+ ruby-version: 3.1
36
36
  bundler-cache: true
37
37
  - run: bundle exec rubocop
data/.rubocop.yml CHANGED
@@ -2,7 +2,7 @@ inherit_from: .rubocop_todo.yml
2
2
 
3
3
  AllCops:
4
4
  DisplayCopNames: true
5
- TargetRubyVersion: 2.6
5
+ TargetRubyVersion: 3.1
6
6
  NewCops: disable
7
7
 
8
8
  Layout/FirstArgumentIndentation:
data/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ # 2.18.0 (2026-01-26)
2
+ ## New features
3
+ - Support EFS
4
+ - Support resource_requirements for GPU
5
+ - Support SecretsManager secrets in dry-run
6
+ - Launch dependent containers on `hako oneshot`
7
+ - Support deployment circuit breaker
8
+ - Support FireLens
9
+
1
10
  # 2.17.0 (2022-05-31)
2
11
  ## New features
3
12
  - Support runtime_platform option
data/hako.gemspec CHANGED
@@ -19,7 +19,7 @@ Gem::Specification.new do |spec|
19
19
  spec.bindir = 'exe'
20
20
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
21
  spec.require_paths = ['lib']
22
- spec.required_ruby_version = '>= 2.6.0'
22
+ spec.required_ruby_version = '>= 3.1.0'
23
23
 
24
24
  spec.add_dependency 'aws-sdk-applicationautoscaling'
25
25
  spec.add_dependency 'aws-sdk-autoscaling'
@@ -30,12 +30,14 @@ Gem::Specification.new do |spec|
30
30
  spec.add_dependency 'aws-sdk-elasticloadbalancing'
31
31
  spec.add_dependency 'aws-sdk-elasticloadbalancingv2', '>= 1.54.0'
32
32
  spec.add_dependency 'aws-sdk-s3'
33
+ spec.add_dependency 'aws-sdk-secretsmanager'
33
34
  spec.add_dependency 'aws-sdk-servicediscovery'
34
35
  spec.add_dependency 'aws-sdk-sns'
35
36
  spec.add_dependency 'aws-sdk-ssm'
36
37
  spec.add_dependency 'jsonnet'
37
38
 
38
39
  spec.add_development_dependency 'bundler'
40
+ spec.add_development_dependency 'oga' # XML library for aws-sdk
39
41
  spec.add_development_dependency 'rake'
40
42
  spec.add_development_dependency 'rspec'
41
43
  spec.add_development_dependency 'rubocop', '>= 0.53.0'
@@ -222,6 +222,29 @@ module Hako
222
222
  end
223
223
  end
224
224
 
225
+ # @return [Array, nil]
226
+ def resource_requirements
227
+ if @definition.key?('resource_requirements')
228
+ @definition['resource_requirements'].map do |rr|
229
+ {
230
+ type: rr.fetch('type'),
231
+ value: rr.fetch('value'),
232
+ }
233
+ end
234
+ end
235
+ end
236
+
237
+ # @return [Hash, nil]
238
+ def firelens_configuration
239
+ if @definition.key?('firelens_configuration')
240
+ conf = @definition['firelens_configuration']
241
+ {
242
+ type: conf.fetch('type'),
243
+ options: conf.fetch('options', nil),
244
+ }
245
+ end
246
+ end
247
+
225
248
  private
226
249
 
227
250
  PROVIDERS_KEY = '$providers'
@@ -50,11 +50,20 @@ module Hako
50
50
  Container.new(@app, sidecars.fetch(name), dry_run: @dry_run)
51
51
  end
52
52
 
53
+ # Load linked containers
53
54
  containers[name].links.each do |link|
54
55
  m = link.match(/\A([^:]+):([^:]+)\z/)
55
56
  names << (m ? m[1] : link)
56
57
  end
57
58
 
59
+ # Load dependent containers
60
+ depends_on = containers[name].depends_on
61
+ if depends_on
62
+ depends_on.each do |depend_on|
63
+ names << depend_on.fetch(:container_name)
64
+ end
65
+ end
66
+
58
67
  containers[name].volumes_from.each do |volumes_from|
59
68
  names << volumes_from[:source_container]
60
69
  end
@@ -4,6 +4,7 @@ require 'aws-sdk-autoscaling'
4
4
  require 'aws-sdk-ec2'
5
5
  require 'aws-sdk-ecs'
6
6
  require 'aws-sdk-s3'
7
+ require 'aws-sdk-secretsmanager'
7
8
  require 'aws-sdk-sns'
8
9
  require 'aws-sdk-ssm'
9
10
  require 'hako'
@@ -59,9 +60,21 @@ module Hako
59
60
  end
60
61
  @oneshot_notification_prefix = options.fetch('oneshot_notification_prefix', nil)
61
62
  if options.key?('deployment_configuration')
63
+ deployment_configuration = options.fetch('deployment_configuration')
64
+
62
65
  @deployment_configuration = {}
66
+ if deployment_configuration.key?('deployment_circuit_breaker')
67
+ circuit_breaker = deployment_configuration.fetch('deployment_circuit_breaker')
68
+ if circuit_breaker.fetch('rollback', nil)
69
+ validation_error!('rollback option of deployment_circuit_breaker is not supported because Hako handles rollback instead of the circuit breaker')
70
+ end
71
+ @deployment_configuration[:deployment_circuit_breaker] = {
72
+ enable: circuit_breaker.fetch('enable'),
73
+ rollback: false,
74
+ }
75
+ end
63
76
  %i[maximum_percent minimum_healthy_percent].each do |key|
64
- @deployment_configuration[key] = options.fetch('deployment_configuration')[key.to_s]
77
+ @deployment_configuration[key] = deployment_configuration[key.to_s]
65
78
  end
66
79
  else
67
80
  @deployment_configuration = nil
@@ -400,9 +413,18 @@ module Hako
400
413
  @ec2_client ||= Aws::EC2::Client.new(region: @region)
401
414
  end
402
415
 
416
+ # @param [String] region
403
417
  # @return [Aws::SSM::Client]
404
- def ssm_client
405
- @ssm_client ||= Aws::SSM::Client.new(region: @region)
418
+ def ssm_client(region)
419
+ @ssm_clients ||= {}
420
+ @ssm_clients[region] ||= Aws::SSM::Client.new(region: region)
421
+ end
422
+
423
+ # @param [String] region
424
+ # @return [Aws::SecretsManager::Client]
425
+ def secretsmanager_client(region)
426
+ @secretsmanager_clients ||= {}
427
+ @secretsmanager_clients[region] ||= Aws::SecretsManager::Client.new(region: region)
406
428
  end
407
429
 
408
430
  # @return [EcsElb, EcsElbV2]
@@ -652,6 +674,23 @@ module Hako
652
674
  labels: configuration['labels'],
653
675
  scope: configuration['scope'],
654
676
  }
677
+ elsif volume.key?('efs_volume_configuration')
678
+ configuration = volume['efs_volume_configuration']
679
+ authorization_config =
680
+ if configuration.key?('authorization_config')
681
+ ac = configuration['authorization_config']
682
+ {
683
+ access_point_id: ac['access_point_id'],
684
+ iam: ac['iam'],
685
+ }
686
+ end
687
+ definition[:efs_volume_configuration] = {
688
+ file_system_id: configuration.fetch('file_system_id'),
689
+ root_directory: configuration['root_directory'],
690
+ transit_encryption: configuration['transit_encryption'],
691
+ transit_encryption_port: configuration['transit_encryption_port'],
692
+ authorization_config: authorization_config,
693
+ }
655
694
  else
656
695
  # When neither 'host' nor 'docker_volume_configuration' is
657
696
  # specified, ECS API treats it as if 'host' is specified without
@@ -702,6 +741,8 @@ module Hako
702
741
  docker_security_options: container.docker_security_options,
703
742
  system_controls: container.system_controls,
704
743
  repository_credentials: container.repository_credentials,
744
+ resource_requirements: container.resource_requirements,
745
+ firelens_configuration: container.firelens_configuration,
705
746
  }
706
747
  end
707
748
 
@@ -1028,13 +1069,14 @@ module Hako
1028
1069
  Hako.logger.debug " latest_event_id=#{latest_event_id}, deployments=#{s.deployments}"
1029
1070
  no_active = s.deployments.all? { |d| d.status != 'ACTIVE' }
1030
1071
  primary = s.deployments.find { |d| d.status == 'PRIMARY' }
1031
- if primary.desired_count * 2 < @started_task_ids.size
1072
+ if primary.rollout_state == 'FAILED'
1073
+ Hako.logger.error("New deployment is failing: #{primary.rollout_state_reason}")
1074
+ report_task_diagnostics(service.cluster_arn, @started_task_ids)
1075
+ return false
1076
+ end
1077
+ if primary.desired_count * 2 < @started_task_ids.size && !ecs_circuit_breaker_enabled?
1032
1078
  Hako.logger.error('Some started tasks are stopped. It seems new deployment is failing to start')
1033
- @started_task_ids.each_slice(100) do |task_ids|
1034
- ecs_client.describe_tasks(cluster: service.cluster_arn, tasks: task_ids).tasks.each do |task|
1035
- report_task_diagnostics(task)
1036
- end
1037
- end
1079
+ report_task_diagnostics(service.cluster_arn, @started_task_ids)
1038
1080
  return false
1039
1081
  end
1040
1082
  primary_ready = primary && primary.running_count == primary.desired_count
@@ -1046,6 +1088,11 @@ module Hako
1046
1088
  end
1047
1089
  end
1048
1090
 
1091
+ # @return [Boolean]
1092
+ def ecs_circuit_breaker_enabled?
1093
+ @deployment_configuration&.dig(:deployment_circuit_breaker, :enable)
1094
+ end
1095
+
1049
1096
  # @param [Array<Aws::ECS::Types::ServiceEvent>] events
1050
1097
  # @return [String, nil]
1051
1098
  def find_latest_event_id(events)
@@ -1056,20 +1103,24 @@ module Hako
1056
1103
  end
1057
1104
  end
1058
1105
 
1059
- TASK_ID_RE = /\(task ([\h-]+)\)\.\z/.freeze
1106
+ TASK_ID_RE = /\(task ([\h-]+)\)\.\z/
1060
1107
  # @param [String] message
1061
1108
  # @return [String, nil]
1062
1109
  def extract_task_id(message)
1063
1110
  message.slice(TASK_ID_RE, 1)
1064
1111
  end
1065
1112
 
1066
- # @param [Aws::ECS::Types::Task] task
1113
+ # @param [Array<String>] task_ids
1067
1114
  # @return [nil]
1068
- def report_task_diagnostics(task)
1069
- Hako.logger.error("task_definition_arn=#{task.task_definition_arn} last_status=#{task.last_status}")
1070
- Hako.logger.error(" stopped_reason: #{task.stopped_reason}")
1071
- task.containers.sort_by(&:name).each do |container|
1072
- Hako.logger.error(" Container #{container.name}: last_status=#{container.last_status} exit_code=#{container.exit_code.inspect} reason=#{container.reason.inspect}")
1115
+ def report_task_diagnostics(cluster, task_ids)
1116
+ task_ids.each_slice(100) do |batch|
1117
+ ecs_client.describe_tasks(cluster: cluster, tasks: batch).tasks.each do |task|
1118
+ Hako.logger.error("task_definition_arn=#{task.task_definition_arn} last_status=#{task.last_status}")
1119
+ Hako.logger.error(" stopped_reason: #{task.stopped_reason}")
1120
+ task.containers.sort_by(&:name).each do |container|
1121
+ Hako.logger.error(" Container #{container.name}: last_status=#{container.last_status} exit_code=#{container.exit_code.inspect} reason=#{container.reason.inspect}")
1122
+ end
1123
+ end
1073
1124
  end
1074
1125
  end
1075
1126
 
@@ -1211,6 +1262,7 @@ module Hako
1211
1262
  # @return [nil]
1212
1263
  def print_volume_definition_in_cli_format(definition)
1213
1264
  return if definition.dig(:docker_volume_configuration, :autoprovision)
1265
+ return if definition[:efs_volume_configuration]
1214
1266
  # From version 1.20.0 of ECS agent, a local volume is provisioned when
1215
1267
  # 'host' is specified without 'source_path'.
1216
1268
  return if definition.dig(:host, :source_path)
@@ -1302,6 +1354,7 @@ module Hako
1302
1354
  opts = dev[:host_path]
1303
1355
  opts += ":#{dev[:container_path]}" if dev[:container_path]
1304
1356
  if dev[:permissions]
1357
+ opts += ':'
1305
1358
  dev[:permissions].each do |permission|
1306
1359
  opts += permission[0] if %w[read write mknod].include?(permission)
1307
1360
  end
@@ -1395,22 +1448,74 @@ module Hako
1395
1448
  nil
1396
1449
  end
1397
1450
 
1451
+ SecretValueFrom = Struct.new(:arn, :secret_name, :json_key, :version_stage, :version_id)
1452
+
1398
1453
  # @param [Hash] container_definition
1399
1454
  # @return [nil]
1400
1455
  def check_secrets(container_definition)
1401
- parameter_names = (container_definition[:secrets] || []).map { |secret| secret.fetch(:value_from) }
1402
- invalid_parameter_names = parameter_names.each_slice(10).flat_map do |names|
1403
- names = names.map do |name|
1404
- if name.start_with?('arn:')
1405
- name.slice(%r{:parameter(/.+)\z}, 1)
1456
+ parameter_arns = []
1457
+ secret_value_arns = []
1458
+ invalid_arns = []
1459
+
1460
+ (container_definition[:secrets] || []).each do |secret|
1461
+ arn = Aws::ARNParser.parse(secret.fetch(:value_from))
1462
+ case arn.service
1463
+ when 'ssm'
1464
+ parameter_arns << arn
1465
+ when 'secretsmanager'
1466
+ secret_value_arns << arn
1467
+ else
1468
+ invalid_arns << arn.to_s
1469
+ end
1470
+ end
1471
+
1472
+ parameter_arns.group_by(&:region).each do |region, region_arns|
1473
+ region_arns.each_slice(10) do |arns|
1474
+ invalid_arns += ssm_client(region).get_parameters(names: arns.map(&:to_s)).invalid_parameters
1475
+ end
1476
+ end
1477
+
1478
+ secret_value_arns.group_by(&:region).each do |region, region_arns|
1479
+ secret_value_froms = region_arns.filter_map do |arn|
1480
+ m = arn.resource.match(/\Asecret:(?<secret_name>[^:]+):(?<json_key>[^:]*):(?<version_stage>[^:]*):(?<version_id>[^:]*)\z/)
1481
+ if m
1482
+ f = SecretValueFrom.new(arn, m[:secret_name])
1483
+ unless m[:json_key].empty?
1484
+ f.json_key = m[:json_key]
1485
+ end
1486
+ unless m[:version_stage].empty?
1487
+ f.version_stage = m[:version_stage]
1488
+ end
1489
+ unless m[:version_id].empty?
1490
+ f.version_id = m[:version_id]
1491
+ end
1492
+ f
1406
1493
  else
1407
- name
1494
+ invalid_arns << arn.to_s
1495
+ nil
1496
+ end
1497
+ end
1498
+ secret_value_froms.group_by { |f| [f.secret_name, f.version_stage, f.version_id] }.each do |(secret_name, version_stage, version_id), fs|
1499
+ secret_arn = Aws::ARN.new(**fs[0].arn.to_h.merge(resource: "secret:#{secret_name}")).to_s
1500
+ begin
1501
+ secret_string = secretsmanager_client(region).get_secret_value(secret_id: secret_arn, version_stage: version_stage, version_id: version_id).secret_string
1502
+ secret_json = nil
1503
+ fs.each do |f|
1504
+ if f.json_key
1505
+ secret_json ||= JSON.parse(secret_string)
1506
+ unless secret_json.key?(f.json_key)
1507
+ invalid_arns << f.arn.to_s
1508
+ end
1509
+ end
1510
+ end
1511
+ rescue Aws::SecretsManager::Errors::ResourceNotFoundException
1512
+ invalid_arns += fs.map { |f| f.arn.to_s }
1408
1513
  end
1409
1514
  end
1410
- ssm_client.get_parameters(names: names).invalid_parameters
1411
1515
  end
1412
- unless invalid_parameter_names.empty?
1413
- raise Error.new("Invalid parameters for secrets: #{invalid_parameter_names}")
1516
+
1517
+ unless invalid_arns.empty?
1518
+ raise Error.new("Invalid ARNs for secrets: #{invalid_arns}")
1414
1519
  end
1415
1520
 
1416
1521
  nil
@@ -45,8 +45,10 @@ module Hako
45
45
  struct.member(:linux_parameters, Schema::Nullable.new(linux_parameters_schema))
46
46
  struct.member(:readonly_root_filesystem, Schema::Nullable.new(Schema::Boolean.new))
47
47
  struct.member(:docker_security_options, Schema::Nullable.new(Schema::UnorderedArray.new(Schema::String.new)))
48
- struct.member(:system_controls, Schema::Nullable.new(system_controls_schema))
48
+ struct.member(:system_controls, Schema::WithDefault.new(system_controls_schema, []))
49
49
  struct.member(:repository_credentials, Schema::Nullable.new(repository_credentials_schema))
50
+ struct.member(:resource_requirements, Schema::Nullable.new(Schema::UnorderedArray.new(resource_requirement_schema)))
51
+ struct.member(:firelens_configuration, Schema::Nullable.new(firelens_configuration_schema))
50
52
  end
51
53
  end
52
54
 
@@ -189,6 +191,20 @@ module Hako
189
191
  struct.member(:credentials_parameter, Schema::String.new)
190
192
  end
191
193
  end
194
+
195
+ def resource_requirement_schema
196
+ Schema::Structure.new.tap do |struct|
197
+ struct.member(:type, Schema::String.new)
198
+ struct.member(:value, Schema::String.new)
199
+ end
200
+ end
201
+
202
+ def firelens_configuration_schema
203
+ Schema::Structure.new.tap do |struct|
204
+ struct.member(:type, Schema::String.new)
205
+ struct.member(:options, Schema::Nullable.new(Schema::Table.new(Schema::String.new, Schema::String.new)))
206
+ end
207
+ end
192
208
  end
193
209
  end
194
210
  end
@@ -22,6 +22,7 @@ module Hako
22
22
  def volume_schema
23
23
  Schema::Structure.new.tap do |struct|
24
24
  struct.member(:docker_volume_configuration, Schema::Nullable.new(docker_volume_configuration_schema))
25
+ struct.member(:efs_volume_configuration, Schema::Nullable.new(efs_volume_configuration_schema))
25
26
  struct.member(:host, Schema::Nullable.new(host_schema))
26
27
  struct.member(:name, Schema::String.new)
27
28
  end
@@ -37,6 +38,23 @@ module Hako
37
38
  end
38
39
  end
39
40
 
41
+ def efs_volume_configuration_schema
42
+ Schema::Structure.new.tap do |struct|
43
+ struct.member(:file_system_id, Schema::String.new)
44
+ struct.member(:root_directory, Schema::WithDefault.new(Schema::String.new, '/'))
45
+ struct.member(:transit_encryption, Schema::Nullable.new(Schema::String.new))
46
+ struct.member(:transit_encryption_port, Schema::Nullable.new(Schema::Integer.new))
47
+ struct.member(:authorization_config, Schema::Nullable.new(efs_authorization_config_schema))
48
+ end
49
+ end
50
+
51
+ def efs_authorization_config_schema
52
+ Schema::Structure.new.tap do |struct|
53
+ struct.member(:access_point_id, Schema::Nullable.new(Schema::String.new))
54
+ struct.member(:iam, Schema::Nullable.new(Schema::String.new))
55
+ end
56
+ end
57
+
40
58
  def host_schema
41
59
  Schema::Structure.new.tap do |struct|
42
60
  struct.member(:source_path, Schema::Nullable.new(Schema::String.new))
@@ -10,7 +10,7 @@ module Hako
10
10
  class NginxFront < Script
11
11
  S3Config = Struct.new(:region, :bucket, :prefix) do
12
12
  # @param [Hash] options
13
- def initialize(options) # rubocop:disable Lint/MissingSuper
13
+ def initialize(options)
14
14
  self.region = options.fetch('region')
15
15
  self.bucket = options.fetch('bucket')
16
16
  self.prefix = options.fetch('prefix', nil)
data/lib/hako/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Hako
4
- VERSION = '2.17.0'
4
+ VERSION = '2.18.0'
5
5
  end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hako
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.17.0
4
+ version: 2.18.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kohei Suzuki
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2022-05-31 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: aws-sdk-applicationautoscaling
@@ -136,6 +135,20 @@ dependencies:
136
135
  - - ">="
137
136
  - !ruby/object:Gem::Version
138
137
  version: '0'
138
+ - !ruby/object:Gem::Dependency
139
+ name: aws-sdk-secretsmanager
140
+ requirement: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - ">="
143
+ - !ruby/object:Gem::Version
144
+ version: '0'
145
+ type: :runtime
146
+ prerelease: false
147
+ version_requirements: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - ">="
150
+ - !ruby/object:Gem::Version
151
+ version: '0'
139
152
  - !ruby/object:Gem::Dependency
140
153
  name: aws-sdk-servicediscovery
141
154
  requirement: !ruby/object:Gem::Requirement
@@ -206,6 +219,20 @@ dependencies:
206
219
  - - ">="
207
220
  - !ruby/object:Gem::Version
208
221
  version: '0'
222
+ - !ruby/object:Gem::Dependency
223
+ name: oga
224
+ requirement: !ruby/object:Gem::Requirement
225
+ requirements:
226
+ - - ">="
227
+ - !ruby/object:Gem::Version
228
+ version: '0'
229
+ type: :development
230
+ prerelease: false
231
+ version_requirements: !ruby/object:Gem::Requirement
232
+ requirements:
233
+ - - ">="
234
+ - !ruby/object:Gem::Version
235
+ version: '0'
209
236
  - !ruby/object:Gem::Dependency
210
237
  name: rake
211
238
  requirement: !ruby/object:Gem::Requirement
@@ -369,7 +396,6 @@ homepage: https://github.com/eagletmt/hako
369
396
  licenses:
370
397
  - MIT
371
398
  metadata: {}
372
- post_install_message:
373
399
  rdoc_options: []
374
400
  require_paths:
375
401
  - lib
@@ -377,15 +403,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
377
403
  requirements:
378
404
  - - ">="
379
405
  - !ruby/object:Gem::Version
380
- version: 2.6.0
406
+ version: 3.1.0
381
407
  required_rubygems_version: !ruby/object:Gem::Requirement
382
408
  requirements:
383
409
  - - ">="
384
410
  - !ruby/object:Gem::Version
385
411
  version: '0'
386
412
  requirements: []
387
- rubygems_version: 3.2.32
388
- signing_key:
413
+ rubygems_version: 3.6.9
389
414
  specification_version: 4
390
415
  summary: Deploy Docker container
391
416
  test_files: []