pray-cli 1.6.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 +7 -0
- data/CHANGELOG.md +47 -0
- data/LICENSE.md +21 -0
- data/README.md +125 -0
- data/SECURITY.md +28 -0
- data/bin/polyrun +108 -0
- data/bin/pray +26 -0
- data/lib/pray/archive.rb +68 -0
- data/lib/pray/auth_client.rb +97 -0
- data/lib/pray/cli/commands/auth.rb +42 -0
- data/lib/pray/cli/commands/distribution.rb +17 -0
- data/lib/pray/cli/commands/init.rb +62 -0
- data/lib/pray/cli/commands/meta.rb +15 -0
- data/lib/pray/cli/commands/packages.rb +107 -0
- data/lib/pray/cli/commands/trust.rb +73 -0
- data/lib/pray/cli/commands/workflow.rb +126 -0
- data/lib/pray/cli/help.rb +168 -0
- data/lib/pray/cli/helpers.rb +166 -0
- data/lib/pray/cli/parse.rb +136 -0
- data/lib/pray/cli/parse_auth.rb +91 -0
- data/lib/pray/cli/parse_trust.rb +152 -0
- data/lib/pray/cli/suggest.rb +57 -0
- data/lib/pray/cli.rb +117 -0
- data/lib/pray/confess.rb +113 -0
- data/lib/pray/config.rb +52 -0
- data/lib/pray/constraint.rb +80 -0
- data/lib/pray/destination.rb +158 -0
- data/lib/pray/dotenv.rb +46 -0
- data/lib/pray/environment.rb +41 -0
- data/lib/pray/error.rb +55 -0
- data/lib/pray/format_manifest.rb +255 -0
- data/lib/pray/format_serialize.rb +135 -0
- data/lib/pray/git_sources.rb +228 -0
- data/lib/pray/hashing.rb +59 -0
- data/lib/pray/invocation.rb +110 -0
- data/lib/pray/literal.rb +382 -0
- data/lib/pray/lockfile.rb +259 -0
- data/lib/pray/lockfile_serialize.rb +125 -0
- data/lib/pray/manifest.rb +126 -0
- data/lib/pray/manifest_formatter.rb +56 -0
- data/lib/pray/manifest_json.rb +128 -0
- data/lib/pray/manifest_parser.rb +152 -0
- data/lib/pray/manifest_parser_blocks.rb +216 -0
- data/lib/pray/manifest_parser_helpers.rb +208 -0
- data/lib/pray/materialize.rb +120 -0
- data/lib/pray/package_spec.rb +245 -0
- data/lib/pray/path_safety.rb +41 -0
- data/lib/pray/plan.rb +83 -0
- data/lib/pray/project_context.rb +67 -0
- data/lib/pray/publish.rb +162 -0
- data/lib/pray/registry.rb +355 -0
- data/lib/pray/render.rb +360 -0
- data/lib/pray/resolve.rb +396 -0
- data/lib/pray/resolve_context.rb +19 -0
- data/lib/pray/serve.rb +130 -0
- data/lib/pray/serve_federation.rb +109 -0
- data/lib/pray/session.rb +90 -0
- data/lib/pray/ssh_agent.rb +115 -0
- data/lib/pray/statement_surface.rb +262 -0
- data/lib/pray/substitute.rb +50 -0
- data/lib/pray/sync.rb +219 -0
- data/lib/pray/terminal.rb +30 -0
- data/lib/pray/trust.rb +201 -0
- data/lib/pray/trust_feed.rb +110 -0
- data/lib/pray/trust_ops.rb +202 -0
- data/lib/pray/verify.rb +257 -0
- data/lib/pray/version.rb +6 -0
- data/lib/pray-cli.rb +4 -0
- data/lib/pray.rb +46 -0
- data/pray-cli.gemspec +48 -0
- metadata +187 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "parse_trust"
|
|
4
|
+
require_relative "parse_auth"
|
|
5
|
+
|
|
6
|
+
module Pray
|
|
7
|
+
module CLI
|
|
8
|
+
def parse_command(arguments)
|
|
9
|
+
arguments = arguments.dup
|
|
10
|
+
flags = {
|
|
11
|
+
check: extract_flag!(arguments, "--check"),
|
|
12
|
+
strict: extract_flag!(arguments, "--strict"),
|
|
13
|
+
semantic: extract_flag!(arguments, "--semantic"),
|
|
14
|
+
locked: false,
|
|
15
|
+
frozen: false,
|
|
16
|
+
offline: false,
|
|
17
|
+
targets: []
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
arguments.each do |argument|
|
|
21
|
+
case argument
|
|
22
|
+
when "--locked" then flags[:locked] = true
|
|
23
|
+
when "--frozen" then flags[:locked] = flags[:frozen] = true
|
|
24
|
+
when "--offline" then flags[:offline] = true
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
arguments.reject! { |argument| %w[--locked --frozen --offline].include?(argument) }
|
|
28
|
+
|
|
29
|
+
command = arguments.shift
|
|
30
|
+
raise Error.usage("pray requires a command; run pray --help") unless command
|
|
31
|
+
|
|
32
|
+
case command
|
|
33
|
+
when "manifest" then [:manifest]
|
|
34
|
+
when "init"
|
|
35
|
+
targets = []
|
|
36
|
+
while (argument = arguments.shift)
|
|
37
|
+
if argument == "--targets"
|
|
38
|
+
targets = arguments.shift.to_s.split(",").map(&:strip).reject(&:empty?)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
[:init, targets]
|
|
42
|
+
when "prayer"
|
|
43
|
+
raise Error.unsupported("prayer requires init") unless arguments.shift == "init"
|
|
44
|
+
|
|
45
|
+
[:prayer_init]
|
|
46
|
+
when "repo"
|
|
47
|
+
raise Error.unsupported("repo requires init") unless arguments.shift == "init"
|
|
48
|
+
|
|
49
|
+
[:repo_init]
|
|
50
|
+
when "install" then [:install, flags]
|
|
51
|
+
when "add" then [:add, parse_add_arguments(arguments)]
|
|
52
|
+
when "remove" then [:remove, arguments.shift]
|
|
53
|
+
when "update" then [:update, arguments]
|
|
54
|
+
when "unlock" then [:unlock, arguments.shift]
|
|
55
|
+
when "render" then [:render, flags]
|
|
56
|
+
when "plan" then [:plan, arguments]
|
|
57
|
+
when "apply" then [:apply]
|
|
58
|
+
when "verify" then [:verify, flags]
|
|
59
|
+
when "drift" then [:drift, flags]
|
|
60
|
+
when "format", "fmt" then [:format]
|
|
61
|
+
when "package" then [:package]
|
|
62
|
+
when "publish" then [:publish, parse_publish_arguments(arguments)]
|
|
63
|
+
when "login" then [:login, parse_login_arguments(arguments)]
|
|
64
|
+
when "serve" then [:serve, parse_serve_arguments(arguments)]
|
|
65
|
+
when "confess" then [:confess, parse_confess_arguments(arguments)]
|
|
66
|
+
when "list" then [:list]
|
|
67
|
+
when "outdated" then [:outdated, arguments]
|
|
68
|
+
when "explain" then [:explain, arguments.shift]
|
|
69
|
+
when "vendor" then [:vendor]
|
|
70
|
+
when "clean" then [:clean]
|
|
71
|
+
when "tree" then [:tree]
|
|
72
|
+
when "sync" then [:sync, parse_sync_arguments(arguments)]
|
|
73
|
+
when "trust" then parse_trust_command(arguments)
|
|
74
|
+
when "version", "-V", "--version" then [:version]
|
|
75
|
+
else
|
|
76
|
+
raise Error.usage(Suggest.unknown_command_message(command))
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def extract_flag!(arguments, flag)
|
|
81
|
+
found = arguments.include?(flag)
|
|
82
|
+
arguments.reject! { |argument| argument == flag }
|
|
83
|
+
found
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def parse_add_arguments(arguments)
|
|
87
|
+
name = arguments.shift
|
|
88
|
+
raise Error.unsupported("add requires a package name") unless name
|
|
89
|
+
|
|
90
|
+
constraint = nil
|
|
91
|
+
path = nil
|
|
92
|
+
while (argument = arguments.shift)
|
|
93
|
+
case argument
|
|
94
|
+
when "--path"
|
|
95
|
+
path = arguments.shift
|
|
96
|
+
else
|
|
97
|
+
constraint ||= argument
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
{name: name, constraint: constraint, path: path}
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def parse_publish_arguments(arguments)
|
|
104
|
+
roots = []
|
|
105
|
+
servers = []
|
|
106
|
+
while (argument = arguments.shift)
|
|
107
|
+
case argument
|
|
108
|
+
when "--root"
|
|
109
|
+
roots << arguments.shift
|
|
110
|
+
when "--server"
|
|
111
|
+
servers << arguments.shift
|
|
112
|
+
else
|
|
113
|
+
raise Error.unsupported("unexpected publish argument: #{argument}")
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
raise Error.unsupported("publish requires at least one --root PATH or --server URL") if roots.empty? && servers.empty?
|
|
117
|
+
|
|
118
|
+
{roots: roots, servers: servers}
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def parse_serve_arguments(arguments)
|
|
122
|
+
options = {root: ".", host: "127.0.0.1", port: 7429, stdio: false}
|
|
123
|
+
while (argument = arguments.shift)
|
|
124
|
+
case argument
|
|
125
|
+
when "--root" then options[:root] = arguments.shift
|
|
126
|
+
when "--host" then options[:host] = arguments.shift
|
|
127
|
+
when "--port" then options[:port] = Integer(arguments.shift)
|
|
128
|
+
when "--stdio" then options[:stdio] = true
|
|
129
|
+
else
|
|
130
|
+
raise Error.unsupported("unexpected serve argument: #{argument}")
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
options
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pray
|
|
4
|
+
module CLI
|
|
5
|
+
def parse_login_arguments(arguments)
|
|
6
|
+
servers = []
|
|
7
|
+
email = nil
|
|
8
|
+
passkey_key = nil
|
|
9
|
+
credential_id = nil
|
|
10
|
+
ssh_agent = false
|
|
11
|
+
public_key = nil
|
|
12
|
+
while (argument = arguments.shift)
|
|
13
|
+
case argument
|
|
14
|
+
when "--server" then servers << arguments.shift
|
|
15
|
+
when "--email" then email = arguments.shift
|
|
16
|
+
when "--passkey-key" then passkey_key = arguments.shift
|
|
17
|
+
when "--credential-id" then credential_id = arguments.shift
|
|
18
|
+
when "--ssh-agent" then ssh_agent = true
|
|
19
|
+
when "--public-key" then public_key = arguments.shift
|
|
20
|
+
else
|
|
21
|
+
raise Error.unsupported("unknown login argument: #{argument}")
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
raise Error.unsupported("login requires --server URL") if servers.empty?
|
|
25
|
+
raise Error.unsupported("login requires --email EMAIL") if email.to_s.empty?
|
|
26
|
+
|
|
27
|
+
passkey = passkey_key || credential_id
|
|
28
|
+
if passkey && ssh_agent
|
|
29
|
+
raise Error.unsupported("login accepts either passkey or ssh-agent mode, not both")
|
|
30
|
+
end
|
|
31
|
+
if passkey_key
|
|
32
|
+
raise Error.unsupported("passkey login requires --credential-id") if credential_id.to_s.empty?
|
|
33
|
+
|
|
34
|
+
{servers: servers, email: email, mode: :passkey, passkey_key: passkey_key,
|
|
35
|
+
credential_id: credential_id}
|
|
36
|
+
elsif ssh_agent
|
|
37
|
+
raise Error.unsupported("ssh-agent login requires --public-key") if public_key.to_s.empty?
|
|
38
|
+
|
|
39
|
+
{servers: servers, email: email, mode: :ssh_agent, public_key: public_key}
|
|
40
|
+
else
|
|
41
|
+
raise Error.unsupported("login requires --passkey-key/--credential-id or --ssh-agent")
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def parse_confess_arguments(arguments)
|
|
46
|
+
package = nil
|
|
47
|
+
from_lock = nil
|
|
48
|
+
version = nil
|
|
49
|
+
accepted = false
|
|
50
|
+
rejected = false
|
|
51
|
+
note = nil
|
|
52
|
+
url = nil
|
|
53
|
+
while (argument = arguments.shift)
|
|
54
|
+
case argument
|
|
55
|
+
when "--from-lock" then from_lock = arguments.shift
|
|
56
|
+
when "--version" then version = arguments.shift
|
|
57
|
+
when "--accepted" then accepted = true
|
|
58
|
+
when "--rejected" then rejected = true
|
|
59
|
+
when "--note" then note = arguments.shift
|
|
60
|
+
when "--url" then url = arguments.shift
|
|
61
|
+
when /\A-/ then raise Error.unsupported("unknown confess argument: #{argument}")
|
|
62
|
+
else package = argument
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
if accepted == rejected
|
|
66
|
+
raise Error.unsupported("confess requires exactly one of --accepted or --rejected")
|
|
67
|
+
end
|
|
68
|
+
if package && from_lock
|
|
69
|
+
raise Error.unsupported("confess accepts either a package name or --from-lock")
|
|
70
|
+
end
|
|
71
|
+
raise Error.unsupported("confess requires a package name") if package.nil? && from_lock.nil?
|
|
72
|
+
|
|
73
|
+
{package: package, from_lock: from_lock, version: version, accepted: accepted,
|
|
74
|
+
rejected: rejected, note: note, url: url}
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def parse_sync_arguments(arguments)
|
|
78
|
+
root = "."
|
|
79
|
+
peers = []
|
|
80
|
+
while (argument = arguments.shift)
|
|
81
|
+
case argument
|
|
82
|
+
when "--root" then root = arguments.shift
|
|
83
|
+
when "--peer" then peers << arguments.shift
|
|
84
|
+
else
|
|
85
|
+
raise Error.unsupported("unknown sync argument: #{argument}")
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
{root: root, peers: peers}
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pray
|
|
4
|
+
module CLI
|
|
5
|
+
def parse_trust_command(arguments)
|
|
6
|
+
subcommand = arguments.shift
|
|
7
|
+
raise Error.unsupported("trust requires a subcommand") unless subcommand
|
|
8
|
+
|
|
9
|
+
case subcommand
|
|
10
|
+
when "list" then [:trust_list, parse_trust_list_arguments(arguments)]
|
|
11
|
+
when "show"
|
|
12
|
+
raise Error.unsupported("unknown trust show argument: #{arguments.first}") if arguments.any?
|
|
13
|
+
|
|
14
|
+
[:trust_show]
|
|
15
|
+
when "add-key" then [:trust_add_key, parse_trust_key_arguments(arguments, "add-key")]
|
|
16
|
+
when "remove-key", "revoke"
|
|
17
|
+
[:trust_remove_key, parse_trust_key_arguments(arguments, "remove-key")]
|
|
18
|
+
when "set-signed" then [:trust_set_signed, parse_trust_set_signed_arguments(arguments)]
|
|
19
|
+
when "set-allow" then [:trust_set_allow, parse_trust_set_allow_arguments(arguments)]
|
|
20
|
+
when "import-repo" then [:trust_import_repo, parse_trust_import_repo_arguments(arguments)]
|
|
21
|
+
when "import-registry"
|
|
22
|
+
[:trust_import_registry, parse_trust_import_registry_arguments(arguments)]
|
|
23
|
+
when "check"
|
|
24
|
+
source = arguments.shift
|
|
25
|
+
raise Error.unsupported("trust check accepts at most one SOURCE argument") if arguments.any?
|
|
26
|
+
|
|
27
|
+
[:trust_check, source]
|
|
28
|
+
else
|
|
29
|
+
raise Error.unsupported("unknown trust command: #{subcommand}")
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def parse_trust_list_arguments(arguments)
|
|
34
|
+
scope = :all
|
|
35
|
+
source_url = nil
|
|
36
|
+
arguments.each do |argument|
|
|
37
|
+
case argument
|
|
38
|
+
when "--global" then scope = :global
|
|
39
|
+
when "--local" then scope = :local
|
|
40
|
+
when /\A-/ then raise Error.unsupported("unknown trust list argument: #{argument}")
|
|
41
|
+
else source_url = argument
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
{scope: scope, source_url: source_url}
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def parse_trust_key_arguments(arguments, label)
|
|
48
|
+
key = arguments.shift
|
|
49
|
+
raise Error.unsupported("trust #{label} requires KEY") unless key
|
|
50
|
+
|
|
51
|
+
match_prefix = nil
|
|
52
|
+
while (argument = arguments.shift)
|
|
53
|
+
if argument == "--match-prefix"
|
|
54
|
+
match_prefix = arguments.shift
|
|
55
|
+
raise Error.unsupported("--match-prefix requires VALUE") unless match_prefix
|
|
56
|
+
else
|
|
57
|
+
raise Error.unsupported("unknown trust #{label} argument: #{argument}")
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
{key: key, match_prefix: match_prefix}
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def parse_trust_set_signed_arguments(arguments)
|
|
64
|
+
match_prefix = nil
|
|
65
|
+
enabled = true
|
|
66
|
+
while (argument = arguments.shift)
|
|
67
|
+
case argument
|
|
68
|
+
when "--match-prefix"
|
|
69
|
+
match_prefix = arguments.shift
|
|
70
|
+
raise Error.unsupported("--match-prefix requires VALUE") unless match_prefix
|
|
71
|
+
when "--enabled"
|
|
72
|
+
value = arguments.shift
|
|
73
|
+
raise Error.unsupported("--enabled requires true|false") unless value
|
|
74
|
+
|
|
75
|
+
enabled = parse_bool_flag(value)
|
|
76
|
+
else
|
|
77
|
+
raise Error.unsupported("unknown trust set-signed argument: #{argument}")
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
raise Error.unsupported("trust set-signed requires --match-prefix PREFIX") unless match_prefix
|
|
81
|
+
|
|
82
|
+
{match_prefix: match_prefix, enabled: enabled}
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def parse_trust_set_allow_arguments(arguments)
|
|
86
|
+
match_prefix = nil
|
|
87
|
+
allow = true
|
|
88
|
+
while (argument = arguments.shift)
|
|
89
|
+
case argument
|
|
90
|
+
when "--match-prefix"
|
|
91
|
+
match_prefix = arguments.shift
|
|
92
|
+
raise Error.unsupported("--match-prefix requires VALUE") unless match_prefix
|
|
93
|
+
when "--allow"
|
|
94
|
+
value = arguments.shift
|
|
95
|
+
raise Error.unsupported("--allow requires true|false") unless value
|
|
96
|
+
|
|
97
|
+
allow = parse_bool_flag(value)
|
|
98
|
+
else
|
|
99
|
+
raise Error.unsupported("unknown trust set-allow argument: #{argument}")
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
raise Error.unsupported("trust set-allow requires --match-prefix PREFIX") unless match_prefix
|
|
103
|
+
|
|
104
|
+
{match_prefix: match_prefix, allow: allow}
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def parse_trust_import_repo_arguments(arguments)
|
|
108
|
+
source_url = arguments.shift
|
|
109
|
+
raise Error.unsupported("trust import-repo requires SOURCE_URL") unless source_url
|
|
110
|
+
|
|
111
|
+
match_prefix = nil
|
|
112
|
+
while (argument = arguments.shift)
|
|
113
|
+
if argument == "--match-prefix"
|
|
114
|
+
match_prefix = arguments.shift
|
|
115
|
+
raise Error.unsupported("--match-prefix requires VALUE") unless match_prefix
|
|
116
|
+
else
|
|
117
|
+
raise Error.unsupported("unknown trust import-repo argument: #{argument}")
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
{source_url: source_url, match_prefix: match_prefix}
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def parse_trust_import_registry_arguments(arguments)
|
|
124
|
+
source_url = arguments.shift
|
|
125
|
+
raise Error.unsupported("trust import-registry requires SOURCE_URL") unless source_url
|
|
126
|
+
|
|
127
|
+
match_prefix = nil
|
|
128
|
+
include_host_key = source_url.start_with?("pray+ssh://", "ssh+pray://")
|
|
129
|
+
while (argument = arguments.shift)
|
|
130
|
+
case argument
|
|
131
|
+
when "--match-prefix"
|
|
132
|
+
match_prefix = arguments.shift
|
|
133
|
+
raise Error.unsupported("--match-prefix requires VALUE") unless match_prefix
|
|
134
|
+
when "--host-key" then include_host_key = true
|
|
135
|
+
when "--no-host-key" then include_host_key = false
|
|
136
|
+
else
|
|
137
|
+
raise Error.unsupported("unknown trust import-registry argument: #{argument}")
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
{source_url: source_url, match_prefix: match_prefix, include_host_key: include_host_key}
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def parse_bool_flag(value)
|
|
144
|
+
case value.downcase
|
|
145
|
+
when "true", "1", "yes", "on" then true
|
|
146
|
+
when "false", "0", "no", "off" then false
|
|
147
|
+
else
|
|
148
|
+
raise Error.unsupported("expected true|false, got #{value}")
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pray
|
|
4
|
+
module CLI
|
|
5
|
+
module Suggest
|
|
6
|
+
TOP_LEVEL_COMMANDS = %w[
|
|
7
|
+
add apply clean confess drift explain fmt format help init install list login manifest
|
|
8
|
+
outdated package plan prayer publish remove render repo serve sync tree trust unlock
|
|
9
|
+
update vendor verify version
|
|
10
|
+
].freeze
|
|
11
|
+
|
|
12
|
+
module_function
|
|
13
|
+
|
|
14
|
+
def unknown_command_message(command)
|
|
15
|
+
message = "unknown command: #{command}"
|
|
16
|
+
suggestion = suggest_command(command, TOP_LEVEL_COMMANDS)
|
|
17
|
+
suggestion ? "#{message}\nDid you mean `#{suggestion}`?" : message
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def suggest_command(input, candidates)
|
|
21
|
+
maximum_distance = (input.length <= 3) ? 1 : 2
|
|
22
|
+
candidates
|
|
23
|
+
.map { |candidate| [candidate, levenshtein_distance(input, candidate)] }
|
|
24
|
+
.select { |(_, distance)| distance <= maximum_distance }
|
|
25
|
+
.min_by { |(_, distance)| distance }
|
|
26
|
+
&.first
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def levenshtein_distance(left, right)
|
|
30
|
+
left_chars = left.chars
|
|
31
|
+
right_chars = right.chars
|
|
32
|
+
left_length = left_chars.length
|
|
33
|
+
right_length = right_chars.length
|
|
34
|
+
return right_length if left_length.zero?
|
|
35
|
+
return left_length if right_length.zero?
|
|
36
|
+
|
|
37
|
+
previous_row = (0..right_length).to_a
|
|
38
|
+
current_row = Array.new(right_length + 1, 0)
|
|
39
|
+
|
|
40
|
+
left_chars.each_with_index do |left_character, left_index|
|
|
41
|
+
current_row[0] = left_index + 1
|
|
42
|
+
right_chars.each_with_index do |right_character, right_index|
|
|
43
|
+
substitution_cost = (left_character == right_character) ? 0 : 1
|
|
44
|
+
current_row[right_index + 1] = [
|
|
45
|
+
previous_row[right_index + 1] + 1,
|
|
46
|
+
current_row[right_index] + 1,
|
|
47
|
+
previous_row[right_index] + substitution_cost
|
|
48
|
+
].min
|
|
49
|
+
end
|
|
50
|
+
previous_row, current_row = current_row, previous_row
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
previous_row[right_length]
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
data/lib/pray/cli.rb
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fileutils"
|
|
4
|
+
|
|
5
|
+
require_relative "invocation"
|
|
6
|
+
require_relative "cli/parse"
|
|
7
|
+
require_relative "cli/help"
|
|
8
|
+
require_relative "cli/suggest"
|
|
9
|
+
require_relative "cli/helpers"
|
|
10
|
+
require_relative "cli/commands/init"
|
|
11
|
+
require_relative "cli/commands/workflow"
|
|
12
|
+
require_relative "cli/commands/packages"
|
|
13
|
+
require_relative "cli/commands/distribution"
|
|
14
|
+
require_relative "cli/commands/trust"
|
|
15
|
+
require_relative "cli/commands/auth"
|
|
16
|
+
require_relative "cli/commands/meta"
|
|
17
|
+
|
|
18
|
+
module Pray
|
|
19
|
+
module CLI
|
|
20
|
+
MANIFEST_PATH = "Prayfile"
|
|
21
|
+
LOCKFILE_PATH = "Prayfile.lock"
|
|
22
|
+
|
|
23
|
+
def run(arguments)
|
|
24
|
+
arguments = arguments.dup
|
|
25
|
+
if arguments.delete("--no-input")
|
|
26
|
+
ENV["PRAY_NO_INPUT"] = "1"
|
|
27
|
+
end
|
|
28
|
+
arguments = Invocation.initialize(arguments)
|
|
29
|
+
return if maybe_print_help(arguments)
|
|
30
|
+
|
|
31
|
+
command = parse_command(arguments)
|
|
32
|
+
dispatch(command)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def maybe_print_help(arguments)
|
|
36
|
+
if arguments.empty?
|
|
37
|
+
Help.print_concise_help
|
|
38
|
+
return true
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
if arguments.length == 1 && %w[help -h --help].include?(arguments[0])
|
|
42
|
+
Help.print_concise_help
|
|
43
|
+
return true
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
if arguments[0] == "help"
|
|
47
|
+
target = arguments[1] || ""
|
|
48
|
+
if target.empty? || %w[-h --help].include?(target)
|
|
49
|
+
Help.print_concise_help
|
|
50
|
+
return true
|
|
51
|
+
end
|
|
52
|
+
return true if Help.print_command_help(target)
|
|
53
|
+
|
|
54
|
+
raise Error.usage(Suggest.unknown_command_message(target))
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
help_position = arguments.index { |argument| %w[--help -h].include?(argument) }
|
|
58
|
+
if help_position
|
|
59
|
+
if help_position.zero?
|
|
60
|
+
Help.print_concise_help
|
|
61
|
+
return true
|
|
62
|
+
end
|
|
63
|
+
command = arguments[0]
|
|
64
|
+
return true if Help.print_command_help(command)
|
|
65
|
+
|
|
66
|
+
raise Error.usage("unknown command: #{command}")
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
false
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def dispatch(command)
|
|
73
|
+
case command
|
|
74
|
+
in [:manifest] then manifest_command
|
|
75
|
+
in [:init, targets] then init_command(targets)
|
|
76
|
+
in [:prayer_init] then prayer_init_command
|
|
77
|
+
in [:repo_init] then repo_init_command
|
|
78
|
+
in [:install, flags] then install_command(flags)
|
|
79
|
+
in [:add, add_args] then add_command(**add_args)
|
|
80
|
+
in [:remove, name] then remove_command(name)
|
|
81
|
+
in [:update, arguments] then update_command(arguments)
|
|
82
|
+
in [:unlock, name] then unlock_command(name)
|
|
83
|
+
in [:render, flags] then render_command(flags)
|
|
84
|
+
in [:plan, arguments] then plan_command(arguments)
|
|
85
|
+
in [:apply] then install_command({locked: false, frozen: false, offline: false})
|
|
86
|
+
in [:verify, flags] then verify_command(flags)
|
|
87
|
+
in [:drift, flags] then drift_command(flags)
|
|
88
|
+
in [:format] then format_command
|
|
89
|
+
in [:package] then package_command
|
|
90
|
+
in [:publish, publish_args] then publish_command(**publish_args)
|
|
91
|
+
in [:unsupported, name] then raise Error.unsupported("#{name} is not implemented yet in pray-cli Ruby")
|
|
92
|
+
in [:serve, serve_args] then serve_command(**serve_args)
|
|
93
|
+
in [:login, login_args] then login_command(**login_args)
|
|
94
|
+
in [:confess, confess_args] then confess_command(**confess_args)
|
|
95
|
+
in [:sync, sync_args] then sync_command(**sync_args)
|
|
96
|
+
in [:list] then list_command
|
|
97
|
+
in [:outdated, arguments] then outdated_command(arguments)
|
|
98
|
+
in [:explain, name] then explain_command(name)
|
|
99
|
+
in [:vendor] then raise Error.unsupported("vendor is not implemented yet in pray-cli Ruby")
|
|
100
|
+
in [:clean] then clean_command
|
|
101
|
+
in [:tree] then tree_command
|
|
102
|
+
in [:trust_list, options] then trust_list_command(**options)
|
|
103
|
+
in [:trust_show] then trust_show_command
|
|
104
|
+
in [:trust_add_key, options] then trust_add_key_command(**options)
|
|
105
|
+
in [:trust_remove_key, options] then trust_remove_key_command(**options)
|
|
106
|
+
in [:trust_set_signed, options] then trust_set_signed_command(**options)
|
|
107
|
+
in [:trust_set_allow, options] then trust_set_allow_command(**options)
|
|
108
|
+
in [:trust_import_repo, options] then trust_import_repo_command(**options)
|
|
109
|
+
in [:trust_import_registry, options] then trust_import_registry_command(**options)
|
|
110
|
+
in [:trust_check, source] then trust_check_command(source)
|
|
111
|
+
in [:version] then version_command
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
extend self
|
|
116
|
+
end
|
|
117
|
+
end
|
data/lib/pray/confess.rb
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require_relative "session"
|
|
5
|
+
|
|
6
|
+
module Pray
|
|
7
|
+
ConfessionSubmission = Struct.new(
|
|
8
|
+
:package, :version, :status, :note, :lockfile,
|
|
9
|
+
:distribution_point, :signer, :timestamp, :signature,
|
|
10
|
+
keyword_init: true
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
module Confess
|
|
14
|
+
module_function
|
|
15
|
+
|
|
16
|
+
def submit(package:, from_lock:, version:, accepted:, rejected:, note:, url:,
|
|
17
|
+
project_root: Dir.pwd)
|
|
18
|
+
raise Error.unsupported("confess requires exactly one of --accepted or --rejected") if accepted == rejected
|
|
19
|
+
|
|
20
|
+
manifest_path = File.join(project_root, "Prayfile")
|
|
21
|
+
project = Resolve.resolve_project(manifest_path)
|
|
22
|
+
lockfile_path = File.join(project_root, "Prayfile.lock")
|
|
23
|
+
lockfile = File.file?(lockfile_path) ? Lockfile.read_lockfile(lockfile_path) : nil
|
|
24
|
+
|
|
25
|
+
package_name, resolved_version, package_resolution =
|
|
26
|
+
resolve_target(project, lockfile, package, from_lock, version)
|
|
27
|
+
source_url = resolve_source_url(project, package_resolution, package_name, url)
|
|
28
|
+
if source_url.start_with?("pray+ssh://", "ssh+pray://")
|
|
29
|
+
raise Error.unsupported("pray_ssh confession submit is not implemented yet in pray-cli Ruby")
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
confession = ConfessionSubmission.new(
|
|
33
|
+
package: package_name,
|
|
34
|
+
version: resolved_version,
|
|
35
|
+
status: accepted ? "accepted" : "rejected",
|
|
36
|
+
note: note,
|
|
37
|
+
lockfile: lockfile&.file_hash,
|
|
38
|
+
distribution_point: source_url,
|
|
39
|
+
signer: Session.current_signer(project_root),
|
|
40
|
+
timestamp: Time.now.to_i.to_s,
|
|
41
|
+
signature: nil
|
|
42
|
+
)
|
|
43
|
+
confession.signature = Hashing.sha256_prefixed(JSON.generate(confession_to_hash(confession)))
|
|
44
|
+
Registry.http_post(
|
|
45
|
+
Registry.join_url(source_url, "v1/confessions"),
|
|
46
|
+
"application/json",
|
|
47
|
+
JSON.generate(confession_to_hash(confession))
|
|
48
|
+
)
|
|
49
|
+
puts "Confession submitted for #{confession.package} #{confession.version}"
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def resolve_target(project, lockfile, package, from_lock, version)
|
|
53
|
+
if from_lock
|
|
54
|
+
raise Error.resolution("confess --from-lock requires an existing lockfile") unless lockfile
|
|
55
|
+
|
|
56
|
+
span = lockfile.managed_span.find { |record| record.id == from_lock }
|
|
57
|
+
raise Error.resolution("lockfile span #{from_lock} not found") unless span
|
|
58
|
+
|
|
59
|
+
package_resolution = project.packages.find { |entry| entry.declaration.name == span.package }
|
|
60
|
+
raise Error.resolution("package #{span.package} not found") unless package_resolution
|
|
61
|
+
|
|
62
|
+
locked = lockfile.package.find { |entry| entry.name == span.package }
|
|
63
|
+
raise Error.resolution("lockfile package #{span.package} not found") unless locked
|
|
64
|
+
|
|
65
|
+
if version && version != locked.version
|
|
66
|
+
raise Error.resolution(
|
|
67
|
+
"lockfile span #{from_lock} version #{locked.version} does not match requested version #{version}"
|
|
68
|
+
)
|
|
69
|
+
end
|
|
70
|
+
[span.package, version || locked.version, package_resolution]
|
|
71
|
+
else
|
|
72
|
+
raise Error.unsupported("confess requires a package name") unless package
|
|
73
|
+
|
|
74
|
+
package_resolution = project.packages.find { |entry| entry.declaration.name == package }
|
|
75
|
+
raise Error.resolution("package #{package} not found") unless package_resolution
|
|
76
|
+
|
|
77
|
+
if version && version != package_resolution.spec.version
|
|
78
|
+
raise Error.resolution(
|
|
79
|
+
"package #{package} version #{version} does not match resolved version #{package_resolution.spec.version}"
|
|
80
|
+
)
|
|
81
|
+
end
|
|
82
|
+
[package, version || package_resolution.spec.version, package_resolution]
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def resolve_source_url(project, package_resolution, package_name, url)
|
|
87
|
+
return url if url
|
|
88
|
+
|
|
89
|
+
source_name = package_resolution.declaration.source
|
|
90
|
+
raise Error.resolution("package #{package_name} is missing a source") unless source_name
|
|
91
|
+
|
|
92
|
+
source = project.manifest.sources.find { |entry| entry.name == source_name }
|
|
93
|
+
raise Error.resolution("unknown source: #{source_name}") unless source
|
|
94
|
+
|
|
95
|
+
source.url
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def confession_to_hash(confession)
|
|
99
|
+
hash = {
|
|
100
|
+
"package" => confession.package,
|
|
101
|
+
"version" => confession.version,
|
|
102
|
+
"status" => confession.status
|
|
103
|
+
}
|
|
104
|
+
hash["note"] = confession.note if confession.note
|
|
105
|
+
hash["lockfile"] = confession.lockfile if confession.lockfile
|
|
106
|
+
hash["distribution_point"] = confession.distribution_point if confession.distribution_point
|
|
107
|
+
hash["signer"] = confession.signer if confession.signer
|
|
108
|
+
hash["timestamp"] = confession.timestamp if confession.timestamp
|
|
109
|
+
hash["signature"] = confession.signature if confession.signature
|
|
110
|
+
hash
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|