pray-cli 1.16.0 → 1.17.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/CHANGELOG.md +10 -0
- data/lib/pray/apply_report.rb +49 -0
- data/lib/pray/cli/commands/init.rb +1 -20
- data/lib/pray/cli/commands/init_prayer.rb +76 -0
- data/lib/pray/cli/commands/init_prayer_manifest.rb +102 -0
- data/lib/pray/cli/commands/packages.rb +13 -2
- data/lib/pray/cli/commands/workflow.rb +5 -0
- data/lib/pray/cli/help.rb +52 -15
- data/lib/pray/cli/helpers.rb +13 -5
- data/lib/pray/cli/parse.rb +21 -1
- data/lib/pray/cli.rb +3 -1
- data/lib/pray/destination.rb +32 -2
- data/lib/pray/distribution.rb +94 -0
- data/lib/pray/format_serialize.rb +1 -4
- data/lib/pray/local_prayer.rb +38 -0
- data/lib/pray/lockfile.rb +8 -2
- data/lib/pray/manifest.rb +6 -3
- data/lib/pray/manifest_json.rb +1 -0
- data/lib/pray/manifest_parser_blocks.rb +15 -15
- data/lib/pray/materialize.rb +7 -1
- data/lib/pray/package_spec.rb +25 -13
- data/lib/pray/package_spec_render.rb +4 -3
- data/lib/pray/package_spec_version.rb +33 -0
- data/lib/pray/plan.rb +2 -1
- data/lib/pray/publish.rb +27 -2
- data/lib/pray/render.rb +6 -36
- data/lib/pray/render_managed.rb +64 -0
- data/lib/pray/render_patch.rb +63 -12
- data/lib/pray/resolve.rb +5 -36
- data/lib/pray/resolve_local.rb +39 -0
- data/lib/pray/resolve_source.rb +5 -0
- data/lib/pray/serve.rb +15 -4
- data/lib/pray/serve_range.rb +36 -0
- data/lib/pray/torrent_manifest.rb +78 -0
- data/lib/pray/upstream_drift.rb +42 -0
- data/lib/pray/upstream_merge.rb +25 -0
- data/lib/pray/upstream_refresh.rb +28 -2
- data/lib/pray/verify.rb +2 -2
- data/lib/pray/version.rb +2 -2
- data/lib/pray.rb +10 -0
- metadata +12 -1
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require "fileutils"
|
|
5
|
+
|
|
6
|
+
module Pray
|
|
7
|
+
DISTRIBUTION_CONFIG_SPEC = "pray-distribution-config-1"
|
|
8
|
+
TORRENT_PROTOCOL = "torrent"
|
|
9
|
+
DISTRIBUTION_CONFIG_FIELDS = %w[spec protocols bootstrap_trackers enable_dht].freeze
|
|
10
|
+
|
|
11
|
+
RegistryDistributionSettings = Struct.new(
|
|
12
|
+
:spec, :protocols, :bootstrap_trackers, :enable_dht,
|
|
13
|
+
keyword_init: true
|
|
14
|
+
) do
|
|
15
|
+
def initialize(spec: DISTRIBUTION_CONFIG_SPEC, protocols: [], bootstrap_trackers: [], enable_dht: false)
|
|
16
|
+
super
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def allows_torrent?
|
|
20
|
+
Array(protocols).include?(TORRENT_PROTOCOL)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
module Distribution
|
|
25
|
+
module_function
|
|
26
|
+
|
|
27
|
+
def default_settings
|
|
28
|
+
RegistryDistributionSettings.new
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def parse_settings(text)
|
|
32
|
+
data = JSON.parse(text)
|
|
33
|
+
unless data.is_a?(Hash)
|
|
34
|
+
raise Error.parse("distribution config", "expected an object")
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
data.each_key do |key|
|
|
38
|
+
next if DISTRIBUTION_CONFIG_FIELDS.include?(key)
|
|
39
|
+
|
|
40
|
+
raise Error.parse("distribution config", "unsupported field: #{key}")
|
|
41
|
+
end
|
|
42
|
+
settings = RegistryDistributionSettings.new(
|
|
43
|
+
spec: data.fetch("spec", DISTRIBUTION_CONFIG_SPEC),
|
|
44
|
+
protocols: Array(data["protocols"]),
|
|
45
|
+
bootstrap_trackers: Array(data["bootstrap_trackers"]),
|
|
46
|
+
enable_dht: data["enable_dht"] == true
|
|
47
|
+
)
|
|
48
|
+
validate_settings!(settings)
|
|
49
|
+
settings
|
|
50
|
+
rescue JSON::ParserError => error
|
|
51
|
+
raise Error.parse("distribution config", error.message)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def read_settings(root)
|
|
55
|
+
path = File.join(root, "v1", "distribution.json")
|
|
56
|
+
return default_settings unless File.file?(path)
|
|
57
|
+
|
|
58
|
+
parse_settings(File.read(path))
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def write_settings(root, settings = default_settings)
|
|
62
|
+
validate_settings!(settings)
|
|
63
|
+
path = File.join(root, "v1", "distribution.json")
|
|
64
|
+
FileUtils.mkdir_p(File.dirname(path))
|
|
65
|
+
payload = {"spec" => settings.spec, "protocols" => Array(settings.protocols)}
|
|
66
|
+
File.write(path, JSON.pretty_generate(payload))
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def validate_settings!(settings)
|
|
70
|
+
unless settings.spec == DISTRIBUTION_CONFIG_SPEC
|
|
71
|
+
raise Error.parse("distribution config", "unsupported distribution config spec: #{settings.spec}")
|
|
72
|
+
end
|
|
73
|
+
Array(settings.protocols).each do |name|
|
|
74
|
+
next if name == TORRENT_PROTOCOL
|
|
75
|
+
|
|
76
|
+
raise Error.parse("distribution config", "unsupported protocol: #{name}")
|
|
77
|
+
end
|
|
78
|
+
if settings.enable_dht
|
|
79
|
+
raise Error.parse("distribution config", "DHT announce is not implemented")
|
|
80
|
+
end
|
|
81
|
+
return if Array(settings.bootstrap_trackers).empty? || settings.allows_torrent?
|
|
82
|
+
|
|
83
|
+
raise Error.parse("distribution config", "bootstrap_trackers requires protocols to include torrent")
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def fetch_settings(source_url)
|
|
87
|
+
parse_settings(Registry.http_get(Registry.join_url(source_url, "v1/distribution.json")))
|
|
88
|
+
rescue Error => error
|
|
89
|
+
return default_settings if error.message.match?(/:\s*404\b/)
|
|
90
|
+
|
|
91
|
+
raise
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
@@ -42,10 +42,7 @@ module Pray
|
|
|
42
42
|
path = target.skills.first || ""
|
|
43
43
|
lines << %(tree "#{path}" do)
|
|
44
44
|
target.entries.each do |entry|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
package = find_package(manifest, entry.name)
|
|
48
|
-
lines << " #{ManifestMethods.format_package_declaration(package)}" if package
|
|
45
|
+
lines << " #{format_destination_entry(entry, manifest)}"
|
|
49
46
|
end
|
|
50
47
|
lines << "end"
|
|
51
48
|
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pray
|
|
4
|
+
DEFAULT_LOCAL_PRAYER_NAME = "project"
|
|
5
|
+
DEFAULT_PATH_SOURCE_NAME = "local"
|
|
6
|
+
DEFAULT_PATH_SOURCE_DIRECTORY = "prayers"
|
|
7
|
+
|
|
8
|
+
module LocalPrayer
|
|
9
|
+
module_function
|
|
10
|
+
|
|
11
|
+
def validate_name!(name)
|
|
12
|
+
trimmed = name.to_s.strip
|
|
13
|
+
raise Error.usage("prayer name is missing") if trimmed.empty?
|
|
14
|
+
if trimmed.include?("/") || trimmed.include?("\\") || trimmed == "." || trimmed == ".."
|
|
15
|
+
raise Error.usage("prayer name must be a single folder name")
|
|
16
|
+
end
|
|
17
|
+
if reserved_distribution_layout_name?(trimmed)
|
|
18
|
+
raise Error.usage("#{trimmed} is reserved for the distribution layout")
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
trimmed
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def path_source_package_directory(source_name, package_name)
|
|
25
|
+
prefix = "#{source_name}/"
|
|
26
|
+
rest = package_name.delete_prefix(prefix)
|
|
27
|
+
if rest != package_name && !rest.empty? && !rest.include?("/") && !rest.include?("\\")
|
|
28
|
+
return rest
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
package_name.to_s.tr("/", "-").tr("\\", "-")
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def reserved_distribution_layout_name?(name)
|
|
35
|
+
name.match?(/\Av[0-9]+\z/)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
data/lib/pray/lockfile.rb
CHANGED
|
@@ -5,6 +5,8 @@ require "perfect_toml"
|
|
|
5
5
|
require_relative "lockfile_serialize"
|
|
6
6
|
|
|
7
7
|
module Pray
|
|
8
|
+
LOCAL_EMBED_PACKAGE = "local"
|
|
9
|
+
|
|
8
10
|
LockSource = Struct.new(:name, :kind, :url, :revision, :host_key_fingerprint)
|
|
9
11
|
LockedPackage = Struct.new(
|
|
10
12
|
:name, :version, :source, :path, :tree_hash, :artifact_hash, :artifact,
|
|
@@ -20,7 +22,11 @@ module Pray
|
|
|
20
22
|
ManagedSpanRecord = Struct.new(
|
|
21
23
|
:id, :target, :open_line, :close_line, :ideal_checksum, :package, :export,
|
|
22
24
|
:source_checksum, :silenced
|
|
23
|
-
)
|
|
25
|
+
) do
|
|
26
|
+
def local_embed?
|
|
27
|
+
package == LOCAL_EMBED_PACKAGE
|
|
28
|
+
end
|
|
29
|
+
end
|
|
24
30
|
ProvisionedFileRecord = Struct.new(:path, :content_hash, :package, :export)
|
|
25
31
|
|
|
26
32
|
Lockfile = Struct.new(
|
|
@@ -179,7 +185,7 @@ module Pray
|
|
|
179
185
|
package: packages.map do |package|
|
|
180
186
|
LockedPackage.new(
|
|
181
187
|
name: package.declaration.name,
|
|
182
|
-
version: package.spec.
|
|
188
|
+
version: package.spec.recorded_version,
|
|
183
189
|
source: package.declaration.source,
|
|
184
190
|
path: relative_lockfile_path(project_root, package.root),
|
|
185
191
|
tree_hash: package.tree_hash,
|
data/lib/pray/manifest.rb
CHANGED
|
@@ -46,8 +46,8 @@ module Pray
|
|
|
46
46
|
end
|
|
47
47
|
end
|
|
48
48
|
|
|
49
|
-
ManifestLocal = Struct.new(:path, :position, :optional, :bound) do
|
|
50
|
-
def initialize(path:, position: "after", optional: false, bound: false)
|
|
49
|
+
ManifestLocal = Struct.new(:path, :position, :optional, :bound, :file) do
|
|
50
|
+
def initialize(path:, position: "after", optional: false, bound: false, file: nil)
|
|
51
51
|
super
|
|
52
52
|
end
|
|
53
53
|
end
|
|
@@ -135,7 +135,10 @@ module Pray
|
|
|
135
135
|
PathSafety.validate_project_relative_path!(package.path) if package.path
|
|
136
136
|
PathSafety.validate_destination_path!(package.file) if package.file
|
|
137
137
|
end
|
|
138
|
-
manifest.local.each
|
|
138
|
+
manifest.local.each do |local|
|
|
139
|
+
PathSafety.validate_project_relative_path!(local.path)
|
|
140
|
+
PathSafety.validate_destination_path!(local.file) if local.file
|
|
141
|
+
end
|
|
139
142
|
end
|
|
140
143
|
end
|
|
141
144
|
|
data/lib/pray/manifest_json.rb
CHANGED
|
@@ -116,7 +116,7 @@ module Pray
|
|
|
116
116
|
while (statement = next_statement)
|
|
117
117
|
if statement == "end"
|
|
118
118
|
unless saw_package
|
|
119
|
-
raise Error.parse("manifest", "file block requires a pray package
|
|
119
|
+
raise Error.parse("manifest", "file block requires a pray package")
|
|
120
120
|
end
|
|
121
121
|
return
|
|
122
122
|
end
|
|
@@ -132,7 +132,15 @@ module Pray
|
|
|
132
132
|
return true
|
|
133
133
|
end
|
|
134
134
|
if (match = statement.match(/\A(?:pray|use|include|package) (.+)\z/))
|
|
135
|
-
|
|
135
|
+
rest = match[1]
|
|
136
|
+
values, keywords = parse_call(rest)
|
|
137
|
+
if values.length == 1 && keywords.empty?
|
|
138
|
+
first = string_from_value(values.first)
|
|
139
|
+
if Destination.local_path_form?(first)
|
|
140
|
+
raise Error.parse("manifest", "file: requires a package")
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
bind_file_package(manifest, rest, file_path)
|
|
136
144
|
return true
|
|
137
145
|
end
|
|
138
146
|
raise Error.parse("manifest", "unsupported statement inside file block: #{statement}")
|
|
@@ -161,19 +169,11 @@ module Pray
|
|
|
161
169
|
end
|
|
162
170
|
|
|
163
171
|
def apply_local_pray_path(manifest, first, keywords, values, destination_index)
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
unless in_compose
|
|
170
|
-
raise Error.parse("manifest", "local pray paths are only valid inside compose blocks")
|
|
171
|
-
end
|
|
172
|
-
|
|
173
|
-
local = ManifestLocal.new(path: first, position: "after", optional: false, bound: true)
|
|
174
|
-
Destination.bind_local_entry(manifest.targets[destination_index], local.path)
|
|
175
|
-
Destination.upsert_local(manifest, local)
|
|
176
|
-
true
|
|
172
|
+
file_dest = keywords["file"]&.as_string
|
|
173
|
+
other_package_signal = values.length > 1 ||
|
|
174
|
+
%w[source export exports optional path git tag rev tarball oci targets features]
|
|
175
|
+
.any? { |key| keywords.key?(key) }
|
|
176
|
+
Destination.try_apply_local_pray(manifest, first, file_dest, other_package_signal, destination_index)
|
|
177
177
|
end
|
|
178
178
|
|
|
179
179
|
def package_signal?(values, keywords)
|
data/lib/pray/materialize.rb
CHANGED
|
@@ -54,9 +54,15 @@ module Pray
|
|
|
54
54
|
raise
|
|
55
55
|
end
|
|
56
56
|
end
|
|
57
|
+
previous_lockfile = project.previous_lockfile
|
|
58
|
+
unless locked || frozen
|
|
59
|
+
if Upstream.apply_path_upstream_refreshes(project, previous_lockfile, nil, options)
|
|
60
|
+
project = Resolve.resolve_project_in_context(manifest_path, project_root, options)
|
|
61
|
+
previous_lockfile = project.previous_lockfile
|
|
62
|
+
end
|
|
63
|
+
end
|
|
57
64
|
rendered = Render.render_project(project)
|
|
58
65
|
lockfile_path = default_lockfile_path(project.project_root)
|
|
59
|
-
previous_lockfile = project.previous_lockfile
|
|
60
66
|
next_lockfile = LockfileIO.build_lockfile(
|
|
61
67
|
project.manifest_hash,
|
|
62
68
|
project.environment,
|
data/lib/pray/package_spec.rb
CHANGED
|
@@ -12,12 +12,12 @@ module Pray
|
|
|
12
12
|
PackageUpstream = Struct.new(:name, :constraint, keyword_init: true)
|
|
13
13
|
|
|
14
14
|
PackageSpec = Struct.new(
|
|
15
|
-
:name, :version, :summary, :description, :authors, :license, :homepage,
|
|
15
|
+
:name, :version, :summary, :description, :authors, :maintainers, :license, :homepage,
|
|
16
16
|
:source_code_uri, :changelog_uri, :prayfile_version, :files, :exports,
|
|
17
17
|
:skills, :templates, :adapters, :targets, :dependencies, :metadata, :upstream
|
|
18
18
|
) do
|
|
19
19
|
def initialize(
|
|
20
|
-
name: "", version: "", summary: nil, description: nil, authors: [], license: nil,
|
|
20
|
+
name: "", version: "", summary: nil, description: nil, authors: [], maintainers: [], license: nil,
|
|
21
21
|
homepage: nil, source_code_uri: nil, changelog_uri: nil, prayfile_version: nil,
|
|
22
22
|
files: [], exports: {}, skills: {}, templates: {}, adapters: {}, targets: [],
|
|
23
23
|
dependencies: [], metadata: {}, upstream: nil
|
|
@@ -47,8 +47,9 @@ module Pray
|
|
|
47
47
|
def canonicalized
|
|
48
48
|
dup.tap do |copy|
|
|
49
49
|
copy.files = files.sort
|
|
50
|
-
copy.authors = authors.sort
|
|
51
|
-
copy.
|
|
50
|
+
copy.authors = Array(authors).sort
|
|
51
|
+
copy.maintainers = Array(maintainers).sort
|
|
52
|
+
copy.targets = Array(targets).sort
|
|
52
53
|
copy.dependencies = dependencies.sort_by { |dependency| [dependency.name, dependency.constraint, dependency.optional] }
|
|
53
54
|
end
|
|
54
55
|
end
|
|
@@ -227,18 +228,29 @@ module Pray
|
|
|
227
228
|
end
|
|
228
229
|
end
|
|
229
230
|
|
|
231
|
+
STRING_ASSIGNMENTS = {
|
|
232
|
+
"name" => :name=,
|
|
233
|
+
"version" => :version=,
|
|
234
|
+
"summary" => :summary=,
|
|
235
|
+
"description" => :description=,
|
|
236
|
+
"license" => :license=,
|
|
237
|
+
"homepage" => :homepage=,
|
|
238
|
+
"source_code_uri" => :source_code_uri=,
|
|
239
|
+
"changelog_uri" => :changelog_uri=,
|
|
240
|
+
"prayfile_version" => :prayfile_version=,
|
|
241
|
+
"pray_version" => :prayfile_version=
|
|
242
|
+
}.freeze
|
|
243
|
+
|
|
230
244
|
def apply_assignment(spec, field, value)
|
|
245
|
+
setter = STRING_ASSIGNMENTS[field]
|
|
246
|
+
if setter
|
|
247
|
+
spec.public_send(setter, string_from_literal(value))
|
|
248
|
+
return
|
|
249
|
+
end
|
|
250
|
+
|
|
231
251
|
case field
|
|
232
|
-
when "name" then spec.name = string_from_literal(value)
|
|
233
|
-
when "version" then spec.version = string_from_literal(value)
|
|
234
|
-
when "summary" then spec.summary = string_from_literal(value)
|
|
235
|
-
when "description" then spec.description = string_from_literal(value)
|
|
236
252
|
when "authors" then spec.authors = array_of_strings(value)
|
|
237
|
-
when "
|
|
238
|
-
when "homepage" then spec.homepage = string_from_literal(value)
|
|
239
|
-
when "source_code_uri" then spec.source_code_uri = string_from_literal(value)
|
|
240
|
-
when "changelog_uri" then spec.changelog_uri = string_from_literal(value)
|
|
241
|
-
when "prayfile_version" then spec.prayfile_version = string_from_literal(value)
|
|
253
|
+
when "maintainers" then spec.maintainers = array_of_strings(value)
|
|
242
254
|
when "files" then spec.files = array_of_strings(value)
|
|
243
255
|
when "targets" then spec.targets = array_of_strings(value)
|
|
244
256
|
when "exports" then spec.exports = parse_exports(value)
|
|
@@ -4,9 +4,9 @@ module Pray
|
|
|
4
4
|
module PackageSpecRender
|
|
5
5
|
module_function
|
|
6
6
|
|
|
7
|
-
def fork_spec_after_refresh(local, new_upstream,
|
|
7
|
+
def fork_spec_after_refresh(local, new_upstream, clean_replica, merged_content_paths)
|
|
8
8
|
spec = local.dup
|
|
9
|
-
spec.files =
|
|
9
|
+
spec.files = merged_content_paths.dup
|
|
10
10
|
if clean_replica
|
|
11
11
|
spec.exports = new_upstream.exports.dup
|
|
12
12
|
spec.templates = new_upstream.templates.dup
|
|
@@ -32,10 +32,11 @@ module Pray
|
|
|
32
32
|
|
|
33
33
|
def push_identity_fields(lines, spec)
|
|
34
34
|
push_assignment(lines, "name", spec.name)
|
|
35
|
-
push_assignment(lines, "version", spec.version)
|
|
35
|
+
push_assignment(lines, "version", spec.version) unless spec.version.to_s.empty?
|
|
36
36
|
push_optional(lines, "summary", spec.summary)
|
|
37
37
|
push_optional(lines, "description", spec.description)
|
|
38
38
|
push_array(lines, "authors", spec.authors) unless spec.authors.nil? || spec.authors.empty?
|
|
39
|
+
push_array(lines, "maintainers", spec.maintainers) unless spec.maintainers.nil? || spec.maintainers.empty?
|
|
39
40
|
push_optional(lines, "license", spec.license)
|
|
40
41
|
push_optional(lines, "homepage", spec.homepage)
|
|
41
42
|
push_optional(lines, "source_code_uri", spec.source_code_uri)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pray
|
|
4
|
+
class PackageSpec
|
|
5
|
+
LOCAL_VERSION = "local"
|
|
6
|
+
|
|
7
|
+
def has_release_version?
|
|
8
|
+
!version.to_s.empty? && version != LOCAL_VERSION
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def recorded_version
|
|
12
|
+
has_release_version? ? version : LOCAL_VERSION
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def require_release_version!
|
|
16
|
+
return if has_release_version?
|
|
17
|
+
|
|
18
|
+
raise Error.manifest("package #{name} needs a version before it can be packaged")
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def satisfy_constraint!(constraint)
|
|
22
|
+
unless has_release_version?
|
|
23
|
+
normalized = Constraint.normalize_version_constraint(constraint)
|
|
24
|
+
return if normalized.empty? || normalized == "*"
|
|
25
|
+
|
|
26
|
+
raise Error.resolution("package #{name} has no version; add spec.version or omit the constraint")
|
|
27
|
+
end
|
|
28
|
+
return if Constraint.version_satisfies(version, constraint)
|
|
29
|
+
|
|
30
|
+
raise Error.resolution("package #{name} version #{version} does not satisfy constraint #{constraint}")
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
data/lib/pray/plan.rb
CHANGED
|
@@ -13,7 +13,8 @@ module Pray
|
|
|
13
13
|
def build_materialization_preview(project, rendered, lockfile, _lockfile_path, previous_lockfile)
|
|
14
14
|
destinations = RenderDest.validate_destinations!(project, previous_lockfile)
|
|
15
15
|
MaterializationPreview.new(
|
|
16
|
-
package_lines:
|
|
16
|
+
package_lines: ApplyReport.local_summary_lines(previous_lockfile, project) +
|
|
17
|
+
package_summary_lines(previous_lockfile, lockfile, project),
|
|
17
18
|
lockfile: lockfile_change_status(previous_lockfile, lockfile),
|
|
18
19
|
targets: rendered.map { |target| target_change(project, target) },
|
|
19
20
|
provisioned: destinations.map { |file, status| [file.path, status.to_s] },
|
data/lib/pray/publish.rb
CHANGED
|
@@ -17,14 +17,16 @@ module Pray
|
|
|
17
17
|
def publish_to_root(project, root, signer: "local", signer_fingerprint: nil)
|
|
18
18
|
root = File.expand_path(root)
|
|
19
19
|
index = load_registry_index(root)
|
|
20
|
+
distribution = Distribution.read_settings(root)
|
|
20
21
|
package_names = index.packages.to_set
|
|
21
22
|
|
|
22
23
|
project.packages.each do |package|
|
|
24
|
+
package.spec.require_release_version!
|
|
23
25
|
artifact_path = registry_artifact_path(package.declaration.name, package.spec.version)
|
|
24
26
|
metadata_path = registry_metadata_path(root, package.declaration.name)
|
|
25
27
|
metadata = load_registry_package_metadata(metadata_path, package.declaration.name)
|
|
26
28
|
existing = metadata.versions.find { |entry| entry.version == package.spec.version }
|
|
27
|
-
stored_artifact = stored_package_artifact(root, artifact_path, package, existing)
|
|
29
|
+
stored_artifact = stored_package_artifact(root, artifact_path, package, existing, distribution)
|
|
28
30
|
if stored_artifact && stored_publish_matches?(
|
|
29
31
|
stored_artifact, package, signer, signer_fingerprint, existing
|
|
30
32
|
)
|
|
@@ -35,6 +37,14 @@ module Pray
|
|
|
35
37
|
|
|
36
38
|
archive_bytes = Archive.build_package_archive_bytes(package)
|
|
37
39
|
write_output_bytes(File.join(root, artifact_path), archive_bytes)
|
|
40
|
+
TorrentManifest.write(
|
|
41
|
+
root,
|
|
42
|
+
name: package.declaration.name,
|
|
43
|
+
version: package.spec.version,
|
|
44
|
+
artifact_path: artifact_path,
|
|
45
|
+
archive_bytes: archive_bytes,
|
|
46
|
+
settings: distribution
|
|
47
|
+
)
|
|
38
48
|
version_entry = published_registry_package_version(
|
|
39
49
|
package,
|
|
40
50
|
signer,
|
|
@@ -54,10 +64,24 @@ module Pray
|
|
|
54
64
|
end
|
|
55
65
|
|
|
56
66
|
def publish_to_server(project, server_url, signer: "local", signer_fingerprint: nil)
|
|
67
|
+
distribution = Distribution.fetch_settings(server_url)
|
|
57
68
|
project.packages.each do |package|
|
|
58
69
|
archive_bytes = Archive.build_package_archive_bytes(package)
|
|
59
70
|
artifact_path = registry_artifact_path(package.declaration.name, package.spec.version)
|
|
60
71
|
Registry.http_put(join_url(server_url, artifact_path), "application/octet-stream", archive_bytes)
|
|
72
|
+
if distribution.allows_torrent?
|
|
73
|
+
Registry.http_put(
|
|
74
|
+
join_url(server_url, TorrentManifest.path_for(artifact_path)),
|
|
75
|
+
"application/json",
|
|
76
|
+
TorrentManifest.bytes(
|
|
77
|
+
name: package.declaration.name,
|
|
78
|
+
version: package.spec.version,
|
|
79
|
+
artifact_path: artifact_path,
|
|
80
|
+
archive_bytes: archive_bytes,
|
|
81
|
+
trackers: distribution.bootstrap_trackers
|
|
82
|
+
)
|
|
83
|
+
)
|
|
84
|
+
end
|
|
61
85
|
|
|
62
86
|
metadata = RegistryPackageMetadata.new(
|
|
63
87
|
name: package.declaration.name,
|
|
@@ -95,7 +119,7 @@ module Pray
|
|
|
95
119
|
)
|
|
96
120
|
end
|
|
97
121
|
|
|
98
|
-
def stored_package_artifact(root, artifact_path, package, existing)
|
|
122
|
+
def stored_package_artifact(root, artifact_path, package, existing, distribution)
|
|
99
123
|
return unless existing&.artifact == artifact_path && existing.tree_hash == package.tree_hash
|
|
100
124
|
|
|
101
125
|
stored_path = File.join(root, artifact_path)
|
|
@@ -104,6 +128,7 @@ module Pray
|
|
|
104
128
|
artifact_bytes = File.binread(stored_path)
|
|
105
129
|
return unless existing.artifact_hash == Hashing.sha256_prefixed(artifact_bytes)
|
|
106
130
|
return unless stored_prayspec_matches?(artifact_bytes, package.root)
|
|
131
|
+
return unless TorrentManifest.descriptor_present?(root, artifact_path, distribution)
|
|
107
132
|
|
|
108
133
|
artifact_bytes
|
|
109
134
|
end
|
data/lib/pray/render.rb
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require "fileutils"
|
|
4
4
|
require "pathname"
|
|
5
5
|
require_relative "render_content"
|
|
6
|
+
require_relative "render_managed"
|
|
6
7
|
|
|
7
8
|
module Pray
|
|
8
9
|
RenderedTarget = Struct.new(:path, :content, :managed_spans) do
|
|
@@ -76,7 +77,7 @@ module Pray
|
|
|
76
77
|
|
|
77
78
|
(target.entries || []).each do |entry|
|
|
78
79
|
if entry.kind == "local"
|
|
79
|
-
append_scoped_local(builder, project, entry.path, symbols)
|
|
80
|
+
append_scoped_local(builder, managed_spans, project, target, output, entry.path, symbols)
|
|
80
81
|
else
|
|
81
82
|
append_scoped_package(builder, managed_spans, project, target, output, entry.name, symbols)
|
|
82
83
|
end
|
|
@@ -99,18 +100,17 @@ module Pray
|
|
|
99
100
|
builder.append_empty_line
|
|
100
101
|
end
|
|
101
102
|
symbols = project.manifest.symbols || {}
|
|
103
|
+
managed_spans = []
|
|
102
104
|
unbound_locals.each do |local|
|
|
103
105
|
next if local.content.empty? && local.optional
|
|
104
106
|
|
|
105
107
|
builder.append_line("### #{local.manifest_path}")
|
|
106
|
-
builder
|
|
107
|
-
builder.append_empty_line
|
|
108
|
+
append_managed_local(builder, managed_spans, local, target, output, symbols)
|
|
108
109
|
end
|
|
109
110
|
|
|
110
111
|
builder.append_line("## Shared instructions")
|
|
111
112
|
builder.append_empty_line
|
|
112
113
|
|
|
113
|
-
managed_spans = []
|
|
114
114
|
project.packages.each do |package|
|
|
115
115
|
next unless Environment.package_matches_environment?(package.declaration.groups, project.environment)
|
|
116
116
|
next unless Destination.package_bound_to_compose?(package.declaration, target)
|
|
@@ -133,13 +133,11 @@ module Pray
|
|
|
133
133
|
builder.append_empty_line
|
|
134
134
|
end
|
|
135
135
|
|
|
136
|
-
def append_scoped_local(builder, project, path, symbols)
|
|
136
|
+
def append_scoped_local(builder, managed_spans, project, target, output, path, symbols)
|
|
137
137
|
local = project.local_files.find { |candidate| candidate.manifest_path == path }
|
|
138
138
|
return unless local
|
|
139
|
-
return if local.content.empty? && local.optional
|
|
140
139
|
|
|
141
|
-
builder
|
|
142
|
-
builder.append_empty_line
|
|
140
|
+
append_managed_local(builder, managed_spans, local, target, output, symbols)
|
|
143
141
|
end
|
|
144
142
|
|
|
145
143
|
def append_scoped_package(builder, managed_spans, project, target, output, package_name, symbols)
|
|
@@ -154,34 +152,6 @@ module Pray
|
|
|
154
152
|
end
|
|
155
153
|
end
|
|
156
154
|
|
|
157
|
-
def append_managed_export(builder, managed_spans, package, export, target, output, symbols)
|
|
158
|
-
body = package.export_bodies[export]
|
|
159
|
-
unless body
|
|
160
|
-
raise Error.integrity("compose cannot write binary export #{export}; use file: for unmarked bytes") if package.spec.exports[export]&.kind == "file"
|
|
161
|
-
raise Error.render("package #{package.declaration.name} is missing cached export #{export}")
|
|
162
|
-
end
|
|
163
|
-
|
|
164
|
-
body = Substitute.substitute_pray_symbols(body, symbols)
|
|
165
|
-
identifier = Hashing.marker_id("#{package.declaration.name}:#{export}:#{target.name}")
|
|
166
|
-
open_line = builder.next_line_number
|
|
167
|
-
builder.append_line("<!-- pray:#{identifier} -->")
|
|
168
|
-
builder.append_body(body)
|
|
169
|
-
close_line = builder.next_line_number
|
|
170
|
-
builder.append_line("<!-- pray:#{identifier} -->")
|
|
171
|
-
managed_spans << ManagedSpanRecord.new(
|
|
172
|
-
id: identifier,
|
|
173
|
-
target: output,
|
|
174
|
-
open_line: open_line,
|
|
175
|
-
close_line: close_line,
|
|
176
|
-
ideal_checksum: Hashing.checksum_managed_span_content(body),
|
|
177
|
-
package: package.declaration.name,
|
|
178
|
-
export: export,
|
|
179
|
-
source_checksum: package.source_checksum,
|
|
180
|
-
silenced: false
|
|
181
|
-
)
|
|
182
|
-
builder.append_empty_line
|
|
183
|
-
end
|
|
184
|
-
|
|
185
155
|
def should_inline_export?(package, export_name)
|
|
186
156
|
export = package.spec.exports[export_name]
|
|
187
157
|
export.nil? || %w[fragment file].include?(export.kind)
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pray
|
|
4
|
+
module Render
|
|
5
|
+
module_function
|
|
6
|
+
|
|
7
|
+
def append_managed_local(builder, managed_spans, local, target, output, symbols)
|
|
8
|
+
return if local.content.empty? && local.optional
|
|
9
|
+
|
|
10
|
+
body = Substitute.substitute_pray_symbols(local.content, symbols)
|
|
11
|
+
append_managed_span(
|
|
12
|
+
builder,
|
|
13
|
+
managed_spans,
|
|
14
|
+
"local:#{local.manifest_path}:#{target.name}",
|
|
15
|
+
body,
|
|
16
|
+
output,
|
|
17
|
+
LOCAL_EMBED_PACKAGE,
|
|
18
|
+
local.manifest_path,
|
|
19
|
+
local.source_checksum
|
|
20
|
+
)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def append_managed_export(builder, managed_spans, package, export, target, output, symbols)
|
|
24
|
+
body = package.export_bodies[export]
|
|
25
|
+
unless body
|
|
26
|
+
raise Error.integrity("compose cannot write binary export #{export}; use file: for unmarked bytes") if package.spec.exports[export]&.kind == "file"
|
|
27
|
+
raise Error.render("package #{package.declaration.name} is missing cached export #{export}")
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
body = Substitute.substitute_pray_symbols(body, symbols)
|
|
31
|
+
append_managed_span(
|
|
32
|
+
builder,
|
|
33
|
+
managed_spans,
|
|
34
|
+
"#{package.declaration.name}:#{export}:#{target.name}",
|
|
35
|
+
body,
|
|
36
|
+
output,
|
|
37
|
+
package.declaration.name,
|
|
38
|
+
export,
|
|
39
|
+
package.source_checksum
|
|
40
|
+
)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def append_managed_span(builder, managed_spans, seed, body, output, package, export, source_checksum)
|
|
44
|
+
identifier = Hashing.marker_id(seed)
|
|
45
|
+
open_line = builder.next_line_number
|
|
46
|
+
builder.append_line("<!-- pray:#{identifier} -->")
|
|
47
|
+
builder.append_body(body)
|
|
48
|
+
close_line = builder.next_line_number
|
|
49
|
+
builder.append_line("<!-- pray:#{identifier} -->")
|
|
50
|
+
managed_spans << ManagedSpanRecord.new(
|
|
51
|
+
id: identifier,
|
|
52
|
+
target: output,
|
|
53
|
+
open_line: open_line,
|
|
54
|
+
close_line: close_line,
|
|
55
|
+
ideal_checksum: Hashing.checksum_managed_span_content(body),
|
|
56
|
+
package: package,
|
|
57
|
+
export: export,
|
|
58
|
+
source_checksum: source_checksum,
|
|
59
|
+
silenced: false
|
|
60
|
+
)
|
|
61
|
+
builder.append_empty_line
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|