capistrano-ops 0.2.13 → 0.2.14

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: cec4486500e5ddac70640e6f3d074b51b361d649079e98f9ef6d337712c3de23
4
- data.tar.gz: ac8df454d33822890a673e800db7492bc7d4d2a7f54c0fe91378648fa163a2f8
3
+ metadata.gz: 72b073b9c2ecb5026ab9f56c3953f9bd664e5537087a07c7a22cd390a69fe7da
4
+ data.tar.gz: 60e5b8b52641c59bd1c6c3ec1b99aef79e90269a2ca11deb23b39283348b00e1
5
5
  SHA512:
6
- metadata.gz: c97bd7b0f7b0f9968b30d81a8807da57d690a4700e6674596f64b970727e4d99606af7ac8e192691fae63a0e5cf67ee54936bda0d1d06d935c37e068b3afdf41
7
- data.tar.gz: aac3a0d13aa1c8f78ab77f5943b2ee5944c561829831c117afd8dc1cfd0e0e638406c9bcda52f92c1cc53b1f8eeef455e9d62e1929ffe984e3408812a22de4eb
6
+ metadata.gz: 6bff0e67cad4a819dd93400b7de6b8acbec6642df2bc15e569cfd3a3a2ae6ec38a70f38eb09483f90b335cc423da5ef3ef9e2a912b036fe576911b35f26f1bfa
7
+ data.tar.gz: eb67fd7c492534f4e4086327631ea52c391802f76f3ac17b17525313483eff0ec8e54441a08f045a78d75714ab4fc9691a71de47706ffa7a03ce56786b85900d
data/.rubocop.yml CHANGED
@@ -1,3 +1,6 @@
1
+ require:
2
+ - rubocop-performance
3
+ - rubocop-rails
1
4
  AllCops:
2
5
  NewCops: disable
3
6
  TargetRubyVersion: 2.7
@@ -19,3 +22,7 @@ Metrics/PerceivedComplexity:
19
22
  Max: 25
20
23
  Style/Documentation:
21
24
  Enabled: false
25
+ Rails/RakeEnvironment:
26
+ Enabled: false
27
+ Rails/Output:
28
+ Enabled: false
@@ -26,4 +26,6 @@ Gem::Specification.new do |s|
26
26
  s.add_dependency 'rails'
27
27
  s.add_development_dependency 'bundler', '~> 2.4.12'
28
28
  s.add_development_dependency 'rake', '~> 10.0'
29
+ s.add_development_dependency 'rubocop', '~> 1.56.2' # rubocop ruby
30
+ s.add_development_dependency 'rubocop-performance', '~> 1.19.0' # speed up rubocop
29
31
  end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'figaro_yaml_helper'
4
+
5
+ namespace :figaro_yml do
6
+ include FigaroYmlHelper
7
+ rake_roles = fetch(:rake_roles, :app)
8
+
9
+ desc 'compare and set the figaro_yml file on the server'
10
+ task :compare do
11
+ env = fetch(:stage).to_s
12
+ # Read and parse local application.yml
13
+ local = local_yaml
14
+
15
+ # Split into stage-specific and global configurations
16
+ local_global_env, local_stage_env = configs(local, env)
17
+
18
+ # puts local_global_env.to_yaml
19
+ # puts "\n\n\n"
20
+ # puts({ env => local_stage_env }.to_yaml)
21
+ on roles(rake_roles) do
22
+ # Read and parse remote application.yml
23
+ remote = YAML.safe_load(capture("cat #{shared_path}/config/application.yml"))
24
+ remote_global_env, remote_stage_env = configs(remote, env)
25
+
26
+ # puts remote_global_env.to_yaml
27
+ # puts "\n\n\n"
28
+ # puts({ env => remote_stage_env }.to_yaml)
29
+ # puts "Comparing local and remote application.yml for '#{env}' environment..."
30
+
31
+ # Compare hashes and handle nil results with empty hashes
32
+ differences_global = compare_hashes(local_global_env, remote_global_env)
33
+ differences_stage = compare_hashes(local_stage_env, remote_stage_env)
34
+
35
+ print_changes(differences_global, 'Local application.yml has extra/different global entries compared to remote.')
36
+ print_changes(differences_stage, "Local application.yml has extra/different entries in #{env} section compared to remote.") if differences_stage
37
+
38
+ puts 'No Differences found between remote and local application.yml' unless differences_global || differences_stage
39
+
40
+ # ask to overwrite remote yml if differences found
41
+ overwrite_remote = ask_to_overwrite('Overwrite remote application.yml') if differences_global || differences_stage
42
+ puts 'Nothing written to remote application.yml' unless overwrite_remote
43
+
44
+ exit unless overwrite_remote
45
+ # sort local yml before updating remote
46
+ puts 'Preparing local application.yml before updating remote...'
47
+ invoke 'figaro_yml:sort_local'
48
+ puts 'Local application.yml is ready to be updated.'
49
+
50
+ # update remote yml
51
+ invoke 'figaro_yml:setup'
52
+ ##
53
+ # TODO: restart server after updating remote yml
54
+ # let user choose to restart server after updating remote yml
55
+ # let user choose to restart server now or later if they choose to restart now
56
+ # ask for time to restart server if he chooses to restart later
57
+ # restart server after the time given
58
+ ##
59
+ puts 'Remote application.yml has been updated.'
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FigaroYmlHelper
4
+ def environments
5
+ Dir.glob('config/deploy/*.rb')
6
+ end
7
+
8
+ def local_yaml
9
+ YAML.safe_load(File.read('config/application.yml')) || {}
10
+ end
11
+
12
+ def write_to_file(file, content)
13
+ File.open(file, 'w') do |f|
14
+ f.write(content)
15
+ end
16
+ end
17
+
18
+ def write_combined_yaml(yamls_combined)
19
+ if yamls_combined.empty?
20
+ info 'No data to write.'
21
+ else
22
+ # write to new file
23
+ info 'writing to config/application.yml'
24
+ write_to_file('config/application.yml', yamls_combined.to_yaml)
25
+ end
26
+ end
27
+
28
+ def compare_hashes(hash1, hash2)
29
+ all_keys = hash1.keys | hash2.keys # Union of all keys from both hashes
30
+ all_keys.each_with_object({}) do |key, changes_hash|
31
+ old_value = hash2[key].nil? ? 'nil' : hash2[key].to_s
32
+ new_value = hash1[key].nil? ? 'nil' : hash1[key].to_s
33
+
34
+ changes_hash[key] = { old: old_value, new: new_value } if old_value != new_value
35
+ end.tap { |changes| return changes.empty? ? nil : changes }
36
+ end
37
+
38
+ def print_changes(changes, message)
39
+ return unless changes
40
+
41
+ puts "#{message}:\n\n"
42
+ changes.each do |key, diff|
43
+ puts "#{key}: #{diff[:old]} => #{diff[:new]}"
44
+ end
45
+ puts "\n"
46
+ end
47
+
48
+ def ask_to_overwrite(question)
49
+ answer = ''
50
+ until %w[y n].include?(answer)
51
+ print "#{question}? (y/N): "
52
+ answer = $stdin.gets.strip.downcase
53
+ end
54
+ answer == 'y'
55
+ end
56
+
57
+ def configs(yaml, env)
58
+ stage_yml = yaml[env.to_s]&.sort.to_h
59
+ global_yml = remove_nested(yaml)&.sort.to_h
60
+ [global_yml, stage_yml]
61
+ end
62
+
63
+ def remove_nested(hash)
64
+ hash.each_with_object({}) do |(key, value), new_hash|
65
+ new_hash[key] = value unless value.is_a?(Hash)
66
+ end
67
+ end
68
+
69
+ def sort_with_nested(hash)
70
+ hash.each_with_object({}) do |(key, value), new_hash|
71
+ new_hash[key] = value.is_a?(Hash) ? sort_with_nested(value) : value
72
+ end.sort.to_h
73
+ end
74
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'figaro_yaml_helper'
4
+
5
+ namespace :figaro_yml do
6
+ include FigaroYmlHelper
7
+
8
+ rake_roles = fetch(:rake_roles, :app)
9
+ desc 'get the `application.yml` file from server and create local if it does not exist'
10
+ task :get do
11
+ env = fetch(:stage)
12
+ if !File.exist?('config/application.yml')
13
+ run_locally do
14
+ puts "found #{environments.count} stages"
15
+ yamls_combined = {}
16
+
17
+ stages.each do |f|
18
+ stage = File.basename(f, '.rb')
19
+ puts "download #{stage} application.yml"
20
+ begin
21
+ res = capture "cap #{stage} figaro_yml:get_stage"
22
+ stage_yaml = YAML.safe_load(res)
23
+ stage_yaml[stage.to_s] = stage_yaml[stage.to_s].sort.to_h
24
+ yamls_combined.merge!(stage_yaml) if stage_yaml
25
+ rescue StandardError
26
+ puts "could not get #{stage} application.yml"
27
+ end
28
+ end
29
+
30
+ write_combined_yaml(yamls_combined.sort.to_h)
31
+ end
32
+ else
33
+ on roles(rake_roles) do
34
+ local_yml = local_yaml.sort.to_h
35
+ local_global, local_stage = configs(local_yml, env)
36
+
37
+ remote = capture("cat #{shared_path}/config/application.yml")
38
+ remote_yml = YAML.safe_load(remote).sort.to_h
39
+ remote_global, remote_stage = configs(remote_yml, env)
40
+
41
+ differences_global = compare_hashes(remote_global, local_global || {})
42
+ differences_stage = compare_hashes(remote_stage, local_stage || {})
43
+
44
+ print_changes(differences_global,
45
+ 'Remote application.yml has extra/different global entries compared to local.')
46
+ print_changes(differences_stage, "Remote application.yml has extra/different entries in #{env} section compared to local.") if differences_stage
47
+
48
+ puts 'No Differences found between remote and local application.yml' unless differences_global || differences_stage
49
+
50
+ # ask to overwrite local yml if differences found
51
+ stage_overwrite = ask_to_overwrite("Overwrite local application.yml #{env} section") if differences_stage
52
+ global_overwrite = ask_to_overwrite('Overwrite local application.yml globals') if differences_global
53
+ puts 'Nothing written to local application.yml' unless stage_overwrite || global_overwrite
54
+ exit unless stage_overwrite || global_overwrite
55
+
56
+ # compose new yml
57
+ composed_yml = {}
58
+ composed_yml.merge!(local_yml) # local yml is always included to avoid losing any data
59
+ composed_yml.merge!(local_global) unless global_overwrite
60
+ composed_yml.merge!(remote_global) if global_overwrite
61
+ composed_yml[env.to_s] = stage_overwrite ? remote_stage : local_stage
62
+
63
+ # write to new file
64
+ write_combined_yaml(composed_yml.sort.to_h)
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'figaro_yaml_helper'
4
+
5
+ namespace :figaro_yml do
6
+ rake_roles = fetch(:rake_roles, :app)
7
+
8
+ task :get_stage do
9
+ on roles(rake_roles) do
10
+ puts capture "cat #{shared_path}/config/application.yml"
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'figaro_yaml_helper'
4
+
5
+ namespace :figaro_yml do
6
+ include FigaroYmlHelper
7
+
8
+ task :sort_local do
9
+ run_locally do
10
+ info 'Sorting local application.yml...'
11
+ local = local_yaml
12
+ sorted_local = sort_with_nested(local)
13
+ write_combined_yaml(sorted_local)
14
+ end
15
+ end
16
+
17
+ task :sort do
18
+ invoke 'figaro_yml:sort_local'
19
+ end
20
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ Dir.glob("#{File.expand_path(__dir__)}/figaro_yml/**/*.rake").each { |f| load f }
@@ -5,12 +5,14 @@ require 'capistrano/version'
5
5
  if defined?(Capistrano::VERSION) && Gem::Version.new(Capistrano::VERSION).release >= Gem::Version.new('3.0.0')
6
6
  load File.expand_path('capistrano/v3/tasks/whenever.rake', __dir__)
7
7
  load File.expand_path('capistrano/v3/tasks/backup.rake', __dir__)
8
- load File.expand_path('capistrano/v3/tasks/figaro_yml.rake', __dir__)
8
+ load File.expand_path('capistrano/v3/tasks/figaro_yml.rb', __dir__)
9
+
9
10
  load File.expand_path('capistrano/v3/tasks/invoke.rake', __dir__)
10
11
  path = File.expand_path(__dir__)
11
12
  Dir.glob("#{path}/capistrano/v3/tasks/backup/**/*.rake").each { |f| load f }
12
13
  Dir.glob("#{path}/capistrano/v3/tasks/logrotate/**/*.rake").each { |f| load f }
13
14
  Dir.glob("#{path}/capistrano/v3/tasks/logs/**/*.rake").each { |f| load f }
15
+
14
16
  else
15
17
  puts 'Capistrano 3 is required to use this gem'
16
18
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Capistrano
4
4
  module Ops
5
- VERSION = '0.2.13'
5
+ VERSION = '0.2.14'
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: capistrano-ops
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.13
4
+ version: 0.2.14
5
5
  platform: ruby
6
6
  authors:
7
7
  - Florian Crusius
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-06-12 00:00:00.000000000 Z
11
+ date: 2024-06-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: aws-sdk-s3
@@ -94,6 +94,34 @@ dependencies:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
96
  version: '10.0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rubocop
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: 1.56.2
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: 1.56.2
111
+ - !ruby/object:Gem::Dependency
112
+ name: rubocop-performance
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: 1.19.0
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: 1.19.0
97
125
  description: A collection of devops tasks for rails applications
98
126
  email:
99
127
  - florian@zauberware.com
@@ -127,7 +155,12 @@ files:
127
155
  - lib/capistrano/ops/capistrano/v3/tasks/backup/database/pull.rake
128
156
  - lib/capistrano/ops/capistrano/v3/tasks/backup/storage/create.rake
129
157
  - lib/capistrano/ops/capistrano/v3/tasks/backup/storage/pull.rake
130
- - lib/capistrano/ops/capistrano/v3/tasks/figaro_yml.rake
158
+ - lib/capistrano/ops/capistrano/v3/tasks/figaro_yml.rb
159
+ - lib/capistrano/ops/capistrano/v3/tasks/figaro_yml/compare.rake
160
+ - lib/capistrano/ops/capistrano/v3/tasks/figaro_yml/figaro_yaml_helper.rb
161
+ - lib/capistrano/ops/capistrano/v3/tasks/figaro_yml/get.rake
162
+ - lib/capistrano/ops/capistrano/v3/tasks/figaro_yml/get_stage.rake
163
+ - lib/capistrano/ops/capistrano/v3/tasks/figaro_yml/sort_local.rake
131
164
  - lib/capistrano/ops/capistrano/v3/tasks/invoke.rake
132
165
  - lib/capistrano/ops/capistrano/v3/tasks/logrotate/check.rake
133
166
  - lib/capistrano/ops/capistrano/v3/tasks/logrotate/disable.rake
@@ -1,139 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # rubocop:disable Metrics/BlockLength
4
- namespace :figaro_yml do
5
- # Defaults to :app role
6
- rake_roles = fetch(:rake_roles, :app)
7
-
8
- desc 'get the `application.yml` file from server and create local if it does not exist'
9
- task :get do
10
- env = fetch(:stage)
11
- if !File.exist?('config/application.yml')
12
- puts 'config/application.yml does not exist, creating it from all stages'
13
- run_locally do
14
- yamls = {}
15
- stages = Dir.glob('config/deploy/*.rb')
16
- puts "found #{stages.count} stages"
17
- stages.map do |f|
18
- stage = File.basename(f, '.rb')
19
- puts "download #{stage} application.yml"
20
- begin
21
- res = capture "cap #{stage} figaro_yml:get_stage"
22
- yamls = yamls.merge(YAML.safe_load(res))
23
- rescue StandardError
24
- puts "could not get #{stage} application.yml"
25
- end
26
- yamls
27
- end
28
- # write to new file
29
- puts 'writing to config/application.yml'
30
- write_to_file('config/application.yml', yamls.to_yaml)
31
- end
32
- else
33
- local_yml = YAML.safe_load(File.read('config/application.yml'))
34
- on roles(rake_roles) do
35
- remote = capture("cat #{shared_path}/config/application.yml")
36
- remote_yml = YAML.safe_load(remote)
37
- remote_stage = remote_yml[env.to_s]
38
- puts "remote application.yml stage '#{env}':\n\n"
39
- puts "#{remote}\r\n"
40
- puts "\r\n"
41
- loop do
42
- print "Overwrite local application.yml stage '#{env}'? (y/N): "
43
- input = $stdin.gets.strip.downcase
44
- answer = (input.empty? ? 'N' : input).downcase.to_s
45
-
46
- next unless %w[y n].include?(answer)
47
-
48
- if answer == 'y'
49
- puts 'Updating local application.yml'
50
- local_yml[env.to_s] = remote_stage
51
- write_to_file('config/application.yml', local_yml.to_yaml)
52
- exit
53
- end
54
- break
55
- end
56
- puts 'Nothing written to local application.yml'
57
- exit
58
- end
59
- end
60
- end
61
-
62
- task :get_stage do
63
- on roles(rake_roles) do
64
- puts capture "cat #{shared_path}/config/application.yml"
65
- end
66
- end
67
-
68
- desc 'compare and set the figaro_yml file on the server'
69
- task :compare do
70
- env = fetch(:stage)
71
- # read local application.yml
72
- local = File.read('config/application.yml')
73
-
74
- # convert to hash
75
- local_global_env = YAML.safe_load(local)
76
-
77
- # split into stage and global
78
- local_stage_env = local_global_env[env.to_s]
79
- local_global_env.delete('staging')
80
- local_global_env.delete('production')
81
-
82
- on roles(rake_roles) do
83
- # read remote application.yml
84
- remote = capture("cat #{shared_path}/config/application.yml")
85
-
86
- remote_global_env = YAML.safe_load(remote)
87
- remote_stage_env = remote_global_env[env.to_s]
88
- remote_global_env.delete(env.to_s)
89
-
90
- puts "with command 'cap #{env} figaro_yml:setup', following variables will be overwritten:"
91
- puts '--------------------------------------------------------------------------------'
92
- result1 = compare_hashes(local_global_env, remote_global_env)
93
- result2 = compare_hashes(local_stage_env, remote_stage_env)
94
- if !result1.empty? || !result2.empty?
95
- loop do
96
- print 'Update remote application.yml? (y/N): '
97
- input = $stdin.gets.strip.downcase
98
- answer = (input.empty? ? 'N' : input).downcase.to_s
99
-
100
- next unless %w[y n].include?(answer)
101
-
102
- if answer == 'y'
103
- puts 'Updating remote application.yml'
104
- invoke 'figaro_yml:setup'
105
- exit
106
- end
107
- break
108
- end
109
- puts 'remote application.yml not updated'
110
- exit
111
- end
112
- puts 'remote application.yml is up to date'
113
- end
114
- end
115
- def compare_hashes(hash1, hash2)
116
- changes = false
117
- local_server = hash1.to_a - hash2.to_a
118
- server_local = hash2.to_a - hash1.to_a
119
-
120
- [local_server + server_local].flatten(1).to_h.each_key do |k|
121
- new_value = hash1[k].to_s
122
- new_value = new_value.empty? ? 'nil' : new_value
123
- old_value = hash2[k].to_s
124
- old_value = old_value.empty? ? 'nil' : old_value
125
-
126
- if old_value != new_value
127
- puts "#{k}: #{old_value} => #{new_value} \r\n"
128
- changes = true
129
- end
130
- end
131
- end
132
-
133
- def write_to_file(file, content)
134
- File.open(file, 'w') do |f|
135
- f.write(content)
136
- end
137
- end
138
- end
139
- # rubocop:enable Metrics/BlockLength