afterlife 1.3.0 → 1.4.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: 785bb4c634a1b73338a56cb64b6119389ca07904457159b14af3a00ad48869f0
4
- data.tar.gz: 0f24b2547eb32ab72ae1807f2c5bcb813a5cc09f9dee589f6343bbd047c32c26
3
+ metadata.gz: 50bb80b8a5c213397eb84b52d78f39fc6ff72491a45df415e0c7700d1c2eb4bc
4
+ data.tar.gz: d6a8963da3e0d48f511e182d4bcf5c49f20e4e7449e29e9d8cc1f35c2a43fcc8
5
5
  SHA512:
6
- metadata.gz: 3ff42032daec81547cc0075ab8ed71d250b2cec8ac677383f6a9702e75124ebb27d173c085c8e5d155cfd9c0b63f49e79c3a7f35630de88424127816f3de4593
7
- data.tar.gz: 48d016ace44ecc8f285eee917e6493766bc914adc324cc582f0e7a7c495fdc9ede0548f5c3529fb6ac296a2e529ed8b83a0208c5f9ae87e5d6d43eafad540afc
6
+ metadata.gz: '0594d2bce7f949b133c67536d8a3115b2bcdc3a12dc6826135335d6aa5d1cd5e0bd938088a5437bb0381aef2b509b06f990c5268b5a21e8d667882b1355ed274'
7
+ data.tar.gz: 8b40060493294350391a2aec32c866cadd04a7aaf087ada970fb267430e836c3f724b4dc1ef683beb3337e59cd8b3a77f420afd168830d6972b7c8ee301e8fb8
data/lib/afterlife/cli.rb CHANGED
@@ -31,12 +31,13 @@ module Afterlife
31
31
  desc 'deploy <stage>', 'Deploy current repo'
32
32
  option 'no-build', type: :boolean
33
33
  option 'no-install', type: :boolean
34
+ option 'no-auth', type: :boolean
34
35
  option 'dry-run', type: :boolean
35
36
  option 'skip-after-hooks', type: :boolean
36
37
  option :yes, type: :boolean
37
38
  def deploy(stage)
38
39
  invoke Deploy::Cli, :call, [stage], options
39
- rescue Deploy::Error
40
+ rescue Error
40
41
  fatal!(e.message)
41
42
  rescue Interrupt
42
43
  log_interrupted
@@ -11,14 +11,14 @@ module Afterlife
11
11
  desc 'deploy <stage>', 'Deploy current repo'
12
12
  def call(stage)
13
13
  Afterlife.cli = self
14
- Deploy.call(stage) do |deployment|
14
+ Deploy.call(stage, options) do |deployment|
15
15
  setup_deploy(deployment)
16
16
  say_status 'Deploying', deployment.initial_message
17
17
  deployment.run
18
18
  say_status 'Deployed', deployment.output
19
19
  run_after_hooks
20
20
  end
21
- rescue Deploy::Error, Afterlife::Error => e
21
+ rescue Afterlife::Error => e
22
22
  fatal!(e.message)
23
23
  end
24
24
 
@@ -3,6 +3,13 @@
3
3
  module Afterlife
4
4
  module Deploy
5
5
  class Deployment
6
+
7
+ attr_reader :options
8
+
9
+ def initialize(options)
10
+ @options = options
11
+ end
12
+
6
13
  def run
7
14
  fail NotImplementedError
8
15
  end
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Afterlife
4
+ module Deploy
5
+ class KubernetesDeployment < Deployment
6
+ def setup
7
+ Afterlife.current_repo.env.set('PLATFORM' => 'linux/amd64') if deploy_stage?
8
+
9
+ Afterlife.current_repo.env.set!(
10
+ 'REGISTRY' => registry,
11
+ 'TAG' => repo.current_revision,
12
+ )
13
+ end
14
+
15
+ def run
16
+ Exec.run(commands)
17
+ end
18
+
19
+ def confirmation_message
20
+ 'You are about to deploy the current directory'
21
+ end
22
+
23
+ private
24
+
25
+ def commands
26
+ [
27
+ authenticate_command,
28
+ build_command,
29
+ set_image_command,
30
+ local_stage? ? delete_kubernetes_resource : nil,
31
+ apply_kubernetes_settings,
32
+ ].flatten.compact
33
+ end
34
+
35
+ # commands
36
+
37
+ def authenticate_command
38
+ return if options['no-auth']
39
+
40
+ <<-BASH
41
+ echo "#{aws_ecr_token}" | docker login --username AWS --password-stdin #{registry} &&
42
+ kubectl delete secret regcred &&
43
+ kubectl create secret docker-registry regcred
44
+ --docker-server=#{registry}
45
+ --docker-username=AWS
46
+ --docker-password=#{aws_ecr_token}
47
+ --docker-email=devs@mifiel.com
48
+ BASH
49
+ end
50
+
51
+ def build_command
52
+ return if options['no-build']
53
+
54
+ <<-BASH
55
+ docker buildx bake -f docker-bake.hcl #{local_stage? ? '--load' : '--push'}
56
+ BASH
57
+ end
58
+
59
+ def set_image_command
60
+ <<-BASH
61
+ $(cd #{kubelocation} && kustomize edit set image #{image_name}:latest=#{full_image_name})
62
+ BASH
63
+ end
64
+
65
+ def delete_kubernetes_resource
66
+ <<-BASH
67
+ kubectl delete -k #{kubelocation}
68
+ BASH
69
+ end
70
+
71
+ def apply_kubernetes_settings
72
+ <<-BASH
73
+ kubectl apply -k #{kubelocation}
74
+ BASH
75
+ end
76
+
77
+ # utils
78
+
79
+ def local_stage?
80
+ Afterlife.current_stage.name.to_sym == :local
81
+ end
82
+
83
+ def deploy_stage?
84
+ %i[qa production staging].include?(Afterlife.current_stage.name.to_sym)
85
+ end
86
+
87
+ def kubelocation
88
+ ".afterlife/#{Afterlife.current_stage.name}"
89
+ end
90
+
91
+ def full_image_name
92
+ "#{registry}/#{image_name}:#{repo.current_revision}"
93
+ end
94
+
95
+ def image_name
96
+ @image_name ||= repo.conf.dig(:deploy, :image_name)
97
+ end
98
+
99
+ # Priority:
100
+ # 1. Uses AFTERLIFE_DEPLOY_REGISTRY from the ENV if it's defined. It can be
101
+ # either in real system ENV, or defined in the env section of the repo
102
+ # .afterlife.yml config
103
+ # 2. Uses repo .afterlife.yml config at deploy.registry
104
+ # 3. Uses stages.$current_stage.registry if exists in ~/.afterlife/config.yml
105
+ def registry
106
+ @registry ||= Afterlife.current_repo.variable('deploy.registry') || Afterlife.current_stage.registry
107
+ end
108
+
109
+ # command outputs
110
+
111
+ def aws_ecr_token
112
+ @aws_ecr_token ||= `aws ecr get-login-password --region us-west-2`.strip
113
+ end
114
+ end
115
+ end
116
+ end
@@ -4,11 +4,9 @@ module Afterlife
4
4
  module Deploy
5
5
  module_function
6
6
 
7
- Error = Class.new StandardError
8
-
9
- def call(stage)
10
- deployment = klass.new
11
- Afterlife.current_stage = find_stage(stage)
7
+ def call(stage, options)
8
+ deployment = klass.new(options)
9
+ Afterlife.current_stage = stage
12
10
  fill_env
13
11
  deployment.setup
14
12
  yield deployment
@@ -28,16 +26,6 @@ module Afterlife
28
26
  end
29
27
  end
30
28
 
31
- def find_stage(stage)
32
- return Stage.build(stage) unless Afterlife.config.file_exist?
33
-
34
- unless Afterlife.config.stages.key?(stage.to_sym)
35
- fail Error, "invalid stage '#{stage}'. Possible values: #{Afterlife.config.stages.keys.map(&:to_s)}"
36
- end
37
-
38
- Afterlife.config.stages[stage.to_sym]
39
- end
40
-
41
29
  def fill_env
42
30
  # in nested deployments we need the current definition, not the parent's
43
31
  Afterlife.current_repo.env.set!(
@@ -21,21 +21,19 @@ module Afterlife
21
21
  from_stage_envs
22
22
  end
23
23
 
24
- def env_hash_by_branch
25
- @env_hash_by_branch ||= env_hash&.dig(:by_branch, repo.current_branch.to_sym)
26
- end
27
-
28
- def env_hash_by_stage
29
- @env_hash_by_stage ||= env_hash&.dig(:by_stage, Afterlife.current_stage.name.to_sym)
30
- end
24
+ def [](key)
25
+ key = key.gsub('.', '_').upcase
26
+ env_name = "AFTERLIFE_#{key}"
27
+ return ENV[env_name] if ENV.key?(env_name)
31
28
 
32
- def env_hash
33
- @env_hash ||= repo.conf.dig(:deploy, :env)
29
+ @env[env_name]
34
30
  end
35
31
 
36
32
  # soft set, do not override existent ENVs
37
33
  def set(arg)
38
34
  arg.each do |k, v|
35
+ fail ArgumentError, "key '#{k}' must be a string" unless k.is_a?(String)
36
+
39
37
  @env[k] ||= ENV.fetch(k, v)
40
38
  end
41
39
  end
@@ -43,6 +41,8 @@ module Afterlife
43
41
  # forces to be exactly dthe value
44
42
  def set!(arg)
45
43
  arg.each do |k, v|
44
+ fail ArgumentError, "key '#{k}' must be a string" unless k.is_a?(String)
45
+
46
46
  @env[k] = v
47
47
  end
48
48
  end
@@ -50,9 +50,9 @@ module Afterlife
50
50
  private
51
51
 
52
52
  def from_flat_envs
53
- return unless env_hash.is_a?(Hash)
53
+ return unless repo_env_config.is_a?(Hash)
54
54
 
55
- env_hash.each do |key, value|
55
+ repo_env_config.each do |key, value|
56
56
  next if key == :by_branch
57
57
  next if key == :by_stage
58
58
 
@@ -68,6 +68,10 @@ module Afterlife
68
68
  end
69
69
  end
70
70
 
71
+ def env_hash_by_branch
72
+ @env_hash_by_branch ||= repo_env_config&.dig(:by_branch, repo.current_branch.to_sym)
73
+ end
74
+
71
75
  def from_stage_envs
72
76
  return unless env_hash_by_stage.is_a?(Hash)
73
77
 
@@ -75,5 +79,15 @@ module Afterlife
75
79
  @env[key.to_s] = value.to_s
76
80
  end
77
81
  end
82
+
83
+ def env_hash_by_stage
84
+ return unless Afterlife.current_stage
85
+
86
+ @env_hash_by_stage ||= repo_env_config&.dig(:by_stage, Afterlife.current_stage.name.to_sym)
87
+ end
88
+
89
+ def repo_env_config
90
+ @repo_env_config ||= repo.conf.dig(:deploy, :env) || {}
91
+ end
78
92
  end
79
93
  end
@@ -2,6 +2,8 @@
2
2
 
3
3
  module Afterlife
4
4
  class Repo
5
+ # Reads configuration from the repo,
6
+ # either from YML or from the repo instance methods
5
7
  class Config
6
8
  Error = Class.new StandardError
7
9
 
@@ -12,7 +14,7 @@ module Afterlife
12
14
  end
13
15
 
14
16
  def initialize(path)
15
- @path = path.split('.').map(&:to_sym)
17
+ @path = path
16
18
  end
17
19
 
18
20
  def read(&block) # rubocop:disable Metrics/MethodLength
@@ -24,7 +26,7 @@ module Afterlife
24
26
  when String, Symbol
25
27
  yield(from_yml)
26
28
  else
27
- fail Error, "No config with path '#{path.join('.')}'" unless repo_method?
29
+ fail Error, "No config with path '#{path}'" unless repo_method?
28
30
 
29
31
  yield(repo.public_send(repo_method))
30
32
  end
@@ -33,7 +35,7 @@ module Afterlife
33
35
  private
34
36
 
35
37
  def repo_method?
36
- path.count == 1 && repo.public_methods(false).include?(repo_method)
38
+ path.count('.').zero? && repo.public_methods(false).include?(repo_method)
37
39
  end
38
40
 
39
41
  def repo_method
@@ -47,7 +49,7 @@ module Afterlife
47
49
  def from_yml
48
50
  return @from_yml if defined?(@from_yml)
49
51
 
50
- @from_yml ||= repo.conf.dig(*path)
52
+ @from_yml ||= repo.variable(path)
51
53
  end
52
54
  end
53
55
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  module Afterlife
4
4
  class Repo
5
+ # Reads (and builds) the .afterlife.yml file in the repo
5
6
  class Provider
6
7
  CONFIG_NAME = '.afterlife.yml'
7
8
 
@@ -46,6 +46,14 @@ module Afterlife
46
46
  full_path.join(conf.dig(:cdn, :dist) || DEFAULT_DIST)
47
47
  end
48
48
 
49
+ # 1. Searchs for the AFTERLIFE_#{name} env var
50
+ # 2. Searchs the path in the repo .afterlife.yml config
51
+ def variable(name)
52
+ return env[name] if env[name]
53
+
54
+ conf.dig(*name.split('.').map(&:to_sym))
55
+ end
56
+
49
57
  def env
50
58
  @env ||= Environment.from(self)
51
59
  end
@@ -109,10 +117,6 @@ module Afterlife
109
117
  @package_json ||= JSON.parse(File.read(@pkg_path), symbolize_names: true)
110
118
  end
111
119
 
112
- def branch_conf
113
- conf.dig(:deploy, :env, :by_branch, current_branch.to_sym)
114
- end
115
-
116
120
  def conf
117
121
  @conf ||= Provider.new(full_path).config
118
122
  end
@@ -6,6 +6,7 @@ module Afterlife
6
6
  :bucket,
7
7
  :region,
8
8
  :cdn_url,
9
+ :registry,
9
10
  keyword_init: true,
10
11
  ) do
11
12
  def self.build(name = nil)
@@ -14,7 +15,18 @@ module Afterlife
14
15
  bucket: ENV.fetch('AWS_BUCKET', nil),
15
16
  region: ENV.fetch('AWS_REGION'),
16
17
  cdn_url: ENV.fetch('AWS_BUCKET', nil),
18
+ registry: ENV.fetch('AFTERLIFE_DEPLOY_REGISTRY', nil),
17
19
  )
18
20
  end
21
+
22
+ def self.find(stage)
23
+ return build(stage) unless Afterlife.config.file_exist?
24
+
25
+ unless Afterlife.config.stages.key?(stage.to_sym)
26
+ fail Error, "invalid stage '#{stage}'. Possible values: #{Afterlife.config.stages.keys.map(&:to_s)}"
27
+ end
28
+
29
+ Afterlife.config.stages[stage.to_sym]
30
+ end
19
31
  end
20
32
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Afterlife
4
- VERSION = '1.3.0'
4
+ VERSION = '1.4.0'
5
5
  end
data/lib/afterlife.rb CHANGED
@@ -33,7 +33,7 @@ module Afterlife
33
33
  end
34
34
 
35
35
  def self.current_stage=(other)
36
- @current_stage = other
36
+ @current_stage = Stage.find(other)
37
37
  end
38
38
 
39
39
  def self.current_stage
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: afterlife
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Genaro Madrid
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-11-21 00:00:00.000000000 Z
11
+ date: 2024-01-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -130,6 +130,7 @@ files:
130
130
  - lib/afterlife/deploy/cli.rb
131
131
  - lib/afterlife/deploy/custom_deployment.rb
132
132
  - lib/afterlife/deploy/deployment.rb
133
+ - lib/afterlife/deploy/kubernetes_deployment.rb
133
134
  - lib/afterlife/environment.rb
134
135
  - lib/afterlife/exec.rb
135
136
  - lib/afterlife/release.rb