Icarus-Mod-Tools 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
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: []