easy_caddy 0.1.0 → 0.1.2
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 +33 -0
- data/README.md +1 -1
- data/exe/ecaddy +8 -1
- data/lib/easy_caddy/caddy.rb +69 -2
- data/lib/easy_caddy/commands/audit.rb +34 -7
- data/lib/easy_caddy/commands/register_helpers.rb +62 -5
- data/lib/easy_caddy/commands/setup.rb +46 -3
- data/lib/easy_caddy/error.rb +9 -0
- data/lib/easy_caddy/version.rb +1 -1
- data/lib/easy_caddy.rb +1 -0
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1c38478a91bcf7a5ef93a10693e73d829b74ab1e88709fb38112c1120d30e499
|
|
4
|
+
data.tar.gz: c4923a78a738fdd89079df4f80f3f35ac3185a421be7b2e3ae3509d985168790
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 59e2de015249c22dc56aa731e9a059655a9548d27582947a6d1ba38bffd713950fc23db4e21867ffee2cb2ae7d6e36bd13a8fe4eff4356e06b25115df2f326c8
|
|
7
|
+
data.tar.gz: 4a2226c3ad545bdd047dfcb896636eaf52a9fa1bae1a8984d68a7bbf84300e0dc9fcc9c61fc64fe259f994c37b1d3f8fd12e4d413764e6b3775577302f569937
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,38 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.1.2] — 2026-06-09
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- `EasyCaddy::Error` exception class for user-facing failures. `exe/ecaddy`
|
|
13
|
+
now rescues it and prints a clean one-line message to stderr (exit 1),
|
|
14
|
+
replacing Ruby's default backtrace dump on expected errors.
|
|
15
|
+
- Registered fragments now have `output file` directives rewritten to
|
|
16
|
+
include `mode 0660` so log files (and rolled successors) stay
|
|
17
|
+
group-writable — Caddy runs as root to bind `:80`/`:443`, but
|
|
18
|
+
`caddy validate` / `caddy reload` run as the unprivileged user and need
|
|
19
|
+
to open them.
|
|
20
|
+
- `ecaddy audit` now reports each declared log file as writable, missing,
|
|
21
|
+
or root-locked, with an interactive `--fix` that escalates to
|
|
22
|
+
`sudo chmod` when needed.
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
|
|
26
|
+
- `ecaddy setup` now starts the Caddy brew service **before** running
|
|
27
|
+
`caddy trust`, fixing a `connection refused` failure on fresh installs
|
|
28
|
+
(the local-CA fetch requires the admin endpoint at `localhost:2019` to
|
|
29
|
+
be running). Setup also polls the admin endpoint for up to 10 s before
|
|
30
|
+
attempting trust.
|
|
31
|
+
- `caddy trust` failures now surface the underlying output plus an
|
|
32
|
+
actionable hint (brew restart, `sudo caddy trust`, or re-run `setup`)
|
|
33
|
+
instead of a generic message.
|
|
34
|
+
- `ecaddy run` / `ecaddy ensure` now pre-create each log file declared
|
|
35
|
+
in the fragment and fail fast with a `sudo chmod` hint when the file
|
|
36
|
+
or its directory is owned by another user (typically left over from a
|
|
37
|
+
previous `sudo` run). The opaque `permission denied` from Caddy's
|
|
38
|
+
config validator is now translated into a one-line, actionable error.
|
|
39
|
+
|
|
8
40
|
## [0.1.0] — 2026-06-09
|
|
9
41
|
|
|
10
42
|
### Added
|
|
@@ -31,4 +63,5 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
31
63
|
`SIGTERM`/`SIGINT`, and unregisters on exit — designed to drop into a
|
|
32
64
|
Procfile alongside the Rails server.
|
|
33
65
|
|
|
66
|
+
[0.1.2]: https://github.com/pniemczyk/easy_caddy/releases/tag/v0.1.2
|
|
34
67
|
[0.1.0]: https://github.com/pniemczyk/easy_caddy/releases/tag/v0.1.0
|
data/README.md
CHANGED
data/exe/ecaddy
CHANGED
|
@@ -3,4 +3,11 @@
|
|
|
3
3
|
|
|
4
4
|
require_relative '../lib/easy_caddy'
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
begin
|
|
7
|
+
EasyCaddy::CLI.start(ARGV)
|
|
8
|
+
rescue EasyCaddy::Error => e
|
|
9
|
+
# Deliberately $stderr.puts, not warn: this message must always surface,
|
|
10
|
+
# even when warnings are disabled (e.g. RUBYOPT=-W0).
|
|
11
|
+
$stderr.puts e.message # rubocop:disable Style/StderrPuts
|
|
12
|
+
exit 1
|
|
13
|
+
end
|
data/lib/easy_caddy/caddy.rb
CHANGED
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'English'
|
|
4
|
+
require 'net/http'
|
|
5
|
+
require 'uri'
|
|
6
|
+
require_relative 'error'
|
|
4
7
|
require_relative 'paths'
|
|
5
8
|
|
|
6
9
|
module EasyCaddy
|
|
7
10
|
module Caddy
|
|
8
11
|
BINARY = 'caddy'
|
|
9
12
|
|
|
13
|
+
# Group-writable mode for Caddy log files. A root-run Caddy (needed to bind :443/:80)
|
|
14
|
+
# creates logs the unprivileged user can't open during `caddy validate`/`reload`;
|
|
15
|
+
# 0660 + macOS staff-group inheritance keeps them openable. See the log-permission fix.
|
|
16
|
+
LOG_FILE_MODE = '0660'
|
|
17
|
+
|
|
10
18
|
def self.installed?
|
|
11
19
|
system('which caddy > /dev/null 2>&1')
|
|
12
20
|
end
|
|
@@ -19,8 +27,34 @@ module EasyCaddy
|
|
|
19
27
|
return unless caddyfile.exist?
|
|
20
28
|
|
|
21
29
|
out = `#{BINARY} validate --config #{caddyfile} 2>&1`
|
|
22
|
-
|
|
30
|
+
return if $CHILD_STATUS.success?
|
|
31
|
+
|
|
32
|
+
raise Error, translate_validate_error(out)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Caddy validate emits a wall of JSON log lines on stderr. Pull out the
|
|
36
|
+
# actual error and, for common cases (log file permission), turn it into
|
|
37
|
+
# an actionable message.
|
|
38
|
+
# rubocop:disable Metrics/MethodLength
|
|
39
|
+
def self.translate_validate_error(output)
|
|
40
|
+
error_line = output.lines.find { |l| l.start_with?('Error:') }&.strip
|
|
41
|
+
|
|
42
|
+
if error_line && error_line.match?(/setting up custom log.*permission denied/i)
|
|
43
|
+
path = error_line[%r{open\s+(/\S+):\s*permission denied}, 1]
|
|
44
|
+
hint =
|
|
45
|
+
if path
|
|
46
|
+
"Caddy runs as root and created this log 0600; validation runs as you and can't " \
|
|
47
|
+
"open it.\nFix it once with: sudo chmod #{LOG_FILE_MODE} #{path}\n" \
|
|
48
|
+
'or run `ecaddy audit --fix` to do it interactively.'
|
|
49
|
+
else
|
|
50
|
+
'Check ownership of the log file referenced above, or run `ecaddy audit --fix`.'
|
|
51
|
+
end
|
|
52
|
+
return "Caddy config invalid — log file not writable:\n #{error_line}\n\n#{hint}"
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
"Caddy config invalid:\n#{error_line || output}"
|
|
23
56
|
end
|
|
57
|
+
# rubocop:enable Metrics/MethodLength
|
|
24
58
|
|
|
25
59
|
def self.reload(caddyfile = Paths.caddyfile)
|
|
26
60
|
unless caddyfile.exist?
|
|
@@ -29,13 +63,46 @@ module EasyCaddy
|
|
|
29
63
|
end
|
|
30
64
|
|
|
31
65
|
out = `#{BINARY} reload --config #{caddyfile} 2>&1`
|
|
32
|
-
raise "Caddy reload failed:\n#{out}" unless $CHILD_STATUS.success?
|
|
66
|
+
raise Error, "Caddy reload failed:\n#{out}" unless $CHILD_STATUS.success?
|
|
33
67
|
end
|
|
34
68
|
|
|
35
69
|
def self.trust
|
|
36
70
|
system("#{BINARY} trust")
|
|
37
71
|
end
|
|
38
72
|
|
|
73
|
+
# Runs `caddy trust` and captures stdout+stderr so callers can inspect failures.
|
|
74
|
+
#
|
|
75
|
+
# @return [Array(String, Boolean)] combined output and whether the command succeeded
|
|
76
|
+
def self.trust_with_output
|
|
77
|
+
out = `#{BINARY} trust 2>&1`
|
|
78
|
+
[out, $CHILD_STATUS.success?]
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
ADMIN_ENDPOINT = 'http://localhost:2019/pki/ca/local'
|
|
82
|
+
|
|
83
|
+
# Polls Caddy's admin API until it responds or the timeout elapses.
|
|
84
|
+
#
|
|
85
|
+
# @param timeout [Numeric] seconds to wait before giving up
|
|
86
|
+
# @return [Boolean] true if the admin endpoint responded
|
|
87
|
+
def self.wait_for_admin_endpoint(timeout: 5)
|
|
88
|
+
deadline = Time.now + timeout
|
|
89
|
+
until Time.now > deadline
|
|
90
|
+
return true if admin_endpoint_reachable?
|
|
91
|
+
|
|
92
|
+
sleep 0.25
|
|
93
|
+
end
|
|
94
|
+
false
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def self.admin_endpoint_reachable?
|
|
98
|
+
uri = URI(ADMIN_ENDPOINT)
|
|
99
|
+
Net::HTTP.start(uri.host, uri.port, open_timeout: 1, read_timeout: 1) do |http|
|
|
100
|
+
http.get(uri.request_uri).is_a?(Net::HTTPSuccess)
|
|
101
|
+
end
|
|
102
|
+
rescue StandardError
|
|
103
|
+
false
|
|
104
|
+
end
|
|
105
|
+
|
|
39
106
|
def self.running?
|
|
40
107
|
pid = brew_service_pid
|
|
41
108
|
pid && pid > 0
|
|
@@ -399,15 +399,42 @@ module EasyCaddy
|
|
|
399
399
|
if log_paths.empty?
|
|
400
400
|
info 'No log files configured (add a log { output file … } block)'
|
|
401
401
|
else
|
|
402
|
-
log_paths.each
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
402
|
+
log_paths.each { |path| print_log_file(path) }
|
|
403
|
+
end
|
|
404
|
+
end
|
|
405
|
+
# rubocop:enable Metrics/MethodLength
|
|
406
|
+
|
|
407
|
+
def print_log_file(path)
|
|
408
|
+
if !File.exist?(path)
|
|
409
|
+
warn("log #{path} — not yet created")
|
|
410
|
+
elsif File.writable?(path)
|
|
411
|
+
ok("log #{path} (#{humanize_bytes(File.size(path))})")
|
|
412
|
+
else
|
|
413
|
+
fail("log #{path} — NOT writable by you (root-owned?)",
|
|
414
|
+
hint: 'Caddy runs as root and created this 0600 log; `caddy validate` runs as ' \
|
|
415
|
+
'you and cannot open it. Make it group-writable.',
|
|
416
|
+
fix: log_permission_fix(path))
|
|
409
417
|
end
|
|
410
418
|
end
|
|
419
|
+
|
|
420
|
+
# rubocop:disable Metrics/MethodLength
|
|
421
|
+
def log_permission_fix(path)
|
|
422
|
+
{
|
|
423
|
+
description: "Make the log group-writable (chmod #{Caddy::LOG_FILE_MODE})",
|
|
424
|
+
command: "chmod #{Caddy::LOG_FILE_MODE} #{path}",
|
|
425
|
+
verify: -> { File.writable?(path) },
|
|
426
|
+
escalation: "You don't own this file — needs sudo.",
|
|
427
|
+
next_fix: Fix.new(
|
|
428
|
+
label: "#{path} still not writable",
|
|
429
|
+
description: 'chmod the root-owned log via sudo',
|
|
430
|
+
command: "sudo chmod #{Caddy::LOG_FILE_MODE} #{path}",
|
|
431
|
+
verify: -> { File.writable?(path) },
|
|
432
|
+
escalation: "Still not writable. Check `ls -l #{path}`; " \
|
|
433
|
+
"you may need `sudo chown $USER:staff #{path}`.",
|
|
434
|
+
next_fix: nil
|
|
435
|
+
)
|
|
436
|
+
}
|
|
437
|
+
end
|
|
411
438
|
# rubocop:enable Metrics/MethodLength
|
|
412
439
|
|
|
413
440
|
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require 'fileutils'
|
|
4
4
|
require 'socket'
|
|
5
5
|
require 'openssl'
|
|
6
|
+
require_relative '../error'
|
|
6
7
|
require_relative '../paths'
|
|
7
8
|
require_relative '../registry'
|
|
8
9
|
require_relative '../conflicts'
|
|
@@ -12,6 +13,7 @@ require_relative '../parser'
|
|
|
12
13
|
|
|
13
14
|
module EasyCaddy
|
|
14
15
|
module Commands
|
|
16
|
+
# rubocop:disable Metrics/ModuleLength
|
|
15
17
|
module RegisterHelpers
|
|
16
18
|
private
|
|
17
19
|
|
|
@@ -19,10 +21,10 @@ module EasyCaddy
|
|
|
19
21
|
# Returns the site name on success.
|
|
20
22
|
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength
|
|
21
23
|
def register(config_path, name)
|
|
22
|
-
raise
|
|
24
|
+
raise Error, 'Pass --site NAME to identify this project.' unless name
|
|
23
25
|
|
|
24
26
|
config_path = File.expand_path(config_path)
|
|
25
|
-
raise
|
|
27
|
+
raise Error, "Config not found: #{config_path}" unless File.exist?(config_path)
|
|
26
28
|
|
|
27
29
|
content = File.read(config_path)
|
|
28
30
|
registry = Registry.load
|
|
@@ -33,11 +35,11 @@ module EasyCaddy
|
|
|
33
35
|
blocks = findings.select { |f| f.severity == 'BLOCK' }
|
|
34
36
|
unless blocks.empty?
|
|
35
37
|
blocks.each { |f| warn " BLOCK: #{f.message}\n Hint: #{f.hint}" }
|
|
36
|
-
raise 'Aborting due to conflict.'
|
|
38
|
+
raise Error, 'Aborting due to conflict.'
|
|
37
39
|
end
|
|
38
40
|
|
|
39
41
|
Paths.sites_dir.mkpath
|
|
40
|
-
rewritten = absolutize_log_paths(content, File.dirname(config_path))
|
|
42
|
+
rewritten = ensure_log_mode(absolutize_log_paths(content, File.dirname(config_path)))
|
|
41
43
|
Paths.site_file(name).write(rewritten)
|
|
42
44
|
|
|
43
45
|
ensure_log_dirs(rewritten)
|
|
@@ -65,12 +67,66 @@ module EasyCaddy
|
|
|
65
67
|
end
|
|
66
68
|
end
|
|
67
69
|
|
|
70
|
+
# Guarantee every `output file` log directive sets a group-writable mode, so a
|
|
71
|
+
# root-run Caddy's logs (and rolled files) stay openable by the staff-group user that
|
|
72
|
+
# runs `caddy validate`/`reload`. Leaves an explicit `mode` untouched.
|
|
73
|
+
def ensure_log_mode(content)
|
|
74
|
+
content.gsub(/(\boutput\s+file\s+\S+)(\s*\{[^}]*\})?/m) do
|
|
75
|
+
directive = Regexp.last_match(1)
|
|
76
|
+
block = Regexp.last_match(2)
|
|
77
|
+
next "#{directive} {\n mode #{Caddy::LOG_FILE_MODE}\n }" if block.nil?
|
|
78
|
+
next "#{directive}#{block}" if block.match?(/\bmode\b/)
|
|
79
|
+
|
|
80
|
+
"#{directive}#{block.sub('{', "{\n mode #{Caddy::LOG_FILE_MODE}")}"
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
68
84
|
def ensure_log_dirs(content)
|
|
69
85
|
Parser.parse(content).log_paths.each do |path|
|
|
70
|
-
|
|
86
|
+
ensure_log_writable(path)
|
|
71
87
|
end
|
|
72
88
|
end
|
|
73
89
|
|
|
90
|
+
# Make sure each log path exists and is writable by the current user.
|
|
91
|
+
# Caddy validates configs by *opening* every log file, so a stray
|
|
92
|
+
# root-owned file (left over from an earlier `sudo` run) makes
|
|
93
|
+
# validation fail with a confusing "permission denied".
|
|
94
|
+
def ensure_log_writable(path)
|
|
95
|
+
dir = File.dirname(path)
|
|
96
|
+
FileUtils.mkdir_p(dir)
|
|
97
|
+
FileUtils.touch(path) unless File.exist?(path)
|
|
98
|
+
return if File.writable?(path) && File.writable?(dir)
|
|
99
|
+
|
|
100
|
+
raise Error, build_log_permission_error(path)
|
|
101
|
+
rescue Errno::EACCES, Errno::EPERM
|
|
102
|
+
raise Error, build_log_permission_error(path)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# rubocop:disable Metrics/MethodLength
|
|
106
|
+
def build_log_permission_error(path)
|
|
107
|
+
owner =
|
|
108
|
+
begin
|
|
109
|
+
require 'etc'
|
|
110
|
+
Etc.getpwuid(File.stat(path).uid).name if File.exist?(path)
|
|
111
|
+
rescue StandardError
|
|
112
|
+
nil
|
|
113
|
+
end
|
|
114
|
+
user = ENV['USER'] || Etc.getlogin
|
|
115
|
+
|
|
116
|
+
owner_line = owner ? " Current owner: #{owner}\n" : ''
|
|
117
|
+
<<~MSG.strip
|
|
118
|
+
Cannot write to Caddy log file: #{path}
|
|
119
|
+
#{owner_line} Caddy runs as root and created this log 0600; `caddy validate` runs as you
|
|
120
|
+
(#{user}) and can't open it. Make it group-writable once:
|
|
121
|
+
|
|
122
|
+
sudo chmod #{Caddy::LOG_FILE_MODE} #{path}
|
|
123
|
+
|
|
124
|
+
or run `ecaddy audit --fix` to do it interactively. Re-registering keeps the mode,
|
|
125
|
+
so it won't recur after log rolls. Then re-run the same `ecaddy` command.
|
|
126
|
+
MSG
|
|
127
|
+
end
|
|
128
|
+
# rubocop:enable Metrics/MethodLength
|
|
129
|
+
|
|
74
130
|
def probe_tls(domains)
|
|
75
131
|
domains.each do |domain|
|
|
76
132
|
ok = tls_handshake_ok?(domain)
|
|
@@ -112,5 +168,6 @@ module EasyCaddy
|
|
|
112
168
|
puts " [ecaddy] #{name} unregistered."
|
|
113
169
|
end
|
|
114
170
|
end
|
|
171
|
+
# rubocop:enable Metrics/ModuleLength
|
|
115
172
|
end
|
|
116
173
|
end
|
|
@@ -23,8 +23,8 @@ module EasyCaddy
|
|
|
23
23
|
step('Scaffolding config directories') { scaffold_dirs }
|
|
24
24
|
step('Writing global Caddyfile') { write_caddyfile }
|
|
25
25
|
step('Symlinking for brew services') { symlink_brew }
|
|
26
|
-
step('Trusting local CA') { trust_ca }
|
|
27
26
|
step('Starting caddy service') { start_service }
|
|
27
|
+
step('Trusting local CA') { trust_ca }
|
|
28
28
|
print_success
|
|
29
29
|
end
|
|
30
30
|
|
|
@@ -76,8 +76,51 @@ module EasyCaddy
|
|
|
76
76
|
end
|
|
77
77
|
|
|
78
78
|
def trust_ca
|
|
79
|
-
puts "\n
|
|
80
|
-
|
|
79
|
+
puts "\n Waiting for Caddy admin endpoint at localhost:2019..."
|
|
80
|
+
unless Caddy.wait_for_admin_endpoint(timeout: 10)
|
|
81
|
+
raise <<~MSG.strip
|
|
82
|
+
Caddy admin endpoint at localhost:2019 is not responding.
|
|
83
|
+
`caddy trust` needs a running Caddy instance to fetch the local CA.
|
|
84
|
+
Check that the brew service is up: brew services list
|
|
85
|
+
Then re-run: ecaddy setup
|
|
86
|
+
MSG
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
puts ' Running `caddy trust` — you may be prompted for your password.'
|
|
90
|
+
output, success = Caddy.trust_with_output
|
|
91
|
+
return if success
|
|
92
|
+
|
|
93
|
+
raise build_trust_error(output)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def build_trust_error(output)
|
|
97
|
+
hint =
|
|
98
|
+
if output.include?('connection refused')
|
|
99
|
+
<<~HINT
|
|
100
|
+
Caddy admin endpoint at localhost:2019 became unreachable.
|
|
101
|
+
Try: brew services restart caddy && ecaddy setup
|
|
102
|
+
HINT
|
|
103
|
+
elsif output.match?(/permission denied|not permitted|requires.*root/i)
|
|
104
|
+
<<~HINT
|
|
105
|
+
`caddy trust` needs to add a root certificate to your system keychain.
|
|
106
|
+
Try running it manually: sudo caddy trust
|
|
107
|
+
HINT
|
|
108
|
+
else
|
|
109
|
+
<<~HINT
|
|
110
|
+
Re-run `ecaddy setup`, or run `caddy trust` manually to see the full error.
|
|
111
|
+
HINT
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
<<~MSG.strip
|
|
115
|
+
`caddy trust` failed:
|
|
116
|
+
#{indent(output.strip)}
|
|
117
|
+
|
|
118
|
+
#{indent(hint.strip)}
|
|
119
|
+
MSG
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def indent(text, prefix = ' ')
|
|
123
|
+
text.lines.map { |l| "#{prefix}#{l}" }.join.rstrip
|
|
81
124
|
end
|
|
82
125
|
|
|
83
126
|
def start_service
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module EasyCaddy
|
|
4
|
+
# Raised for expected, user-actionable failures: invalid Caddy config,
|
|
5
|
+
# domain/port conflicts, unwritable log files. The CLI prints the message
|
|
6
|
+
# and exits non-zero WITHOUT a Ruby backtrace — these are not bugs, they're
|
|
7
|
+
# things the user is expected to fix and re-run.
|
|
8
|
+
class Error < StandardError; end
|
|
9
|
+
end
|
data/lib/easy_caddy/version.rb
CHANGED
data/lib/easy_caddy.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: easy_caddy
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Pawel Niemczyk
|
|
@@ -139,6 +139,7 @@ files:
|
|
|
139
139
|
- lib/easy_caddy/commands/status.rb
|
|
140
140
|
- lib/easy_caddy/commands/up.rb
|
|
141
141
|
- lib/easy_caddy/conflicts.rb
|
|
142
|
+
- lib/easy_caddy/error.rb
|
|
142
143
|
- lib/easy_caddy/parser.rb
|
|
143
144
|
- lib/easy_caddy/paths.rb
|
|
144
145
|
- lib/easy_caddy/registry.rb
|
|
@@ -167,7 +168,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
167
168
|
- !ruby/object:Gem::Version
|
|
168
169
|
version: '0'
|
|
169
170
|
requirements: []
|
|
170
|
-
rubygems_version:
|
|
171
|
+
rubygems_version: 3.6.9
|
|
171
172
|
specification_version: 4
|
|
172
173
|
summary: CLI to manage a single global Caddy for multiple local Rails projects
|
|
173
174
|
test_files: []
|