Icarus-Mod-Tools 1.3.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: b87da2114c2b631fc25173b50133591414801cab6098d8c42cfa630caa00573c
4
+ data.tar.gz: 58c4148ca81ac3f90c8fd2b19d11089bb09c310ff362df4b054f489eeaab5a2e
5
+ SHA512:
6
+ metadata.gz: 7cb7ece0dc6f41bc01786927320978ebef9e6dc15d6fc28ee4512694f04c166a5094b16b9e0def5a8b72b9f59d2182e3535319a3c11b2966b7c549105ece6b87
7
+ data.tar.gz: 62493cc38c36c1da82176a4d8f8f53bd26bd98ee331ae122650f355fa9165a49db9d596eef1d2df6c928db6f63a5cd958e457ff03ecf711c4bbf15b4b2dbf023
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --require spec_helper
2
+ --format doc
data/.rubocop.yml ADDED
@@ -0,0 +1,49 @@
1
+ require: rubocop-rspec
2
+
3
+ AllCops:
4
+ TargetRubyVersion: 3.1
5
+ NewCops: enable
6
+ SuggestExtensions: false
7
+
8
+ Style/StringLiterals:
9
+ Enabled: true
10
+ EnforcedStyle: double_quotes
11
+
12
+ Style/StringLiteralsInInterpolation:
13
+ Enabled: true
14
+ EnforcedStyle: double_quotes
15
+
16
+ Layout/LineLength:
17
+ Max: 160
18
+
19
+ Style/FrozenStringLiteralComment:
20
+ Exclude:
21
+ - Guardfile
22
+ - spec/**/*_spec.rb
23
+
24
+ Metrics/CyclomaticComplexity:
25
+ Max: 15
26
+ Exclude:
27
+ - spec/**/*_spec.rb
28
+ - lib/icarus/mod/cli/*.rb # Thor command files
29
+
30
+ Metrics/PerceivedComplexity:
31
+ Max: 15
32
+ Exclude:
33
+ - spec/**/*_spec.rb
34
+ - lib/icarus/mod/cli/*.rb # Thor command files
35
+
36
+ Metrics/AbcSize:
37
+ Max: 25
38
+ Exclude:
39
+ - lib/icarus/mod/cli/*.rb # Thor command files
40
+
41
+ Metrics/MethodLength:
42
+ Max: 50
43
+
44
+ RSpec/MultipleMemoizedHelpers:
45
+ Enabled: false
46
+
47
+ Style/Documentation:
48
+ Exclude:
49
+ - lib/icarus/mod/cli/*.rb # Thor command files
data/CHANGES.md ADDED
@@ -0,0 +1,24 @@
1
+ # Change Log
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ ## History (reverse chronological order)
6
+
7
+ ### v1.3.0 - 2023-02
8
+
9
+ - First public release
10
+ - Added sorting and filtering to the `list mods` command
11
+ - Bugfixes
12
+
13
+ ### v1.2.0 - 2023-01 _[Unreleased]_
14
+
15
+ - initial `sync mods` working functionality
16
+ - Bugfixes
17
+
18
+ ### v1.1.0 - 2022-12 _[Unreleased]_
19
+
20
+ - Initial alpha release
21
+
22
+ ### v1.0.0 - 2022-12 _[Unreleased]_
23
+
24
+ - Development
data/Gemfile ADDED
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in icarus-mod-tools.gemspec
6
+ gemspec
7
+
8
+ gem "rake", "~> 13.0"
9
+
10
+ group :develop do
11
+ gem "guard", "~> 2.18"
12
+ gem "guard-rspec", "~> 4.7"
13
+ gem "pry", "~> 0.14.1"
14
+ gem "rspec", "~> 3.12"
15
+ gem "rubocop", "~> 1.41"
16
+ gem "rubocop-rspec", "~> 2.16", require: false
17
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,170 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ Icarus-Mod-Tools (1.3.0)
5
+ google-cloud-firestore (~> 2.7)
6
+ octokit (~> 6.0)
7
+ thor (~> 1.2)
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ addressable (2.8.1)
13
+ public_suffix (>= 2.0.2, < 6.0)
14
+ ast (2.4.2)
15
+ coderay (1.1.3)
16
+ concurrent-ruby (1.1.10)
17
+ diff-lcs (1.5.0)
18
+ faraday (2.7.2)
19
+ faraday-net_http (>= 2.0, < 3.1)
20
+ ruby2_keywords (>= 0.0.4)
21
+ faraday-net_http (3.0.2)
22
+ faraday-retry (2.0.0)
23
+ faraday (~> 2.0)
24
+ ffi (1.15.5)
25
+ formatador (1.1.0)
26
+ gapic-common (0.16.0)
27
+ faraday (>= 1.9, < 3.a)
28
+ faraday-retry (>= 1.0, < 3.a)
29
+ google-protobuf (~> 3.14)
30
+ googleapis-common-protos (>= 1.3.12, < 2.a)
31
+ googleapis-common-protos-types (>= 1.3.1, < 2.a)
32
+ googleauth (~> 1.0)
33
+ grpc (~> 1.36)
34
+ google-cloud-core (1.6.0)
35
+ google-cloud-env (~> 1.0)
36
+ google-cloud-errors (~> 1.0)
37
+ google-cloud-env (1.6.0)
38
+ faraday (>= 0.17.3, < 3.0)
39
+ google-cloud-errors (1.3.0)
40
+ google-cloud-firestore (2.7.2)
41
+ concurrent-ruby (~> 1.0)
42
+ google-cloud-core (~> 1.5)
43
+ google-cloud-firestore-v1 (~> 0.0)
44
+ rbtree (~> 0.4.2)
45
+ google-cloud-firestore-v1 (0.8.0)
46
+ gapic-common (>= 0.10, < 2.a)
47
+ google-cloud-errors (~> 1.0)
48
+ google-cloud-location (>= 0.0, < 2.a)
49
+ google-cloud-location (0.2.0)
50
+ gapic-common (>= 0.10, < 2.a)
51
+ google-cloud-errors (~> 1.0)
52
+ google-protobuf (3.21.12-x86_64-linux)
53
+ googleapis-common-protos (1.4.0)
54
+ google-protobuf (~> 3.14)
55
+ googleapis-common-protos-types (~> 1.2)
56
+ grpc (~> 1.27)
57
+ googleapis-common-protos-types (1.4.0)
58
+ google-protobuf (~> 3.14)
59
+ googleauth (1.3.0)
60
+ faraday (>= 0.17.3, < 3.a)
61
+ jwt (>= 1.4, < 3.0)
62
+ memoist (~> 0.16)
63
+ multi_json (~> 1.11)
64
+ os (>= 0.9, < 2.0)
65
+ signet (>= 0.16, < 2.a)
66
+ grpc (1.50.0-x86_64-linux)
67
+ google-protobuf (~> 3.21)
68
+ googleapis-common-protos-types (~> 1.0)
69
+ guard (2.18.0)
70
+ formatador (>= 0.2.4)
71
+ listen (>= 2.7, < 4.0)
72
+ lumberjack (>= 1.0.12, < 2.0)
73
+ nenv (~> 0.1)
74
+ notiffany (~> 0.0)
75
+ pry (>= 0.13.0)
76
+ shellany (~> 0.0)
77
+ thor (>= 0.18.1)
78
+ guard-compat (1.2.1)
79
+ guard-rspec (4.7.3)
80
+ guard (~> 2.1)
81
+ guard-compat (~> 1.1)
82
+ rspec (>= 2.99.0, < 4.0)
83
+ json (2.6.3)
84
+ jwt (2.6.0)
85
+ listen (3.7.1)
86
+ rb-fsevent (~> 0.10, >= 0.10.3)
87
+ rb-inotify (~> 0.9, >= 0.9.10)
88
+ lumberjack (1.2.8)
89
+ memoist (0.16.2)
90
+ method_source (1.0.0)
91
+ multi_json (1.15.0)
92
+ nenv (0.3.0)
93
+ notiffany (0.1.3)
94
+ nenv (~> 0.1)
95
+ shellany (~> 0.0)
96
+ octokit (6.0.1)
97
+ faraday (>= 1, < 3)
98
+ sawyer (~> 0.9)
99
+ os (1.1.4)
100
+ parallel (1.22.1)
101
+ parser (3.1.3.0)
102
+ ast (~> 2.4.1)
103
+ pry (0.14.1)
104
+ coderay (~> 1.1)
105
+ method_source (~> 1.0)
106
+ public_suffix (5.0.1)
107
+ rainbow (3.1.1)
108
+ rake (13.0.6)
109
+ rb-fsevent (0.11.2)
110
+ rb-inotify (0.10.1)
111
+ ffi (~> 1.0)
112
+ rbtree (0.4.6)
113
+ regexp_parser (2.6.1)
114
+ rexml (3.2.5)
115
+ rspec (3.12.0)
116
+ rspec-core (~> 3.12.0)
117
+ rspec-expectations (~> 3.12.0)
118
+ rspec-mocks (~> 3.12.0)
119
+ rspec-core (3.12.0)
120
+ rspec-support (~> 3.12.0)
121
+ rspec-expectations (3.12.1)
122
+ diff-lcs (>= 1.2.0, < 2.0)
123
+ rspec-support (~> 3.12.0)
124
+ rspec-mocks (3.12.1)
125
+ diff-lcs (>= 1.2.0, < 2.0)
126
+ rspec-support (~> 3.12.0)
127
+ rspec-support (3.12.0)
128
+ rubocop (1.42.0)
129
+ json (~> 2.3)
130
+ parallel (~> 1.10)
131
+ parser (>= 3.1.2.1)
132
+ rainbow (>= 2.2.2, < 4.0)
133
+ regexp_parser (>= 1.8, < 3.0)
134
+ rexml (>= 3.2.5, < 4.0)
135
+ rubocop-ast (>= 1.24.1, < 2.0)
136
+ ruby-progressbar (~> 1.7)
137
+ unicode-display_width (>= 1.4.0, < 3.0)
138
+ rubocop-ast (1.24.1)
139
+ parser (>= 3.1.1.0)
140
+ rubocop-rspec (2.16.0)
141
+ rubocop (~> 1.33)
142
+ ruby-progressbar (1.11.0)
143
+ ruby2_keywords (0.0.5)
144
+ sawyer (0.9.2)
145
+ addressable (>= 2.3.5)
146
+ faraday (>= 0.17.3, < 3)
147
+ shellany (0.0.1)
148
+ signet (0.17.0)
149
+ addressable (~> 2.8)
150
+ faraday (>= 0.17.5, < 3.a)
151
+ jwt (>= 1.5, < 3.0)
152
+ multi_json (~> 1.10)
153
+ thor (1.2.1)
154
+ unicode-display_width (2.3.0)
155
+
156
+ PLATFORMS
157
+ x86_64-linux
158
+
159
+ DEPENDENCIES
160
+ Icarus-Mod-Tools!
161
+ guard (~> 2.18)
162
+ guard-rspec (~> 4.7)
163
+ pry (~> 0.14.1)
164
+ rake (~> 13.0)
165
+ rspec (~> 3.12)
166
+ rubocop (~> 1.41)
167
+ rubocop-rspec (~> 2.16)
168
+
169
+ BUNDLED WITH
170
+ 2.3.14
data/Guardfile ADDED
@@ -0,0 +1,18 @@
1
+ # More info at https://github.com/guard/guard#readme
2
+
3
+ guard :rspec, cmd: "bundle exec rspec" do
4
+ require "guard/rspec/dsl"
5
+ dsl = Guard::RSpec::Dsl.new(self)
6
+
7
+ # Feel free to open issues for suggestions and improvements
8
+
9
+ # RSpec files
10
+ rspec = dsl.rspec
11
+ watch(rspec.spec_helper) { rspec.spec_dir }
12
+ watch(rspec.spec_support) { rspec.spec_dir }
13
+ watch(rspec.spec_files)
14
+
15
+ # Ruby files
16
+ ruby = dsl.ruby
17
+ dsl.watch_spec_files_for(ruby.lib_files)
18
+ end
data/LICENSE.md ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2022 Donovan522
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # Icarus::Mod::Tools
2
+
3
+ Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/icarus/mod/tools`. To experiment with that code, run `bin/console` for an interactive prompt.
4
+
5
+ TODO: Delete this and the text above, and describe your gem
6
+
7
+ ## Installation
8
+
9
+ Install the gem and add to the application's Gemfile by executing:
10
+
11
+ $ bundle add icarus-mod-tools
12
+
13
+ If bundler is not being used to manage dependencies, install the gem by executing:
14
+
15
+ $ gem install icarus-mod-tools
16
+
17
+ ## Usage
18
+
19
+ TODO: Write usage instructions here
20
+
21
+ ## Development
22
+
23
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
24
+
25
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
26
+
27
+ ## Contributing
28
+
29
+ Bug reports and pull requests are welcome on GitHub at https://github.com/donovan522/icarus-mod-tools.
data/Rakefile ADDED
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rake/testtask"
5
+ require "rspec/core/rake_task"
6
+ require "rubocop/rake_task"
7
+
8
+ RuboCop::RakeTask.new
9
+
10
+ # Default directory to look in is `/specs`
11
+ # Run with `rake spec`
12
+ RSpec::Core::RakeTask.new(:spec) do |task|
13
+ task.rspec_opts = ["--color", "--format", "doc"]
14
+ end
15
+
16
+ task default: %i[spec rubocop]
data/exe/imt ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "cli/base"
5
+
6
+ Icarus::Mod::CLI::Base.start
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/icarus/mod/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "Icarus-Mod-Tools"
7
+ spec.version = Icarus::Mod::VERSION
8
+ spec.authors = ["Donovan Young"]
9
+ spec.email = ["dyoung522@gmail.com"]
10
+
11
+ spec.summary = "Various tools for Icarus Modding"
12
+ spec.description = spec.summary
13
+ spec.homepage = "https://github.com/Donovan522/icarus-mod-tools"
14
+ spec.required_ruby_version = ">= 3.1"
15
+
16
+ spec.metadata["homepage_uri"] = spec.homepage
17
+ spec.metadata["source_code_uri"] = spec.homepage
18
+ spec.metadata["changelog_uri"] = "#{spec.homepage}/CHANGES.md"
19
+ spec.metadata["rubygems_mfa_required"] = "true"
20
+
21
+ # Specify which files should be added to the gem when it is released.
22
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
23
+ spec.files = Dir.chdir(__dir__) do
24
+ `git ls-files -z`.split("\x0").reject do |f|
25
+ (f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
26
+ end
27
+ end
28
+ spec.bindir = "exe"
29
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
30
+ spec.require_paths = ["lib", "lib/icarus/mod"]
31
+
32
+ # Uncomment to register a new dependency of your gem
33
+ # spec.add_dependency "example-gem", "~> 1.0"
34
+ spec.add_dependency "google-cloud-firestore", "~> 2.7"
35
+ spec.add_dependency "octokit", "~> 6.0"
36
+ spec.add_dependency "thor", "~> 1.2"
37
+
38
+ # For more information and examples about making a new gem, check out our
39
+ # guide at: https://bundler.io/guides/creating_gem.html
40
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "firestore"
4
+
5
+ module Icarus
6
+ module Mod
7
+ module CLI
8
+ # Sync CLI command definitions
9
+ class Add < SubCommandBase
10
+ desc "modinfo", "Adds an entry to 'meta/modinfo/list'"
11
+ def modinfo(item)
12
+ firestore = Firestore.new
13
+ payload = [firestore.list(:modinfo), item].flatten.compact
14
+
15
+ puts firestore.update(:modinfo, payload, merge: true) ? "Success" : "Failure"
16
+ end
17
+
18
+ desc "repos", "Adds an entry to 'meta/repos/list'"
19
+ def repos(item)
20
+ firestore = Firestore.new
21
+ payload = [firestore.list(:repositories), item].flatten.compact
22
+
23
+ puts firestore.update(:repositories, payload, merge: true) ? "Success" : "Failure"
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "version"
4
+ require "tools"
5
+ require "thor"
6
+
7
+ module Icarus
8
+ module Mod
9
+ module CLI
10
+ # Base class for all subcommands
11
+ class SubCommandBase < Thor
12
+ class_option :verbose,
13
+ aliases: "-v",
14
+ type: :boolean,
15
+ repeatable: true,
16
+ default: [true],
17
+ desc: "Increase verbosity. May be repeated for even more verbosity."
18
+
19
+ no_commands do
20
+ def check_false
21
+ options[:verbose] = [] if options[:verbose].include?(false)
22
+ end
23
+
24
+ def verbose
25
+ check_false
26
+ options[:verbose]&.count || 0
27
+ end
28
+
29
+ def verbose?
30
+ check_false
31
+ options[:verbose]&.count&.positive?
32
+ end
33
+ end
34
+
35
+ def self.banner(command, _namespace = nil, _subcommand = false) # rubocop:disable Style/OptionalBooleanParameter
36
+ "#{basename} #{subcommand_prefix} #{command.usage}"
37
+ end
38
+
39
+ def self.subcommand_prefix
40
+ name.gsub(/.*::/, "").gsub(/^[A-Z]/) { |match| match[0].downcase }.gsub(/[A-Z]/) { |match| "-#{match[0].downcase}" }
41
+ end
42
+ end
43
+
44
+ # Require subcommands after the SubCommandBase class is defined
45
+ require "cli/sync"
46
+ require "cli/list"
47
+ require "cli/add"
48
+
49
+ # The main CLI for Icarus Mod Tools
50
+ class Base < Thor
51
+ def self.exit_on_failure?
52
+ true
53
+ end
54
+
55
+ map %w[--version -V] => :__print_version
56
+
57
+ desc "--version, -V", "print the version and exit"
58
+ def __print_version
59
+ puts "IcarusModTool (imt) v#{Icarus::Mod::VERSION}"
60
+ end
61
+
62
+ desc "sync", "Syncs the databases"
63
+ subcommand "sync", Sync
64
+
65
+ desc "list", "Lists the databases"
66
+ subcommand "list", List
67
+
68
+ desc "add", "Adds entries to the databases"
69
+ subcommand "add", Add
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "firestore"
4
+ require "tools/modinfo"
5
+
6
+ module Icarus
7
+ module Mod
8
+ module CLI
9
+ # Sync CLI command definitions
10
+ class List < SubCommandBase
11
+ desc "modinfo", "Displays data from 'meta/modinfo/list'"
12
+ def modinfo
13
+ puts Firestore.new.list(:modinfo)
14
+ end
15
+
16
+ desc "repos", "Displays data from 'meta/repos/list'"
17
+ def repos
18
+ puts Firestore.new.list(:repositories)
19
+ end
20
+
21
+ desc "mods", "Displays data from 'mods'"
22
+ method_option :sort, type: :string, default: "name", desc: "Sort by field (name, author, etc.)"
23
+ method_option :filter, type: :array, default: [], desc: "Filter by field (name, author, etc.)"
24
+ def mods
25
+ valid_keys = Icarus::Mod::Tools::Modinfo::HASHKEYS + [:updated_at]
26
+ sort_field = options[:sort].to_sym
27
+ filter_field = options[:filter].first.to_sym
28
+ filter_value = options[:filter].last.to_s
29
+
30
+ raise "Invalid filter option" unless options[:filter]&.count == 2
31
+
32
+ raise "Invalid filter field '#{filter_field}'" unless filter_field && valid_keys.include?(filter_field)
33
+
34
+ raise "Invalid sort field '#{sort_field}'" unless valid_keys.include?(sort_field)
35
+
36
+ puts "Sorted by #{sort_field}" if sort_field && verbose > 2
37
+ puts "Filtered by #{filter_field} = #{filter_value}" if filter_field && verbose > 2
38
+
39
+ mods = Firestore.new.list(:mods)
40
+
41
+ # Filter by field
42
+ mods.select! { |mod| mod.send(filter_field).downcase =~ /#{filter_value&.downcase}/ } if filter_field
43
+
44
+ if mods.empty?
45
+ puts "no mods found" if verbose?
46
+ return
47
+ end
48
+
49
+ header_format = "%-<name>50s %-<author>20s %-<version>10s %-<updated_at>20s"
50
+ header_format += " %-<id>20s %<description>s" if verbose > 1
51
+
52
+ if verbose?
53
+ puts format(
54
+ header_format,
55
+ name: "NAME",
56
+ author: "AUTHOR",
57
+ version: "VERSION",
58
+ updated_at: "LAST UPDATED",
59
+ id: "ID",
60
+ description: "DESCRIPTION"
61
+ )
62
+ end
63
+
64
+ # Sort by field, optionally subsorting by name
65
+ (sort_field == :name ? mods.sort_by(&:name) : mods.sort_by { |mod| [mod.send(sort_field), mod.name] }).each do |mod|
66
+ data_format = "%-<name>50s %-<author>20s v%-<version>10s%-<updated_at>20s"
67
+ data_format += " %-<id>20s %<description>s" if verbose > 1
68
+
69
+ puts format(data_format, mod.to_h.merge(id: mod.id, updated_at: mod.updated_at.strftime("%Y-%m-%d %H:%M:%S")))
70
+ end
71
+
72
+ puts "Total: #{mods.count}" if verbose?
73
+ rescue StandardError => e
74
+ puts e.message
75
+ exit 1
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "tools/modinfo_sync"
4
+ require "tools/mod_sync"
5
+
6
+ module Icarus
7
+ module Mod
8
+ module CLI
9
+ # Sync CLI command definitions
10
+ class Sync < SubCommandBase
11
+ desc "modinfo", "Reads from 'meta/repos/list' and Syncs any modinfo files we find (github only for now)"
12
+ def modinfo
13
+ modinfo_sync = Icarus::Mod::Tools::ModinfoSync.new
14
+
15
+ puts "Retrieving repository Data..." if verbose?
16
+ repositories = modinfo_sync.repositories
17
+
18
+ raise "Unable to find any repositories!" unless repositories.any?
19
+
20
+ puts "Retrieving modinfo Array..." if verbose?
21
+ modinfo_array = modinfo_sync.modinfo_data(repositories, verbose: verbose > 1)&.map(&:download_url)&.compact
22
+
23
+ raise "Unable to find any modinfo.json files!" unless modinfo_array&.any?
24
+
25
+ puts "Saving to Firestore..." if verbose?
26
+ response = modinfo_sync.update(modinfo_array)
27
+ puts response ? "Success" : "Failure (may be no changes)" if verbose?
28
+ end
29
+
30
+ desc "mods", "Reads from 'meta/modinfo/list' and updates the 'mods' database accordingly"
31
+ def mods
32
+ modsync = Icarus::Mod::Tools::ModSync.new
33
+
34
+ puts "Retrieving modinfo Data..." if verbose?
35
+ modinfo_array = modsync.modinfo_array
36
+
37
+ puts "Retrieving mod Data..." if verbose?
38
+ mod_array = modsync.mods
39
+
40
+ puts "Updating mod Data..." if verbose?
41
+ modinfo_array.each do |mod|
42
+ verb = "Creating"
43
+ doc_id = modsync.find_mod(mod)
44
+
45
+ if doc_id
46
+ puts "Found existing mod #{mod.name} at #{doc_id}" if verbose > 2
47
+ mod.id = doc_id
48
+ verb = "Updating"
49
+ end
50
+
51
+ print format("#{verb} %-<name>60s", name: "'#{mod.author || "NoOne"}/#{mod.name || "Unnamed"}'") if verbose > 1
52
+ response = modsync.update(mod)
53
+ puts format("%<status>10s", status: response ? "Success" : "Failure") if verbose > 1
54
+ end
55
+
56
+ puts "Created/Updated #{modinfo_array.count} mods" if verbose?
57
+
58
+ delete_array = mod_array.filter { |mod| modsync.find_modinfo(mod).nil? }
59
+
60
+ return unless delete_array.any?
61
+
62
+ puts "Deleting outdated mods..." if verbose?
63
+ delete_array.each do |mod|
64
+ print format("Deleting %-<name>60s", name: "'#{mod.author || "NoOne"}/#{mod.name || "Unnamed'"}") if verbose > 1
65
+ response = modsync.delete(mod)
66
+ puts format("%<status>10s", status: response ? "Success" : "Failure") if verbose > 1
67
+ end
68
+
69
+ puts "Deleted #{delete_array.count} outdated mods" if verbose?
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "google/cloud/firestore"
4
+ require "tools/modinfo"
5
+
6
+ module Icarus
7
+ module Mod
8
+ # Helper methods for interacting with the Firestore API
9
+ class Firestore
10
+ attr_reader :client
11
+
12
+ COLLECTIONS = {
13
+ modinfo: "meta/modinfo",
14
+ repositories: "meta/repos",
15
+ mods: "mods"
16
+ }.freeze
17
+
18
+ def initialize
19
+ @client = Google::Cloud::Firestore.new(project_id: "projectdaedalus-fb09f", credentials: ENV.fetch("GOOGLE_APPLICATION_CREDENTIALS", nil))
20
+ end
21
+
22
+ def repos
23
+ @repos ||= list(:repositories)
24
+ end
25
+
26
+ def modinfo_array
27
+ @modinfo_array ||= list(:modinfo)
28
+ end
29
+
30
+ def mods
31
+ @mods ||= list(:mods)
32
+ end
33
+
34
+ def find_mod(field, value)
35
+ mods.find { |mod| mod.send(field) == value }
36
+ end
37
+
38
+ def list(type)
39
+ case type
40
+ when :modinfo
41
+ @client.doc(COLLECTIONS[:modinfo]).get[:list]
42
+ when :repositories
43
+ @client.doc(COLLECTIONS[:repositories]).get[:list]
44
+ when :mods
45
+ @client.col(COLLECTIONS[:mods]).get.map do |doc|
46
+ Icarus::Mod::Tools::Modinfo.new(doc.data, id: doc.document_id, created: doc.create_time, updated: doc.update_time)
47
+ end
48
+ else
49
+ raise "Invalid type: #{type}"
50
+ end
51
+ end
52
+
53
+ def update_or_create_mod(payload, merge:)
54
+ doc_id = payload.id || find_mod(:name, payload.name)&.id
55
+
56
+ return @client.doc("#{COLLECTIONS[:mods]}/#{doc_id}").set(payload.to_h, merge:) if doc_id
57
+
58
+ @client.col(COLLECTIONS[:mods]).add(payload.to_h)
59
+ end
60
+
61
+ def update(type, payload, merge: false)
62
+ raise "You must specify a payload to update" if payload&.empty? || payload.nil?
63
+
64
+ case type
65
+ when :modinfo
66
+ response = @client.doc(COLLECTIONS[:modinfo]).set({ list: payload }, merge:)
67
+ when :repositories
68
+ response = @client.doc(COLLECTIONS[:repositories]).set({ list: payload }, merge:)
69
+ when :mod
70
+ response = update_or_create_mod(payload, merge:)
71
+ else
72
+ raise "Invalid type: #{type}"
73
+ end
74
+
75
+ response.is_a?(Google::Cloud::Firestore::DocumentReference) || response.is_a?(Google::Cloud::Firestore::CommitResponse::WriteResult)
76
+ end
77
+
78
+ def delete(type, payload)
79
+ case type
80
+ when :mod
81
+ response = @client.doc("#{COLLECTIONS[:mods]}/#{payload.id}").delete
82
+ else
83
+ raise "Invalid type: #{type}"
84
+ end
85
+
86
+ response.is_a?(Google::Cloud::Firestore::CommitResponse::WriteResult)
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "octokit"
4
+
5
+ module Icarus
6
+ module Mod
7
+ # Helper methods for interacting with the Github API
8
+ class Github
9
+ attr_reader :client, :resources
10
+
11
+ def initialize(repo = nil)
12
+ self.repository = repo if repo
13
+ @client = Octokit::Client.new(access_token: ENV.fetch("GITHUB_TOKEN", nil))
14
+ @resources = []
15
+ end
16
+
17
+ def repository
18
+ raise "You must specify a repository to use" unless @repository
19
+
20
+ @repository
21
+ end
22
+
23
+ def repository=(repo)
24
+ @resources = [] # reset the resources cache
25
+ @repository = repo.gsub(%r{https?://.*github\.com/}, "")
26
+ end
27
+
28
+ # Recursively returns all resources in the repository
29
+ # path: the path to search in
30
+ # cache: whether to use the cached resources
31
+ # recursive: whether to recursively search subdirectories
32
+ def all_files(path: nil, cache: true, recursive: false, &block)
33
+ # If we've already been called for this repository, use the cached resources
34
+ use_cache = @resources.any? && cache
35
+
36
+ if use_cache
37
+ @resources.each { |file| block.call(file) } if block_given?
38
+ else
39
+ @client.contents(repository, path:).each do |entry|
40
+ if entry[:type] == "dir"
41
+ all_files(path: entry[:path], cache: false, recursive: true, &block) if recursive
42
+ next # we don't need directories in our output
43
+ end
44
+
45
+ block.call(entry) if block_given?
46
+ @resources << entry # cache the file
47
+ end
48
+ end
49
+
50
+ @resources unless block_given?
51
+ end
52
+
53
+ def find(pattern)
54
+ all_files { |file| return file if file[:name] =~ /#{pattern}/i }
55
+ end
56
+
57
+ def get_contents(url)
58
+ @client.contents(url)
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "firestore"
4
+ require "tools/sync_helpers"
5
+
6
+ module Icarus
7
+ module Mod
8
+ module Tools
9
+ # Sync methods
10
+ class ModSync
11
+ include SyncHelpers
12
+
13
+ def initialize
14
+ @firestore = Firestore.new
15
+ end
16
+
17
+ def mods
18
+ @firestore.mods
19
+ end
20
+
21
+ def modinfo_array
22
+ @modinfo_array ||= @firestore.modinfo_array.map do |url|
23
+ retrieve_from_url(url)[:mods].map { |mod| Modinfo.new(mod) }
24
+ end.flatten
25
+ end
26
+
27
+ def find_mod(modinfo)
28
+ @firestore.find_mod(:name, modinfo.name)&.id
29
+ end
30
+
31
+ def find_modinfo(modinfo)
32
+ @modinfo_array.find { |mod| mod.name == modinfo.name }
33
+ end
34
+
35
+ def update(modinfo)
36
+ @firestore.update(:mod, modinfo, merge: false)
37
+ end
38
+
39
+ def delete(modinfo)
40
+ @firestore.delete(:mod, modinfo)
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Icarus
4
+ module Mod
5
+ module Tools
6
+ # Sync methods
7
+ class Modinfo
8
+ attr_reader :data, :id, :created_at, :updated_at
9
+
10
+ HASHKEYS = %i[name author version compatibility description fileType fileURL].freeze
11
+
12
+ def initialize(data, id: nil, created: nil, updated: nil)
13
+ @id = id
14
+ @created_at = created
15
+ @updated_at = updated
16
+ read(data)
17
+ end
18
+
19
+ def read(data)
20
+ @data = data.is_a?(String) ? JSON.parse(data, symbolize_names: true) : data
21
+ end
22
+
23
+ def to_json(*args)
24
+ JSON.generate(@data, *args)
25
+ end
26
+
27
+ def to_h
28
+ @data || {}
29
+ end
30
+
31
+ def to_s
32
+ format(
33
+ "%-<name>30s %-<author>20s v%-<version>10s %<description>s",
34
+ name:, author:, version: (version || "None"), description:
35
+ )
36
+ end
37
+
38
+ def method_missing(method_name, *_args, &)
39
+ to_h[method_name.to_sym]&.strip
40
+ end
41
+
42
+ def respond_to_missing?(method_name, include_private = false)
43
+ @data&.key?(method_name.to_sym) || super
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "firestore"
4
+ require "github"
5
+ require "tools/sync_helpers"
6
+
7
+ module Icarus
8
+ module Mod
9
+ module Tools
10
+ # Sync methods
11
+ class ModinfoSync
12
+ include SyncHelpers
13
+
14
+ def initialize
15
+ @firestore = Firestore.new
16
+ @github = Github.new
17
+ @repositories = []
18
+ end
19
+
20
+ def repositories
21
+ @firestore.repos
22
+ end
23
+
24
+ def update(modinfo_array)
25
+ @firestore.update(:modinfo, modinfo_array)
26
+ end
27
+
28
+ def modinfo(url)
29
+ retrieve_from_url(url)
30
+ end
31
+
32
+ def modinfo_data(repositories, verbose: false)
33
+ repositories.map do |repo|
34
+ print "searching #{repo}..." if verbose
35
+
36
+ case repo
37
+ when /github/
38
+ @github.repository = repo
39
+ modinfo_url = @github.find("modinfo.json")
40
+
41
+ unless modinfo_url
42
+ puts "Skipped...no modinfo.json" if verbose
43
+ next
44
+ end
45
+
46
+ puts "Found!" if verbose
47
+ modinfo_url
48
+ else
49
+ puts "Skipped...repository type not supported yet" if verbose
50
+ end
51
+ end.compact
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "uri"
4
+ require "net/http"
5
+ require "json"
6
+
7
+ module Icarus
8
+ module Mod
9
+ module Tools
10
+ # Sync helper methods
11
+ module SyncHelpers
12
+ def retrieve_from_url(url)
13
+ res = Net::HTTP.get_response(URI(url))
14
+
15
+ raise "HTTP Request failed (#{res.code}): #{res.message}" unless res&.code == "200"
16
+
17
+ JSON.parse(res.body, symbolize_names: true)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "firestore"
4
+ require "github"
5
+
6
+ module Icarus
7
+ module Mod
8
+ module Tools
9
+ class Error < StandardError; end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Icarus
4
+ module Mod
5
+ VERSION = "1.3.0"
6
+ end
7
+ end
@@ -0,0 +1,15 @@
1
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
2
+ #
3
+ module Icarus
4
+ module Mod
5
+ class Github
6
+ attr_reader :client: Octokit::Client
7
+ attr_accessor :repo: String
8
+
9
+ def initialize: (void) -> void
10
+ def all_files: (String) -> Array[String]
11
+
12
+ VERSION: String
13
+ end
14
+ end
15
+ end
metadata ADDED
@@ -0,0 +1,114 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: Icarus-Mod-Tools
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.3.0
5
+ platform: ruby
6
+ authors:
7
+ - Donovan Young
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2023-01-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: google-cloud-firestore
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.7'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: octokit
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '6.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '6.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: thor
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.2'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.2'
55
+ description: Various tools for Icarus Modding
56
+ email:
57
+ - dyoung522@gmail.com
58
+ executables:
59
+ - imt
60
+ extensions: []
61
+ extra_rdoc_files: []
62
+ files:
63
+ - ".rspec"
64
+ - ".rubocop.yml"
65
+ - CHANGES.md
66
+ - Gemfile
67
+ - Gemfile.lock
68
+ - Guardfile
69
+ - LICENSE.md
70
+ - README.md
71
+ - Rakefile
72
+ - exe/imt
73
+ - icarus-mod-tools.gemspec
74
+ - lib/icarus/mod/cli/add.rb
75
+ - lib/icarus/mod/cli/base.rb
76
+ - lib/icarus/mod/cli/list.rb
77
+ - lib/icarus/mod/cli/sync.rb
78
+ - lib/icarus/mod/firestore.rb
79
+ - lib/icarus/mod/github.rb
80
+ - lib/icarus/mod/tools.rb
81
+ - lib/icarus/mod/tools/mod_sync.rb
82
+ - lib/icarus/mod/tools/modinfo.rb
83
+ - lib/icarus/mod/tools/modinfo_sync.rb
84
+ - lib/icarus/mod/tools/sync_helpers.rb
85
+ - lib/icarus/mod/version.rb
86
+ - sig/database/sync/sync.rbs
87
+ homepage: https://github.com/Donovan522/icarus-mod-tools
88
+ licenses: []
89
+ metadata:
90
+ homepage_uri: https://github.com/Donovan522/icarus-mod-tools
91
+ source_code_uri: https://github.com/Donovan522/icarus-mod-tools
92
+ changelog_uri: https://github.com/Donovan522/icarus-mod-tools/CHANGES.md
93
+ rubygems_mfa_required: 'true'
94
+ post_install_message:
95
+ rdoc_options: []
96
+ require_paths:
97
+ - lib
98
+ - lib/icarus/mod
99
+ required_ruby_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '3.1'
104
+ required_rubygems_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ requirements: []
110
+ rubygems_version: 3.3.7
111
+ signing_key:
112
+ specification_version: 4
113
+ summary: Various tools for Icarus Modding
114
+ test_files: []