pray-cli 1.18.0 → 1.19.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 +8 -0
- data/lib/pray/archive.rb +1 -0
- data/lib/pray/cli/commands/distribution.rb +53 -5
- data/lib/pray/cli/commands/init.rb +26 -0
- data/lib/pray/cli/commands/packages.rb +6 -1
- data/lib/pray/cli/help.rb +7 -5
- data/lib/pray/cli/parse.rb +11 -4
- data/lib/pray/format_manifest.rb +1 -0
- data/lib/pray/format_serialize.rb +17 -0
- data/lib/pray/manifest.rb +7 -1
- data/lib/pray/manifest_json.rb +10 -0
- data/lib/pray/manifest_parser.rb +3 -0
- data/lib/pray/manifest_parser_publish.rb +52 -0
- data/lib/pray/publish.rb +1 -0
- data/lib/pray/publish_remote.rb +76 -0
- data/lib/pray/publish_select.rb +110 -0
- data/lib/pray/version.rb +2 -2
- metadata +4 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6df9a00a7c009e00fdd00987564489607a77bf48cb6472e06951c7709a91a32f
|
|
4
|
+
data.tar.gz: 93ad851e71a69c13764f2a289d3f32dd7856a749aa341ef728b4c49ebc651149
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 56060ecc2267d2f8a2c1b555591b3db551383d8350a9b3125a0647831ee2438dfb3f78b6424e2af98bbfc184ef8a91c181e1da5bf35835a160f34bcde865c1b0
|
|
7
|
+
data.tar.gz: 55b5590131213f639bc63be84817d65c17297354a1387b363c94d8953192ca03f80c9245b47d5cc1d7d99cd51569c2a9647147fb0de124966dfb294e125bf68b
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# CHANGELOG
|
|
2
2
|
|
|
3
|
+
## 1.19.0 (2026-09-18)
|
|
4
|
+
|
|
5
|
+
- Declare named `publish` remotes in Prayfile (RFC 0118). `pray publish` uses them when dest flags are omitted, and publishes only path-owned packages.
|
|
6
|
+
|
|
7
|
+
## 1.18.1 (2026-09-17)
|
|
8
|
+
|
|
9
|
+
- Fix `pray package` and `pray publish` when the CLI cannot create a staging directory.
|
|
10
|
+
|
|
3
11
|
## 1.18.0 (2026-09-17)
|
|
4
12
|
|
|
5
13
|
- Clone a git catalog without unused package blobs, and fetch a used package file when install needs it.
|
data/lib/pray/archive.rb
CHANGED
|
@@ -2,16 +2,64 @@
|
|
|
2
2
|
|
|
3
3
|
module Pray
|
|
4
4
|
module CLI
|
|
5
|
-
def publish_command(roots:, servers:)
|
|
5
|
+
def publish_command(roots:, servers:, to: [], dry_run: false)
|
|
6
6
|
project = resolve_current_project
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
dests = PublishSelect.resolve_publish_destinations(
|
|
8
|
+
project.manifest.publish_remotes || [],
|
|
9
|
+
PublishSelect::PublishCliDest.new(to: to, roots: roots, servers: servers),
|
|
10
|
+
Dir.pwd
|
|
11
|
+
)
|
|
12
|
+
if dry_run
|
|
13
|
+
print_publish_plan(project, dests)
|
|
14
|
+
return
|
|
15
|
+
end
|
|
16
|
+
dests.each do |dest|
|
|
17
|
+
packages = selected_publish_packages(project, dest.packages)
|
|
18
|
+
filtered = project.dup
|
|
19
|
+
filtered.packages = packages
|
|
20
|
+
Publish.publish_to_root(filtered, dest.root) if dest.root
|
|
21
|
+
Publish.publish_to_server(filtered, dest.server) if dest.server
|
|
22
|
+
end
|
|
9
23
|
end
|
|
10
24
|
|
|
11
|
-
def
|
|
25
|
+
def selected_publish_packages(project, listed)
|
|
26
|
+
allowed = PublishRemote.allowed_publish_names(project.manifest, listed)
|
|
27
|
+
selected = project.packages.select { |package| allowed.include?(package.declaration.name) }
|
|
28
|
+
if selected.empty?
|
|
29
|
+
raise Error.usage("no path packages to publish; remote dependencies are not published")
|
|
30
|
+
end
|
|
31
|
+
selected.each { |package| package.spec.require_release_version! }
|
|
32
|
+
selected
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def print_publish_plan(project, dests)
|
|
36
|
+
dests.each do |dest|
|
|
37
|
+
packages = selected_publish_packages(project, dest.packages)
|
|
38
|
+
target = dest.root || dest.server || dest.name
|
|
39
|
+
packages.each do |package|
|
|
40
|
+
puts "publish #{package.declaration.name} #{package.spec.version} -> #{dest.name} (#{target})"
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def serve_command(root:, host:, port:, stdio:, to: nil)
|
|
12
46
|
raise Error.unsupported("serve --stdio is not implemented yet in pray-cli Ruby") if stdio
|
|
13
47
|
|
|
14
|
-
Serve.run_server(root: root, host: host, port: port)
|
|
48
|
+
Serve.run_server(root: optional_path_remote_root(to, root), host: host, port: port)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def optional_path_remote_root(to, root)
|
|
52
|
+
remotes = begin
|
|
53
|
+
Pray.parse_manifest(Pray.read_manifest_text(manifest_path)).publish_remotes || []
|
|
54
|
+
rescue Error
|
|
55
|
+
return root if to.nil?
|
|
56
|
+
|
|
57
|
+
raise
|
|
58
|
+
end
|
|
59
|
+
return root if remotes.empty? && to.nil?
|
|
60
|
+
|
|
61
|
+
cli_root = (root == ".") ? nil : root
|
|
62
|
+
PublishSelect.resolve_path_remote(remotes, to, cli_root, Dir.pwd)
|
|
15
63
|
end
|
|
16
64
|
end
|
|
17
65
|
end
|
|
@@ -34,6 +34,32 @@ module Pray
|
|
|
34
34
|
FileUtils.mkdir_p(File.join(distribution_root, "v1", "artifacts"))
|
|
35
35
|
Publish.write_registry_index(distribution_root, RegistryIndex.new)
|
|
36
36
|
Distribution.write_settings(distribution_root)
|
|
37
|
+
maybe_declare_publish_remote(Dir.pwd, distribution_root)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def maybe_declare_publish_remote(project_root, distribution_root)
|
|
41
|
+
manifest_path_value = File.join(project_root, "Prayfile")
|
|
42
|
+
return unless File.exist?(manifest_path_value)
|
|
43
|
+
|
|
44
|
+
text = File.read(manifest_path_value)
|
|
45
|
+
manifest = Pray.parse_manifest(text)
|
|
46
|
+
return unless (manifest.publish_remotes || []).empty?
|
|
47
|
+
|
|
48
|
+
relative = if distribution_root == project_root
|
|
49
|
+
"."
|
|
50
|
+
else
|
|
51
|
+
prefix = project_root.end_with?("/") ? project_root : "#{project_root}/"
|
|
52
|
+
distribution_root.start_with?(prefix) ? distribution_root.delete_prefix(prefix).tr("\\", "/") : "prayers"
|
|
53
|
+
end
|
|
54
|
+
statement = %(publish "prayers", path: "#{relative}")
|
|
55
|
+
lines = text.split("\n")
|
|
56
|
+
insertion = lines.rindex { |line| line.lstrip.start_with?("source ") }
|
|
57
|
+
insertion ||= lines.index { |line| line.lstrip.start_with?("prayfile ") }
|
|
58
|
+
insertion = insertion ? insertion + 1 : 1
|
|
59
|
+
lines.insert(insertion, statement)
|
|
60
|
+
updated = lines.join("\n")
|
|
61
|
+
updated += "\n" unless updated.end_with?("\n")
|
|
62
|
+
File.write(manifest_path_value, updated)
|
|
37
63
|
end
|
|
38
64
|
|
|
39
65
|
def repo_distribution_root(root)
|
|
@@ -53,7 +53,12 @@ module Pray
|
|
|
53
53
|
|
|
54
54
|
def package_command
|
|
55
55
|
project = resolve_current_project
|
|
56
|
-
project.
|
|
56
|
+
allowed = PublishRemote.path_owned_package_names(project.manifest)
|
|
57
|
+
packages = project.packages.select { |package| allowed.include?(package.declaration.name) }
|
|
58
|
+
if packages.empty?
|
|
59
|
+
raise Error.usage("no path packages to package; remote dependencies are not packed")
|
|
60
|
+
end
|
|
61
|
+
packages.each do |package|
|
|
57
62
|
package.spec.require_release_version!
|
|
58
63
|
output_path = Archive.package_archive_path(package.declaration.name, package.spec.version)
|
|
59
64
|
Archive.write_package_archive(package, output_path)
|
data/lib/pray/cli/help.rb
CHANGED
|
@@ -23,9 +23,9 @@ module Pray
|
|
|
23
23
|
].freeze
|
|
24
24
|
|
|
25
25
|
DISTRIBUTION_COMMANDS = [
|
|
26
|
-
"publish --root PATH
|
|
26
|
+
"publish [--root PATH|--server URL|--to NAME] [--dry-run]",
|
|
27
27
|
"login --server URL --email EMAIL",
|
|
28
|
-
"serve [--root PATH] [--host HOST] [--port PORT] [--stdio]",
|
|
28
|
+
"serve [--root PATH | --to NAME] [--host HOST] [--port PORT] [--stdio]",
|
|
29
29
|
"sync [--root PATH] [--peer URL ...]",
|
|
30
30
|
"confess <package> | --from-lock SPAN_ID [--accepted|--rejected]"
|
|
31
31
|
].freeze
|
|
@@ -146,9 +146,11 @@ module Pray
|
|
|
146
146
|
"vendor" => "copy resolved packages locally\n\nUsage: pray vendor",
|
|
147
147
|
"clean" => "remove local cache and vendor trees, or only unused registry entries\n\nUsage: pray clean [--unused]",
|
|
148
148
|
"publish" => <<~TEXT.strip,
|
|
149
|
-
upload packages to a registry or local root
|
|
149
|
+
upload path packages to a registry or local root
|
|
150
150
|
|
|
151
|
-
Usage: pray publish --root PATH [--server URL ...]
|
|
151
|
+
Usage: pray publish [--root PATH] [--server URL ...] [--to NAME] [--dry-run]
|
|
152
|
+
|
|
153
|
+
Prayfile publish remotes supply dests when flags are omitted.
|
|
152
154
|
TEXT
|
|
153
155
|
"login" => <<~TEXT.strip,
|
|
154
156
|
authenticate to a registry server
|
|
@@ -158,7 +160,7 @@ module Pray
|
|
|
158
160
|
"serve" => <<~TEXT.strip,
|
|
159
161
|
run a local registry server
|
|
160
162
|
|
|
161
|
-
Usage: pray serve [--root PATH] [--host HOST] [--port PORT] [--stdio]
|
|
163
|
+
Usage: pray serve [--root PATH | --to NAME] [--host HOST] [--port PORT] [--stdio]
|
|
162
164
|
TEXT
|
|
163
165
|
"sync" => <<~TEXT.strip,
|
|
164
166
|
sync packages with peer registries
|
data/lib/pray/cli/parse.rb
CHANGED
|
@@ -141,26 +141,33 @@ module Pray
|
|
|
141
141
|
def parse_publish_arguments(arguments)
|
|
142
142
|
roots = []
|
|
143
143
|
servers = []
|
|
144
|
+
to = []
|
|
145
|
+
dry_run = false
|
|
144
146
|
while (argument = arguments.shift)
|
|
145
147
|
case argument
|
|
146
148
|
when "--root"
|
|
147
149
|
roots << arguments.shift
|
|
148
150
|
when "--server"
|
|
149
151
|
servers << arguments.shift
|
|
152
|
+
when "--to"
|
|
153
|
+
to << arguments.shift
|
|
154
|
+
when "--dry-run"
|
|
155
|
+
dry_run = true
|
|
156
|
+
when "--signing-key"
|
|
157
|
+
arguments.shift
|
|
150
158
|
else
|
|
151
159
|
raise Error.unsupported("unexpected publish argument: #{argument}")
|
|
152
160
|
end
|
|
153
161
|
end
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
{roots: roots, servers: servers}
|
|
162
|
+
{roots: roots, servers: servers, to: to, dry_run: dry_run}
|
|
157
163
|
end
|
|
158
164
|
|
|
159
165
|
def parse_serve_arguments(arguments)
|
|
160
|
-
options = {root: ".", host: "127.0.0.1", port: 7429, stdio: false}
|
|
166
|
+
options = {root: ".", to: nil, host: "127.0.0.1", port: 7429, stdio: false}
|
|
161
167
|
while (argument = arguments.shift)
|
|
162
168
|
case argument
|
|
163
169
|
when "--root" then options[:root] = arguments.shift
|
|
170
|
+
when "--to" then options[:to] = arguments.shift
|
|
164
171
|
when "--host" then options[:host] = arguments.shift
|
|
165
172
|
when "--port" then options[:port] = Integer(arguments.shift)
|
|
166
173
|
when "--stdio" then options[:stdio] = true
|
data/lib/pray/format_manifest.rb
CHANGED
|
@@ -79,6 +79,7 @@ module Pray
|
|
|
79
79
|
targets: [],
|
|
80
80
|
packages: manifest.packages.map { |package| clone_package(package) },
|
|
81
81
|
local: manifest.local.map { |local| local.dup },
|
|
82
|
+
publish_remotes: (manifest.publish_remotes || []).map(&:dup),
|
|
82
83
|
symbols: manifest.symbols.dup,
|
|
83
84
|
render: manifest.render.dup
|
|
84
85
|
)
|
|
@@ -12,6 +12,11 @@ module Pray
|
|
|
12
12
|
manifest.sources.each { |source| lines << format_source(source) }
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
+
unless (manifest.publish_remotes || []).empty?
|
|
16
|
+
lines << ""
|
|
17
|
+
manifest.publish_remotes.each { |remote| lines << format_publish_remote(remote) }
|
|
18
|
+
end
|
|
19
|
+
|
|
15
20
|
unless manifest.symbols.empty?
|
|
16
21
|
lines << ""
|
|
17
22
|
lines << "pray do"
|
|
@@ -109,6 +114,18 @@ module Pray
|
|
|
109
114
|
parts.join(", ")
|
|
110
115
|
end
|
|
111
116
|
|
|
117
|
+
def format_publish_remote(remote)
|
|
118
|
+
parts = [%(publish "#{remote.name}")]
|
|
119
|
+
parts << %(path: "#{remote.path}") if remote.path
|
|
120
|
+
parts << %("#{remote.url}") if remote.url
|
|
121
|
+
return parts.join(", ") if remote.packages.empty?
|
|
122
|
+
|
|
123
|
+
lines = ["#{parts.join(", ")} do"]
|
|
124
|
+
remote.packages.each { |name| lines << %( pray "#{name}") }
|
|
125
|
+
lines << "end"
|
|
126
|
+
lines.join("\n")
|
|
127
|
+
end
|
|
128
|
+
|
|
112
129
|
def format_destination_entry(entry, manifest)
|
|
113
130
|
if entry.kind == "local"
|
|
114
131
|
return %(pray "#{entry.path}")
|
data/lib/pray/manifest.rb
CHANGED
|
@@ -4,8 +4,11 @@ require_relative "manifest_json"
|
|
|
4
4
|
require_relative "manifest_formatter"
|
|
5
5
|
require_relative "manifest_parser_helpers"
|
|
6
6
|
require_relative "manifest_parser_blocks"
|
|
7
|
+
require_relative "manifest_parser_publish"
|
|
7
8
|
require_relative "manifest_parser"
|
|
8
9
|
require_relative "path_safety"
|
|
10
|
+
require_relative "publish_remote"
|
|
11
|
+
require_relative "publish_select"
|
|
9
12
|
|
|
10
13
|
module Pray
|
|
11
14
|
RenderPolicy = Struct.new(
|
|
@@ -53,7 +56,7 @@ module Pray
|
|
|
53
56
|
end
|
|
54
57
|
|
|
55
58
|
Manifest = Struct.new(
|
|
56
|
-
:prayfile_version, :sources, :targets, :packages, :local, :symbols, :render,
|
|
59
|
+
:prayfile_version, :sources, :targets, :packages, :local, :publish_remotes, :symbols, :render,
|
|
57
60
|
:deprecated_keywords,
|
|
58
61
|
keyword_init: true
|
|
59
62
|
) do
|
|
@@ -63,6 +66,7 @@ module Pray
|
|
|
63
66
|
targets: [],
|
|
64
67
|
packages: [],
|
|
65
68
|
local: [],
|
|
69
|
+
publish_remotes: [],
|
|
66
70
|
symbols: {},
|
|
67
71
|
render: RenderPolicy.default,
|
|
68
72
|
deprecated_keywords: []
|
|
@@ -99,6 +103,7 @@ module Pray
|
|
|
99
103
|
copy.targets = targets.sort_by(&:name)
|
|
100
104
|
copy.packages = packages.sort_by { |package| [package.name, package.source.to_s, package.constraint] }
|
|
101
105
|
copy.local = local.sort_by(&:path)
|
|
106
|
+
copy.publish_remotes = (publish_remotes || []).sort_by(&:name)
|
|
102
107
|
copy.deprecated_keywords = []
|
|
103
108
|
end
|
|
104
109
|
end
|
|
@@ -139,6 +144,7 @@ module Pray
|
|
|
139
144
|
PathSafety.validate_project_relative_path!(local.path)
|
|
140
145
|
PathSafety.validate_destination_path!(local.file) if local.file
|
|
141
146
|
end
|
|
147
|
+
PublishRemote.validate_publish_remotes!(manifest)
|
|
142
148
|
end
|
|
143
149
|
end
|
|
144
150
|
|
data/lib/pray/manifest_json.rb
CHANGED
|
@@ -17,10 +17,20 @@ module Pray
|
|
|
17
17
|
"local" => manifest.local.map { |entry| local_fields(entry) }
|
|
18
18
|
}
|
|
19
19
|
fields["symbols"] = manifest.symbols.sort.to_h unless manifest.symbols.empty?
|
|
20
|
+
unless (manifest.publish_remotes || []).empty?
|
|
21
|
+
fields["publish_remotes"] = manifest.publish_remotes.map { |remote| publish_remote_fields(remote) }
|
|
22
|
+
end
|
|
20
23
|
fields["render"] = render_fields(manifest.render)
|
|
21
24
|
fields
|
|
22
25
|
end
|
|
23
26
|
|
|
27
|
+
def publish_remote_fields(remote)
|
|
28
|
+
fields = {"name" => remote.name, "packages" => remote.packages}
|
|
29
|
+
fields["path"] = remote.path if remote.path
|
|
30
|
+
fields["url"] = remote.url if remote.url
|
|
31
|
+
fields
|
|
32
|
+
end
|
|
33
|
+
|
|
24
34
|
def source_fields(source)
|
|
25
35
|
fields = {
|
|
26
36
|
"name" => source.name,
|
data/lib/pray/manifest_parser.rb
CHANGED
|
@@ -5,6 +5,7 @@ module Pray
|
|
|
5
5
|
class BlockParser
|
|
6
6
|
include ParserHelpers
|
|
7
7
|
include ParserBlocks
|
|
8
|
+
include ParserPublish
|
|
8
9
|
|
|
9
10
|
def initialize(lines)
|
|
10
11
|
@lines = lines
|
|
@@ -41,6 +42,8 @@ module Pray
|
|
|
41
42
|
manifest.prayfile_version = string_from_literal(Regexp.last_match(1))
|
|
42
43
|
when /\Asource (.+)\z/
|
|
43
44
|
manifest.sources << parse_source(Regexp.last_match(1))
|
|
45
|
+
when /\Apublish (.+)\z/
|
|
46
|
+
apply_publish(manifest, Regexp.last_match(1))
|
|
44
47
|
when /\Atarget (.+)\z/
|
|
45
48
|
apply_legacy_target(manifest, statement, Regexp.last_match(1), allow_target)
|
|
46
49
|
when /\Agroup (.+)\z/
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pray
|
|
4
|
+
module ManifestMethods
|
|
5
|
+
module ParserPublish
|
|
6
|
+
def apply_publish(manifest, rest)
|
|
7
|
+
is_block = rest.rstrip.end_with?(" do")
|
|
8
|
+
header = rest.sub(/\s+do\z/, "").strip
|
|
9
|
+
remote = parse_publish_header(header)
|
|
10
|
+
parse_publish_block(remote) if is_block
|
|
11
|
+
manifest.publish_remotes << remote
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def parse_publish_header(rest)
|
|
15
|
+
values, keywords = parse_call(rest)
|
|
16
|
+
raise Error.parse("manifest", "publish requires a name") if values.empty?
|
|
17
|
+
|
|
18
|
+
name = string_from_value(values.first)
|
|
19
|
+
if %w[git source signing_key token].any? { |key| keywords.key?(key) }
|
|
20
|
+
raise Error.parse(
|
|
21
|
+
"manifest",
|
|
22
|
+
"publish \"#{name}\" does not take git:, source:, signing_key:, or token:"
|
|
23
|
+
)
|
|
24
|
+
end
|
|
25
|
+
ManifestPublishRemote.new(
|
|
26
|
+
name: name,
|
|
27
|
+
path: keyword_string(keywords, "path"),
|
|
28
|
+
url: values[1] && string_from_value(values[1]),
|
|
29
|
+
packages: []
|
|
30
|
+
)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def parse_publish_block(remote)
|
|
34
|
+
while (statement = next_statement)
|
|
35
|
+
return if statement == "end"
|
|
36
|
+
|
|
37
|
+
pray_rest = statement[/\A(?:pray|use|include|agent|package) (.+)\z/, 1]
|
|
38
|
+
unless pray_rest
|
|
39
|
+
raise Error.parse("manifest", "publish blocks only support pray package names: #{statement}")
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
package = parse_package_decl(pray_rest)
|
|
43
|
+
if remote.packages.include?(package.name)
|
|
44
|
+
raise Error.parse("manifest", "duplicate package #{package.name} in publish \"#{remote.name}\"")
|
|
45
|
+
end
|
|
46
|
+
remote.packages << package.name
|
|
47
|
+
end
|
|
48
|
+
raise Error.parse("manifest", "missing 'end' for publish block")
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
data/lib/pray/publish.rb
CHANGED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pray
|
|
4
|
+
ManifestPublishRemote = Struct.new(:name, :path, :url, :packages) do
|
|
5
|
+
def initialize(name:, path: nil, url: nil, packages: [])
|
|
6
|
+
super
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
module PublishRemote
|
|
11
|
+
module_function
|
|
12
|
+
|
|
13
|
+
def validate_publish_remotes!(manifest)
|
|
14
|
+
seen = {}
|
|
15
|
+
(manifest.publish_remotes || []).each do |remote|
|
|
16
|
+
raise Error.parse("manifest", "publish requires a name") if remote.name.to_s.strip.empty?
|
|
17
|
+
if seen[remote.name]
|
|
18
|
+
raise Error.manifest("duplicate publish remote: #{remote.name}")
|
|
19
|
+
end
|
|
20
|
+
seen[remote.name] = true
|
|
21
|
+
if remote.path && remote.url
|
|
22
|
+
raise Error.parse("manifest", "publish \"#{remote.name}\" must set path: or a URL, not both")
|
|
23
|
+
end
|
|
24
|
+
if remote.path.nil? && remote.url.nil?
|
|
25
|
+
raise Error.parse("manifest", "publish \"#{remote.name}\" requires path: or a URL")
|
|
26
|
+
end
|
|
27
|
+
PathSafety.validate_project_relative_path!(remote.path) if remote.path
|
|
28
|
+
if remote.url && !publish_url?(remote.url)
|
|
29
|
+
raise Error.parse(
|
|
30
|
+
"manifest",
|
|
31
|
+
"publish \"#{remote.name}\" URL must be https://, http://, pray+ssh://, or ssh+pray://"
|
|
32
|
+
)
|
|
33
|
+
end
|
|
34
|
+
validate_remote_packages!(manifest, remote)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def validate_remote_packages!(manifest, remote)
|
|
39
|
+
remote.packages.each do |package_name|
|
|
40
|
+
package = manifest.packages.find { |entry| entry.name == package_name }
|
|
41
|
+
unless package
|
|
42
|
+
raise Error.manifest("publish \"#{remote.name}\" lists unknown package #{package_name}")
|
|
43
|
+
end
|
|
44
|
+
next if package_is_path_owned?(package, manifest.sources)
|
|
45
|
+
|
|
46
|
+
raise Error.manifest(
|
|
47
|
+
"publish \"#{remote.name}\" lists #{package_name}, which is not a path package"
|
|
48
|
+
)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def package_is_path_owned?(package, sources)
|
|
53
|
+
return true if package.path
|
|
54
|
+
return false if package.git || package.tarball || package.oci
|
|
55
|
+
|
|
56
|
+
map = sources.to_h { |source| [source.name, source] }
|
|
57
|
+
name = ResolveSource.implied_source_name(package, map)
|
|
58
|
+
name && map[name]&.kind == "path"
|
|
59
|
+
rescue Error
|
|
60
|
+
false
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def path_owned_package_names(manifest)
|
|
64
|
+
manifest.packages.select { |package| package_is_path_owned?(package, manifest.sources) }
|
|
65
|
+
.map(&:name)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def allowed_publish_names(manifest, listed)
|
|
69
|
+
listed.empty? ? path_owned_package_names(manifest) : listed
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def publish_url?(url)
|
|
73
|
+
url.start_with?("https://", "http://", "pray+ssh://", "ssh+pray://")
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pray
|
|
4
|
+
module PublishSelect
|
|
5
|
+
module_function
|
|
6
|
+
|
|
7
|
+
PublishDestination = Struct.new(:name, :root, :server, :packages) do
|
|
8
|
+
def initialize(name:, root: nil, server: nil, packages: [])
|
|
9
|
+
super
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
PublishCliDest = Struct.new(:to, :roots, :servers) do
|
|
13
|
+
def initialize(to: [], roots: [], servers: [])
|
|
14
|
+
super
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def resolve_publish_destinations(remotes, cli, project_root)
|
|
19
|
+
if !cli.to.empty? && (!cli.roots.empty? || !cli.servers.empty?)
|
|
20
|
+
raise Error.usage("publish --to cannot be combined with --root or --server")
|
|
21
|
+
end
|
|
22
|
+
return resolve_undeclared_destinations(cli) if remotes.empty?
|
|
23
|
+
|
|
24
|
+
select_remotes(remotes, cli, project_root).map do |remote|
|
|
25
|
+
PublishDestination.new(
|
|
26
|
+
name: remote.name,
|
|
27
|
+
root: remote.path && File.join(project_root, remote.path),
|
|
28
|
+
server: remote.url,
|
|
29
|
+
packages: remote.packages
|
|
30
|
+
)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def resolve_path_remote(remotes, to, root, project_root)
|
|
35
|
+
path_remotes = remotes.select(&:path)
|
|
36
|
+
if to
|
|
37
|
+
remote = remotes.find { |entry| entry.name == to }
|
|
38
|
+
raise Error.usage("unknown publish remote: #{to}") unless remote
|
|
39
|
+
unless remote.path
|
|
40
|
+
raise Error.usage("publish remote #{to} is a URL; yank, serve, and token need a path remote")
|
|
41
|
+
end
|
|
42
|
+
return File.join(project_root, remote.path)
|
|
43
|
+
end
|
|
44
|
+
if remotes.empty?
|
|
45
|
+
raise Error.usage("requires --root PATH") unless root
|
|
46
|
+
|
|
47
|
+
return root
|
|
48
|
+
end
|
|
49
|
+
if root
|
|
50
|
+
matched = remotes.find { |remote| remote.path && root_matches?(root, remote.path, project_root) }
|
|
51
|
+
raise Error.usage("path #{root} is not a declared publish remote") unless matched
|
|
52
|
+
|
|
53
|
+
return root
|
|
54
|
+
end
|
|
55
|
+
if path_remotes.length == 1 && path_remotes.first.path
|
|
56
|
+
return File.join(project_root, path_remotes.first.path)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
raise Error.usage("say which path remote with --to NAME or --root PATH")
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def resolve_undeclared_destinations(cli)
|
|
63
|
+
if !cli.to.empty?
|
|
64
|
+
raise Error.usage("Prayfile has no publish remotes; pass --root PATH or --server URL")
|
|
65
|
+
end
|
|
66
|
+
if cli.roots.empty? && cli.servers.empty?
|
|
67
|
+
raise Error.unsupported("publish requires at least one --root PATH or --server URL")
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
dests = cli.roots.map { |root| PublishDestination.new(name: root, root: root, packages: []) }
|
|
71
|
+
dests + cli.servers.map { |server| PublishDestination.new(name: server, server: server, packages: []) }
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def select_remotes(remotes, cli, project_root)
|
|
75
|
+
unless cli.to.empty?
|
|
76
|
+
return cli.to.map do |name|
|
|
77
|
+
remote = remotes.find { |entry| entry.name == name }
|
|
78
|
+
raise Error.usage("unknown publish remote: #{name}") unless remote
|
|
79
|
+
|
|
80
|
+
remote
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
return remotes if cli.roots.empty? && cli.servers.empty?
|
|
84
|
+
|
|
85
|
+
selected = cli.roots.map do |root|
|
|
86
|
+
remote = remotes.find { |entry| entry.path && root_matches?(root, entry.path, project_root) }
|
|
87
|
+
raise Error.usage("path #{root} is not a declared publish remote") unless remote
|
|
88
|
+
|
|
89
|
+
remote
|
|
90
|
+
end
|
|
91
|
+
selected + cli.servers.map do |server|
|
|
92
|
+
remote = remotes.find { |entry| entry.url == server }
|
|
93
|
+
raise Error.usage("server #{server} is not a declared publish remote") unless remote
|
|
94
|
+
|
|
95
|
+
remote
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def root_matches?(cli_root, remote_path, project_root)
|
|
100
|
+
declared = File.join(project_root, remote_path)
|
|
101
|
+
return true if cli_root == declared || cli_root == remote_path
|
|
102
|
+
|
|
103
|
+
strip_dot_slash(cli_root) == strip_dot_slash(remote_path)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def strip_dot_slash(value)
|
|
107
|
+
value.to_s.strip.delete_prefix("./").delete_suffix("/").tr("\\", "/")
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
data/lib/pray/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: pray-cli
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.19.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Andrei Makarov
|
|
@@ -156,6 +156,7 @@ files:
|
|
|
156
156
|
- lib/pray/manifest_parser.rb
|
|
157
157
|
- lib/pray/manifest_parser_blocks.rb
|
|
158
158
|
- lib/pray/manifest_parser_helpers.rb
|
|
159
|
+
- lib/pray/manifest_parser_publish.rb
|
|
159
160
|
- lib/pray/materialize.rb
|
|
160
161
|
- lib/pray/package_spec.rb
|
|
161
162
|
- lib/pray/package_spec_render.rb
|
|
@@ -164,6 +165,8 @@ files:
|
|
|
164
165
|
- lib/pray/plan.rb
|
|
165
166
|
- lib/pray/project_context.rb
|
|
166
167
|
- lib/pray/publish.rb
|
|
168
|
+
- lib/pray/publish_remote.rb
|
|
169
|
+
- lib/pray/publish_select.rb
|
|
167
170
|
- lib/pray/registry.rb
|
|
168
171
|
- lib/pray/registry_http_cache.rb
|
|
169
172
|
- lib/pray/registry_install.rb
|