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,135 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pray
|
|
4
|
+
module FormatSerialize
|
|
5
|
+
module_function
|
|
6
|
+
|
|
7
|
+
def serialize_recommended(manifest)
|
|
8
|
+
lines = [%(prayfile "#{manifest.prayfile_version}")]
|
|
9
|
+
|
|
10
|
+
unless manifest.sources.empty?
|
|
11
|
+
lines << ""
|
|
12
|
+
manifest.sources.each { |source| lines << format_source(source) }
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
unless manifest.symbols.empty?
|
|
16
|
+
lines << ""
|
|
17
|
+
lines << "pray do"
|
|
18
|
+
manifest.symbols.sort.each do |key, value|
|
|
19
|
+
lines << %( #{key} "#{value}")
|
|
20
|
+
end
|
|
21
|
+
lines << "end"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
manifest.targets.each do |target|
|
|
25
|
+
next unless target.scoped
|
|
26
|
+
|
|
27
|
+
lines << ""
|
|
28
|
+
case target.mode
|
|
29
|
+
when "compose"
|
|
30
|
+
path = target.outputs.first || ""
|
|
31
|
+
lines << %(compose "#{path}" do)
|
|
32
|
+
target.entries.each do |entry|
|
|
33
|
+
lines << " #{format_destination_entry(entry, manifest)}"
|
|
34
|
+
end
|
|
35
|
+
lines << "end"
|
|
36
|
+
when "tree"
|
|
37
|
+
path = target.skills.first || ""
|
|
38
|
+
lines << %(tree "#{path}" do)
|
|
39
|
+
target.entries.each do |entry|
|
|
40
|
+
next unless entry.kind == "package"
|
|
41
|
+
|
|
42
|
+
package = find_package(manifest, entry.name)
|
|
43
|
+
lines << " #{ManifestMethods.format_package_declaration(package)}" if package
|
|
44
|
+
end
|
|
45
|
+
lines << "end"
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
file_packages = manifest.packages.select(&:file)
|
|
50
|
+
unless file_packages.empty?
|
|
51
|
+
lines << ""
|
|
52
|
+
file_packages.each { |package| lines << ManifestMethods.format_package_declaration(package) }
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
unbound = manifest.packages.select { |package| !package.bound && !package.file && package.groups.empty? }
|
|
56
|
+
unless unbound.empty?
|
|
57
|
+
lines << ""
|
|
58
|
+
unbound.each { |package| lines << ManifestMethods.format_package_declaration(package) }
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
grouped_packages(manifest).each do |group_names, packages|
|
|
62
|
+
lines << ""
|
|
63
|
+
lines << "group #{group_names.map { |name| ":#{name}" }.join(", ")} do"
|
|
64
|
+
packages.each { |package| lines << " #{ManifestMethods.format_package_declaration(package)}" }
|
|
65
|
+
lines << "end"
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
manifest.targets.each do |target|
|
|
69
|
+
next if target.scoped || !target_has_extras?(target)
|
|
70
|
+
|
|
71
|
+
lines << ""
|
|
72
|
+
lines << "target :#{target.name} do"
|
|
73
|
+
target.commands.each { |command| lines << %( commands "#{command}") }
|
|
74
|
+
target.rules.each { |rule| lines << %( rules "#{rule}") }
|
|
75
|
+
lines << " max_bytes #{target.max_bytes}" if target.max_bytes
|
|
76
|
+
lines << "end"
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
if manifest.render != RenderPolicy.default
|
|
80
|
+
lines << ""
|
|
81
|
+
lines << "render mode: :#{manifest.render.mode}, conflict: :#{manifest.render.conflict}, " \
|
|
82
|
+
"churn: :#{manifest.render.churn}"
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
lines << ""
|
|
86
|
+
lines.join("\n")
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def target_has_extras?(target)
|
|
90
|
+
!target.commands.empty? || !target.rules.empty? || !target.max_bytes.nil?
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def format_source(source)
|
|
94
|
+
parts = [%(source "#{source.name}")]
|
|
95
|
+
case source.kind
|
|
96
|
+
when "path"
|
|
97
|
+
parts << %(path: "#{source.url}")
|
|
98
|
+
when "git"
|
|
99
|
+
url = source.url.delete_prefix("git+")
|
|
100
|
+
parts << %(git: "#{url}")
|
|
101
|
+
else
|
|
102
|
+
parts << %("#{source.url}")
|
|
103
|
+
end
|
|
104
|
+
parts << %(distribution: "#{source.subdir}") if source.subdir
|
|
105
|
+
parts << %(tag: "#{source.tag}") if source.tag
|
|
106
|
+
parts << %(rev: "#{source.rev}") if source.rev
|
|
107
|
+
parts.join(", ")
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def format_destination_entry(entry, manifest)
|
|
111
|
+
if entry.kind == "local"
|
|
112
|
+
return %(pray "#{entry.path}")
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
package = find_package(manifest, entry.name)
|
|
116
|
+
package ? ManifestMethods.format_package_declaration(package) : %(pray "#{entry.name}")
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def find_package(manifest, name)
|
|
120
|
+
manifest.packages.find { |package| package.name == name }
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def grouped_packages(manifest)
|
|
124
|
+
groups = {}
|
|
125
|
+
manifest.packages.each do |package|
|
|
126
|
+
next if package.groups.empty?
|
|
127
|
+
|
|
128
|
+
key = package.groups.dup
|
|
129
|
+
groups[key] ||= []
|
|
130
|
+
groups[key] << package
|
|
131
|
+
end
|
|
132
|
+
groups.sort_by { |names, _| names }.map { |names, packages| [names, packages] }
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "open3"
|
|
4
|
+
require "fileutils"
|
|
5
|
+
require "pathname"
|
|
6
|
+
|
|
7
|
+
module Pray
|
|
8
|
+
GitSourceCheckout = Struct.new(:cache_directory, :revision, :subdir)
|
|
9
|
+
|
|
10
|
+
module GitSources
|
|
11
|
+
module_function
|
|
12
|
+
|
|
13
|
+
def prepare_git_sources(project_root, sources, lockfile, refresh: false)
|
|
14
|
+
checkouts = {}
|
|
15
|
+
sources.each do |source|
|
|
16
|
+
next unless source.kind == "git"
|
|
17
|
+
|
|
18
|
+
clone_url = source.url.delete_prefix("git+")
|
|
19
|
+
if local_filesystem_source?(clone_url) && !local_git_repo_path(clone_url)
|
|
20
|
+
source_root = local_git_source_root(clone_url)
|
|
21
|
+
if source_root
|
|
22
|
+
checkouts[source.name] = GitSourceCheckout.new(
|
|
23
|
+
cache_directory: source_root,
|
|
24
|
+
revision: "",
|
|
25
|
+
subdir: source.subdir
|
|
26
|
+
)
|
|
27
|
+
end
|
|
28
|
+
next
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
pinned_revision = refresh ? nil : pinned_revision_for_source(lockfile, source)
|
|
32
|
+
cache_directory, revision = ensure_git_repository(
|
|
33
|
+
project_root,
|
|
34
|
+
clone_url,
|
|
35
|
+
refresh: refresh,
|
|
36
|
+
pinned_revision: pinned_revision,
|
|
37
|
+
sparse_subdir: source.subdir
|
|
38
|
+
)
|
|
39
|
+
checkouts[source.name] = GitSourceCheckout.new(
|
|
40
|
+
cache_directory: cache_directory,
|
|
41
|
+
revision: revision,
|
|
42
|
+
subdir: source.subdir
|
|
43
|
+
)
|
|
44
|
+
end
|
|
45
|
+
checkouts
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def resolve_distribution_root(repo_root, subdir)
|
|
49
|
+
if subdir
|
|
50
|
+
path = File.join(repo_root, subdir)
|
|
51
|
+
return path if local_distribution_root?(path)
|
|
52
|
+
|
|
53
|
+
raise Error.resolution(
|
|
54
|
+
"no pray distribution root at subdir #{path.inspect} in git source #{repo_root.inspect}"
|
|
55
|
+
)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
discover_distribution_root(repo_root) ||
|
|
59
|
+
raise(Error.resolution(
|
|
60
|
+
"no pray distribution root in git source #{repo_root.inspect}. " \
|
|
61
|
+
"Expected v1/packages at the repository root or under prayers/. " \
|
|
62
|
+
"Publish with `pray publish --root ./prayers` or point the source at a distribution repository."
|
|
63
|
+
))
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def discover_distribution_root(path)
|
|
67
|
+
return path if local_distribution_root?(path)
|
|
68
|
+
|
|
69
|
+
prayers_root = File.join(path, "prayers")
|
|
70
|
+
return prayers_root if local_distribution_root?(prayers_root)
|
|
71
|
+
|
|
72
|
+
nil
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def local_distribution_root?(path)
|
|
76
|
+
File.directory?(File.join(path, "v1", "packages"))
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def local_git_source_root(clone_url)
|
|
80
|
+
path = if clone_url.start_with?("file://")
|
|
81
|
+
clone_url.delete_prefix("file://")
|
|
82
|
+
else
|
|
83
|
+
clone_url
|
|
84
|
+
end
|
|
85
|
+
return nil unless File.exist?(path)
|
|
86
|
+
|
|
87
|
+
discover_distribution_root(path)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def ensure_git_repository(project_root, clone_url, refresh:, pinned_revision:, sparse_subdir:)
|
|
91
|
+
cache_directory = git_source_cache_directory(project_root, clone_url)
|
|
92
|
+
if File.directory?(File.join(cache_directory, ".git"))
|
|
93
|
+
refresh_global_git_cache(clone_url) if refresh
|
|
94
|
+
if pinned_revision
|
|
95
|
+
checkout_git_revision(cache_directory, clone_url, pinned_revision, refresh)
|
|
96
|
+
elsif refresh
|
|
97
|
+
refresh_git_worktree(cache_directory, clone_url)
|
|
98
|
+
end
|
|
99
|
+
apply_sparse_checkout(cache_directory, sparse_subdir) if sparse_subdir
|
|
100
|
+
revision = git_head_revision(cache_directory)
|
|
101
|
+
return [cache_directory, revision]
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
FileUtils.rm_rf(cache_directory) if File.exist?(cache_directory)
|
|
105
|
+
FileUtils.mkdir_p(File.dirname(cache_directory))
|
|
106
|
+
if seed_git_cache_from_global(clone_url, cache_directory, project_root)
|
|
107
|
+
run_git_in_repo(cache_directory, "remote", "set-url", "origin", clone_url)
|
|
108
|
+
else
|
|
109
|
+
run_git(project_root, "clone", "--depth", "1", clone_url, cache_directory)
|
|
110
|
+
mirror_git_cache_to_global(clone_url, cache_directory)
|
|
111
|
+
end
|
|
112
|
+
checkout_git_revision(cache_directory, clone_url, pinned_revision, true) if pinned_revision
|
|
113
|
+
apply_sparse_checkout(cache_directory, sparse_subdir) if sparse_subdir
|
|
114
|
+
[cache_directory, git_head_revision(cache_directory)]
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def git_source_cache_directory(project_root, clone_url)
|
|
118
|
+
File.join(project_root, ".pray", "cache", "git", cache_key(clone_url))
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def cache_key(text)
|
|
122
|
+
Hashing.sha256_prefixed(text)[7, 16]
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def pinned_revision_for_source(lockfile, source)
|
|
126
|
+
if lockfile
|
|
127
|
+
entry = lockfile.source.find { |item| item.name == source.name && item.kind == "git" }
|
|
128
|
+
return entry.revision if entry&.revision
|
|
129
|
+
end
|
|
130
|
+
return source.rev if source.kind == "git" && source.rev
|
|
131
|
+
|
|
132
|
+
source.tag if source.kind == "git"
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def local_filesystem_source?(clone_url)
|
|
136
|
+
clone_url.start_with?("file://") || Pathname.new(clone_url).absolute?
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def local_git_repo_path(clone_url)
|
|
140
|
+
path = clone_url.delete_prefix("file://")
|
|
141
|
+
git_directory = File.join(path, ".git")
|
|
142
|
+
File.directory?(git_directory) ? path : nil
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def global_cache_root
|
|
146
|
+
return ENV["PRAY_CACHE"] if ENV["PRAY_CACHE"]
|
|
147
|
+
return File.join(ENV["PRAY_HOME"], "cache") if ENV["PRAY_HOME"]
|
|
148
|
+
|
|
149
|
+
home = ENV["HOME"]
|
|
150
|
+
home ? File.join(home, ".cache", "pray") : nil
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def global_git_cache_directory(clone_url)
|
|
154
|
+
root = global_cache_root
|
|
155
|
+
root ? File.join(root, "git", cache_key(clone_url)) : nil
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def global_git_cache_ready?(global_cache)
|
|
159
|
+
File.directory?(File.join(global_cache, ".git")) || File.file?(File.join(global_cache, "HEAD"))
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def seed_git_cache_from_global(clone_url, destination, working_directory)
|
|
163
|
+
global_cache = global_git_cache_directory(clone_url)
|
|
164
|
+
return false unless global_cache && global_git_cache_ready?(global_cache)
|
|
165
|
+
|
|
166
|
+
run_git(working_directory, "clone", "--depth", "1", "--quiet", global_cache, destination)
|
|
167
|
+
true
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def mirror_git_cache_to_global(clone_url, project_cache)
|
|
171
|
+
global_cache = global_git_cache_directory(clone_url)
|
|
172
|
+
return unless global_cache
|
|
173
|
+
return if global_git_cache_ready?(global_cache)
|
|
174
|
+
|
|
175
|
+
FileUtils.mkdir_p(File.dirname(global_cache))
|
|
176
|
+
FileUtils.rm_rf(global_cache) if File.exist?(global_cache)
|
|
177
|
+
run_git(File.dirname(project_cache), "clone", "--bare", "--quiet", File.basename(project_cache), global_cache)
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def apply_sparse_checkout(repository, subdir)
|
|
181
|
+
run_git_in_repo(repository, "sparse-checkout", "init", "--cone")
|
|
182
|
+
run_git_in_repo(repository, "sparse-checkout", "set", subdir)
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def checkout_git_revision(repository, _clone_url, revision, refresh)
|
|
186
|
+
run_git_in_repo(repository, "fetch", "--depth", "1", "origin", revision) if refresh
|
|
187
|
+
run_git_in_repo(repository, "checkout", "--force", revision)
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def refresh_git_worktree(repository, _clone_url)
|
|
191
|
+
run_git_in_repo(repository, "fetch", "--depth", "1", "origin")
|
|
192
|
+
run_git_in_repo(repository, "reset", "--hard", "origin/HEAD")
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def refresh_global_git_cache(clone_url)
|
|
196
|
+
global_cache = global_git_cache_directory(clone_url)
|
|
197
|
+
return unless global_cache && global_git_cache_ready?(global_cache)
|
|
198
|
+
|
|
199
|
+
run_git_in_repo(global_cache, "fetch", "--depth", "1", "origin")
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def git_head_revision(repository)
|
|
203
|
+
output, status = Open3.capture2e("git", "-C", repository, "rev-parse", "HEAD")
|
|
204
|
+
raise Error.resolution(command_error("git rev-parse HEAD", output)) unless status.success?
|
|
205
|
+
|
|
206
|
+
revision = output.strip
|
|
207
|
+
raise Error.resolution("git repository has no HEAD revision") if revision.empty?
|
|
208
|
+
|
|
209
|
+
revision
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def run_git(cwd, *arguments)
|
|
213
|
+
output, status = Open3.capture2e("git", "-C", cwd, *arguments)
|
|
214
|
+
return if status.success?
|
|
215
|
+
|
|
216
|
+
raise Error.resolution(command_error("git #{arguments.join(" ")}", output))
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
def run_git_in_repo(repository, *arguments)
|
|
220
|
+
run_git(repository, *arguments)
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def command_error(program, output)
|
|
224
|
+
message = output.strip
|
|
225
|
+
message.empty? ? "#{program} failed" : "#{program} failed: #{message}"
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
end
|
data/lib/pray/hashing.rb
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "digest"
|
|
4
|
+
require "openssl"
|
|
5
|
+
|
|
6
|
+
module Pray
|
|
7
|
+
module Hashing
|
|
8
|
+
module_function
|
|
9
|
+
|
|
10
|
+
def sha256_hex(bytes)
|
|
11
|
+
Digest::SHA256.hexdigest(bytes)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def sha256_prefixed(bytes)
|
|
15
|
+
prefixed_hex_digest(Digest::SHA256.digest(bytes))
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def prefixed_hex_digest(digest_bytes)
|
|
19
|
+
"sha256:#{digest_bytes.unpack1("H*")}"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def marker_id(seed)
|
|
23
|
+
sha256_hex(seed)[0, 8]
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def normalize_line_endings(text)
|
|
27
|
+
text.gsub("\r\n", "\n").tr("\r", "\n")
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def checksum_managed_span_content(body)
|
|
31
|
+
normalized = normalize_line_endings(body).sub(/\n+\z/, "")
|
|
32
|
+
sha256_prefixed(normalized)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def checksum_managed_body_line_refs(body_lines)
|
|
36
|
+
lines = trim_trailing_empty_lines(body_lines)
|
|
37
|
+
digest = OpenSSL::Digest::SHA256.new
|
|
38
|
+
lines.each_with_index do |line, index|
|
|
39
|
+
digest << "\n" if index.positive?
|
|
40
|
+
update_line_endings_normalized(digest, line)
|
|
41
|
+
end
|
|
42
|
+
prefixed_hex_digest(digest.digest)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def trim_trailing_empty_lines(lines)
|
|
46
|
+
trimmed = lines.dup
|
|
47
|
+
trimmed.pop while trimmed.last == ""
|
|
48
|
+
trimmed
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def update_line_endings_normalized(digest, line)
|
|
52
|
+
digest << if line.include?("\r")
|
|
53
|
+
normalize_line_endings(line)
|
|
54
|
+
else
|
|
55
|
+
line
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "pathname"
|
|
4
|
+
|
|
5
|
+
module Pray
|
|
6
|
+
module Invocation
|
|
7
|
+
module_function
|
|
8
|
+
|
|
9
|
+
def initialize(arguments)
|
|
10
|
+
options, remaining = parse_global_options(arguments)
|
|
11
|
+
self.context = ProjectContext.from_options(options)
|
|
12
|
+
remaining
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def context
|
|
16
|
+
Thread.current[:pray_invocation_context]
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def context=(value)
|
|
20
|
+
Thread.current[:pray_invocation_context] = value
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def invocation_context
|
|
24
|
+
context || ProjectContext.from_current_directory
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def manifest_path
|
|
28
|
+
invocation_context.manifest_path
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def lockfile_path
|
|
32
|
+
invocation_context.lockfile_path
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def project_root
|
|
36
|
+
invocation_context.project_root
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def resolve_current_project(options = ResolveOptions.new)
|
|
40
|
+
context = invocation_context
|
|
41
|
+
resolved_options = options.dup
|
|
42
|
+
resolved_options.environment ||= context.environment
|
|
43
|
+
Resolve.resolve_project_in_context(context.manifest_path, context.project_root, resolved_options)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def resolve_current_project_with_git_refresh_fallback(options = ResolveOptions.new, allow_git_refresh_fallback: false)
|
|
47
|
+
resolve_current_project(options)
|
|
48
|
+
rescue Error => error
|
|
49
|
+
if allow_git_refresh_fallback &&
|
|
50
|
+
!options.offline &&
|
|
51
|
+
!options.refresh &&
|
|
52
|
+
Resolve.resolution_may_benefit_from_git_source_refresh?(error)
|
|
53
|
+
refreshed = options.dup
|
|
54
|
+
refreshed.refresh = true
|
|
55
|
+
resolve_current_project(refreshed)
|
|
56
|
+
else
|
|
57
|
+
raise
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def parse_global_options(arguments)
|
|
62
|
+
options = ProjectInvocationOptions.new
|
|
63
|
+
remaining = []
|
|
64
|
+
index = 0
|
|
65
|
+
while index < arguments.length
|
|
66
|
+
argument = arguments[index]
|
|
67
|
+
case argument
|
|
68
|
+
when "--path"
|
|
69
|
+
index += 1
|
|
70
|
+
options.project_root = require_option_value("--path", arguments[index])
|
|
71
|
+
when "--file-path"
|
|
72
|
+
index += 1
|
|
73
|
+
options.manifest_path = require_option_value("--file-path", arguments[index])
|
|
74
|
+
when "--env", "--environment"
|
|
75
|
+
index += 1
|
|
76
|
+
options.environment = require_environment_value(argument, arguments[index])
|
|
77
|
+
else
|
|
78
|
+
if top_level_command?(argument) || argument.start_with?("-")
|
|
79
|
+
remaining.concat(arguments[index..])
|
|
80
|
+
break
|
|
81
|
+
end
|
|
82
|
+
remaining.concat(arguments[index..])
|
|
83
|
+
break
|
|
84
|
+
end
|
|
85
|
+
index += 1
|
|
86
|
+
end
|
|
87
|
+
[options, remaining]
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def require_option_value(flag, value)
|
|
91
|
+
raise Error.usage("#{flag} requires a value") if value.nil? || value.empty?
|
|
92
|
+
|
|
93
|
+
value
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def require_environment_value(flag, value)
|
|
97
|
+
raise Error.usage("#{flag} requires a value") if value.nil?
|
|
98
|
+
|
|
99
|
+
value
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def top_level_command?(token)
|
|
103
|
+
%w[
|
|
104
|
+
manifest init prayer repo install add remove update unlock render plan apply verify drift
|
|
105
|
+
format fmt package publish login serve confess list outdated explain vendor clean tree sync trust
|
|
106
|
+
version -V --version -h --help help
|
|
107
|
+
].include?(token)
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|