cron_to_go_sync 0.0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 6d51ed36d2923293ce7c72f8dff71b16f2703adda73bd9769e1734ce474b4ec8
4
+ data.tar.gz: 80cb621f80dc461a634c94a3df036d765e45d5bd97de7eb0e380fc8d51d98a0a
5
+ SHA512:
6
+ metadata.gz: d9b43e4dd3dc46960ad077c9e47507e21c1019cf05071656101653efee5f4f896cb65f89cb4887ef51a145038975ea8c7715c32b098cdc52086496c8a6a4c554
7
+ data.tar.gz: c014625e1f23df03a36164cfb4995f4b70a2f3bce2eadc40b7350b410b2891029a3a7ed3ee22a13f90afefb95f66fa340d016f1a110b52d84a4e08def4a08a9a
data/.editorconfig ADDED
@@ -0,0 +1,9 @@
1
+ root = true
2
+
3
+ [*]
4
+ end_of_line = lf
5
+ insert_final_newline = true
6
+
7
+ [*.rb]
8
+ indent_style = space
9
+ indent_size = 2
data/.gitattributes ADDED
@@ -0,0 +1,2 @@
1
+ /Gemfile.lock linguist-generated
2
+ spec/cassettes/** linguist-generated
@@ -0,0 +1,29 @@
1
+ # To get started with Dependabot version updates, you'll need to specify which
2
+ # package ecosystems to update and where the package manifests are located.
3
+ # Please see the documentation for all configuration options:
4
+ # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
5
+
6
+ version: 2
7
+ updates:
8
+ - package-ecosystem: "github-actions"
9
+ directory: "/"
10
+ schedule:
11
+ interval: "weekly"
12
+ day: "wednesday"
13
+ labels:
14
+ - ":robot: dependabot"
15
+ - ":octocat: github-actions"
16
+ - ":heavy_plus_sign: dependencies"
17
+ reviewers:
18
+ - instrumentl/code-reviewers
19
+ - package-ecosystem: "bundler"
20
+ directory: "/"
21
+ schedule:
22
+ interval: "weekly"
23
+ day: "wednesday"
24
+ labels:
25
+ - ":robot: dependabot"
26
+ - ":gem: ruby"
27
+ - ":heavy_plus_sign: dependencies"
28
+ reviewers:
29
+ - instrumentl/code-reviewers
@@ -0,0 +1,53 @@
1
+ name: "CI"
2
+ on:
3
+ pull_request:
4
+ push:
5
+ branches: [ main ]
6
+
7
+ jobs:
8
+ test:
9
+ runs-on: ubuntu-latest
10
+ timeout-minutes: 30
11
+ permissions:
12
+ contents: read
13
+ checks: write
14
+ env:
15
+ RAILS_ENV: test
16
+ CRONTOGO_ORGANIZATION_ID: "fake-crontogo-organization-id"
17
+ CRONTOGO_API_KEY: "fake-crontogo-api-key"
18
+ strategy:
19
+ fail-fast: true
20
+ matrix:
21
+ ruby: ["3.0", "3.1", "3.2"]
22
+ steps:
23
+ - name: Checkout code
24
+ uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab
25
+ - name: Install Ruby and gems
26
+ uses: ruby/setup-ruby@8a45918450651f5e4784b6031db26f4b9f76b251
27
+ with:
28
+ bundler-cache: true
29
+ ruby-version: ${{ matrix.ruby }}
30
+ - name: Run RSpec Tests
31
+ timeout-minutes: 20
32
+ run: bundle exec rspec -f doc
33
+ lint:
34
+ runs-on: ubuntu-latest
35
+ timeout-minutes: 20
36
+ permissions:
37
+ contents: read
38
+ steps:
39
+ - name: Checkout code
40
+ uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab
41
+ - name: Install Ruby and gems
42
+ uses: ruby/setup-ruby@8a45918450651f5e4784b6031db26f4b9f76b251
43
+ with:
44
+ bundler-cache: true
45
+ ruby-version: "3.2"
46
+ - name: Bundle Audit Check
47
+ run: bundle exec bundle-audit update && bundle exec bundle-audit check
48
+ - name: Setup Python
49
+ uses: actions/setup-python@bd6b4b6205c4dbad673328db7b31b7fab9e241c0
50
+ with:
51
+ python-version: "3.10"
52
+ - name: Run pre-commit
53
+ uses: pre-commit/action@5f528da5c95691c4cf42ff76a4d10854b62cbb82
@@ -0,0 +1,25 @@
1
+ name: Release to RubyGems
2
+ on:
3
+ release:
4
+ types: [published]
5
+ jobs:
6
+ release:
7
+ runs-on: ubuntu-latest
8
+ steps:
9
+ - name: Checkout code
10
+ uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab
11
+ - name: Install Ruby and gems
12
+ uses: ruby/setup-ruby@8a45918450651f5e4784b6031db26f4b9f76b251
13
+ with:
14
+ bundler-cache: true
15
+ ruby-version: "3.2"
16
+ - name: Publish gem
17
+ run: |
18
+ umask 077
19
+ mkdir -p "$HOME/.gem"
20
+ printf -- "---\n:rubygems_api_key: ${RUBYGEMS_API_KEY}\n" > $HOME/.gem/credentials
21
+ gem build *.gemspec
22
+ gem push *.gem
23
+ rm -f "$HOME/.gem/credentials"
24
+ env:
25
+ RUBYGEMS_API_KEY: "${{secrets.RUBYGEMS_PUSH_API_KEY}}"
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ .bundle
2
+ .env
3
+ *.gem
@@ -0,0 +1,18 @@
1
+ repos:
2
+ - repo: https://github.com/pre-commit/pre-commit-hooks
3
+ rev: v3.2.0
4
+ hooks:
5
+ - id: trailing-whitespace
6
+ - id: end-of-file-fixer
7
+ exclude: '^(config/secrets.yml.enc$)|(spec/data/)|(db/.*\.sql$)'
8
+ - id: check-yaml
9
+ exclude: '.rubocop.yml'
10
+ - id: check-added-large-files
11
+ - id: check-byte-order-marker
12
+ exclude: '^spec/data/'
13
+ - id: check-merge-conflict
14
+ - repo: https://github.com/rubocop/rubocop.git
15
+ rev: '675114c1b5a1999a112f8adaad8c99f8de6f7bcc'
16
+ hooks:
17
+ - id: rubocop
18
+ additional_dependencies: ["standard:1.25.3"]
data/.rubocop.yml ADDED
@@ -0,0 +1,9 @@
1
+ require: standard
2
+ inherit_gem:
3
+ standard: config/base.yml
4
+ AllCops:
5
+ TargetRubyVersion: 3.2
6
+ Exclude:
7
+ - 'vendor/bundle/**/*'
8
+ - 'tmp/**/*'
9
+ - 'public/**/*'
data/.standard.yml ADDED
@@ -0,0 +1,3 @@
1
+ parallel: true
2
+ format: progress
3
+ ruby_version: 3.2.0
data/CHANGELOG.md ADDED
@@ -0,0 +1,3 @@
1
+ 0.0.1
2
+ -----
3
+ - Initial release
data/Gemfile ADDED
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+ git_source(:github) { |repo| "https://github.com/#{repo}.git" }
5
+
6
+ gemspec
7
+
8
+ group :development, :test do
9
+ gem "rspec", "~> 3.0"
10
+ gem "rspec-its", "~>1.3"
11
+ gem "standard", "~> 1.28.3"
12
+ gem "pry", "~> 0.14"
13
+ gem "webmock", "~> 3"
14
+ gem "vcr", "~> 6"
15
+ gem "bundle-audit", "~> 0.1.0"
16
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,171 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ cron_to_go_sync (0.0.1)
5
+ activesupport
6
+ dry-types
7
+ dry-validation
8
+ faraday (>= 1.0, < 3.0)
9
+ faraday-follow_redirects
10
+ faraday-retry
11
+ interactor
12
+ interactor-contracts
13
+ toml-rb
14
+
15
+ GEM
16
+ remote: https://rubygems.org/
17
+ specs:
18
+ activesupport (7.0.5)
19
+ concurrent-ruby (~> 1.0, >= 1.0.2)
20
+ i18n (>= 1.6, < 2)
21
+ minitest (>= 5.1)
22
+ tzinfo (~> 2.0)
23
+ addressable (2.8.4)
24
+ public_suffix (>= 2.0.2, < 6.0)
25
+ ast (2.4.2)
26
+ bundle-audit (0.1.0)
27
+ bundler-audit
28
+ bundler-audit (0.9.1)
29
+ bundler (>= 1.2.0, < 3)
30
+ thor (~> 1.0)
31
+ citrus (3.0.2)
32
+ coderay (1.1.3)
33
+ concurrent-ruby (1.2.2)
34
+ crack (0.4.5)
35
+ rexml
36
+ diff-lcs (1.5.0)
37
+ dry-configurable (1.0.1)
38
+ dry-core (~> 1.0, < 2)
39
+ zeitwerk (~> 2.6)
40
+ dry-core (1.0.0)
41
+ concurrent-ruby (~> 1.0)
42
+ zeitwerk (~> 2.6)
43
+ dry-inflector (1.0.0)
44
+ dry-initializer (3.1.1)
45
+ dry-logic (1.5.0)
46
+ concurrent-ruby (~> 1.0)
47
+ dry-core (~> 1.0, < 2)
48
+ zeitwerk (~> 2.6)
49
+ dry-schema (1.13.1)
50
+ concurrent-ruby (~> 1.0)
51
+ dry-configurable (~> 1.0, >= 1.0.1)
52
+ dry-core (~> 1.0, < 2)
53
+ dry-initializer (~> 3.0)
54
+ dry-logic (>= 1.4, < 2)
55
+ dry-types (>= 1.7, < 2)
56
+ zeitwerk (~> 2.6)
57
+ dry-types (1.7.1)
58
+ concurrent-ruby (~> 1.0)
59
+ dry-core (~> 1.0)
60
+ dry-inflector (~> 1.0)
61
+ dry-logic (~> 1.4)
62
+ zeitwerk (~> 2.6)
63
+ dry-validation (1.10.0)
64
+ concurrent-ruby (~> 1.0)
65
+ dry-core (~> 1.0, < 2)
66
+ dry-initializer (~> 3.0)
67
+ dry-schema (>= 1.12, < 2)
68
+ zeitwerk (~> 2.6)
69
+ faraday (2.7.5)
70
+ faraday-net_http (>= 2.0, < 3.1)
71
+ ruby2_keywords (>= 0.0.4)
72
+ faraday-follow_redirects (0.3.0)
73
+ faraday (>= 1, < 3)
74
+ faraday-net_http (3.0.2)
75
+ faraday-retry (2.1.0)
76
+ faraday (~> 2.0)
77
+ hashdiff (1.0.1)
78
+ i18n (1.13.0)
79
+ concurrent-ruby (~> 1.0)
80
+ interactor (3.1.2)
81
+ interactor-contracts (0.3.0)
82
+ dry-validation (~> 1.0)
83
+ interactor (~> 3)
84
+ json (2.6.3)
85
+ language_server-protocol (3.17.0.3)
86
+ lint_roller (1.0.0)
87
+ method_source (1.0.0)
88
+ minitest (5.18.0)
89
+ parallel (1.23.0)
90
+ parser (3.2.2.1)
91
+ ast (~> 2.4.1)
92
+ pry (0.14.2)
93
+ coderay (~> 1.1)
94
+ method_source (~> 1.0)
95
+ public_suffix (5.0.1)
96
+ rainbow (3.1.1)
97
+ regexp_parser (2.8.0)
98
+ rexml (3.2.5)
99
+ rspec (3.12.0)
100
+ rspec-core (~> 3.12.0)
101
+ rspec-expectations (~> 3.12.0)
102
+ rspec-mocks (~> 3.12.0)
103
+ rspec-core (3.12.2)
104
+ rspec-support (~> 3.12.0)
105
+ rspec-expectations (3.12.3)
106
+ diff-lcs (>= 1.2.0, < 2.0)
107
+ rspec-support (~> 3.12.0)
108
+ rspec-its (1.3.0)
109
+ rspec-core (>= 3.0.0)
110
+ rspec-expectations (>= 3.0.0)
111
+ rspec-mocks (3.12.5)
112
+ diff-lcs (>= 1.2.0, < 2.0)
113
+ rspec-support (~> 3.12.0)
114
+ rspec-support (3.12.0)
115
+ rubocop (1.50.2)
116
+ json (~> 2.3)
117
+ parallel (~> 1.10)
118
+ parser (>= 3.2.0.0)
119
+ rainbow (>= 2.2.2, < 4.0)
120
+ regexp_parser (>= 1.8, < 3.0)
121
+ rexml (>= 3.2.5, < 4.0)
122
+ rubocop-ast (>= 1.28.0, < 2.0)
123
+ ruby-progressbar (~> 1.7)
124
+ unicode-display_width (>= 2.4.0, < 3.0)
125
+ rubocop-ast (1.28.1)
126
+ parser (>= 3.2.1.0)
127
+ rubocop-performance (1.16.0)
128
+ rubocop (>= 1.7.0, < 2.0)
129
+ rubocop-ast (>= 0.4.0)
130
+ ruby-progressbar (1.13.0)
131
+ ruby2_keywords (0.0.5)
132
+ standard (1.28.3)
133
+ language_server-protocol (~> 3.17.0.2)
134
+ lint_roller (~> 1.0)
135
+ rubocop (~> 1.50.2)
136
+ standard-custom (~> 1.0.0)
137
+ standard-performance (~> 1.0.1)
138
+ standard-custom (1.0.0)
139
+ lint_roller (~> 1.0)
140
+ standard-performance (1.0.1)
141
+ lint_roller (~> 1.0)
142
+ rubocop-performance (~> 1.16.0)
143
+ thor (1.2.2)
144
+ toml-rb (2.2.0)
145
+ citrus (~> 3.0, > 3.0)
146
+ tzinfo (2.0.6)
147
+ concurrent-ruby (~> 1.0)
148
+ unicode-display_width (2.4.2)
149
+ vcr (6.1.0)
150
+ webmock (3.18.1)
151
+ addressable (>= 2.8.0)
152
+ crack (>= 0.3.2)
153
+ hashdiff (>= 0.4.0, < 2.0.0)
154
+ zeitwerk (2.6.8)
155
+
156
+ PLATFORMS
157
+ arm64-darwin-22
158
+ x86_64-linux
159
+
160
+ DEPENDENCIES
161
+ bundle-audit (~> 0.1.0)
162
+ cron_to_go_sync!
163
+ pry (~> 0.14)
164
+ rspec (~> 3.0)
165
+ rspec-its (~> 1.3)
166
+ standard (~> 1.28.3)
167
+ vcr (~> 6)
168
+ webmock (~> 3)
169
+
170
+ BUNDLED WITH
171
+ 2.4.13
data/LICENSE.txt ADDED
@@ -0,0 +1,5 @@
1
+ Copyright 2023 Instrumentl, Inc.
2
+
3
+ Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
4
+
5
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,6 @@
1
+ This repository contains a Ruby library for synchronizing a checked-in YAML file with [CronToGo](https://crontogo.com/).
2
+
3
+ CronToGo is a trademark of Crazy Ant Labs; this library is an independent work and is not endorsed by or maintained
4
+ by Crazy Ant Labs.
5
+
6
+ This work is licensed under the ISC license, a copy of which can be found at [LICENSE.txt](LICENSE.txt)
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/cron_to_go_sync/version"
4
+
5
+ Gem::Specification.new do |gem|
6
+ gem.name = "cron_to_go_sync"
7
+ gem.version = CronToGoSync::VERSION
8
+ gem.authors = ["Instrumentl, Inc."]
9
+ gem.email = ["oss@instrumentl.com"]
10
+ gem.description = <<-EOF
11
+ Library to synchronize a checked-in YAML file with the SaaS scheduled task service CronToGo
12
+ EOF
13
+ gem.summary = "CronToGo configuration syncing library"
14
+ gem.homepage = "https://github.com/instrumentl/cron_to_go_sync-rb"
15
+ gem.license = "ISC"
16
+ gem.files = `git ls-files`.split($/)
17
+ gem.require_paths = ["lib"]
18
+ gem.metadata["homepage_uri"] = gem.homepage
19
+ gem.metadata["source_code_uri"] = gem.homepage
20
+ gem.metadata["changelog_uri"] = "https://github.com/instrumentl/cron_to_go_sync-rb/blob/main/CHANGELOG.md"
21
+ gem.required_ruby_version = ">= 3.0.0"
22
+
23
+ gem.add_dependency "activesupport"
24
+ gem.add_dependency "dry-types"
25
+ gem.add_dependency "dry-validation"
26
+ gem.add_dependency "faraday", ">= 1.0", "< 3.0"
27
+ gem.add_dependency "faraday-follow_redirects"
28
+ gem.add_dependency "faraday-retry"
29
+ gem.add_dependency "interactor"
30
+ gem.add_dependency "interactor-contracts"
31
+ gem.add_dependency "toml-rb"
32
+ end
@@ -0,0 +1,104 @@
1
+ require "dry-types"
2
+ require "dry-validation"
3
+ require "toml-rb"
4
+
5
+ module CronToGoSync
6
+ module Parser
7
+ module Types
8
+ include Dry::Types()
9
+
10
+ State = Types::String.enum("enabled", "paused")
11
+ DynoSize = Types::String.enum("Eco", "Basic", "Standard-1X", "Standard-2X", "Performance-M", "Performance-L")
12
+ end
13
+
14
+ class JobContract < Dry::Validation::Contract
15
+ params do
16
+ required(:alias).filled(:string)
17
+ required(:enabled).filled(:bool)
18
+ required(:schedule).filled(:string)
19
+ required(:ttl).value(:integer)
20
+ required(:dyno_size).value(Types::DynoSize)
21
+ required(:command).filled(:string)
22
+ end
23
+
24
+ rule(:schedule) do
25
+ fields = value.split(" ")
26
+ unless fields.size == 5
27
+ key.failure("must contain exactly five fields")
28
+ next
29
+ end
30
+ unless (error = valid_cron_field(fields[0], 0, 59)).nil?
31
+ key.failure("in minute: #{error}")
32
+ end
33
+ unless (error = valid_cron_field(fields[1], 0, 23)).nil?
34
+ key.failure("in hour: #{error}")
35
+ end
36
+ unless (error = valid_cron_field(fields[2], 1, 31)).nil?
37
+ key.failure("in day of month: #{error}")
38
+ end
39
+ unless (error = valid_cron_field(fields[3], 1, 12)).nil?
40
+ key.failure("in month: #{error}")
41
+ end
42
+ unless (error = valid_cron_field(fields[4], 0, 7)).nil?
43
+ key.failure("in day of week: #{error}")
44
+ end
45
+ end
46
+
47
+ private
48
+
49
+ def valid_cron_field(value, lower_limit, upper_limit)
50
+ parts = value.split("/")
51
+ if parts.size == 1
52
+ numerator = parts[0]
53
+ elsif parts.size == 2
54
+ numerator, denominator = parts
55
+ return "invalid denominator" unless denominator.match?(/\A[0-9]+\z/)
56
+ denominator = denominator.to_i
57
+ return "invalid denominator value" if denominator < lower_limit || denominator > upper_limit
58
+ else
59
+ return "too many characters"
60
+ end
61
+
62
+ return if numerator == "*"
63
+
64
+ # the numerator is a comma-separated list of ranges
65
+ numerator.split(",").each do |range|
66
+ if range.count("-") == 1
67
+ lower, upper = range.split("-")
68
+ lower = lower.to_i
69
+ upper = upper.to_i
70
+ return "invalid range lower #{lower}" if lower < lower_limit || lower > upper_limit
71
+ return "invalid range upper #{upper}" if upper < upper_limit || upper > upper_limit
72
+ elsif !range.match?(/\A[0-9]+\z/)
73
+ return "invalid range value #{range}"
74
+ else
75
+ value = range.to_i
76
+ return "invalid range value #{value}" if value < lower_limit || value > upper_limit
77
+ end
78
+ end
79
+ nil
80
+ end
81
+ end
82
+
83
+ # Parse a hash of {alias => properties}
84
+ def self.parse_hash(hash)
85
+ contract = JobContract.new
86
+ hash.map do |key, value|
87
+ res = contract.call(
88
+ alias: key, schedule: value[:schedule], dyno_size: value[:dyno_size],
89
+ command: value[:command], ttl: value[:ttl], enabled: value.fetch(:enabled, true)
90
+ )
91
+ if res.errors.present?
92
+ raise "Invalid schedule for #{key}: #{res.errors.to_h}"
93
+ end
94
+ [key, res.to_h]
95
+ end.to_h
96
+ end
97
+
98
+ def self.parse_toml_file(path)
99
+ hash = TomlRB.load_file(path).with_indifferent_access
100
+ defaults = hash.delete("DEFAULTS")
101
+ parse_hash(hash.transform_values { defaults.merge(_1) })
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,127 @@
1
+ require "faraday"
2
+ require "faraday/follow_redirects"
3
+ require "faraday/retry"
4
+ require "interactor"
5
+ require "interactor-contracts"
6
+
7
+ module CronToGoSync
8
+ class SyncInteractor
9
+ include ::Interactor
10
+ include ::Interactor::Contracts
11
+
12
+ BASE_URL = "https://api.crontogo.com".freeze
13
+
14
+ expects do
15
+ required(:api_key).value(:string)
16
+ required(:organization_id).value(:string)
17
+ required(:input_filename).value(:string)
18
+ optional(:timeout).value(:float)
19
+ optional(:retries).value(:integer)
20
+ end
21
+
22
+ before :set_variables
23
+
24
+ def call
25
+ unless File.exist? context.input_filename
26
+ context.fail!(error: "No such file found", filename: context.input_filename)
27
+ end
28
+ target_jobs = Parser.parse_toml_file(context.input_filename)
29
+ existing_jobs = get_jobs_from_crontogo
30
+ context.deleted = (existing_jobs.keys.to_set - target_jobs.keys.to_set).map do |alias_to_remove|
31
+ delete_job existing_jobs[alias_to_remove][:id]
32
+ end.size
33
+ context.created = (target_jobs.keys.to_set - existing_jobs.keys.to_set).map do |alias_to_add|
34
+ create_job target_jobs[alias_to_add]
35
+ end.size
36
+ context.updated = (target_jobs.keys.to_set & existing_jobs.keys.to_set).sum do |alias_to_update|
37
+ existing = existing_jobs[alias_to_update]
38
+ target = target_jobs[alias_to_update]
39
+ if target.merge({id: existing[:id]}) != existing
40
+ update_job(target, existing[:id])
41
+ 1
42
+ else
43
+ 0
44
+ end
45
+ end
46
+ end
47
+
48
+ private
49
+
50
+ def get_jobs_from_crontogo
51
+ client.get(organization_url("/jobs")).body.map do |job|
52
+ [job["Alias"], {
53
+ alias: job["Alias"],
54
+ schedule: job["ScheduleExpression"],
55
+ dyno_size: job["Target"]["Size"],
56
+ command: job["Target"]["Command"],
57
+ ttl: job["Target"]["TimeToLive"],
58
+ enabled: job["State"] == "enabled",
59
+ id: job["Id"]
60
+ }]
61
+ end.to_h
62
+ end
63
+
64
+ def delete_job(id)
65
+ client.delete(organization_url("/jobs/#{id}"))
66
+ end
67
+
68
+ def request_for(struct)
69
+ {
70
+ "Alias" => struct[:alias],
71
+ "ScheduleExpression" => struct[:schedule],
72
+ "ScheduleType" => "cron",
73
+ "Target" => {
74
+ "Type" => "dyno",
75
+ "Size" => struct[:dyno_size],
76
+ "Command" => struct[:command],
77
+ "TimeToLive" => struct[:ttl]
78
+ },
79
+ "State" => struct[:enabled] ? "enabled" : "paused"
80
+ }
81
+ end
82
+
83
+ def create_job(struct)
84
+ client.post(organization_url("/jobs"), request_for(struct))
85
+ end
86
+
87
+ def update_job(struct, id)
88
+ client.patch(organization_url("/jobs/#{id}"), request_for(struct))
89
+ end
90
+
91
+ def set_variables
92
+ context.timeout ||= 10.0
93
+ context.retries ||= 3
94
+ end
95
+
96
+ def api_key
97
+ context.api_key
98
+ end
99
+
100
+ def organization_id
101
+ context.organization_id
102
+ end
103
+
104
+ def url(path)
105
+ "#{BASE_URL}#{path}"
106
+ end
107
+
108
+ def organization_url(path)
109
+ url "/organizations/#{organization_id}#{path}"
110
+ end
111
+
112
+ def client
113
+ @client ||= Faraday.new do |builder|
114
+ builder.headers["Authorization"] = "Bearer #{api_key}"
115
+ builder.options.timeout = context.timeout
116
+ builder.options.open_timeout = context.timeout
117
+ builder.request :json
118
+ builder.response :json
119
+ builder.response :follow_redirects
120
+ builder.response :raise_error
121
+ if context.retries > 0
122
+ builder.request :retry, {max: context.retries}
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,3 @@
1
+ module CronToGoSync
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,6 @@
1
+ require "active_support"
2
+ require "active_support/core_ext"
3
+
4
+ require "cron_to_go_sync/sync_interactor"
5
+ require "cron_to_go_sync/parser"
6
+ require "cron_to_go_sync/version"
@@ -0,0 +1 @@
1
+ {"http_interactions":[{"request":{"method":"get","uri":"https://api.crontogo.com/organizations/<crontogo_organization_id>/jobs","body":{"encoding":"US-ASCII","string":""},"headers":{"Authorization":["<basic auth header>"],"User-Agent":["Faraday v1.10.2"],"Accept-Encoding":["gzip;q=1.0,deflate;q=0.6,identity;q=0.3"],"Accept":["*/*"]}},"response":{"status":{"code":200,"message":"OK"},"headers":{"Date":["Tue, 21 Mar 2023 19:40:09 GMT"],"Content-Type":["application/json"],"Content-Length":["4126"],"Connection":["keep-alive"],"X-Amzn-Requestid":["b4bd6613-07c5-4a9c-a006-e0710adf419d"],"Access-Control-Allow-Origin":["*"],"X-Amz-Apigw-Id":["CJYz-F-6oAMF6hA="],"Access-Control-Expose-Headers":["Link, X-Total-Count"],"X-Amzn-Trace-Id":["Root=1-641a0819-40683a372ddb6288582bbcaf;Sampled=0"]},"body":{"encoding":"ASCII-8BIT","string":"[{\"UpdatedAt\":1679427593355,\"ScheduleType\":\"cron\",\"Timezone\":\"UTC\",\"ScheduleExpression\":\"45 2 * * *\",\"State\":\"enabled\",\"OrganizationId\":\"<crontogo_organization_id>\",\"Alias\":\"bmf:parse_latest\",\"Retries\":2,\"JobExecutionsCount\":0,\"Target\":{\"Type\":\"dyno\",\"TimeToLive\":86400,\"AppId\":\"38277779-272e-46f2-afab-7bd91bf8535e\",\"Command\":\"env STATEMENT_TIMEOUT=120s rake bmf:parse_latest\",\"Size\":\"Standard-1X\"},\"Id\":\"eaa0254e-0b99-4918-89ff-bca99e845448\",\"LastAttempt\":null,\"CreatedAt\":1679427593355},{\"UpdatedAt\":1679424153176,\"ScheduleType\":\"cron\",\"Timezone\":\"UTC\",\"ScheduleExpression\":\"0 4 * * 0\",\"State\":\"enabled\",\"OrganizationId\":\"<crontogo_organization_id>\",\"Alias\":\"canonical_taxpayer:maybe_run_non_canonicalized\",\"Retries\":2,\"JobExecutionsCount\":0,\"Target\":{\"Type\":\"dyno\",\"TimeToLive\":86400,\"AppId\":\"38277779-272e-46f2-afab-7bd91bf8535e\",\"Command\":\"env STATEMENT_TIMEOUT=1000s rake canonical_taxpayer:maybe_run_non_canonicalized\",\"Size\":\"Standard-1X\"},\"Id\":\"a6584e57-cd00-4df4-a52d-e4374d17c1de\",\"LastAttempt\":null,\"CreatedAt\":1679424153176},{\"UpdatedAt\":1679424263971,\"ScheduleType\":\"cron\",\"Timezone\":\"UTC\",\"ScheduleExpression\":\"10 7 * * 5\",\"State\":\"enabled\",\"OrganizationId\":\"<crontogo_organization_id>\",\"Alias\":\"awards:populate_problematic_awards\",\"Retries\":2,\"JobExecutionsCount\":0,\"Target\":{\"Type\":\"dyno\",\"TimeToLive\":86400,\"AppId\":\"38277779-272e-46f2-afab-7bd91bf8535e\",\"Command\":\"env STATEMENT_TIMEOUT=1000s rake awards:populate_problematic_awards\",\"Size\":\"Standard-1X\"},\"Id\":\"3068c3e4-23e8-4588-9a3e-9b1c0f5c07b9\",\"LastAttempt\":null,\"CreatedAt\":1679416937522},{\"UpdatedAt\":1679424156282,\"ScheduleType\":\"cron\",\"Timezone\":\"UTC\",\"ScheduleExpression\":\"30 1 * * *\",\"State\":\"enabled\",\"OrganizationId\":\"<crontogo_organization_id>\",\"Alias\":\"irs:process_epostcards\",\"Retries\":2,\"JobExecutionsCount\":0,\"Target\":{\"Type\":\"dyno\",\"TimeToLive\":86400,\"AppId\":\"38277779-272e-46f2-afab-7bd91bf8535e\",\"Command\":\"rake irs:process_epostcards\",\"Size\":\"Standard-1X\"},\"Id\":\"a2ba7e29-176d-4d7d-b315-55400e9959e4\",\"LastAttempt\":null,\"CreatedAt\":1679424156282},{\"UpdatedAt\":1679424151827,\"ScheduleType\":\"cron\",\"Timezone\":\"UTC\",\"ScheduleExpression\":\"0 9 * * *\",\"State\":\"enabled\",\"OrganizationId\":\"<crontogo_organization_id>\",\"Alias\":\"materialized_views:refresh_all\",\"Retries\":2,\"JobExecutionsCount\":0,\"Target\":{\"Type\":\"dyno\",\"TimeToLive\":7200,\"AppId\":\"38277779-272e-46f2-afab-7bd91bf8535e\",\"Command\":\"env STATEMENT_TIMEOUT=1000s rake materialized_views:refresh_all\",\"Size\":\"Standard-1X\"},\"Id\":\"10965d9a-5e9a-4f4d-b661-3efa880aa799\",\"LastAttempt\":null,\"CreatedAt\":1679424151827},{\"UpdatedAt\":1679427595382,\"ScheduleType\":\"cron\",\"Timezone\":\"UTC\",\"ScheduleExpression\":\"10 3 * * 5\",\"State\":\"enabled\",\"OrganizationId\":\"<crontogo_organization_id>\",\"Alias\":\"awards:populate_non_canonicalized_awards\",\"Retries\":2,\"JobExecutionsCount\":0,\"Target\":{\"Type\":\"dyno\",\"TimeToLive\":14400,\"AppId\":\"38277779-272e-46f2-afab-7bd91bf8535e\",\"Command\":\"env STATEMENT_TIMEOUT=1000s rake awards:populate_non_canonicalized_awards\",\"Size\":\"Standard-1X\"},\"Id\":\"fe5fd04b-00b4-46c1-89e4-289b6df4f247\",\"LastAttempt\":null,\"CreatedAt\":1679416994777},{\"UpdatedAt\":1679424155097,\"ScheduleType\":\"cron\",\"Timezone\":\"UTC\",\"ScheduleExpression\":\"10 13 * * *\",\"State\":\"enabled\",\"OrganizationId\":\"<crontogo_organization_id>\",\"Alias\":\"irs:process_revocation_list\",\"Retries\":2,\"JobExecutionsCount\":0,\"Target\":{\"Type\":\"dyno\",\"TimeToLive\":86400,\"AppId\":\"38277779-272e-46f2-afab-7bd91bf8535e\",\"Command\":\"rake irs:process_revocation_list\",\"Size\":\"Standard-1X\"},\"Id\":\"24e1fd36-15f4-4b59-bd5d-05c6f8fd7694\",\"LastAttempt\":null,\"CreatedAt\":1679424155097},{\"UpdatedAt\":1679424150306,\"ScheduleType\":\"cron\",\"Timezone\":\"UTC\",\"ScheduleExpression\":\"0 0 * * *\",\"State\":\"enabled\",\"OrganizationId\":\"<crontogo_organization_id>\",\"Alias\":\"content_stat:perform\",\"Retries\":2,\"JobExecutionsCount\":0,\"Target\":{\"Type\":\"dyno\",\"TimeToLive\":3600,\"AppId\":\"38277779-272e-46f2-afab-7bd91bf8535e\",\"Command\":\"rake content_stat:perform\",\"Size\":\"Standard-1X\"},\"Id\":\"d5d2e441-ca23-4814-ad9d-b677fd3ec3e0\",\"LastAttempt\":null,\"CreatedAt\":1679424150306}]"}},"recorded_at":"Tue, 21 Mar 2023 19:40:09 GMT"},{"request":{"method":"delete","uri":"https://api.crontogo.com/organizations/<crontogo_organization_id>/jobs/eaa0254e-0b99-4918-89ff-bca99e845448","body":{"encoding":"US-ASCII","string":""},"headers":{"Authorization":["<basic auth header>"],"User-Agent":["Faraday v1.10.2"],"Accept-Encoding":["gzip;q=1.0,deflate;q=0.6,identity;q=0.3"],"Accept":["*/*"]}},"response":{"status":{"code":200,"message":"OK"},"headers":{"Date":["Tue, 21 Mar 2023 19:40:09 GMT"],"Content-Type":["application/json"],"Content-Length":["475"],"Connection":["keep-alive"],"X-Amzn-Requestid":["f57dcdb6-c06a-4400-a3b6-5650b7c5f500"],"Access-Control-Allow-Origin":["*"],"X-Amz-Apigw-Id":["CJY0DGcAoAMF_pg="],"Access-Control-Expose-Headers":["Link, X-Total-Count"],"X-Amzn-Trace-Id":["Root=1-641a0819-5012ee9514806d175b5afe1b;Sampled=0"]},"body":{"encoding":"UTF-8","string":"{\"Id\":\"eaa0254e-0b99-4918-89ff-bca99e845448\",\"OrganizationId\":\"<crontogo_organization_id>\",\"State\":\"enabled\",\"Alias\":\"bmf:parse_latest\",\"ScheduleType\":\"cron\",\"ScheduleExpression\":\"45 2 * * *\",\"Timezone\":\"UTC\",\"Retries\":2,\"Target\":{\"Type\":\"dyno\",\"TimeToLive\":86400,\"AppId\":\"38277779-272e-46f2-afab-7bd91bf8535e\",\"Command\":\"env STATEMENT_TIMEOUT=120s rake bmf:parse_latest\",\"Size\":\"Standard-1X\"},\"LastAttempt\":null,\"CreatedAt\":1679427593355,\"UpdatedAt\":1679427593355}"}},"recorded_at":"Tue, 21 Mar 2023 19:40:09 GMT"},{"request":{"method":"post","uri":"https://api.crontogo.com/organizations/<crontogo_organization_id>/jobs","body":{"encoding":"UTF-8","string":"{\"Alias\":\"new\",\"ScheduleExpression\":\"* * * * *\",\"ScheduleType\":\"cron\",\"Target\":{\"Type\":\"dyno\",\"Size\":\"Standard-1X\",\"Command\":\"echo 'coolio'\",\"TimeToLive\":86400},\"State\":\"enabled\"}"},"headers":{"Authorization":["<basic auth header>"],"User-Agent":["Faraday v1.10.2"],"Content-Type":["application/json"],"Accept-Encoding":["gzip;q=1.0,deflate;q=0.6,identity;q=0.3"],"Accept":["*/*"]}},"response":{"status":{"code":201,"message":"Created"},"headers":{"Date":["Tue, 21 Mar 2023 19:40:10 GMT"],"Content-Type":["application/json"],"Content-Length":["426"],"Connection":["keep-alive"],"X-Amzn-Requestid":["9d2d625c-2961-4b27-b967-5c42433c15c3"],"Access-Control-Allow-Origin":["*"],"X-Amz-Apigw-Id":["CJY0KHcDIAMFg6g="],"Access-Control-Expose-Headers":["Link, X-Total-Count"],"X-Amzn-Trace-Id":["Root=1-641a081a-0d58daea2f8fcc096a142d92;Sampled=0"]},"body":{"encoding":"UTF-8","string":"{\"Id\":\"514fdcb1-ae38-4deb-b3cd-501da311e0e9\",\"OrganizationId\":\"<crontogo_organization_id>\",\"State\":\"enabled\",\"Alias\":\"new\",\"ScheduleType\":\"cron\",\"ScheduleExpression\":\"* * * * *\",\"Timezone\":\"UTC\",\"Retries\":2,\"Target\":{\"Type\":\"dyno\",\"Size\":\"Standard-1X\",\"Command\":\"echo 'coolio'\",\"TimeToLive\":86400,\"AppId\":\"38277779-272e-46f2-afab-7bd91bf8535e\"},\"LastAttempt\":null,\"CreatedAt\":1679427610495,\"UpdatedAt\":1679427610495}"}},"recorded_at":"Tue, 21 Mar 2023 19:40:11 GMT"},{"request":{"method":"patch","uri":"https://api.crontogo.com/organizations/<crontogo_organization_id>/jobs/fe5fd04b-00b4-46c1-89e4-289b6df4f247","body":{"encoding":"UTF-8","string":"{\"Alias\":\"awards:populate_non_canonicalized_awards\",\"ScheduleExpression\":\"10 3 * * 6\",\"ScheduleType\":\"cron\",\"Target\":{\"Type\":\"dyno\",\"Size\":\"Standard-1X\",\"Command\":\"env STATEMENT_TIMEOUT=1000s rake awards:populate_non_canonicalized_awards\",\"TimeToLive\":14400},\"State\":\"enabled\"}"},"headers":{"Authorization":["<basic auth header>"],"User-Agent":["Faraday v1.10.2"],"Content-Type":["application/json"],"Accept-Encoding":["gzip;q=1.0,deflate;q=0.6,identity;q=0.3"],"Accept":["*/*"]}},"response":{"status":{"code":200,"message":"OK"},"headers":{"Date":["Tue, 21 Mar 2023 19:40:11 GMT"],"Content-Type":["application/json"],"Content-Length":["524"],"Connection":["keep-alive"],"X-Amzn-Requestid":["8c730638-f16d-4395-82d4-97d449b5eb75"],"Access-Control-Allow-Origin":["*"],"X-Amz-Apigw-Id":["CJY0WGAcIAMFtEA="],"Access-Control-Expose-Headers":["Link, X-Total-Count"],"X-Amzn-Trace-Id":["Root=1-641a081b-5f0571cc116018d2332bb83a;Sampled=0"]},"body":{"encoding":"UTF-8","string":"{\"Id\":\"fe5fd04b-00b4-46c1-89e4-289b6df4f247\",\"OrganizationId\":\"<crontogo_organization_id>\",\"State\":\"enabled\",\"Alias\":\"awards:populate_non_canonicalized_awards\",\"ScheduleType\":\"cron\",\"ScheduleExpression\":\"10 3 * * 6\",\"Timezone\":\"UTC\",\"Retries\":2,\"Target\":{\"Type\":\"dyno\",\"TimeToLive\":14400,\"AppId\":\"38277779-272e-46f2-afab-7bd91bf8535e\",\"Command\":\"env STATEMENT_TIMEOUT=1000s rake awards:populate_non_canonicalized_awards\",\"Size\":\"Standard-1X\"},\"LastAttempt\":null,\"CreatedAt\":1679416994777,\"UpdatedAt\":1679427611930}"}},"recorded_at":"Tue, 21 Mar 2023 19:40:12 GMT"}],"recorded_with":"VCR 6.1.0"}
@@ -0,0 +1,42 @@
1
+ [DEFAULTS]
2
+ dyno_size="Standard-1X"
3
+ ttl=86400
4
+
5
+ ["awards:populate_non_canonicalized_awards"]
6
+ schedule="10 3 * * 6"
7
+ dyno_size="Standard-1X"
8
+ ttl=14400
9
+ command="env STATEMENT_TIMEOUT=1000s rake awards:populate_non_canonicalized_awards"
10
+
11
+ ["awards:populate_problematic_awards"]
12
+ schedule="10 7 * * 5"
13
+ dyno_size="Standard-1X"
14
+ command="env STATEMENT_TIMEOUT=1000s rake awards:populate_problematic_awards"
15
+
16
+ ["content_stat:perform"]
17
+ schedule="0 0 * * *"
18
+ dyno_size="Standard-1X"
19
+ ttl=3600
20
+ command="rake content_stat:perform"
21
+
22
+ ["materialized_views:refresh_all"]
23
+ schedule="0 9 * * *"
24
+ dyno_size="Standard-1X"
25
+ ttl=7200
26
+ command="env STATEMENT_TIMEOUT=1000s rake materialized_views:refresh_all"
27
+
28
+ ["canonical_taxpayer:maybe_run_non_canonicalized"]
29
+ schedule="0 4 * * 0"
30
+ command="env STATEMENT_TIMEOUT=1000s rake canonical_taxpayer:maybe_run_non_canonicalized"
31
+
32
+ ["irs:process_revocation_list"]
33
+ schedule="10 13 * * *"
34
+ command="rake irs:process_revocation_list"
35
+
36
+ ["irs:process_epostcards"]
37
+ schedule="30 1 * * *"
38
+ command="rake irs:process_epostcards"
39
+
40
+ [new]
41
+ schedule="* * * * *"
42
+ command="echo 'coolio'"
@@ -0,0 +1,34 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe CronToGoSync::Parser do
4
+ describe "#parse_hash" do
5
+ it "accepts a valid schedule" do
6
+ input_hash = {"job_name" => {schedule: "* * * * *", enabled: true, ttl: 86400, dyno_size: "Basic", command: "/bin/true"}}
7
+ expect(described_class.parse_hash(input_hash)).to eq({
8
+ "job_name" => {
9
+ alias: "job_name",
10
+ schedule: "* * * * *",
11
+ enabled: true,
12
+ ttl: 86400,
13
+ dyno_size: "Basic",
14
+ command: "/bin/true"
15
+ }
16
+ })
17
+ end
18
+
19
+ it "validates number of fields" do
20
+ input_hash = {"job_name" => {schedule: "* * * *", enabled: true, ttl: 86400, dyno_size: "Basic", command: "/bin/true"}}
21
+ expect { described_class.parse_hash(input_hash) }.to raise_error(RuntimeError, /must contain exactly five fields/)
22
+ end
23
+
24
+ it "validates schedule numerator" do
25
+ input_hash = {"job_name" => {schedule: "* 25 * * *", enabled: true, ttl: 86400, dyno_size: "Basic", command: "/bin/true"}}
26
+ expect { described_class.parse_hash(input_hash) }.to raise_error(RuntimeError, /invalid range value/)
27
+ end
28
+
29
+ it "validates schedule denominator" do
30
+ input_hash = {"job_name" => {schedule: "*/99 * * * *", enabled: true, ttl: 86400, dyno_size: "Basic", command: "/bin/true"}}
31
+ expect { described_class.parse_hash(input_hash) }.to raise_error(RuntimeError, /invalid denominator value/)
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,57 @@
1
+ require "rspec"
2
+ require "rspec/its"
3
+ require "webmock/rspec"
4
+ require "vcr"
5
+
6
+ require "cron_to_go_sync"
7
+
8
+ RSpec.configure do |config|
9
+ config.expect_with :rspec do |expectations|
10
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
11
+ end
12
+
13
+ config.mock_with :rspec do |mocks|
14
+ mocks.verify_partial_doubles = true
15
+ end
16
+
17
+ config.shared_context_metadata_behavior = :apply_to_host_groups
18
+
19
+ WebMock.disable_net_connect!(
20
+ allow_localhost: true
21
+ )
22
+ end
23
+
24
+ class JsonNlSerializer
25
+ class << self
26
+ def file_extension
27
+ "json"
28
+ end
29
+
30
+ def serialize(hash)
31
+ JSON.dump(hash) + "\n"
32
+ end
33
+
34
+ def deserialize(s)
35
+ JSON.parse(s)
36
+ end
37
+ end
38
+ end
39
+
40
+ VCR.configure do |config|
41
+ config.cassette_library_dir = "spec/cassettes"
42
+ config.hook_into :webmock
43
+ config.ignore_localhost = true
44
+ config.ignore_hosts "localhost", "127.0.0.1"
45
+ config.cassette_serializers[:jsonnl] = JsonNlSerializer
46
+ config.default_cassette_options = {
47
+ serialize_with: :jsonnl,
48
+ record: (ENV["CI"] == "true") ? :none : :once
49
+ }
50
+ config.configure_rspec_metadata!
51
+ ["CRONTOGO_API_KEY", "CRONTOGO_ORGANIZATION_ID"].each do |sensitive_env_var|
52
+ config.filter_sensitive_data("<#{sensitive_env_var.downcase}>") { ENV[sensitive_env_var] }
53
+ end
54
+ config.filter_sensitive_data("<basic auth header>") do |interaction|
55
+ interaction.request.headers["Authorization"]&.first&.sub("Basic ", "")
56
+ end
57
+ end
@@ -0,0 +1,18 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe CronToGoSync::SyncInteractor, :vcr, type: :interactor do
4
+ describe "#call" do
5
+ let(:api_key) { ENV["CRONTOGO_API_KEY"] }
6
+ let(:organization_id) { ENV["CRONTOGO_ORGANIZATION_ID"] }
7
+ let(:data_file) { "spec/data/staging.toml".to_s }
8
+
9
+ subject(:context) { described_class.call(input_filename: data_file, api_key: api_key, organization_id: organization_id) }
10
+
11
+ it "works" do
12
+ expect(context).to be_a_success
13
+ expect(context.created).to eq 1
14
+ expect(context.updated).to eq 1
15
+ expect(context.deleted).to eq 1
16
+ end
17
+ end
18
+ end
metadata ADDED
@@ -0,0 +1,203 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cron_to_go_sync
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Instrumentl, Inc.
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-05-26 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: dry-types
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: dry-validation
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: faraday
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '1.0'
62
+ - - "<"
63
+ - !ruby/object:Gem::Version
64
+ version: '3.0'
65
+ type: :runtime
66
+ prerelease: false
67
+ version_requirements: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: '1.0'
72
+ - - "<"
73
+ - !ruby/object:Gem::Version
74
+ version: '3.0'
75
+ - !ruby/object:Gem::Dependency
76
+ name: faraday-follow_redirects
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ type: :runtime
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ - !ruby/object:Gem::Dependency
90
+ name: faraday-retry
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ type: :runtime
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ - !ruby/object:Gem::Dependency
104
+ name: interactor
105
+ requirement: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ type: :runtime
111
+ prerelease: false
112
+ version_requirements: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ - !ruby/object:Gem::Dependency
118
+ name: interactor-contracts
119
+ requirement: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ type: :runtime
125
+ prerelease: false
126
+ version_requirements: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ - !ruby/object:Gem::Dependency
132
+ name: toml-rb
133
+ requirement: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - ">="
136
+ - !ruby/object:Gem::Version
137
+ version: '0'
138
+ type: :runtime
139
+ prerelease: false
140
+ version_requirements: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - ">="
143
+ - !ruby/object:Gem::Version
144
+ version: '0'
145
+ description: " Library to synchronize a checked-in YAML file with the SaaS scheduled
146
+ task service CronToGo\n"
147
+ email:
148
+ - oss@instrumentl.com
149
+ executables: []
150
+ extensions: []
151
+ extra_rdoc_files: []
152
+ files:
153
+ - ".editorconfig"
154
+ - ".gitattributes"
155
+ - ".github/dependabot.yml"
156
+ - ".github/workflows/ci.yml"
157
+ - ".github/workflows/release.yml"
158
+ - ".gitignore"
159
+ - ".pre-commit-config.yaml"
160
+ - ".rubocop.yml"
161
+ - ".standard.yml"
162
+ - CHANGELOG.md
163
+ - Gemfile
164
+ - Gemfile.lock
165
+ - LICENSE.txt
166
+ - README.md
167
+ - cron_to_go_sync.gemspec
168
+ - lib/cron_to_go_sync.rb
169
+ - lib/cron_to_go_sync/parser.rb
170
+ - lib/cron_to_go_sync/sync_interactor.rb
171
+ - lib/cron_to_go_sync/version.rb
172
+ - spec/cassettes/CronToGoSync_SyncInteractor/_call/works.json
173
+ - spec/data/staging.toml
174
+ - spec/parser_spec.rb
175
+ - spec/spec_helper.rb
176
+ - spec/sync_interactor_spec.rb
177
+ homepage: https://github.com/instrumentl/cron_to_go_sync-rb
178
+ licenses:
179
+ - ISC
180
+ metadata:
181
+ homepage_uri: https://github.com/instrumentl/cron_to_go_sync-rb
182
+ source_code_uri: https://github.com/instrumentl/cron_to_go_sync-rb
183
+ changelog_uri: https://github.com/instrumentl/cron_to_go_sync-rb/blob/main/CHANGELOG.md
184
+ post_install_message:
185
+ rdoc_options: []
186
+ require_paths:
187
+ - lib
188
+ required_ruby_version: !ruby/object:Gem::Requirement
189
+ requirements:
190
+ - - ">="
191
+ - !ruby/object:Gem::Version
192
+ version: 3.0.0
193
+ required_rubygems_version: !ruby/object:Gem::Requirement
194
+ requirements:
195
+ - - ">="
196
+ - !ruby/object:Gem::Version
197
+ version: '0'
198
+ requirements: []
199
+ rubygems_version: 3.4.10
200
+ signing_key:
201
+ specification_version: 4
202
+ summary: CronToGo configuration syncing library
203
+ test_files: []