dash 4.0.8 → 4.1.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/dash/build/progress_parser.rb +136 -0
- data/lib/dash/build/report.rb +104 -0
- data/lib/dash/build/step.rb +49 -0
- data/lib/dash/cli/app/boot.rb +46 -24
- data/lib/dash/cli/app.rb +4 -4
- data/lib/dash/cli/base.rb +117 -2
- data/lib/dash/cli/build.rb +83 -20
- data/lib/dash/cli/doctor/config_checks.rb +36 -1
- data/lib/dash/cli/doctor.rb +2 -1
- data/lib/dash/cli/healthcheck/poller.rb +15 -3
- data/lib/dash/cli/healthcheck/progress_reporter.rb +39 -0
- data/lib/dash/cli/main.rb +24 -7
- data/lib/dash/cli/proxy/drift.rb +17 -2
- data/lib/dash/cli/proxy/legacy_rename.rb +8 -21
- data/lib/dash/cli/proxy/loadbalancer_reboot.rb +8 -1
- data/lib/dash/cli/proxy/reboot.rb +6 -1
- data/lib/dash/cli/proxy.rb +19 -12
- data/lib/dash/cli/prune.rb +5 -8
- data/lib/dash/cli/report.rb +97 -0
- data/lib/dash/cli/templates/sample_hooks/post-deploy.sample +5 -0
- data/lib/dash/commander.rb +10 -2
- data/lib/dash/commands/app.rb +81 -0
- data/lib/dash/commands/auditor.rb +10 -0
- data/lib/dash/commands/base.rb +42 -1
- data/lib/dash/commands/builder/base.rb +18 -0
- data/lib/dash/commands/builder.rb +1 -1
- data/lib/dash/commands/loadbalancer.rb +54 -0
- data/lib/dash/commands/proxy/state.rb +31 -0
- data/lib/dash/commands/proxy.rb +74 -2
- data/lib/dash/commands/registry.rb +14 -0
- data/lib/dash/configuration/docs/configuration.yml +6 -0
- data/lib/dash/configuration/docs/report.yml +39 -0
- data/lib/dash/configuration/docs/role.yml +7 -7
- data/lib/dash/configuration/proxy.rb +3 -0
- data/lib/dash/configuration/report.rb +65 -0
- data/lib/dash/configuration.rb +2 -1
- data/lib/dash/dockerfile/analyzer.rb +66 -0
- data/lib/dash/dockerfile/context.rb +147 -0
- data/lib/dash/dockerfile/dockerignore.rb +29 -0
- data/lib/dash/dockerfile/document.rb +28 -0
- data/lib/dash/dockerfile/finding.rb +20 -0
- data/lib/dash/dockerfile/hadolint.rb +75 -0
- data/lib/dash/dockerfile/instruction.rb +58 -0
- data/lib/dash/dockerfile/parser.rb +199 -0
- data/lib/dash/dockerfile/rules/apt_hygiene.rb +35 -0
- data/lib/dash/dockerfile/rules/base.rb +44 -0
- data/lib/dash/dockerfile/rules/cache_busting_arg.rb +40 -0
- data/lib/dash/dockerfile/rules/cache_export_cost.rb +20 -0
- data/lib/dash/dockerfile/rules/context_size.rb +20 -0
- data/lib/dash/dockerfile/rules/copy_before_install.rb +34 -0
- data/lib/dash/dockerfile/rules/curl_pipe_shell.rb +16 -0
- data/lib/dash/dockerfile/rules/dockerignore_gaps.rb +36 -0
- data/lib/dash/dockerfile/rules/inline_env_blob.rb +23 -0
- data/lib/dash/dockerfile/rules/latest_base.rb +24 -0
- data/lib/dash/dockerfile/rules/missing_dockerignore.rb +11 -0
- data/lib/dash/dockerfile/rules/no_cache_mount.rb +26 -0
- data/lib/dash/dockerfile/rules/root_user.rb +12 -0
- data/lib/dash/dockerfile/rules/secret_in_build_arg.rb +30 -0
- data/lib/dash/dockerfile/rules/single_stage_build_deps.rb +19 -0
- data/lib/dash/dockerfile/rules/uncached_install.rb +20 -0
- data/lib/dash/dockerfile/stage.rb +65 -0
- data/lib/dash/otel_shipper.rb +5 -4
- data/lib/dash/output/otel_logger.rb +52 -0
- data/lib/dash/report/history.rb +94 -0
- data/lib/dash/report/trends.rb +129 -0
- data/lib/dash/report/writer.rb +142 -0
- data/lib/dash/report.rb +170 -0
- data/lib/dash/sshkit_with_ext.rb +62 -0
- data/lib/dash/timings.rb +163 -10
- data/lib/dash/utils.rb +7 -0
- data/lib/dash/version.rb +1 -1
- data/lib/dash.rb +4 -0
- metadata +38 -1
data/lib/dash/sshkit_with_ext.rb
CHANGED
|
@@ -6,6 +6,27 @@ require "json"
|
|
|
6
6
|
require "resolv"
|
|
7
7
|
require "concurrent/atomic/semaphore"
|
|
8
8
|
|
|
9
|
+
# Deploy-report timing reaches into dash's runtime from code that runs for everyone else
|
|
10
|
+
# using SSHKit in this process too, and this file can be required on its own — without the
|
|
11
|
+
# gem's autoloader and without the DASH commander. So every reach is optional, and it is
|
|
12
|
+
# funnelled through here rather than repeated at each hook, where one site would sooner or
|
|
13
|
+
# later forget the guard and take down every parallel run with a NameError.
|
|
14
|
+
module DashTimings
|
|
15
|
+
class << self
|
|
16
|
+
def timings
|
|
17
|
+
DASH.timings if defined?(DASH)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def current_entry
|
|
21
|
+
Dash::Timings.current_entry if defined?(Dash::Timings)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def current_entry=(entry)
|
|
25
|
+
Dash::Timings.current_entry = entry if defined?(Dash::Timings)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
9
30
|
class SSHKit::Backend::Abstract
|
|
10
31
|
def capture_with_info(*args, **kwargs)
|
|
11
32
|
capture(*args, **kwargs, verbosity: Logger::INFO)
|
|
@@ -64,6 +85,22 @@ class SSHKit::Backend::Abstract
|
|
|
64
85
|
end
|
|
65
86
|
end
|
|
66
87
|
prepend CommandEnvMerge
|
|
88
|
+
|
|
89
|
+
# Attributes the wall time of every command to the timing entry that is current on
|
|
90
|
+
# this thread, so a phase can report how much of its total was spent waiting on round
|
|
91
|
+
# trips rather than on the app. Nothing is executed that would not have run anyway —
|
|
92
|
+
# this only stamps the commands dash was already issuing.
|
|
93
|
+
module TimedCommands
|
|
94
|
+
private
|
|
95
|
+
def create_command_and_execute(args, options)
|
|
96
|
+
started = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
97
|
+
|
|
98
|
+
super
|
|
99
|
+
ensure
|
|
100
|
+
DashTimings.timings&.attribute_command(Process.clock_gettime(Process::CLOCK_MONOTONIC) - started, local: !!host&.local?)
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
prepend TimedCommands
|
|
67
104
|
end
|
|
68
105
|
|
|
69
106
|
class SSHKit::Backend::Netssh::Configuration
|
|
@@ -171,6 +208,21 @@ class SSHKit::Backend::Netssh
|
|
|
171
208
|
end
|
|
172
209
|
prepend LimitConcurrentStartsInstance
|
|
173
210
|
|
|
211
|
+
# Prepended last, so it is in front of the concurrency limiter and the DNS retries:
|
|
212
|
+
# what a phase pays for a connection includes queueing behind max_concurrent_starts.
|
|
213
|
+
# The pool only calls through on a cache miss, so this measures real connects.
|
|
214
|
+
module TimedConnects
|
|
215
|
+
private
|
|
216
|
+
def connect_ssh(...)
|
|
217
|
+
started = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
218
|
+
|
|
219
|
+
super
|
|
220
|
+
ensure
|
|
221
|
+
DashTimings.timings&.attribute_connect(Process.clock_gettime(Process::CLOCK_MONOTONIC) - started)
|
|
222
|
+
end
|
|
223
|
+
end
|
|
224
|
+
prepend TimedConnects
|
|
225
|
+
|
|
174
226
|
# A pooled session that sat idle while dash was busy elsewhere (a server-lock
|
|
175
227
|
# wait, a loadbalancer reboot) gets dropped by NATs and cloud networks without
|
|
176
228
|
# either end noticing: net-ssh only sends keepalives from inside its event
|
|
@@ -235,9 +287,15 @@ class SSHKit::Runner::Parallel
|
|
|
235
287
|
# problem occurs on multiple hosts.
|
|
236
288
|
module CompleteAll
|
|
237
289
|
def execute
|
|
290
|
+
# A new thread starts with none of its parent's thread-locals, so the timing entry
|
|
291
|
+
# has to be handed over explicitly or every command run on a host would be
|
|
292
|
+
# attributed to no phase at all.
|
|
293
|
+
timing_entry = DashTimings.current_entry
|
|
294
|
+
|
|
238
295
|
threads = hosts.map do |host|
|
|
239
296
|
Thread.new(host) do |h|
|
|
240
297
|
Thread.current.report_on_exception = false
|
|
298
|
+
DashTimings.current_entry = timing_entry
|
|
241
299
|
backend(h, &block).run
|
|
242
300
|
rescue ::StandardError => e
|
|
243
301
|
e2 = SSHKit::Runner::ExecuteError.new e
|
|
@@ -315,9 +373,13 @@ module SSHKitDslRoles
|
|
|
315
373
|
# end
|
|
316
374
|
def on_roles(roles, hosts:, parallel: true, rolling: false, &block)
|
|
317
375
|
if parallel
|
|
376
|
+
# See CompleteAll#execute: thread-locals do not cross Thread.new.
|
|
377
|
+
timing_entry = DashTimings.current_entry
|
|
378
|
+
|
|
318
379
|
threads = roles.filter_map do |role|
|
|
319
380
|
if (role_hosts = role.hosts & hosts).any?
|
|
320
381
|
Thread.new do
|
|
382
|
+
DashTimings.current_entry = timing_entry
|
|
321
383
|
on(role_hosts, rolling ? role.boot_runner_options(role_hosts) : {}) { |host| instance_exec(host, role, &block) }
|
|
322
384
|
rescue StandardError => e
|
|
323
385
|
raise SSHKit::Runner::ExecuteError.new(e), "Exception while executing on #{role}: #{e.message}"
|
data/lib/dash/timings.rb
CHANGED
|
@@ -5,40 +5,193 @@
|
|
|
5
5
|
# Entries land in start order and carry a depth, so a parent phase (Boot) prints above
|
|
6
6
|
# the host entries it wraps even though the hosts finish first. Boot runs one thread per
|
|
7
7
|
# host, so every mutation is behind the mutex.
|
|
8
|
+
#
|
|
9
|
+
# Each entry also accounts for the commands issued while it was the current phase, which
|
|
10
|
+
# is what separates "the boot took 55s" from "the boot spent 41s of that in forty serial
|
|
11
|
+
# SSH round trips". Attribution is by thread-local: `phase` marks its entry as current for
|
|
12
|
+
# the duration of the block, and `lib/dash/sshkit_with_ext.rb` stamps every command and
|
|
13
|
+
# every SSH connect onto whatever entry is current on that thread. Nothing extra is
|
|
14
|
+
# executed — dash only measures the round trips it was already making.
|
|
8
15
|
class Dash::Timings
|
|
9
|
-
|
|
16
|
+
# The entry the current thread is inside. SSHKit's per-host threads inherit it from
|
|
17
|
+
# the thread that spawned them (see CompleteAll#execute and SSHKitDslRoles#on_roles),
|
|
18
|
+
# so a command issued on a boot thread lands on that host's row.
|
|
19
|
+
CURRENT_KEY = :dash_timing_entry
|
|
20
|
+
|
|
21
|
+
Entry = Struct.new(:name, :seconds, :detail, :depth, :parent, :commands, :command_seconds, :connect_seconds, :local)
|
|
22
|
+
|
|
23
|
+
# Read and written from lib/dash/sshkit_with_ext.rb, which hands the entry across the
|
|
24
|
+
# per-host and per-role threads SSHKit spawns. Class-level because the patches run
|
|
25
|
+
# without a commander in reach.
|
|
26
|
+
class << self
|
|
27
|
+
def current_entry
|
|
28
|
+
Thread.current[CURRENT_KEY]
|
|
29
|
+
end
|
|
10
30
|
|
|
11
|
-
|
|
12
|
-
|
|
31
|
+
def current_entry=(entry)
|
|
32
|
+
Thread.current[CURRENT_KEY] = entry
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Rebuilds a table from what #to_h exported, so `dash report` can print a saved
|
|
36
|
+
# deploy the way the deploy printed it. The entries come back parentless: their
|
|
37
|
+
# counters are already the subtree totals #to_h computed, and re-nesting them would
|
|
38
|
+
# roll those totals up a second time.
|
|
39
|
+
def from_h(phases)
|
|
40
|
+
new entries: Array(phases).map { |phase| entry_from(phase.transform_keys(&:to_sym)) }
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
def entry_from(phase)
|
|
45
|
+
Entry.new \
|
|
46
|
+
phase[:name], phase[:seconds].to_f, phase[:detail], phase[:depth].to_i, nil,
|
|
47
|
+
phase[:commands].to_i, phase[:command_seconds].to_f, phase[:connect_seconds].to_f,
|
|
48
|
+
phase[:local].nil? ? true : phase[:local]
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def initialize(entries: [])
|
|
53
|
+
@entries = entries
|
|
13
54
|
@mutex = Mutex.new
|
|
14
55
|
end
|
|
15
56
|
|
|
16
57
|
# Times the block. The entry is yielded so the block can annotate it — a boot can say
|
|
17
58
|
# how long of its total was spent waiting for the container to become healthy.
|
|
18
59
|
def phase(name, depth: 0)
|
|
19
|
-
entry =
|
|
60
|
+
entry = new_entry(name, depth: depth)
|
|
20
61
|
@mutex.synchronize { @entries << entry }
|
|
21
62
|
started = clock
|
|
22
63
|
|
|
23
|
-
|
|
64
|
+
previous = self.class.current_entry
|
|
65
|
+
self.class.current_entry = entry
|
|
66
|
+
|
|
67
|
+
begin
|
|
68
|
+
yield entry
|
|
69
|
+
ensure
|
|
70
|
+
self.class.current_entry = previous
|
|
71
|
+
end
|
|
24
72
|
ensure
|
|
25
73
|
entry.seconds = clock - started
|
|
26
74
|
end
|
|
27
75
|
|
|
76
|
+
# For time that was measured elsewhere — the gem load and config parse that happened
|
|
77
|
+
# before there was a Timings to record into.
|
|
78
|
+
def record(name, seconds, depth: 0, detail: nil)
|
|
79
|
+
new_entry(name, depth: depth).tap do |entry|
|
|
80
|
+
entry.seconds = seconds
|
|
81
|
+
entry.detail = detail
|
|
82
|
+
@mutex.synchronize { @entries << entry }
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def current
|
|
87
|
+
self.class.current_entry
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def attribute_command(seconds, local:)
|
|
91
|
+
entry = current or return
|
|
92
|
+
|
|
93
|
+
@mutex.synchronize do
|
|
94
|
+
entry.commands += 1
|
|
95
|
+
entry.command_seconds += seconds
|
|
96
|
+
# A phase that issued even one remote command is reported as ssh: the local
|
|
97
|
+
# label is only honest when nothing left the machine.
|
|
98
|
+
entry.local &&= local
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def attribute_connect(seconds)
|
|
103
|
+
entry = current or return
|
|
104
|
+
|
|
105
|
+
@mutex.synchronize { entry.connect_seconds += seconds }
|
|
106
|
+
end
|
|
107
|
+
|
|
28
108
|
def any?
|
|
29
109
|
@mutex.synchronize { @entries.any? }
|
|
30
110
|
end
|
|
31
111
|
|
|
112
|
+
# Where an entry's row sits in #lines, so Dash::Report can splice its build rows in
|
|
113
|
+
# under the phase they belong to. Identity, not equality: two phases of the same name
|
|
114
|
+
# and duration are equal as Structs but are not the same row.
|
|
115
|
+
# The row at a position, so a consumer that saved an index (Dash::Report, reattaching
|
|
116
|
+
# its build rows to the phase they hung under) can find the entry again.
|
|
117
|
+
def entry_at(index)
|
|
118
|
+
@mutex.synchronize { @entries[index] } if index
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def index_of(entry)
|
|
122
|
+
@mutex.synchronize { @entries.index { |candidate| candidate.equal?(entry) } }
|
|
123
|
+
end
|
|
124
|
+
|
|
32
125
|
def lines
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
126
|
+
with_totals do |entry, totals|
|
|
127
|
+
line = format(" %s%-36s %6.1fs", " " * entry.depth, entry.name, entry.seconds.to_f)
|
|
128
|
+
line += format(" %3d %-5s %5.1fs", totals[:commands], totals[:local] ? "local" : "ssh", totals[:command_seconds]) if totals[:commands] > 0
|
|
129
|
+
entry.detail ? "#{line} (#{entry.detail})" : line
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# A top-level phase's wall time by name, for consumers that know a phase by what it is
|
|
134
|
+
# called rather than by where it sits — the post-deploy hook's build and boot runtimes.
|
|
135
|
+
# Depth-0 only: a per-host row inside Boot is named after the host, but a role could be
|
|
136
|
+
# named "Boot" and must not be mistaken for the phase.
|
|
137
|
+
def seconds_for(name)
|
|
138
|
+
@mutex.synchronize { @entries.find { |entry| entry.name == name && entry.depth.to_i.zero? }&.seconds }
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# The command counts here are subtree totals, matching what the table prints — phases
|
|
142
|
+
# nest, so a consumer must not sum them across depths.
|
|
143
|
+
def to_h
|
|
144
|
+
with_totals do |entry, totals|
|
|
145
|
+
{
|
|
146
|
+
name: entry.name,
|
|
147
|
+
depth: entry.depth,
|
|
148
|
+
seconds: entry.seconds.to_f,
|
|
149
|
+
detail: entry.detail,
|
|
150
|
+
commands: totals[:commands],
|
|
151
|
+
command_seconds: totals[:command_seconds],
|
|
152
|
+
connect_seconds: totals[:connect_seconds],
|
|
153
|
+
local: totals[:local]
|
|
154
|
+
}
|
|
38
155
|
end
|
|
39
156
|
end
|
|
40
157
|
|
|
41
158
|
private
|
|
159
|
+
def new_entry(name, depth:)
|
|
160
|
+
Entry.new(name, nil, nil, depth, current, 0, 0.0, 0.0, true)
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def with_totals
|
|
164
|
+
@mutex.synchronize do
|
|
165
|
+
totals = subtree_totals
|
|
166
|
+
@entries.map { |entry| yield entry, totals[entry.object_id] }
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# A parent phase issues few commands of its own — Boot opens one thread per host and
|
|
171
|
+
# then waits — so its own counters say nothing. Roll each entry's counters up through
|
|
172
|
+
# its ancestors at render time rather than at record time, so the host rows keep
|
|
173
|
+
# their own numbers while the parent shows what the phase cost in total.
|
|
174
|
+
def subtree_totals
|
|
175
|
+
totals = @entries.to_h { |entry| [ entry.object_id, { commands: 0, command_seconds: 0.0, connect_seconds: 0.0, local: true } ] }
|
|
176
|
+
|
|
177
|
+
@entries.each do |entry|
|
|
178
|
+
node = entry
|
|
179
|
+
|
|
180
|
+
while node
|
|
181
|
+
if (total = totals[node.object_id])
|
|
182
|
+
total[:commands] += entry.commands
|
|
183
|
+
total[:command_seconds] += entry.command_seconds
|
|
184
|
+
total[:connect_seconds] += entry.connect_seconds
|
|
185
|
+
total[:local] &&= entry.local if entry.commands > 0
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
node = node.parent
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
totals
|
|
193
|
+
end
|
|
194
|
+
|
|
42
195
|
def clock
|
|
43
196
|
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
44
197
|
end
|
data/lib/dash/utils.rb
CHANGED
|
@@ -134,6 +134,13 @@ module Dash::Utils
|
|
|
134
134
|
end
|
|
135
135
|
end
|
|
136
136
|
|
|
137
|
+
# buildx reports decimal units, so dash does too — an operator comparing a report row
|
|
138
|
+
# with what buildx printed should see the same number.
|
|
139
|
+
def human_bytes(bytes)
|
|
140
|
+
divisor, unit = [ [ 1_000_000_000, "GB" ], [ 1_000_000, "MB" ], [ 1_000, "kB" ] ].find { |size, _| bytes >= size }
|
|
141
|
+
divisor ? format("%.1f%s", bytes.to_f / divisor, unit) : "#{bytes}B"
|
|
142
|
+
end
|
|
143
|
+
|
|
137
144
|
def older_version?(version, other_version)
|
|
138
145
|
Gem::Version.new(version.delete_prefix("v")) < Gem::Version.new(other_version.delete_prefix("v"))
|
|
139
146
|
end
|
data/lib/dash/version.rb
CHANGED
data/lib/dash.rb
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
module Dash
|
|
2
2
|
class ConfigurationError < StandardError; end
|
|
3
|
+
|
|
4
|
+
# Stamped before the first require so the Startup row in the deploy report covers what
|
|
5
|
+
# it actually costs to get here: gem load, Zeitwerk, and everything bin/dash pulls in.
|
|
6
|
+
PROCESS_STARTED_AT = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
3
7
|
end
|
|
4
8
|
|
|
5
9
|
require "active_support"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: dash
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 4.
|
|
4
|
+
version: 4.1.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Mikael Henriksson
|
|
@@ -177,6 +177,9 @@ files:
|
|
|
177
177
|
- README.md
|
|
178
178
|
- bin/dash
|
|
179
179
|
- lib/dash.rb
|
|
180
|
+
- lib/dash/build/progress_parser.rb
|
|
181
|
+
- lib/dash/build/report.rb
|
|
182
|
+
- lib/dash/build/step.rb
|
|
180
183
|
- lib/dash/cli.rb
|
|
181
184
|
- lib/dash/cli/accessory.rb
|
|
182
185
|
- lib/dash/cli/alias/command.rb
|
|
@@ -198,6 +201,7 @@ files:
|
|
|
198
201
|
- lib/dash/cli/healthcheck/drift_error.rb
|
|
199
202
|
- lib/dash/cli/healthcheck/error.rb
|
|
200
203
|
- lib/dash/cli/healthcheck/poller.rb
|
|
204
|
+
- lib/dash/cli/healthcheck/progress_reporter.rb
|
|
201
205
|
- lib/dash/cli/lock.rb
|
|
202
206
|
- lib/dash/cli/main.rb
|
|
203
207
|
- lib/dash/cli/main/migrate.rb
|
|
@@ -209,6 +213,7 @@ files:
|
|
|
209
213
|
- lib/dash/cli/proxy/reboot.rb
|
|
210
214
|
- lib/dash/cli/prune.rb
|
|
211
215
|
- lib/dash/cli/registry.rb
|
|
216
|
+
- lib/dash/cli/report.rb
|
|
212
217
|
- lib/dash/cli/secrets.rb
|
|
213
218
|
- lib/dash/cli/server.rb
|
|
214
219
|
- lib/dash/cli/templates/deploy.yml
|
|
@@ -255,6 +260,7 @@ files:
|
|
|
255
260
|
- lib/dash/commands/lock.rb
|
|
256
261
|
- lib/dash/commands/proxy.rb
|
|
257
262
|
- lib/dash/commands/proxy/cert_transfer.rb
|
|
263
|
+
- lib/dash/commands/proxy/state.rb
|
|
258
264
|
- lib/dash/commands/prune.rb
|
|
259
265
|
- lib/dash/commands/registry.rb
|
|
260
266
|
- lib/dash/commands/server.rb
|
|
@@ -273,6 +279,7 @@ files:
|
|
|
273
279
|
- lib/dash/configuration/docs/output.yml
|
|
274
280
|
- lib/dash/configuration/docs/proxy.yml
|
|
275
281
|
- lib/dash/configuration/docs/registry.yml
|
|
282
|
+
- lib/dash/configuration/docs/report.yml
|
|
276
283
|
- lib/dash/configuration/docs/role.yml
|
|
277
284
|
- lib/dash/configuration/docs/servers.yml
|
|
278
285
|
- lib/dash/configuration/docs/ssh.yml
|
|
@@ -287,6 +294,7 @@ files:
|
|
|
287
294
|
- lib/dash/configuration/proxy/boot.rb
|
|
288
295
|
- lib/dash/configuration/proxy/run.rb
|
|
289
296
|
- lib/dash/configuration/registry.rb
|
|
297
|
+
- lib/dash/configuration/report.rb
|
|
290
298
|
- lib/dash/configuration/role.rb
|
|
291
299
|
- lib/dash/configuration/role/healthcheck.rb
|
|
292
300
|
- lib/dash/configuration/servers.rb
|
|
@@ -305,6 +313,31 @@ files:
|
|
|
305
313
|
- lib/dash/configuration/validator/servers.rb
|
|
306
314
|
- lib/dash/configuration/volume.rb
|
|
307
315
|
- lib/dash/docker.rb
|
|
316
|
+
- lib/dash/dockerfile/analyzer.rb
|
|
317
|
+
- lib/dash/dockerfile/context.rb
|
|
318
|
+
- lib/dash/dockerfile/dockerignore.rb
|
|
319
|
+
- lib/dash/dockerfile/document.rb
|
|
320
|
+
- lib/dash/dockerfile/finding.rb
|
|
321
|
+
- lib/dash/dockerfile/hadolint.rb
|
|
322
|
+
- lib/dash/dockerfile/instruction.rb
|
|
323
|
+
- lib/dash/dockerfile/parser.rb
|
|
324
|
+
- lib/dash/dockerfile/rules/apt_hygiene.rb
|
|
325
|
+
- lib/dash/dockerfile/rules/base.rb
|
|
326
|
+
- lib/dash/dockerfile/rules/cache_busting_arg.rb
|
|
327
|
+
- lib/dash/dockerfile/rules/cache_export_cost.rb
|
|
328
|
+
- lib/dash/dockerfile/rules/context_size.rb
|
|
329
|
+
- lib/dash/dockerfile/rules/copy_before_install.rb
|
|
330
|
+
- lib/dash/dockerfile/rules/curl_pipe_shell.rb
|
|
331
|
+
- lib/dash/dockerfile/rules/dockerignore_gaps.rb
|
|
332
|
+
- lib/dash/dockerfile/rules/inline_env_blob.rb
|
|
333
|
+
- lib/dash/dockerfile/rules/latest_base.rb
|
|
334
|
+
- lib/dash/dockerfile/rules/missing_dockerignore.rb
|
|
335
|
+
- lib/dash/dockerfile/rules/no_cache_mount.rb
|
|
336
|
+
- lib/dash/dockerfile/rules/root_user.rb
|
|
337
|
+
- lib/dash/dockerfile/rules/secret_in_build_arg.rb
|
|
338
|
+
- lib/dash/dockerfile/rules/single_stage_build_deps.rb
|
|
339
|
+
- lib/dash/dockerfile/rules/uncached_install.rb
|
|
340
|
+
- lib/dash/dockerfile/stage.rb
|
|
308
341
|
- lib/dash/env_file.rb
|
|
309
342
|
- lib/dash/git.rb
|
|
310
343
|
- lib/dash/otel_shipper.rb
|
|
@@ -313,6 +346,10 @@ files:
|
|
|
313
346
|
- lib/dash/output/formatter.rb
|
|
314
347
|
- lib/dash/output/otel_logger.rb
|
|
315
348
|
- lib/dash/project_directory.rb
|
|
349
|
+
- lib/dash/report.rb
|
|
350
|
+
- lib/dash/report/history.rb
|
|
351
|
+
- lib/dash/report/trends.rb
|
|
352
|
+
- lib/dash/report/writer.rb
|
|
316
353
|
- lib/dash/secrets.rb
|
|
317
354
|
- lib/dash/secrets/adapters.rb
|
|
318
355
|
- lib/dash/secrets/adapters/aws_secrets_manager.rb
|