afterlife 1.3.0 → 1.5.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 +4 -4
- data/lib/afterlife/cli.rb +3 -1
- data/lib/afterlife/deploy/cli.rb +2 -2
- data/lib/afterlife/deploy/deployment.rb +7 -0
- data/lib/afterlife/deploy/kubernetes_deployment.rb +140 -0
- data/lib/afterlife/deploy.rb +3 -15
- data/lib/afterlife/environment.rb +25 -11
- data/lib/afterlife/exec.rb +12 -5
- data/lib/afterlife/repo/config.rb +6 -4
- data/lib/afterlife/repo/provider.rb +1 -0
- data/lib/afterlife/repo.rb +8 -4
- data/lib/afterlife/stage.rb +12 -0
- data/lib/afterlife/version.rb +1 -1
- data/lib/afterlife.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e03a1499e1af905014e5fb48d8865601a0bb354a856d56dc0a629a3b3c08a99b
|
4
|
+
data.tar.gz: 345db972b71109a69d3117e3ef5e3a8fc8cc4aec094c230f9b49ea46e2c206e3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2a4b14afb3f286756663da5d5c6c039cd0c327cf8a9ad60a3c20b04d994fdba73b2270892b209cbf9ece8f424e17e2e6eab9798f93416c254aa4549b486a41fc
|
7
|
+
data.tar.gz: 89e9ec3e6d10345f8baeea198d0dde968d96b438cc5312a7d2f545176b4755bd6953b0ad0ad60663015c33b8ac477f5bacb3aa1948bc320a1495cfd1602396ef
|
data/lib/afterlife/cli.rb
CHANGED
@@ -31,12 +31,14 @@ 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
|
35
|
+
option 'no-apply', type: :boolean
|
34
36
|
option 'dry-run', type: :boolean
|
35
37
|
option 'skip-after-hooks', type: :boolean
|
36
38
|
option :yes, type: :boolean
|
37
39
|
def deploy(stage)
|
38
40
|
invoke Deploy::Cli, :call, [stage], options
|
39
|
-
rescue
|
41
|
+
rescue Error
|
40
42
|
fatal!(e.message)
|
41
43
|
rescue Interrupt
|
42
44
|
log_interrupted
|
data/lib/afterlife/deploy/cli.rb
CHANGED
@@ -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
|
21
|
+
rescue Afterlife::Error => e
|
22
22
|
fatal!(e.message)
|
23
23
|
end
|
24
24
|
|
@@ -0,0 +1,140 @@
|
|
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
|
+
apply_kubernetes_settings,
|
31
|
+
].flatten.compact
|
32
|
+
end
|
33
|
+
|
34
|
+
# commands
|
35
|
+
|
36
|
+
def authenticate_command
|
37
|
+
return if options['no-auth'] || local_stage?
|
38
|
+
|
39
|
+
AwsAuth.new(registry).commands
|
40
|
+
end
|
41
|
+
|
42
|
+
def build_command
|
43
|
+
return if options['no-build']
|
44
|
+
|
45
|
+
<<-BASH
|
46
|
+
docker buildx bake -f docker-bake.hcl #{targets.join(' ')} #{local_stage? ? '--load' : '--push'}
|
47
|
+
BASH
|
48
|
+
end
|
49
|
+
|
50
|
+
def set_image_command
|
51
|
+
<<-BASH
|
52
|
+
cd #{kubelocation} &&
|
53
|
+
#{
|
54
|
+
targets.map do |target|
|
55
|
+
"kustomize edit set image #{full_image_name(target)}:latest=#{registry_image_name(target)}"
|
56
|
+
end.join(' && ')
|
57
|
+
}
|
58
|
+
BASH
|
59
|
+
end
|
60
|
+
|
61
|
+
def targets
|
62
|
+
repo.conf.dig(:deploy, :targets) || %w[app]
|
63
|
+
end
|
64
|
+
|
65
|
+
def registry_image_name(target)
|
66
|
+
"#{registry}/#{full_image_name(target)}:#{repo.current_revision}"
|
67
|
+
end
|
68
|
+
|
69
|
+
def full_image_name(target)
|
70
|
+
return image_name if target == 'app'
|
71
|
+
|
72
|
+
"#{image_name}-#{target}"
|
73
|
+
end
|
74
|
+
|
75
|
+
def apply_kubernetes_settings
|
76
|
+
return if options['no-apply']
|
77
|
+
|
78
|
+
<<-BASH
|
79
|
+
kubectl apply -k #{kubelocation}
|
80
|
+
BASH
|
81
|
+
end
|
82
|
+
|
83
|
+
# utils
|
84
|
+
|
85
|
+
def local_stage?
|
86
|
+
Afterlife.current_stage.name.to_sym == :local
|
87
|
+
end
|
88
|
+
|
89
|
+
def deploy_stage?
|
90
|
+
%i[qa production staging].include?(Afterlife.current_stage.name.to_sym)
|
91
|
+
end
|
92
|
+
|
93
|
+
def kubelocation
|
94
|
+
".afterlife/#{Afterlife.current_stage.name}"
|
95
|
+
end
|
96
|
+
|
97
|
+
def image_name
|
98
|
+
@image_name ||= repo.conf.dig(:deploy, :image_name).tap do |result|
|
99
|
+
fail Error, 'deploy.image_name for kubernetes deployments' unless result
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# Priority:
|
104
|
+
# 1. Uses AFTERLIFE_DEPLOY_REGISTRY from the ENV if it's defined. It can be
|
105
|
+
# either in real system ENV, or defined in the env section of the repo
|
106
|
+
# .afterlife.yml config
|
107
|
+
# 2. Uses repo .afterlife.yml config at deploy.registry
|
108
|
+
# 3. Uses stages.$current_stage.registry if exists in ~/.afterlife/config.yml
|
109
|
+
def registry
|
110
|
+
@registry ||= Afterlife.current_repo.variable('deploy.registry') || Afterlife.current_stage.registry
|
111
|
+
end
|
112
|
+
|
113
|
+
class AwsAuth
|
114
|
+
attr_reader :registry
|
115
|
+
|
116
|
+
def initialize(registry)
|
117
|
+
@registry = registry
|
118
|
+
end
|
119
|
+
|
120
|
+
def commands
|
121
|
+
[docker_login]
|
122
|
+
end
|
123
|
+
|
124
|
+
def docker_login
|
125
|
+
<<-BASH
|
126
|
+
echo "#{aws_ecr_token}" | docker login --username AWS --password-stdin #{registry}
|
127
|
+
BASH
|
128
|
+
end
|
129
|
+
|
130
|
+
def aws_ecr_token
|
131
|
+
@aws_ecr_token ||= Exec.result("aws ecr get-login-password --region #{region}")
|
132
|
+
end
|
133
|
+
|
134
|
+
def region
|
135
|
+
@region ||= registry.gsub(/[^.]+\.dkr\.ecr\.([^.]+)\.amazonaws\.com/, '\1')
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
data/lib/afterlife/deploy.rb
CHANGED
@@ -4,11 +4,9 @@ module Afterlife
|
|
4
4
|
module Deploy
|
5
5
|
module_function
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
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
|
25
|
-
|
26
|
-
|
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
|
-
|
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
|
53
|
+
return unless repo_env_config.is_a?(Hash)
|
54
54
|
|
55
|
-
|
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
|
data/lib/afterlife/exec.rb
CHANGED
@@ -5,7 +5,11 @@ require 'active_support/core_ext/string'
|
|
5
5
|
|
6
6
|
module Afterlife
|
7
7
|
class Exec
|
8
|
-
|
8
|
+
def self.result(arg)
|
9
|
+
fail Error, 'Exec.result only accepts strings' unless arg.is_a?(String)
|
10
|
+
|
11
|
+
new(arg).run(result: true).first
|
12
|
+
end
|
9
13
|
|
10
14
|
def self.run(arg)
|
11
15
|
new(arg).run
|
@@ -23,11 +27,14 @@ module Afterlife
|
|
23
27
|
@commands = Array(arg)
|
24
28
|
end
|
25
29
|
|
26
|
-
def run
|
27
|
-
parsed_commands.
|
30
|
+
def run(result: false)
|
31
|
+
parsed_commands.map do |command|
|
28
32
|
Afterlife.cli.log_info(command) if Afterlife.cli.options['verbose']
|
29
|
-
|
30
|
-
|
33
|
+
next if Afterlife.cli.options['dry-run']
|
34
|
+
next `#{command}`.squish if result
|
35
|
+
|
36
|
+
system(env_hash, command.squish, exception: true)
|
37
|
+
end.compact
|
31
38
|
rescue RuntimeError => e
|
32
39
|
raise Error, e
|
33
40
|
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
|
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
|
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
|
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.
|
52
|
+
@from_yml ||= repo.variable(path)
|
51
53
|
end
|
52
54
|
end
|
53
55
|
end
|
data/lib/afterlife/repo.rb
CHANGED
@@ -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
|
data/lib/afterlife/stage.rb
CHANGED
@@ -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
|
data/lib/afterlife/version.rb
CHANGED
data/lib/afterlife.rb
CHANGED
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.
|
4
|
+
version: 1.5.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:
|
11
|
+
date: 2024-01-31 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
|