lexicon-common 0.1.0 → 0.2.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/lexicon-common.gemspec +2 -0
- data/lib/lexicon-common.rb +3 -0
- data/lib/lexicon/common.rb +1 -0
- data/lib/lexicon/common/mixin/logger_aware.rb +2 -2
- data/lib/lexicon/common/mixin/nameable.rb +16 -0
- data/lib/lexicon/common/package/directory_package_loader.rb +56 -12
- data/lib/lexicon/common/package/package.rb +11 -43
- data/lib/lexicon/common/package/package_file.rb +50 -0
- data/lib/lexicon/common/package/v1/package.rb +83 -0
- data/lib/lexicon/common/package/v1/package_builder.rb +70 -0
- data/lib/lexicon/common/package/v1/source_file_set.rb +28 -0
- data/lib/lexicon/common/package/v2/package.rb +56 -0
- data/lib/lexicon/common/package/v2/package_builder.rb +72 -0
- data/lib/lexicon/common/package/v2/source_file_set.rb +26 -0
- data/lib/lexicon/common/production/datasource_loader.rb +119 -60
- data/lib/lexicon/common/production/table_locker.rb +58 -0
- data/lib/lexicon/common/psql.rb +47 -0
- data/lib/lexicon/common/remote/package_downloader.rb +61 -43
- data/lib/lexicon/common/remote/package_uploader.rb +37 -28
- data/lib/lexicon/common/remote/s3_client.rb +6 -0
- data/lib/lexicon/common/schema/validator_factory.rb +1 -1
- data/lib/lexicon/common/shell_executor.rb +0 -3
- data/lib/lexicon/common/version.rb +1 -1
- data/resources/lexicon.schema.json +116 -31
- metadata +39 -3
- data/lib/lexicon/common/package/package_builder.rb +0 -68
- data/lib/lexicon/common/package/source_file_set.rb +0 -24
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Lexicon
|
4
|
+
module Common
|
5
|
+
class Psql
|
6
|
+
# @param [String] url
|
7
|
+
# @param [ShellExecutor] executor
|
8
|
+
def initialize(url:, executor:)
|
9
|
+
@url = url
|
10
|
+
@executor = executor
|
11
|
+
end
|
12
|
+
|
13
|
+
# @param [String] command
|
14
|
+
# @param [String, Array<String>] search_path
|
15
|
+
def execute(command, search_path:)
|
16
|
+
command = <<~SQL
|
17
|
+
SET search_path TO #{Array(search_path).join(', ')};
|
18
|
+
#{command}
|
19
|
+
SQL
|
20
|
+
|
21
|
+
execute_raw(command)
|
22
|
+
end
|
23
|
+
|
24
|
+
# @param [String] command
|
25
|
+
def execute_raw(command)
|
26
|
+
@executor.execute <<~BASH
|
27
|
+
psql '#{url}' --quiet -c #{Shellwords.escape(command)}
|
28
|
+
BASH
|
29
|
+
end
|
30
|
+
|
31
|
+
# @param [Pathname] file
|
32
|
+
# @param [String, Array<String>] search_path
|
33
|
+
def load_sql(file, search_path:)
|
34
|
+
@executor.execute <<~BASH
|
35
|
+
echo 'SET SEARCH_PATH TO #{Array(search_path).join(', ')};' | cat - #{file} | psql '#{url}'
|
36
|
+
BASH
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
# @return [ShellExecutor]
|
42
|
+
attr_reader :executor
|
43
|
+
# @return [String]
|
44
|
+
attr_reader :url
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
using Corindon::Result::Ext
|
4
|
+
|
3
5
|
module Lexicon
|
4
6
|
module Common
|
5
7
|
module Remote
|
@@ -9,71 +11,87 @@ module Lexicon
|
|
9
11
|
# @param [DirectoryPackageLoader] package_loader
|
10
12
|
def initialize(s3:, out_dir:, package_loader:)
|
11
13
|
super(s3: s3)
|
14
|
+
|
12
15
|
@out_dir = out_dir
|
13
16
|
@package_loader = package_loader
|
14
17
|
end
|
15
18
|
|
16
19
|
# @param [Semantic::Version] version
|
17
|
-
# @return [
|
20
|
+
# @return [Corindon::Result::Result]
|
18
21
|
def download(version)
|
19
|
-
|
22
|
+
rescue_failure do
|
23
|
+
bucket = version.to_s
|
20
24
|
|
21
|
-
|
22
|
-
|
23
|
-
|
25
|
+
if s3.bucket_exist?(bucket)
|
26
|
+
Dir.mktmpdir(nil, out_dir) do |tmp_dir|
|
27
|
+
tmp_dir = Pathname.new(tmp_dir)
|
24
28
|
|
25
|
-
|
26
|
-
bucket: bucket,
|
27
|
-
key: Package::Package::SPEC_FILE_NAME,
|
28
|
-
response_target: tmp_dir.join(Package::Package::SPEC_FILE_NAME).to_s
|
29
|
-
)
|
30
|
-
s3.raw.get_object(
|
31
|
-
bucket: bucket,
|
32
|
-
key: Package::Package::CHECKSUM_FILE_NAME,
|
33
|
-
response_target: tmp_dir.join(Package::Package::CHECKSUM_FILE_NAME).to_s
|
34
|
-
)
|
29
|
+
download_spec_files(bucket, tmp_dir).unwrap!
|
35
30
|
|
36
|
-
|
37
|
-
|
38
|
-
|
31
|
+
package = package_loader.load_package(tmp_dir.basename.to_s)
|
32
|
+
if !package.nil?
|
33
|
+
puts "[ OK ] Found package with key #{version}, version is #{package.version}".green
|
39
34
|
|
40
|
-
|
35
|
+
download_data_files(package, bucket).unwrap!
|
41
36
|
|
42
|
-
|
43
|
-
|
44
|
-
s3.raw.get_object(bucket: bucket, key: "data/#{file.basename.to_s}", response_target: file.to_s)
|
45
|
-
puts "[ OK ] Downloaded #{file.basename}".green
|
46
|
-
end
|
47
|
-
end.each(&:join)
|
37
|
+
dest_dir = out_dir.join(version.to_s)
|
38
|
+
FileUtils.mkdir_p(dest_dir)
|
48
39
|
|
49
|
-
|
50
|
-
|
51
|
-
path = package.data_path(fs)
|
52
|
-
s3.raw.get_object(bucket: bucket, key: "data/#{path.basename.to_s}", response_target: path.to_s)
|
53
|
-
puts "[ OK ] Downloaded #{path.basename}".green
|
40
|
+
tmp_dir.children.each do |child|
|
41
|
+
FileUtils.mv(child.to_s, dest_dir.join(child.basename).to_s)
|
54
42
|
end
|
55
|
-
end.each(&:join)
|
56
|
-
|
57
|
-
dest_dir = out_dir.join(version.to_s)
|
58
|
-
FileUtils.mkdir_p(dest_dir)
|
59
|
-
tmp_dir.children.each do |child|
|
60
|
-
FileUtils.mv(child.to_s, dest_dir.join(child.basename).to_s)
|
61
|
-
end
|
62
43
|
|
63
|
-
|
64
|
-
|
65
|
-
|
44
|
+
Success(package)
|
45
|
+
else
|
46
|
+
puts "[ NOK ] The remote contains a bucket '#{version}' but it does not contains a valid package.".red
|
66
47
|
|
67
|
-
|
48
|
+
Failure(StandardError.new("The folder #{bucket} on the server does not contain a valid package"))
|
49
|
+
end
|
68
50
|
end
|
51
|
+
else
|
52
|
+
Failure(StandardError.new("The server does not have a directory named #{bucket}"))
|
69
53
|
end
|
70
|
-
else
|
71
|
-
false
|
72
54
|
end
|
73
55
|
end
|
74
56
|
|
75
57
|
private
|
76
58
|
|
59
|
+
def download_data_files(package, bucket)
|
60
|
+
rescue_failure do
|
61
|
+
threads = package.files.map do |file|
|
62
|
+
Thread.new do
|
63
|
+
destination = package.dir.join(file.path)
|
64
|
+
FileUtils.mkdir_p(destination.dirname)
|
65
|
+
|
66
|
+
s3.raw.get_object(bucket: bucket, key: file.to_s, response_target: destination)
|
67
|
+
|
68
|
+
puts "[ OK ] Downloaded #{file}".green
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
threads.each(&:join)
|
73
|
+
|
74
|
+
Success(nil)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def download_spec_files(bucket, tmp_dir)
|
79
|
+
rescue_failure do
|
80
|
+
s3.raw.get_object(
|
81
|
+
bucket: bucket,
|
82
|
+
key: Package::Package::SPEC_FILE_NAME,
|
83
|
+
response_target: tmp_dir.join(Package::Package::SPEC_FILE_NAME).to_s
|
84
|
+
)
|
85
|
+
s3.raw.get_object(
|
86
|
+
bucket: bucket,
|
87
|
+
key: Package::Package::CHECKSUM_FILE_NAME,
|
88
|
+
response_target: tmp_dir.join(Package::Package::CHECKSUM_FILE_NAME).to_s
|
89
|
+
)
|
90
|
+
|
91
|
+
Success(nil)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
77
95
|
# @return [DirectoryPackageLoader]
|
78
96
|
attr_reader :package_loader
|
79
97
|
# @return [Pathname]
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
using Corindon::Result::Ext
|
4
|
+
|
3
5
|
module Lexicon
|
4
6
|
module Common
|
5
7
|
module Remote
|
@@ -7,51 +9,58 @@ module Lexicon
|
|
7
9
|
include Mixin::LoggerAware
|
8
10
|
|
9
11
|
# @param [Package] package
|
10
|
-
# @return [
|
12
|
+
# @return [Corindon::Result::Result]
|
11
13
|
def upload(package)
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
14
|
+
rescue_failure do
|
15
|
+
bucket_name = package.version.to_s
|
16
|
+
|
17
|
+
if s3.bucket_exist?(bucket_name)
|
18
|
+
Failure(StandardError.new("The server already has a folder named #{bucket_name}"))
|
19
|
+
else
|
20
|
+
upload_package(package, bucket_name)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
16
24
|
|
17
|
-
|
18
|
-
puts '[ OK ] Structure uploaded.'.green
|
25
|
+
private
|
19
26
|
|
20
|
-
|
21
|
-
|
22
|
-
|
27
|
+
# @return [Corindon::Result::Result]
|
28
|
+
def upload_package(package, bucket_name)
|
29
|
+
s3.raw.create_bucket(bucket: bucket_name)
|
23
30
|
|
24
|
-
|
25
|
-
puts "[ OK ] #{path.basename}".green
|
26
|
-
end
|
31
|
+
relative_paths = [*base_files, *package.files.map(&:path)]
|
27
32
|
|
28
|
-
upload_files(
|
33
|
+
upload_files(*relative_paths, from: package.dir, bucket: bucket_name) do |path|
|
29
34
|
puts "[ OK ] #{path.basename}".green
|
30
35
|
end
|
31
36
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
end
|
36
|
-
rescue StandardError => e
|
37
|
-
log_error(e)
|
38
|
-
|
39
|
-
false
|
40
|
-
end
|
37
|
+
Success(package)
|
38
|
+
rescue StandardError => e
|
39
|
+
s3.ensure_bucket_absent(bucket_name)
|
41
40
|
|
42
|
-
|
41
|
+
Failure(e)
|
42
|
+
end
|
43
43
|
|
44
44
|
# @param [Array<Pathname>] files
|
45
|
-
#
|
45
|
+
# @param [Pathname] from
|
46
46
|
# @yieldparam [Pathname] path
|
47
|
-
def upload_files(*files, bucket:,
|
47
|
+
def upload_files(*files, bucket:, from:)
|
48
48
|
files.each do |path|
|
49
|
-
path.open do |f|
|
50
|
-
s3.put_object(bucket: bucket, key:
|
49
|
+
from.join(path).open do |f|
|
50
|
+
s3.raw.put_object(bucket: bucket, key: path.to_s, body: f)
|
51
51
|
end
|
52
|
+
|
52
53
|
yield path if block_given?
|
53
54
|
end
|
54
55
|
end
|
56
|
+
|
57
|
+
# @return [Array<Pathname>]
|
58
|
+
def base_files
|
59
|
+
[
|
60
|
+
Pathname.new(Package::Package::CHECKSUM_FILE_NAME),
|
61
|
+
Pathname.new(Package::Package::SPEC_FILE_NAME),
|
62
|
+
]
|
63
|
+
end
|
55
64
|
end
|
56
65
|
end
|
57
66
|
end
|
@@ -4,7 +4,6 @@ module Lexicon
|
|
4
4
|
module Common
|
5
5
|
class ShellExecutor
|
6
6
|
include Mixin::Finalizable
|
7
|
-
include Mixin::LoggerAware
|
8
7
|
|
9
8
|
def initialize
|
10
9
|
@command_dir = Dir.mktmpdir
|
@@ -13,8 +12,6 @@ module Lexicon
|
|
13
12
|
# @param [String] command
|
14
13
|
# @return [String]
|
15
14
|
def execute(command)
|
16
|
-
log(command.cyan)
|
17
|
-
|
18
15
|
cmd = Tempfile.new('command-', @command_dir)
|
19
16
|
cmd.write <<~BASH
|
20
17
|
#!/usr/bin/env bash
|
@@ -5,47 +5,132 @@
|
|
5
5
|
"description": "",
|
6
6
|
"type": "object",
|
7
7
|
"properties": {
|
8
|
+
"schema_version": {
|
9
|
+
"$ref": "#/definitions/schema_versions"
|
10
|
+
},
|
8
11
|
"version": {
|
12
|
+
"$ref": "#/definitions/semver"
|
13
|
+
}
|
14
|
+
},
|
15
|
+
"oneOf": [
|
16
|
+
{
|
17
|
+
"properties": {
|
18
|
+
"schema_version": {
|
19
|
+
"type": "integer",
|
20
|
+
"const": 1
|
21
|
+
},
|
22
|
+
"content": {
|
23
|
+
"$ref": "#/definitions/content/1"
|
24
|
+
}
|
25
|
+
}
|
26
|
+
},
|
27
|
+
{
|
28
|
+
"properties": {
|
29
|
+
"schema_version": {
|
30
|
+
"type": "integer",
|
31
|
+
"const": 2
|
32
|
+
},
|
33
|
+
"content": {
|
34
|
+
"$ref": "#/definitions/content/2"
|
35
|
+
}
|
36
|
+
}
|
37
|
+
}
|
38
|
+
],
|
39
|
+
"required": [
|
40
|
+
"version",
|
41
|
+
"content"
|
42
|
+
],
|
43
|
+
"definitions": {
|
44
|
+
"schema_versions": {
|
45
|
+
"type": "integer",
|
46
|
+
"default": 2,
|
47
|
+
"enum": [
|
48
|
+
2
|
49
|
+
]
|
50
|
+
},
|
51
|
+
"semver": {
|
9
52
|
"description": "The version of the packaged version",
|
10
53
|
"type": "string",
|
11
54
|
"$comment": "Regex is from https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string",
|
12
55
|
"pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$"
|
13
56
|
},
|
14
57
|
"content": {
|
15
|
-
"
|
16
|
-
|
17
|
-
"
|
18
|
-
"
|
19
|
-
|
20
|
-
"
|
21
|
-
"
|
22
|
-
|
23
|
-
|
24
|
-
"
|
25
|
-
|
26
|
-
|
27
|
-
"
|
58
|
+
"1": {
|
59
|
+
"type": "object",
|
60
|
+
"patternProperties": {
|
61
|
+
"^[a-z][a-z_]*$": {
|
62
|
+
"type": "object",
|
63
|
+
"properties": {
|
64
|
+
"name": {
|
65
|
+
"type": "string"
|
66
|
+
},
|
67
|
+
"structure": {
|
68
|
+
"type": "string"
|
69
|
+
},
|
70
|
+
"data": {
|
71
|
+
"type": "string"
|
72
|
+
},
|
73
|
+
"tables": {
|
74
|
+
"type": "array",
|
75
|
+
"items": {
|
76
|
+
"type": "string"
|
77
|
+
},
|
78
|
+
"additionalItems": false
|
79
|
+
}
|
28
80
|
},
|
29
|
-
"
|
30
|
-
"
|
31
|
-
"
|
81
|
+
"required": [
|
82
|
+
"name",
|
83
|
+
"structure",
|
84
|
+
"tables"
|
85
|
+
]
|
86
|
+
}
|
87
|
+
},
|
88
|
+
"additionalProperties": false
|
89
|
+
},
|
90
|
+
"2": {
|
91
|
+
"type": "object",
|
92
|
+
"patternProperties": {
|
93
|
+
"^[a-z][a-z_]*$": {
|
94
|
+
"type": "object",
|
95
|
+
"properties": {
|
96
|
+
"name": {
|
97
|
+
"type": "string"
|
98
|
+
},
|
99
|
+
"structure": {
|
32
100
|
"type": "string"
|
33
101
|
},
|
34
|
-
"
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
102
|
+
"tables": {
|
103
|
+
"type": "object",
|
104
|
+
"patternProperties": {
|
105
|
+
"^([a-z]+)(_[a-z]+)*$": {
|
106
|
+
"type": "array",
|
107
|
+
"items": {
|
108
|
+
"type": "string"
|
109
|
+
},
|
110
|
+
"additionalItems": false
|
111
|
+
}
|
112
|
+
},
|
113
|
+
"additionalProperties": false
|
114
|
+
}
|
115
|
+
},
|
116
|
+
"required": [
|
117
|
+
"name",
|
118
|
+
"structure",
|
119
|
+
"tables"
|
120
|
+
]
|
121
|
+
}
|
122
|
+
},
|
123
|
+
"additionalProperties": false
|
124
|
+
}
|
125
|
+
},
|
126
|
+
"versions": {
|
127
|
+
"default": {
|
128
|
+
"properties": {
|
129
|
+
"content": {
|
130
|
+
"$ref": "#/definitions/content/1"
|
131
|
+
}
|
42
132
|
}
|
43
|
-
}
|
44
|
-
"additionalProperties": false
|
133
|
+
}
|
45
134
|
}
|
46
|
-
}
|
47
|
-
"required": [
|
48
|
-
"version",
|
49
|
-
"content"
|
50
|
-
]
|
135
|
+
}
|
51
136
|
}
|