pray-cli 1.8.0 → 1.8.1
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/lib/pray/verify.rb +26 -16
- data/lib/pray/verify_position.rb +135 -0
- data/lib/pray/version.rb +2 -2
- data/lib/pray.rb +1 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7712d0c9a8ea370b50c90c7212ab7d15ea2c8ea90a304365194f9c34242b72dc
|
|
4
|
+
data.tar.gz: 0a5a5de36c49409e1e9bb8d689a1fdd7284eaa79b472eee210f19144835812a4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6750b00e4f4cc372849209ec4e23ae222cee7ef49134afe7dd2f2fbd19d2efcfd1bed5a143ae829b17c68b596d2320e1a8095be36c01c335c894f07c4415bcc0
|
|
7
|
+
data.tar.gz: 173d7e11c528fec7a3d816aedb511952e8189e2ba8fd061245c0cf13b3a00a9f658df255be54c0e44b75326c47435f88933d867bea0db4ffa99cc0456089effa
|
data/lib/pray/verify.rb
CHANGED
|
@@ -20,7 +20,7 @@ module Pray
|
|
|
20
20
|
module_function
|
|
21
21
|
|
|
22
22
|
def inspect_project(project, lockfile)
|
|
23
|
-
collect_verification_report(project, lockfile)
|
|
23
|
+
collect_verification_report(project, lockfile)[0]
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
def verify_project(project, lockfile, strict: false)
|
|
@@ -35,21 +35,21 @@ module Pray
|
|
|
35
35
|
end
|
|
36
36
|
|
|
37
37
|
def drift_project(project, lockfile)
|
|
38
|
-
report, rendered_targets = collect_verification_report(project, lockfile)
|
|
39
|
-
|
|
40
|
-
normalized_fresh = Hashing.normalize_line_endings(
|
|
41
|
-
on_disk = rendered_targets[
|
|
38
|
+
report, rendered_targets, fresh_targets = collect_verification_report(project, lockfile)
|
|
39
|
+
fresh_targets.each do |path, fresh_content|
|
|
40
|
+
normalized_fresh = Hashing.normalize_line_endings(fresh_content)
|
|
41
|
+
on_disk = rendered_targets[path]
|
|
42
42
|
on_disk = Hashing.normalize_line_endings(on_disk) if on_disk
|
|
43
43
|
unless on_disk == normalized_fresh
|
|
44
44
|
report.findings << VerificationFinding.new(
|
|
45
45
|
kind: "renderer_drift",
|
|
46
|
-
message: "#{
|
|
46
|
+
message: "#{path} differs from fresh render"
|
|
47
47
|
)
|
|
48
48
|
end
|
|
49
|
-
unless lockfile_targets(lockfile).include?(
|
|
49
|
+
unless lockfile_targets(lockfile).include?(path)
|
|
50
50
|
report.findings << VerificationFinding.new(
|
|
51
51
|
kind: "renderer_drift",
|
|
52
|
-
message: "#{
|
|
52
|
+
message: "#{path} is not tracked in lockfile"
|
|
53
53
|
)
|
|
54
54
|
end
|
|
55
55
|
end
|
|
@@ -64,6 +64,7 @@ module Pray
|
|
|
64
64
|
def collect_verification_report(project, lockfile)
|
|
65
65
|
report = VerificationReport.new
|
|
66
66
|
rendered_targets = {}
|
|
67
|
+
fresh_targets = Render.render_project(project).to_h { |target| [target.path, target.content] }
|
|
67
68
|
|
|
68
69
|
if project.manifest_hash != lockfile.manifest_hash
|
|
69
70
|
report.findings << VerificationFinding.new(
|
|
@@ -125,21 +126,30 @@ module Pray
|
|
|
125
126
|
message: "`#{target_path}` is missing managed marker `#{span.id}` for `#{span.package}::#{span.export}`. Run `pray install` to restore the managed span."
|
|
126
127
|
)
|
|
127
128
|
else
|
|
128
|
-
|
|
129
|
+
_open_line, _close_line, checksum = marker
|
|
129
130
|
if checksum != span.ideal_checksum
|
|
130
131
|
report.findings << VerificationFinding.new(
|
|
131
132
|
kind: "custom_implementation",
|
|
132
133
|
message: "`#{target_path}` marker `#{span.id}` (`#{span.package}::#{span.export}`) was edited. Restore the managed block or run `pray install` to regenerate it."
|
|
133
134
|
)
|
|
134
135
|
end
|
|
135
|
-
if open_line != span.open_line || close_line != span.close_line
|
|
136
|
-
report.findings << VerificationFinding.new(
|
|
137
|
-
kind: "position_drift",
|
|
138
|
-
message: "`#{target_path}` marker `#{span.id}` (`#{span.package}::#{span.export}`) moved to different lines. Run `pray install` to restore expected positions."
|
|
139
|
-
)
|
|
140
|
-
end
|
|
141
136
|
end
|
|
142
137
|
end
|
|
138
|
+
fresh_lines = fresh_targets[target_path]&.lines(chomp: true)
|
|
139
|
+
summary = VerifyPosition.summarize_position_drift(
|
|
140
|
+
target_path,
|
|
141
|
+
spans,
|
|
142
|
+
markers,
|
|
143
|
+
lines,
|
|
144
|
+
fresh_lines,
|
|
145
|
+
project.local_files
|
|
146
|
+
)
|
|
147
|
+
if summary
|
|
148
|
+
report.findings << VerificationFinding.new(
|
|
149
|
+
kind: "position_drift",
|
|
150
|
+
message: VerifyPosition.format_position_drift_message(summary)
|
|
151
|
+
)
|
|
152
|
+
end
|
|
143
153
|
find_orphan_marker_findings(spans, markers, target_path).each do |finding|
|
|
144
154
|
report.findings << finding
|
|
145
155
|
end
|
|
@@ -155,7 +165,7 @@ module Pray
|
|
|
155
165
|
)
|
|
156
166
|
end
|
|
157
167
|
|
|
158
|
-
[report, rendered_targets]
|
|
168
|
+
[report, rendered_targets, fresh_targets]
|
|
159
169
|
end
|
|
160
170
|
|
|
161
171
|
def find_orphan_marker_findings(spans, markers, target_path)
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pray
|
|
4
|
+
module VerifyPosition
|
|
5
|
+
module_function
|
|
6
|
+
|
|
7
|
+
PositionDriftSummary = Struct.new(
|
|
8
|
+
:target_path,
|
|
9
|
+
:marker_count,
|
|
10
|
+
:uniform_delta,
|
|
11
|
+
:first_id,
|
|
12
|
+
:lock_open,
|
|
13
|
+
:lock_close,
|
|
14
|
+
:file_open,
|
|
15
|
+
:file_close,
|
|
16
|
+
:cause,
|
|
17
|
+
keyword_init: true
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
def summarize_position_drift(target_path, spans, markers, on_disk_lines, fresh_lines, local_files)
|
|
21
|
+
drifted = []
|
|
22
|
+
spans.each do |span|
|
|
23
|
+
marker = markers[span.id]
|
|
24
|
+
next if marker.nil?
|
|
25
|
+
|
|
26
|
+
open_line, close_line, checksum = marker
|
|
27
|
+
next if checksum != span.ideal_checksum
|
|
28
|
+
next if open_line == span.open_line && close_line == span.close_line
|
|
29
|
+
|
|
30
|
+
drifted << {
|
|
31
|
+
id: span.id,
|
|
32
|
+
lock_open: span.open_line,
|
|
33
|
+
lock_close: span.close_line,
|
|
34
|
+
file_open: open_line,
|
|
35
|
+
file_close: close_line
|
|
36
|
+
}
|
|
37
|
+
end
|
|
38
|
+
return nil if drifted.empty?
|
|
39
|
+
|
|
40
|
+
drifted.sort_by! { |marker| marker[:file_open] }
|
|
41
|
+
first = drifted.first
|
|
42
|
+
deltas = drifted.map { |marker| marker[:file_open] - marker[:lock_open] }
|
|
43
|
+
uniform_delta = (deltas.all? { |delta| delta == deltas.first }) ? deltas.first : nil
|
|
44
|
+
cause = if fresh_lines
|
|
45
|
+
unmarked_drift_cause(target_path, on_disk_lines, fresh_lines, local_files)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
PositionDriftSummary.new(
|
|
49
|
+
target_path: target_path,
|
|
50
|
+
marker_count: drifted.length,
|
|
51
|
+
uniform_delta: uniform_delta,
|
|
52
|
+
first_id: first[:id],
|
|
53
|
+
lock_open: first[:lock_open],
|
|
54
|
+
lock_close: first[:lock_close],
|
|
55
|
+
file_open: first[:file_open],
|
|
56
|
+
file_close: first[:file_close],
|
|
57
|
+
cause: cause
|
|
58
|
+
)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def format_position_drift_message(summary)
|
|
62
|
+
shift = if summary.uniform_delta && !summary.uniform_delta.zero?
|
|
63
|
+
format(" (%+d lines)", summary.uniform_delta)
|
|
64
|
+
else
|
|
65
|
+
""
|
|
66
|
+
end
|
|
67
|
+
marker_word = (summary.marker_count == 1) ? "marker" : "markers"
|
|
68
|
+
parts = [
|
|
69
|
+
"`#{summary.target_path}` position drift#{shift} across #{summary.marker_count} #{marker_word}",
|
|
70
|
+
"first marker `#{summary.first_id}` lock #{summary.lock_open}:#{summary.lock_close}, file #{summary.file_open}:#{summary.file_close}"
|
|
71
|
+
]
|
|
72
|
+
parts << "cause: #{summary.cause}" if summary.cause
|
|
73
|
+
parts << "Align unmarked text with compose sources, or run `pray install` to refresh lock positions."
|
|
74
|
+
parts.join("; ")
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def unmarked_drift_cause(target_path, on_disk_lines, fresh_lines, local_files)
|
|
78
|
+
disk_preamble = preamble_lines(on_disk_lines)
|
|
79
|
+
fresh_preamble = preamble_lines(fresh_lines)
|
|
80
|
+
diff = first_line_diff(disk_preamble, fresh_preamble)
|
|
81
|
+
return nil unless diff
|
|
82
|
+
|
|
83
|
+
index, disk_line, fresh_line = diff
|
|
84
|
+
target_line = index + 1
|
|
85
|
+
if (local = locate_line_in_locals(local_files, fresh_line))
|
|
86
|
+
return "`#{target_path}:#{target_line}` unmarked text differs from `#{local[0]}:#{local[1]}`"
|
|
87
|
+
end
|
|
88
|
+
if (local = locate_line_in_locals(local_files, disk_line))
|
|
89
|
+
return "`#{target_path}:#{target_line}` unmarked text differs from `#{local[0]}:#{local[1]}`"
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
"`#{target_path}:#{target_line}` unmarked text differs from fresh composition"
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def preamble_lines(lines)
|
|
96
|
+
preamble = []
|
|
97
|
+
lines.each do |line|
|
|
98
|
+
break if managed_marker?(line)
|
|
99
|
+
|
|
100
|
+
preamble << line
|
|
101
|
+
end
|
|
102
|
+
preamble
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def managed_marker?(line)
|
|
106
|
+
trimmed = line.strip
|
|
107
|
+
return false unless trimmed.start_with?("<!-- pray:") && trimmed.end_with?(" -->")
|
|
108
|
+
|
|
109
|
+
id = trimmed.delete_prefix("<!-- pray:").delete_suffix(" -->")
|
|
110
|
+
id != "0 ignore-comments" && id.match?(/\A[a-z0-9]+\z/)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def first_line_diff(left, right)
|
|
114
|
+
shared = [left.length, right.length].min
|
|
115
|
+
shared.times do |index|
|
|
116
|
+
return [index, left[index], right[index]] if left[index] != right[index]
|
|
117
|
+
end
|
|
118
|
+
return nil if left.length == right.length
|
|
119
|
+
|
|
120
|
+
index = shared
|
|
121
|
+
[index, left[index] || "", right[index] || ""]
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def locate_line_in_locals(local_files, line)
|
|
125
|
+
return nil if line.nil? || line.empty?
|
|
126
|
+
|
|
127
|
+
local_files.each do |local|
|
|
128
|
+
local.content.each_line(chomp: true).with_index do |candidate, index|
|
|
129
|
+
return [local.manifest_path, index + 1] if candidate == line
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
nil
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
data/lib/pray/version.rb
CHANGED
data/lib/pray.rb
CHANGED
|
@@ -22,6 +22,7 @@ require_relative "pray/resolve_context"
|
|
|
22
22
|
require_relative "pray/invocation"
|
|
23
23
|
require_relative "pray/resolve"
|
|
24
24
|
require_relative "pray/render"
|
|
25
|
+
require_relative "pray/verify_position"
|
|
25
26
|
require_relative "pray/verify"
|
|
26
27
|
require_relative "pray/registry"
|
|
27
28
|
require_relative "pray/archive"
|
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.8.
|
|
4
|
+
version: 1.8.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Andrei Makarov
|
|
@@ -155,6 +155,7 @@ files:
|
|
|
155
155
|
- lib/pray/trust_feed.rb
|
|
156
156
|
- lib/pray/trust_ops.rb
|
|
157
157
|
- lib/pray/verify.rb
|
|
158
|
+
- lib/pray/verify_position.rb
|
|
158
159
|
- lib/pray/version.rb
|
|
159
160
|
- pray-cli.gemspec
|
|
160
161
|
homepage: https://pray.kisko.dev
|