zonesync 0.12.1 → 0.13.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.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/CLAUDE.md +0 -12
  3. data/lib/zonesync/cli.rb +1 -7
  4. data/lib/zonesync/cloudflare/proxied_support.rb +61 -0
  5. data/lib/zonesync/cloudflare.rb +48 -35
  6. data/lib/zonesync/diff.rb +1 -9
  7. data/lib/zonesync/errors.rb +5 -39
  8. data/lib/zonesync/generate.rb +1 -5
  9. data/lib/zonesync/http.rb +10 -21
  10. data/lib/zonesync/logger.rb +2 -7
  11. data/lib/zonesync/manifest.rb +7 -24
  12. data/lib/zonesync/parser.rb +3 -5
  13. data/lib/zonesync/provider.rb +19 -51
  14. data/lib/zonesync/record.rb +6 -18
  15. data/lib/zonesync/record_hash.rb +3 -6
  16. data/lib/zonesync/route53.rb +9 -24
  17. data/lib/zonesync/sync.rb +2 -6
  18. data/lib/zonesync/validator.rb +4 -17
  19. data/lib/zonesync/version.rb +1 -1
  20. data/lib/zonesync/zonefile.rb +2 -10
  21. data/lib/zonesync.rb +7 -17
  22. data/zonesync.gemspec +0 -3
  23. metadata +6 -100
  24. data/sorbet/config +0 -4
  25. data/sorbet/rbi/annotations/.gitattributes +0 -1
  26. data/sorbet/rbi/annotations/activesupport.rbi +0 -457
  27. data/sorbet/rbi/annotations/minitest.rbi +0 -119
  28. data/sorbet/rbi/annotations/webmock.rbi +0 -9
  29. data/sorbet/rbi/gems/.gitattributes +0 -1
  30. data/sorbet/rbi/gems/activesupport@8.0.1.rbi +0 -18474
  31. data/sorbet/rbi/gems/addressable@2.8.7.rbi +0 -1994
  32. data/sorbet/rbi/gems/base64@0.2.0.rbi +0 -507
  33. data/sorbet/rbi/gems/benchmark@0.4.0.rbi +0 -618
  34. data/sorbet/rbi/gems/bigdecimal@3.1.9.rbi +0 -9
  35. data/sorbet/rbi/gems/concurrent-ruby@1.3.4.rbi +0 -11645
  36. data/sorbet/rbi/gems/connection_pool@2.4.1.rbi +0 -9
  37. data/sorbet/rbi/gems/crack@1.0.0.rbi +0 -145
  38. data/sorbet/rbi/gems/date@3.4.1.rbi +0 -75
  39. data/sorbet/rbi/gems/diff-lcs@1.5.1.rbi +0 -1131
  40. data/sorbet/rbi/gems/drb@2.2.1.rbi +0 -1347
  41. data/sorbet/rbi/gems/erubi@1.13.1.rbi +0 -155
  42. data/sorbet/rbi/gems/hashdiff@1.1.2.rbi +0 -353
  43. data/sorbet/rbi/gems/i18n@1.14.6.rbi +0 -2275
  44. data/sorbet/rbi/gems/io-console@0.8.0.rbi +0 -9
  45. data/sorbet/rbi/gems/logger@1.6.4.rbi +0 -940
  46. data/sorbet/rbi/gems/minitest@5.25.4.rbi +0 -1547
  47. data/sorbet/rbi/gems/netrc@0.11.0.rbi +0 -159
  48. data/sorbet/rbi/gems/parallel@1.26.3.rbi +0 -291
  49. data/sorbet/rbi/gems/polyglot@0.3.5.rbi +0 -42
  50. data/sorbet/rbi/gems/prism@1.3.0.rbi +0 -40040
  51. data/sorbet/rbi/gems/psych@5.2.2.rbi +0 -1785
  52. data/sorbet/rbi/gems/public_suffix@6.0.1.rbi +0 -936
  53. data/sorbet/rbi/gems/rake@13.2.1.rbi +0 -3028
  54. data/sorbet/rbi/gems/rbi@0.2.2.rbi +0 -4527
  55. data/sorbet/rbi/gems/rdoc@6.10.0.rbi +0 -12766
  56. data/sorbet/rbi/gems/reline@0.6.0.rbi +0 -9
  57. data/sorbet/rbi/gems/rexml@3.4.0.rbi +0 -4974
  58. data/sorbet/rbi/gems/rspec-core@3.13.2.rbi +0 -10896
  59. data/sorbet/rbi/gems/rspec-expectations@3.13.3.rbi +0 -8183
  60. data/sorbet/rbi/gems/rspec-mocks@3.13.2.rbi +0 -5341
  61. data/sorbet/rbi/gems/rspec-support@3.13.2.rbi +0 -1630
  62. data/sorbet/rbi/gems/rspec@3.13.0.rbi +0 -83
  63. data/sorbet/rbi/gems/securerandom@0.4.1.rbi +0 -75
  64. data/sorbet/rbi/gems/spoom@1.5.0.rbi +0 -4932
  65. data/sorbet/rbi/gems/stringio@3.1.2.rbi +0 -9
  66. data/sorbet/rbi/gems/tapioca@0.16.6.rbi +0 -3611
  67. data/sorbet/rbi/gems/thor@1.3.2.rbi +0 -4378
  68. data/sorbet/rbi/gems/treetop@1.6.12.rbi +0 -1895
  69. data/sorbet/rbi/gems/tzinfo@2.0.6.rbi +0 -5918
  70. data/sorbet/rbi/gems/uri@1.0.2.rbi +0 -2340
  71. data/sorbet/rbi/gems/webmock@3.24.0.rbi +0 -1780
  72. data/sorbet/rbi/gems/yard-sorbet@0.9.0.rbi +0 -435
  73. data/sorbet/rbi/gems/yard@0.9.37.rbi +0 -18379
  74. data/sorbet/rbi/todo.rbi +0 -7
  75. data/sorbet/tapioca/config.yml +0 -13
  76. data/sorbet/tapioca/require.rb +0 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c395a08b3c2cc504eecef12cf44298872295adf778a7087128fb009b8709cfd1
4
- data.tar.gz: 38f349d9182226d849c1a4a4688328aaa96d64dbda182803aeedffefea7d99e3
3
+ metadata.gz: d52bed9a567bd04886887c0846ca81a74086ca75e56636ccfd90ec16ca0a113f
4
+ data.tar.gz: 7561f624f4360a4977a59941483f917832ab126f48843915216af9a51d0904c6
5
5
  SHA512:
6
- metadata.gz: 89364406f0bddc66568e3e908424699d3cc6daeb8a0b51ab7da94338369ae3a3f6be302f3c7439e23c0e771e41d5ebe0155c8cef71ad4dfa16184d2d516554d6
7
- data.tar.gz: 282091580a65383b954a6b2da783e591d9f4f289e811ee4bc57dd9b61b004c3d3fdfa06f2f000971611b0924059573fd0000d2cf29e96d716fb2fee747f5c434
6
+ metadata.gz: 9b187df8cf8cce75a84187704c069e12b9d10d3e830f42065f26ab0d223d85ca79889c9b9f6b6d0edebd486fba38184ce5ad7047aa26759d39b6715ca9f1f785
7
+ data.tar.gz: 021437d1e18832be6dbc131a60a47c7672d5018d01588a3f6de00cff2571948ca265a3f40b8a4185f6362c054d5f35471b876fabee3bb7da9dc2623c351db7b5
data/CLAUDE.md CHANGED
@@ -21,10 +21,6 @@ Zonesync is a Ruby gem that synchronizes DNS zone files with DNS providers (Clou
21
21
  - `bundle exec zonesync --force` - Force sync ignoring checksum mismatches
22
22
  - `bundle exec zonesync generate` - Generate Zonefile from DNS provider
23
23
 
24
- ### Type Checking (Sorbet)
25
- - `bundle exec srb tc` - Run Sorbet type checker
26
- - `bundle exec tapioca gem` - Generate RBI files for gems
27
-
28
24
  ## Architecture
29
25
 
30
26
  ### Core Components
@@ -73,14 +69,6 @@ Credentials stored in Rails-style encrypted configuration:
73
69
  - **Unit specs** test individual classes and methods
74
70
  - All tests should pass before committing changes
75
71
 
76
- ## Type Safety
77
-
78
- Uses **Sorbet** for gradual typing:
79
- - All files have `# typed: strict` or similar headers
80
- - Method signatures use `sig { ... }` blocks
81
- - `extend T::Sig` enables signature checking
82
- - RBI files in `sorbet/rbi/` define external gem types
83
-
84
72
  ## Error Handling
85
73
 
86
74
  Custom exceptions in `lib/zonesync/errors.rb`:
data/lib/zonesync/cli.rb CHANGED
@@ -1,19 +1,15 @@
1
- # typed: strict
2
- require "sorbet-runtime"
1
+ # frozen_string_literal: true
3
2
 
4
3
  require "thor"
5
4
 
6
5
  module Zonesync
7
6
  class CLI < Thor
8
- extend T::Sig
9
-
10
7
  default_command :sync
11
8
  desc "sync --source=Zonefile --destination=zonesync", "syncs the contents of the Zonefile to the DNS server configured in Rails.application.credentials.zonesync"
12
9
  option :source, default: "Zonefile", desc: "path to the zonefile"
13
10
  option :destination, default: "zonesync", desc: "key to the DNS server configuration in Rails.application.credentials"
14
11
  method_option :dry_run, type: :boolean, default: false, aliases: :n, desc: "log operations to STDOUT but don't perform the sync"
15
12
  method_option :force, type: :boolean, default: false, desc: "ignore checksum mismatches and force the sync"
16
- sig { void }
17
13
  def sync
18
14
  kwargs = options.to_hash.transform_keys(&:to_sym)
19
15
  Zonesync.call(**kwargs)
@@ -25,13 +21,11 @@ module Zonesync
25
21
  desc "generate --source=zonesync --destination=Zonefile", "generates a Zonefile from the DNS server configured in Rails.application.credentials.zonesync"
26
22
  option :source, default: "zonesync", desc: "key to the DNS server configuration in Rails.application.credentials"
27
23
  option :destination, default: "Zonefile", desc: "path to the zonefile"
28
- sig { void }
29
24
  def generate
30
25
  kwargs = options.to_hash.transform_keys(&:to_sym)
31
26
  Zonesync.generate(**kwargs)
32
27
  end
33
28
 
34
- sig { returns(TrueClass) }
35
29
  def self.exit_on_failure? = true
36
30
  end
37
31
  end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Zonesync
4
+ class Cloudflare < Provider
5
+ # Module that adds proxied support to individual Record instances.
6
+ # When extended onto a record, it parses cf_tags=cf-proxied:true/false
7
+ # from the comment and provides a proxied accessor.
8
+ #
9
+ # Semantics:
10
+ # - cf_tags=cf-proxied:true → explicitly enable Cloudflare proxy
11
+ # - cf_tags=cf-proxied:false → explicitly disable Cloudflare proxy
12
+ # - No cf_tags → don't touch proxied (use Cloudflare default)
13
+ module ProxiedSupport
14
+ CF_TAGS_PATTERN = /\bcf_tags=cf-proxied:(true|false)\b/
15
+
16
+ def self.extended(record)
17
+ record.instance_variable_set :@original_comment, record[:comment]
18
+ end
19
+
20
+ def proxied
21
+ @original_comment&.match(CF_TAGS_PATTERN) { |m| m[1] == "true" }
22
+ end
23
+
24
+ def comment
25
+ return @original_comment unless @original_comment&.match?(CF_TAGS_PATTERN)
26
+ cleaned = @original_comment.sub(/\s*#{CF_TAGS_PATTERN}\s*/, " ").strip
27
+ cleaned.empty? ? nil : cleaned
28
+ end
29
+
30
+ def to_h
31
+ super.merge(comment: comment, proxied: proxied)
32
+ end
33
+
34
+ def ==(other)
35
+ other_proxied = other.respond_to?(:proxied) ? other.proxied : nil
36
+ name == other.name &&
37
+ type == other.type &&
38
+ ttl == other.ttl &&
39
+ rdata == other.rdata &&
40
+ comment == other.comment &&
41
+ proxied == other_proxied
42
+ end
43
+ alias eql? ==
44
+
45
+ def hash
46
+ [name, type, ttl, rdata, comment, proxied].hash
47
+ end
48
+
49
+ def to_s
50
+ string = [name, ttl, type, rdata].join(" ")
51
+
52
+ comment_parts = []
53
+ comment_parts << "cf_tags=cf-proxied:true" if proxied == true
54
+ comment_parts << comment if comment
55
+
56
+ string << " ; #{comment_parts.join(' ')}" if comment_parts.any?
57
+ string
58
+ end
59
+ end
60
+ end
61
+ end
@@ -1,31 +1,42 @@
1
- # typed: strict
2
- require "sorbet-runtime"
1
+ # frozen_string_literal: true
3
2
 
4
3
  require "zonesync/record"
5
4
  require "zonesync/http"
5
+ require "zonesync/cloudflare/proxied_support"
6
6
 
7
7
  module Zonesync
8
8
  class Cloudflare < Provider
9
- sig { returns(String) }
10
9
  def read
11
- records = [fake_soa] + all.keys
10
+ records = [fake_soa.extend(ProxiedSupport)] + all.keys
12
11
  records.map(&:to_s).join("\n") + "\n"
13
12
  end
14
13
 
15
- sig { params(record: Record).void }
16
- def remove record
17
- id = all.fetch(record)
14
+ def diff(other)
15
+ source_records = other.diffable_records.map { |r| r.extend(ProxiedSupport) }
16
+ Diff.new(
17
+ from: diffable_records,
18
+ to: source_records,
19
+ )
20
+ end
21
+
22
+ def remove(record)
23
+ id = find_record_id(record)
18
24
  http.delete("/#{id}")
19
25
  end
20
26
 
21
- sig { params(old_record: Record, new_record: Record).void }
22
- def change old_record, new_record
23
- id = all.fetch(old_record)
27
+ def change(old_record, new_record)
28
+ id = find_record_id(old_record)
24
29
  http.patch("/#{id}", to_hash(new_record))
25
30
  end
26
31
 
27
- sig { params(record: Record).void }
28
- def add record
32
+ def find_record_id(record)
33
+ all.each do |existing, id|
34
+ return id if existing.identical_to?(record)
35
+ end
36
+ raise KeyError, "record not found: #{record.inspect}"
37
+ end
38
+
39
+ def add(record)
29
40
  add_with_duplicate_handling(record) do
30
41
  begin
31
42
  http.post("", to_hash(record))
@@ -41,67 +52,71 @@ module Zonesync
41
52
  end
42
53
  end
43
54
 
44
- sig { returns(T::Hash[Record, String]) }
45
55
  def all
46
56
  response = http.get("")
47
57
  response["result"].reduce({}) do |map, attrs|
48
- map.merge to_record(attrs) => attrs["id"]
58
+ map.merge(to_record(attrs) => attrs["id"])
49
59
  end
50
60
  end
51
61
 
52
62
  private
53
63
 
54
- sig { params(record: Record).returns(T::Hash[String, String]) }
55
- def to_hash record
64
+ def to_hash(record)
56
65
  hash = record.to_h
57
66
  content = hash.delete(:rdata)
67
+ proxied = hash.delete(:proxied)
58
68
 
59
69
  if record.type == "MX"
60
70
  # For MX records, split "priority hostname" into separate fields
61
- priority, hostname = T.must(content).split(" ", 2)
71
+ priority, hostname = content.split(" ", 2)
62
72
  hash[:priority] = priority.to_i
63
73
  hash[:content] = hostname.sub(/\.$/, "") # remove trailing dot
64
74
  else
65
75
  hash[:content] = content
66
76
  end
67
77
 
78
+ hash[:proxied] = proxied if proxied != nil
68
79
  hash[:comment] = hash.delete(:comment) # maintain original order
69
80
  hash
70
81
  end
71
82
 
72
- sig { params(attrs: T::Hash[String, String]).returns(Record) }
73
- def to_record attrs
83
+ def to_record(attrs)
74
84
  rdata = attrs["content"]
75
85
  if %w[CNAME MX].include?(attrs["type"])
76
- rdata = normalize_trailing_period(T.must(rdata))
86
+ rdata = normalize_trailing_period(rdata)
77
87
  end
78
88
  if attrs["type"] == "MX"
79
89
  rdata = "#{attrs["priority"]} #{rdata}"
80
90
  end
81
91
  if %w[TXT SPF NAPTR].include?(attrs["type"])
82
- rdata = normalize_quoting(T.must(rdata))
92
+ rdata = normalize_quoting(rdata)
83
93
  end
84
- Record.new(
85
- name: normalize_trailing_period(T.must(attrs["name"])),
94
+
95
+ record = Record.new(
96
+ name: normalize_trailing_period(attrs["name"]),
86
97
  type: attrs["type"],
87
98
  ttl: attrs["ttl"].to_i,
88
- rdata:,
89
- comment: attrs["comment"],
99
+ rdata: rdata,
100
+ comment: comment_with_proxied(attrs["comment"], attrs["proxied"]),
90
101
  )
102
+ record.extend(ProxiedSupport)
91
103
  end
92
104
 
93
- sig { params(value: String).returns(String) }
94
- def normalize_trailing_period value
105
+ def comment_with_proxied(comment, proxied)
106
+ return comment if proxied.nil?
107
+ cf_tag = "cf_tags=cf-proxied:#{proxied}"
108
+ [comment, cf_tag].compact.join(" ")
109
+ end
110
+
111
+ def normalize_trailing_period(value)
95
112
  value =~ /\.$/ ? value : value + "."
96
113
  end
97
114
 
98
- sig { params(value: String).returns(String) }
99
- def normalize_quoting value
115
+ def normalize_quoting(value)
100
116
  value = value =~ /^".+"$/ ? value : %("#{value}") # handle quote wrapping
101
117
  value.gsub('" "', "") # handle multiple txt record joining
102
118
  end
103
119
 
104
- sig { returns(Zonesync::Record) }
105
120
  def fake_soa
106
121
  zone_name = http.get("/..")["result"]["name"]
107
122
  Record.new(
@@ -113,11 +128,10 @@ module Zonesync
113
128
  )
114
129
  end
115
130
 
116
- sig { returns(HTTP) }
117
131
  def http
118
132
  return @http if @http
119
- @http = T.let(HTTP.new("https://api.cloudflare.com/client/v4/zones/#{config.fetch(:zone_id)}/dns_records"), T.nilable(Zonesync::HTTP))
120
- T.must(@http).before_request do |request|
133
+ @http = HTTP.new("https://api.cloudflare.com/client/v4/zones/#{config.fetch(:zone_id)}/dns_records")
134
+ @http.before_request do |request|
121
135
  request["Content-Type"] = "application/json"
122
136
  if config[:token]
123
137
  request["Authorization"] = "Bearer #{config[:token]}"
@@ -126,8 +140,7 @@ module Zonesync
126
140
  request["X-Auth-Key"] = config.fetch(:key)
127
141
  end
128
142
  end
129
- T.must(@http)
143
+ @http
130
144
  end
131
145
  end
132
146
  end
133
-
data/lib/zonesync/diff.rb CHANGED
@@ -1,18 +1,11 @@
1
- # typed: strict
2
- require "sorbet-runtime"
1
+ # frozen_string_literal: true
3
2
 
4
3
  module Zonesync
5
- Operation = T.type_alias { [Symbol, T::Array[Record]] }
6
-
7
4
  Diff = Struct.new(:from, :to) do
8
- extend T::Sig
9
-
10
- sig { params(from: T::Array[Record], to: T::Array[Record]).returns(T.untyped) }
11
5
  def self.call(from:, to:)
12
6
  new(from, to).call
13
7
  end
14
8
 
15
- sig { returns(T::Array[[Symbol, T::Array[Record]]]) }
16
9
  def call
17
10
  # Group records by their primary key (name + type)
18
11
  from_by_key = from.group_by { |r| [r.name, r.type] }
@@ -58,4 +51,3 @@ module Zonesync
58
51
  end
59
52
  end
60
53
  end
61
-
@@ -1,44 +1,33 @@
1
- # typed: strict
2
- require "sorbet-runtime"
1
+ # frozen_string_literal: true
2
+
3
3
  require "zonesync/record_hash"
4
4
 
5
5
  module Zonesync
6
6
  class ValidationError < StandardError
7
- extend T::Sig
8
-
9
- sig { void }
10
7
  def initialize
11
- @errors = T.let([], T::Array[StandardError])
8
+ @errors = []
12
9
  end
13
10
 
14
- sig { params(error: StandardError).void }
15
11
  def add(error)
16
12
  @errors << error
17
13
  end
18
14
 
19
- sig { returns(T::Boolean) }
20
15
  def any?
21
16
  @errors.any?
22
17
  end
23
18
 
24
- sig { returns(String) }
25
19
  def message
26
20
  @errors.map(&:message).join("\n\n#{'-' * 60}\n\n")
27
21
  end
28
22
 
29
- sig { returns(T::Array[StandardError]) }
30
23
  attr_reader :errors
31
24
  end
32
25
 
33
26
  class ConflictError < StandardError
34
- extend T::Sig
35
-
36
- sig { params(conflicts: T::Array[[T.nilable(Record), Record]]).void }
37
27
  def initialize(conflicts)
38
28
  @conflicts = conflicts
39
29
  end
40
30
 
41
- sig { returns(String) }
42
31
  def message
43
32
  conflicts_text = @conflicts.sort_by { |_existing, new_rec| new_rec.name }.map do |existing_rec, new_rec|
44
33
  " existing: #{existing_rec}\n new: #{new_rec}"
@@ -56,14 +45,10 @@ module Zonesync
56
45
  end
57
46
 
58
47
  class MissingManifestError < StandardError
59
- extend T::Sig
60
-
61
- sig { params(manifest: Record).void }
62
- def initialize manifest
48
+ def initialize(manifest)
63
49
  @manifest = manifest
64
50
  end
65
51
 
66
- sig { returns(String) }
67
52
  def message
68
53
  <<~MSG
69
54
  The zonesync_manifest TXT record is missing. If this is the very first sync, make sure the Zonefile matches what's on the DNS server exactly. Otherwise, someone else may have removed it.
@@ -73,17 +58,6 @@ module Zonesync
73
58
  end
74
59
 
75
60
  class ChecksumMismatchError < StandardError
76
- extend T::Sig
77
-
78
- sig {
79
- params(
80
- existing: T.nilable(Record),
81
- new: T.nilable(Record),
82
- expected_record: T.nilable(Record),
83
- actual_record: T.nilable(Record),
84
- missing_hash: T.nilable(String)
85
- ).void
86
- }
87
61
  def initialize(existing = nil, new = nil, expected_record: nil, actual_record: nil, missing_hash: nil)
88
62
  @existing = existing
89
63
  @new = new
@@ -92,7 +66,6 @@ module Zonesync
92
66
  @missing_hash = missing_hash
93
67
  end
94
68
 
95
- sig { returns(String) }
96
69
  def message
97
70
  # V2 manifest integrity violation
98
71
  if @missing_hash
@@ -109,7 +82,6 @@ module Zonesync
109
82
 
110
83
  private
111
84
 
112
- sig { returns(String) }
113
85
  def generate_v2_message
114
86
  if @expected_record && @actual_record
115
87
  # Record was modified
@@ -143,23 +115,17 @@ module Zonesync
143
115
  end
144
116
 
145
117
  class DuplicateRecordError < StandardError
146
- extend T::Sig
147
-
148
- sig { params(record: Record, provider_message: T.nilable(String)).void }
149
- def initialize record, provider_message = nil
118
+ def initialize(record, provider_message = nil)
150
119
  @record = record
151
120
  @provider_message = provider_message
152
121
  end
153
122
 
154
- sig { returns(String) }
155
123
  def message
156
124
  msg = "Record already exists: #{@record.name} #{@record.type}"
157
125
  msg += " (#{@provider_message})" if @provider_message
158
126
  msg
159
127
  end
160
128
 
161
- sig { returns(Record) }
162
129
  attr_reader :record
163
130
  end
164
131
  end
165
-
@@ -1,11 +1,7 @@
1
- # typed: strict
2
- require "sorbet-runtime"
1
+ # frozen_string_literal: true
3
2
 
4
3
  module Zonesync
5
4
  Generate = Struct.new(:source, :destination) do
6
- extend T::Sig
7
-
8
- sig { void }
9
5
  def call
10
6
  destination.write(source.read)
11
7
  nil
data/lib/zonesync/http.rb CHANGED
@@ -1,52 +1,41 @@
1
- # typed: strict
2
- require "sorbet-runtime"
1
+ # frozen_string_literal: true
3
2
 
4
3
  require "net/http"
5
4
  require "json"
6
5
 
7
6
  module Zonesync
8
7
  HTTP = Struct.new(:base) do
9
- extend T::Sig
10
-
11
- sig { params(base: String).void }
12
8
  def initialize(base)
13
9
  super
14
- @before_request = T.let([], T::Array[T.untyped])
15
- @after_response = T.let([], T::Array[T.untyped])
10
+ @before_request = []
11
+ @after_response = []
16
12
  end
17
13
 
18
- sig { params(path: String).returns(T.untyped) }
19
- def get path
14
+ def get(path)
20
15
  request("get", path)
21
16
  end
22
17
 
23
- sig { params(path: String, body: T.untyped).returns(T.untyped) }
24
- def post path, body
18
+ def post(path, body)
25
19
  request("post", path, body)
26
20
  end
27
21
 
28
- sig { params(path: String, body: T.untyped).returns(T.untyped) }
29
- def patch path, body
22
+ def patch(path, body)
30
23
  request("patch", path, body)
31
24
  end
32
25
 
33
- sig { params(path: String).returns(T.untyped) }
34
- def delete path
26
+ def delete(path)
35
27
  request("delete", path)
36
28
  end
37
29
 
38
- sig { params(block: T.proc.params(arg0: T.untyped, arg1: T.untyped, arg2: T.untyped).void).void }
39
- def before_request &block
30
+ def before_request(&block)
40
31
  @before_request << block
41
32
  end
42
33
 
43
- sig { params(block: T.proc.params(arg0: T.untyped).void).void }
44
- def after_response &block
34
+ def after_response(&block)
45
35
  @after_response << block
46
36
  end
47
37
 
48
- sig { params(method: String, path: String, body: T.untyped).returns(T.untyped) }
49
- def request method, path, body=nil
38
+ def request(method, path, body = nil)
50
39
  uri = URI.parse("#{base}#{path}")
51
40
  request = Net::HTTP.const_get(method.to_s.capitalize).new(uri.path)
52
41
 
@@ -1,15 +1,11 @@
1
- # typed: strict
2
- require "sorbet-runtime"
1
+ # frozen_string_literal: true
3
2
 
4
3
  require "logger"
5
4
  require "fileutils"
6
5
 
7
6
  module Zonesync
8
7
  class Logger
9
- extend T::Sig
10
-
11
- sig { params(method: Symbol, records: T::Array[Record], dry_run: T::Boolean).void }
12
- def self.log method, records, dry_run: false
8
+ def self.log(method, records, dry_run: false)
13
9
  loggers = [::Logger.new(STDOUT)]
14
10
 
15
11
  if !dry_run
@@ -26,4 +22,3 @@ module Zonesync
26
22
  end
27
23
  end
28
24
  end
29
-
@@ -1,5 +1,4 @@
1
- # typed: strict
2
- require "sorbet-runtime"
1
+ # frozen_string_literal: true
3
2
 
4
3
  require "zonesync/record"
5
4
  require "zonesync/record_hash"
@@ -7,31 +6,24 @@ require "digest"
7
6
 
8
7
  module Zonesync
9
8
  Manifest = Struct.new(:records, :zone) do
10
- extend T::Sig
11
- DIFFABLE_RECORD_TYPES =
12
- T.let(%w[A AAAA CNAME MX TXT SPF NAPTR PTR].sort, T::Array[String])
9
+ DIFFABLE_RECORD_TYPES = %w[A AAAA CNAME MX TXT SPF NAPTR PTR].sort
13
10
 
14
- sig { returns(T.nilable(Zonesync::Record)) }
15
11
  def existing
16
12
  records.find(&:manifest?)
17
13
  end
18
14
 
19
- sig { returns(T::Boolean) }
20
15
  def existing?
21
16
  !!existing
22
17
  end
23
18
 
24
- sig { returns(Zonesync::Record) }
25
19
  def generate
26
20
  generate_v2
27
21
  end
28
22
 
29
- sig { returns(T.nilable(Zonesync::Record)) }
30
23
  def existing_checksum
31
24
  records.find(&:checksum?)
32
25
  end
33
26
 
34
- sig { returns(Zonesync::Record) }
35
27
  def generate_checksum
36
28
  input_string = diffable_records.map(&:to_s).join
37
29
  sha256 = Digest::SHA256.hexdigest(input_string)
@@ -44,7 +36,6 @@ module Zonesync
44
36
  )
45
37
  end
46
38
 
47
- sig { returns(Zonesync::Record) }
48
39
  def generate_v2
49
40
  hashes = diffable_records.map { |record| RecordHash.generate(record) }
50
41
  Record.new(
@@ -56,8 +47,7 @@ module Zonesync
56
47
  )
57
48
  end
58
49
 
59
- sig { params(record: Zonesync::Record).returns(T::Boolean) }
60
- def diffable? record
50
+ def diffable?(record)
61
51
  if existing?
62
52
  matches?(record)
63
53
  else
@@ -65,24 +55,21 @@ module Zonesync
65
55
  end
66
56
  end
67
57
 
68
- sig { returns(T::Boolean) }
69
58
  def v1_format?
70
59
  return false unless existing?
71
- manifest_data = T.must(existing).rdata[1..-2]
60
+ manifest_data = existing.rdata[1..-2]
72
61
  # V1 format uses "TYPE:" syntax, v2 uses comma-separated hashes
73
62
  manifest_data.include?(":") || manifest_data.include?(";")
74
63
  end
75
64
 
76
- sig { returns(T::Boolean) }
77
65
  def v2_format?
78
66
  return false unless existing?
79
67
  !v1_format?
80
68
  end
81
69
 
82
- sig { params(record: Zonesync::Record).returns(T::Boolean) }
83
- def matches? record
70
+ def matches?(record)
84
71
  return false unless existing?
85
- manifest_data = T.must(existing).rdata[1..-2] # remove quotes
72
+ manifest_data = existing.rdata[1..-2] # remove quotes
86
73
 
87
74
  # Check if this is v2 format (comma-separated hashes) or v1 format (type:names)
88
75
  if manifest_data.include?(";")
@@ -104,8 +91,7 @@ module Zonesync
104
91
  end
105
92
  end
106
93
 
107
- sig { params(record: Zonesync::Record, with_type: T::Boolean).returns(String) }
108
- def shorthand_for record, with_type: false
94
+ def shorthand_for(record, with_type: false)
109
95
  shorthand = record.short_name(zone.origin)
110
96
  shorthand = "#{record.type}:#{shorthand}" if with_type
111
97
  if record.type == "MX"
@@ -116,21 +102,18 @@ module Zonesync
116
102
 
117
103
  private
118
104
 
119
- sig { returns(String) }
120
105
  def generate_rdata
121
106
  generate_manifest.map do |type, short_names|
122
107
  "#{type}:#{short_names.join(",")}"
123
108
  end.join(";").inspect
124
109
  end
125
110
 
126
- sig { returns(T::Array[Zonesync::Record]) }
127
111
  def diffable_records
128
112
  records.select do |record|
129
113
  diffable?(record)
130
114
  end.sort
131
115
  end
132
116
 
133
- sig { returns(T::Hash[String, T::Array[String]]) }
134
117
  def generate_manifest
135
118
  hash = diffable_records.reduce({}) do |hash, record|
136
119
  hash[record.type] ||= []