Icarus-Mod-Tools 2.3.0 → 2.5.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 +4 -4
- data/.claude-context.md +178 -0
- data/CHANGELOG.md +33 -0
- data/Gemfile.lock +41 -40
- data/README.md +9 -2
- data/lib/icarus/mod/cli/remove.rb +250 -9
- data/lib/icarus/mod/firestore.rb +6 -0
- data/lib/icarus/mod/github.rb +9 -7
- data/lib/icarus/mod/tools/baseinfo.rb +31 -0
- data/lib/icarus/mod/tools/modinfo.rb +16 -0
- data/lib/icarus/mod/tools/sync/mods.rb +1 -1
- data/lib/icarus/mod/tools/sync/tools.rb +1 -1
- data/lib/icarus/mod/tools/toolinfo.rb +10 -0
- data/lib/icarus/mod/version.rb +1 -1
- metadata +2 -2
- data/.claude/settings.local.json +0 -9
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4de5e1f299177ea6e8681ae6d92fd808086ed601aa0c61346b9dc5552925bb82
|
|
4
|
+
data.tar.gz: e76f59e4cf4774e737240e310015a5870b99338b7c16f85599d7881f836f090f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d1e1f69dbf0a7ccb9558e9eac6b4ae802cd14cf78aeac3a18b54cb9c533008d98eaed562347cdf65712429173ddf614e018cf69d3366271753ba27c93c3fb80e
|
|
7
|
+
data.tar.gz: a95184b4d2be164e626390012630d86bdf94359e2e832396496f30dfd74911cee9d01dc03f605357e408ee912dd4dbf850cbaf8886206babd9cfbb08231facb8
|
data/.claude-context.md
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# Claude Code Session Context - 2026-01-11
|
|
2
|
+
|
|
3
|
+
## Status: COMPLETE - PR Open for Review
|
|
4
|
+
|
|
5
|
+
**Date:** 2026-01-11
|
|
6
|
+
**Pull Request:** #13 - https://github.com/DonovanMods/icarus-mod-tools/pull/13
|
|
7
|
+
**Branch:** feature/remove-mods-tools
|
|
8
|
+
**Issues Resolved:** #12, #5
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## What Was Implemented
|
|
13
|
+
|
|
14
|
+
Added three new removal commands to the CLI:
|
|
15
|
+
|
|
16
|
+
1. **`imt remove mod <MOD_ID>`** - Removes entries from the `mods` collection
|
|
17
|
+
2. **`imt remove tool <TOOL_ID>`** - Removes entries from the `tools` collection
|
|
18
|
+
3. **Enhanced `imt remove repos`** - Added cascade delete functionality (enabled by default via `--cascade` flag)
|
|
19
|
+
|
|
20
|
+
### Cascade Delete Feature
|
|
21
|
+
|
|
22
|
+
When removing a repository with `--cascade` (default: true):
|
|
23
|
+
- Removes the repository from `meta/repos/list`
|
|
24
|
+
- Finds and removes all associated modinfo URLs from `meta/modinfo/list`
|
|
25
|
+
- Finds and removes all associated toolinfo URLs from `meta/toolinfo/list`
|
|
26
|
+
- Fetches each modinfo/toolinfo JSON and removes associated mods/tools from collections
|
|
27
|
+
- Gracefully handles fetch errors with warnings
|
|
28
|
+
|
|
29
|
+
Users can disable cascade with `--no-cascade` to only remove the repository entry.
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Files Modified
|
|
34
|
+
|
|
35
|
+
1. **lib/icarus/mod/cli/remove.rb** (+151 lines)
|
|
36
|
+
- Added `mod(mod_id)` method
|
|
37
|
+
- Added `tool(tool_id)` method
|
|
38
|
+
- Enhanced `repos(repo)` with cascade logic
|
|
39
|
+
- Added private methods: `remove_entity`, `cascade_delete_repo`, `delete_entities_from_url`
|
|
40
|
+
- Includes `Tools::Sync::Helpers` for `retrieve_from_url` functionality
|
|
41
|
+
|
|
42
|
+
2. **spec/icarus/mod/cli/remove_spec.rb** (+192 lines)
|
|
43
|
+
- Comprehensive test coverage for all new commands
|
|
44
|
+
- Tests for cascade delete functionality
|
|
45
|
+
- Tests for `--dry-run` compatibility
|
|
46
|
+
- Tests for error handling when URL fetch fails
|
|
47
|
+
|
|
48
|
+
3. **lib/icarus/mod/version.rb**
|
|
49
|
+
- Bumped from 2.3.0 → 2.4.0
|
|
50
|
+
|
|
51
|
+
4. **CHANGELOG.md**
|
|
52
|
+
- Added v2.4.0 entry documenting all changes
|
|
53
|
+
|
|
54
|
+
5. **README.md**
|
|
55
|
+
- Updated `imt remove` command documentation
|
|
56
|
+
- Added `--cascade` and `--dry-run` options
|
|
57
|
+
|
|
58
|
+
6. **Gemfile.lock**
|
|
59
|
+
- Updated version reference
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## Test Results
|
|
64
|
+
|
|
65
|
+
✅ All 290 tests passing
|
|
66
|
+
- 16 tests for remove command (including new mod/tool/cascade tests)
|
|
67
|
+
- Full test suite verified
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## Key Implementation Details
|
|
72
|
+
|
|
73
|
+
### Finding Entities by ID
|
|
74
|
+
Both `mod` and `tool` commands find entities by their Firestore document ID:
|
|
75
|
+
```ruby
|
|
76
|
+
entity = collection.find { |e| e.id == entity_id }
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Cascade Delete Logic
|
|
80
|
+
1. Filters modinfo/toolinfo URLs by repository name
|
|
81
|
+
2. Deletes each URL from meta collections
|
|
82
|
+
3. Fetches the JSON from each URL to get entity names
|
|
83
|
+
4. Finds and deletes matching entities from mods/tools collections by name+author
|
|
84
|
+
5. Finally removes the repository
|
|
85
|
+
|
|
86
|
+
### Error Handling
|
|
87
|
+
- Warnings (not failures) when URLs can't be fetched during cascade
|
|
88
|
+
- Proper exit codes for not found scenarios
|
|
89
|
+
- Dry-run support for all operations
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## PR Details
|
|
94
|
+
|
|
95
|
+
**Title:** feat: add remove commands for mods and tools collections (#12)
|
|
96
|
+
**URL:** https://github.com/DonovanMods/icarus-mod-tools/pull/13
|
|
97
|
+
**Status:** Open, mergeable
|
|
98
|
+
**Reviewer:** GitHub Copilot (auto-requested)
|
|
99
|
+
**Changes:** +349 lines, -19 lines across 6 files
|
|
100
|
+
|
|
101
|
+
**PR Body:**
|
|
102
|
+
```markdown
|
|
103
|
+
## Summary
|
|
104
|
+
|
|
105
|
+
Resolves #12
|
|
106
|
+
Resolves #5
|
|
107
|
+
|
|
108
|
+
Adds direct removal commands for the `mods` and `tools` collections, along with cascade delete functionality for repository removal.
|
|
109
|
+
|
|
110
|
+
## Changes
|
|
111
|
+
|
|
112
|
+
- Add `imt remove mod <MOD_ID>` to delete entries from mods collection
|
|
113
|
+
- Add `imt remove tool <TOOL_ID>` to delete entries from tools collection
|
|
114
|
+
- Enhance `imt remove repos` with `--cascade` flag (enabled by default) to automatically remove associated modinfo, toolinfo, mods, and tools
|
|
115
|
+
- Include comprehensive test coverage for all new functionality
|
|
116
|
+
- Update documentation (README, CHANGELOG) for new commands
|
|
117
|
+
- Bump version to 2.4.0
|
|
118
|
+
|
|
119
|
+
## Test Plan
|
|
120
|
+
|
|
121
|
+
- [x] All 290 tests passing
|
|
122
|
+
- [x] Added comprehensive test coverage for new commands
|
|
123
|
+
- [x] Tested cascade delete functionality
|
|
124
|
+
- [x] Tested --dry-run flag compatibility
|
|
125
|
+
- [x] Updated documentation
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Git State
|
|
131
|
+
|
|
132
|
+
**Current Branch:** main (reset to match origin/main after creating PR)
|
|
133
|
+
**Main HEAD:** de2146d - "Add comprehensive test coverage and fix sync 404 errors (#9)"
|
|
134
|
+
|
|
135
|
+
**Feature Branch:** feature/remove-mods-tools
|
|
136
|
+
**Feature HEAD:** 5109a1a - "feat: add remove commands for mods and tools collections (#12)"
|
|
137
|
+
**Pushed to:** origin/feature/remove-mods-tools
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## Next Steps
|
|
142
|
+
|
|
143
|
+
1. ✅ PR created and open for review
|
|
144
|
+
2. ⏳ Wait for PR review/approval
|
|
145
|
+
3. ⏳ Merge PR when approved
|
|
146
|
+
4. ⏳ Issues #12 and #5 will auto-close on merge
|
|
147
|
+
5. ⏳ Consider tagging v2.4.0 release after merge
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## How to Resume
|
|
152
|
+
|
|
153
|
+
When you move the repository and restart Claude Code:
|
|
154
|
+
|
|
155
|
+
1. The PR is already created: https://github.com/DonovanMods/icarus-mod-tools/pull/13
|
|
156
|
+
2. All code is committed to `feature/remove-mods-tools` branch
|
|
157
|
+
3. The main branch is clean
|
|
158
|
+
4. If you need to make changes to the PR, checkout the feature branch:
|
|
159
|
+
```bash
|
|
160
|
+
git checkout feature/remove-mods-tools
|
|
161
|
+
# make changes
|
|
162
|
+
git add -A && git commit -m "update: ..."
|
|
163
|
+
git push
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
5. To merge the PR (when ready):
|
|
167
|
+
```bash
|
|
168
|
+
gh pr merge 13 --squash # or --merge or --rebase
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## Related Issues
|
|
174
|
+
|
|
175
|
+
- **#12** - "Add remove commands for mods and tools collections" (main issue)
|
|
176
|
+
- **#5** - "When a repo is deleted, the associated entry is not removed from the modinfo db" (cascade delete)
|
|
177
|
+
|
|
178
|
+
Both issues are linked in the PR and will auto-close when merged.
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,39 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
## History (reverse chronological order)
|
|
6
6
|
|
|
7
|
+
### v2.5.0 - 2026-01-12
|
|
8
|
+
|
|
9
|
+
- Add automatic conversion of GitHub URLs to `raw.githubusercontent.com` format during sync
|
|
10
|
+
- Converts both `/blob/` and `/raw/` URLs from `github.com` domain
|
|
11
|
+
- Applies to all file URLs in modinfo and toolinfo files
|
|
12
|
+
- Also applies to imageURL and readmeURL fields
|
|
13
|
+
- Warnings added to alert users to update source files
|
|
14
|
+
- Ensures compatibility with GitHub's current raw file hosting on `raw.githubusercontent.com`
|
|
15
|
+
|
|
16
|
+
### v2.4.1 - 2026-01-12
|
|
17
|
+
|
|
18
|
+
- Fix JSON parsing error messages during sync to show concise error instead of full stack trace
|
|
19
|
+
- Include URL in JSON parsing error messages to identify which repository has invalid JSON
|
|
20
|
+
|
|
21
|
+
### v2.4.0 - 2026-01-11
|
|
22
|
+
|
|
23
|
+
- Add `imt remove mod` command to remove entries from `mods` collection
|
|
24
|
+
- Add `imt remove tool` command to remove entries from `tools` collection
|
|
25
|
+
- Add cascade delete to `imt remove repos` (enabled by default via `--cascade` flag)
|
|
26
|
+
- When removing a repository, also removes associated modinfo, toolinfo, mods, and tools entries
|
|
27
|
+
- Improvements to cascade delete functionality:
|
|
28
|
+
- Fix URL matching to prevent false matches (e.g., "owner/repo" no longer matches "owner/repo-fork")
|
|
29
|
+
- Handle multiple entities with same name and author (deletes all matches)
|
|
30
|
+
- Improve error handling with specific exception types and comprehensive reporting
|
|
31
|
+
- Track and report both fetch failures and delete failures with detailed summaries
|
|
32
|
+
- Enhanced dry-run output showing all entities that would be deleted
|
|
33
|
+
- Add Firestore cache invalidation for mod/tool deletions
|
|
34
|
+
|
|
35
|
+
### v2.3.0 - 2025-12-18
|
|
36
|
+
|
|
37
|
+
- Add comprehensive test coverage
|
|
38
|
+
- Fix sync 404 errors
|
|
39
|
+
|
|
7
40
|
### v2.1 - 2023-02-11
|
|
8
41
|
|
|
9
42
|
- Remove support for `fileType` and `fileURL` in `modinfo.json` files
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
Icarus-Mod-Tools (2.
|
|
4
|
+
Icarus-Mod-Tools (2.5.0)
|
|
5
5
|
google-cloud-firestore (~> 2.7)
|
|
6
6
|
octokit (~> 6.0)
|
|
7
7
|
paint (~> 2.3)
|
|
@@ -10,13 +10,13 @@ PATH
|
|
|
10
10
|
GEM
|
|
11
11
|
remote: https://rubygems.org/
|
|
12
12
|
specs:
|
|
13
|
-
addressable (2.8.
|
|
14
|
-
public_suffix (>= 2.0.2, <
|
|
13
|
+
addressable (2.8.8)
|
|
14
|
+
public_suffix (>= 2.0.2, < 8.0)
|
|
15
15
|
ast (2.4.3)
|
|
16
16
|
base64 (0.3.0)
|
|
17
17
|
bigdecimal (3.3.1)
|
|
18
18
|
coderay (1.1.3)
|
|
19
|
-
concurrent-ruby (1.3.
|
|
19
|
+
concurrent-ruby (1.3.6)
|
|
20
20
|
diff-lcs (1.6.2)
|
|
21
21
|
faraday (2.14.0)
|
|
22
22
|
faraday-net_http (>= 2.0, < 3.5)
|
|
@@ -24,20 +24,20 @@ GEM
|
|
|
24
24
|
logger
|
|
25
25
|
faraday-net_http (3.4.2)
|
|
26
26
|
net-http (~> 0.5)
|
|
27
|
-
faraday-retry (2.
|
|
27
|
+
faraday-retry (2.4.0)
|
|
28
28
|
faraday (~> 2.0)
|
|
29
|
-
ffi (1.17.
|
|
30
|
-
ffi (1.17.
|
|
31
|
-
ffi (1.17.
|
|
32
|
-
ffi (1.17.
|
|
33
|
-
ffi (1.17.
|
|
34
|
-
ffi (1.17.
|
|
35
|
-
ffi (1.17.
|
|
36
|
-
ffi (1.17.
|
|
37
|
-
ffi (1.17.
|
|
38
|
-
ffi (1.17.
|
|
39
|
-
ffi (1.17.
|
|
40
|
-
formatador (1.2.
|
|
29
|
+
ffi (1.17.3)
|
|
30
|
+
ffi (1.17.3-aarch64-linux-gnu)
|
|
31
|
+
ffi (1.17.3-aarch64-linux-musl)
|
|
32
|
+
ffi (1.17.3-arm-linux-gnu)
|
|
33
|
+
ffi (1.17.3-arm-linux-musl)
|
|
34
|
+
ffi (1.17.3-arm64-darwin)
|
|
35
|
+
ffi (1.17.3-x86-linux-gnu)
|
|
36
|
+
ffi (1.17.3-x86-linux-musl)
|
|
37
|
+
ffi (1.17.3-x86_64-darwin)
|
|
38
|
+
ffi (1.17.3-x86_64-linux-gnu)
|
|
39
|
+
ffi (1.17.3-x86_64-linux-musl)
|
|
40
|
+
formatador (1.2.3)
|
|
41
41
|
reline
|
|
42
42
|
fuubar (2.5.1)
|
|
43
43
|
rspec-core (~> 3.0)
|
|
@@ -73,31 +73,31 @@ GEM
|
|
|
73
73
|
gapic-common (~> 1.2)
|
|
74
74
|
google-cloud-errors (~> 1.0)
|
|
75
75
|
google-logging-utils (0.2.0)
|
|
76
|
-
google-protobuf (4.33.
|
|
76
|
+
google-protobuf (4.33.2)
|
|
77
77
|
bigdecimal
|
|
78
78
|
rake (>= 13)
|
|
79
|
-
google-protobuf (4.33.
|
|
79
|
+
google-protobuf (4.33.2-aarch64-linux-gnu)
|
|
80
80
|
bigdecimal
|
|
81
81
|
rake (>= 13)
|
|
82
|
-
google-protobuf (4.33.
|
|
82
|
+
google-protobuf (4.33.2-aarch64-linux-musl)
|
|
83
83
|
bigdecimal
|
|
84
84
|
rake (>= 13)
|
|
85
|
-
google-protobuf (4.33.
|
|
85
|
+
google-protobuf (4.33.2-arm64-darwin)
|
|
86
86
|
bigdecimal
|
|
87
87
|
rake (>= 13)
|
|
88
|
-
google-protobuf (4.33.
|
|
88
|
+
google-protobuf (4.33.2-x86-linux-gnu)
|
|
89
89
|
bigdecimal
|
|
90
90
|
rake (>= 13)
|
|
91
|
-
google-protobuf (4.33.
|
|
91
|
+
google-protobuf (4.33.2-x86-linux-musl)
|
|
92
92
|
bigdecimal
|
|
93
93
|
rake (>= 13)
|
|
94
|
-
google-protobuf (4.33.
|
|
94
|
+
google-protobuf (4.33.2-x86_64-darwin)
|
|
95
95
|
bigdecimal
|
|
96
96
|
rake (>= 13)
|
|
97
|
-
google-protobuf (4.33.
|
|
97
|
+
google-protobuf (4.33.2-x86_64-linux-gnu)
|
|
98
98
|
bigdecimal
|
|
99
99
|
rake (>= 13)
|
|
100
|
-
google-protobuf (4.33.
|
|
100
|
+
google-protobuf (4.33.2-x86_64-linux-musl)
|
|
101
101
|
bigdecimal
|
|
102
102
|
rake (>= 13)
|
|
103
103
|
googleapis-common-protos (1.9.0)
|
|
@@ -106,7 +106,7 @@ GEM
|
|
|
106
106
|
grpc (~> 1.41)
|
|
107
107
|
googleapis-common-protos-types (1.22.0)
|
|
108
108
|
google-protobuf (~> 4.26)
|
|
109
|
-
googleauth (1.
|
|
109
|
+
googleauth (1.16.0)
|
|
110
110
|
faraday (>= 1.0, < 3.a)
|
|
111
111
|
google-cloud-env (~> 2.2)
|
|
112
112
|
google-logging-utils (~> 0.1)
|
|
@@ -157,8 +157,8 @@ GEM
|
|
|
157
157
|
guard (~> 2.1)
|
|
158
158
|
guard-compat (~> 1.1)
|
|
159
159
|
rspec (>= 2.99.0, < 4.0)
|
|
160
|
-
io-console (0.8.
|
|
161
|
-
json (2.
|
|
160
|
+
io-console (0.8.2)
|
|
161
|
+
json (2.18.0)
|
|
162
162
|
jwt (3.1.2)
|
|
163
163
|
base64
|
|
164
164
|
language_server-protocol (3.17.0.5)
|
|
@@ -169,10 +169,10 @@ GEM
|
|
|
169
169
|
logger (1.7.0)
|
|
170
170
|
lumberjack (1.4.2)
|
|
171
171
|
method_source (1.1.0)
|
|
172
|
-
multi_json (1.
|
|
172
|
+
multi_json (1.19.1)
|
|
173
173
|
nenv (0.3.0)
|
|
174
|
-
net-http (0.
|
|
175
|
-
uri
|
|
174
|
+
net-http (0.9.1)
|
|
175
|
+
uri (>= 0.11.1)
|
|
176
176
|
notiffany (0.1.3)
|
|
177
177
|
nenv (~> 0.1)
|
|
178
178
|
shellany (~> 0.0)
|
|
@@ -186,11 +186,11 @@ GEM
|
|
|
186
186
|
parser (3.3.10.0)
|
|
187
187
|
ast (~> 2.4.1)
|
|
188
188
|
racc
|
|
189
|
-
prism (1.
|
|
189
|
+
prism (1.7.0)
|
|
190
190
|
pry (0.14.2)
|
|
191
191
|
coderay (~> 1.1)
|
|
192
192
|
method_source (~> 1.0)
|
|
193
|
-
public_suffix (
|
|
193
|
+
public_suffix (7.0.2)
|
|
194
194
|
racc (1.8.1)
|
|
195
195
|
rainbow (3.1.1)
|
|
196
196
|
rake (13.3.1)
|
|
@@ -214,7 +214,7 @@ GEM
|
|
|
214
214
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
215
215
|
rspec-support (~> 3.13.0)
|
|
216
216
|
rspec-support (3.13.6)
|
|
217
|
-
rubocop (1.
|
|
217
|
+
rubocop (1.82.1)
|
|
218
218
|
json (~> 2.3)
|
|
219
219
|
language_server-protocol (~> 3.17.0.2)
|
|
220
220
|
lint_roller (~> 1.1.0)
|
|
@@ -222,16 +222,16 @@ GEM
|
|
|
222
222
|
parser (>= 3.3.0.2)
|
|
223
223
|
rainbow (>= 2.2.2, < 4.0)
|
|
224
224
|
regexp_parser (>= 2.9.3, < 3.0)
|
|
225
|
-
rubocop-ast (>= 1.
|
|
225
|
+
rubocop-ast (>= 1.48.0, < 2.0)
|
|
226
226
|
ruby-progressbar (~> 1.7)
|
|
227
227
|
unicode-display_width (>= 2.4.0, < 4.0)
|
|
228
|
-
rubocop-ast (1.
|
|
228
|
+
rubocop-ast (1.49.0)
|
|
229
229
|
parser (>= 3.3.7.2)
|
|
230
|
-
prism (~> 1.
|
|
230
|
+
prism (~> 1.7)
|
|
231
231
|
rubocop-capybara (2.22.1)
|
|
232
232
|
lint_roller (~> 1.1)
|
|
233
233
|
rubocop (~> 1.72, >= 1.72.1)
|
|
234
|
-
rubocop-factory_bot (2.
|
|
234
|
+
rubocop-factory_bot (2.28.0)
|
|
235
235
|
lint_roller (~> 1.1)
|
|
236
236
|
rubocop (~> 1.72, >= 1.72.1)
|
|
237
237
|
rubocop-rspec (2.31.0)
|
|
@@ -254,7 +254,7 @@ GEM
|
|
|
254
254
|
thor (1.4.0)
|
|
255
255
|
unicode-display_width (3.2.0)
|
|
256
256
|
unicode-emoji (~> 4.1)
|
|
257
|
-
unicode-emoji (4.
|
|
257
|
+
unicode-emoji (4.2.0)
|
|
258
258
|
uri (1.1.1)
|
|
259
259
|
|
|
260
260
|
PLATFORMS
|
|
@@ -267,6 +267,7 @@ PLATFORMS
|
|
|
267
267
|
x86-linux-gnu
|
|
268
268
|
x86-linux-musl
|
|
269
269
|
x86_64-darwin
|
|
270
|
+
x86_64-linux
|
|
270
271
|
x86_64-linux-gnu
|
|
271
272
|
x86_64-linux-musl
|
|
272
273
|
|
data/README.md
CHANGED
|
@@ -110,9 +110,11 @@ Options:
|
|
|
110
110
|
```sh
|
|
111
111
|
Commands:
|
|
112
112
|
imt remove help [COMMAND] # Describe subcommands or one specific subcommand
|
|
113
|
+
imt remove mod MOD_ID # Removes a mod from the 'mods' collection
|
|
113
114
|
imt remove modinfo ITEM # Removes an entry from 'meta/modinfo/list'
|
|
114
|
-
imt remove
|
|
115
|
-
imt remove
|
|
115
|
+
imt remove repos REPO # Removes an entry from 'meta/repos/list' and cascades to associated mods/tools
|
|
116
|
+
imt remove tool TOOL_ID # Removes a tool from the 'tools' collection
|
|
117
|
+
imt remove toolinfo ITEM # Removes an entry from 'meta/toolinfo/list'
|
|
116
118
|
|
|
117
119
|
Options:
|
|
118
120
|
-C, [--config=CONFIG] # Path to the config file
|
|
@@ -120,6 +122,9 @@ Options:
|
|
|
120
122
|
-V, [--version], [--no-version] # Print the version and exit
|
|
121
123
|
-v, [--verbose], [--no-verbose] # Increase verbosity. May be repeated for even more verbosity.
|
|
122
124
|
# Default: [true]
|
|
125
|
+
[--dry-run], [--no-dry-run] # Dry run (no changes will be made)
|
|
126
|
+
[--cascade], [--no-cascade] # Also remove associated modinfo, toolinfo, mods, and tools entries (for repos only)
|
|
127
|
+
# Default: true
|
|
123
128
|
```
|
|
124
129
|
|
|
125
130
|
#### `imt sync`
|
|
@@ -142,6 +147,8 @@ Options:
|
|
|
142
147
|
[--dry-run], [--no-dry-run] # Dry run (no changes will be made)
|
|
143
148
|
```
|
|
144
149
|
|
|
150
|
+
**Note**: Sync operations automatically convert GitHub URLs to the correct `raw.githubusercontent.com` format for direct downloads. If you're using GitHub URLs in `modinfo.json` or `toolinfo.json` files, you can use either `/blob/` or `/raw/` formats and they will be automatically converted. Warnings will be displayed when URLs are auto-fixed so you can update your source files.
|
|
151
|
+
|
|
145
152
|
## Development
|
|
146
153
|
|
|
147
154
|
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.
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require "firestore"
|
|
4
4
|
require "cli/subcommand_base"
|
|
5
|
+
require "tools/sync/helpers"
|
|
5
6
|
|
|
6
7
|
module Icarus
|
|
7
8
|
module Mod
|
|
@@ -9,19 +10,36 @@ module Icarus
|
|
|
9
10
|
# Remove CLI command definitions
|
|
10
11
|
# rubocop:disable Style/GlobalVars
|
|
11
12
|
class Remove < SubcommandBase
|
|
13
|
+
include Tools::Sync::Helpers
|
|
12
14
|
class_option :dry_run, type: :boolean, default: false, desc: "Dry run (no changes will be made)"
|
|
13
15
|
|
|
14
|
-
desc "repos REPO", "Removes an entry from 'meta/repos/list'"
|
|
16
|
+
desc "repos REPO", "Removes an entry from 'meta/repos/list' and cascades to associated mods/tools"
|
|
17
|
+
method_option :cascade, type: :boolean, default: true,
|
|
18
|
+
desc: "Also remove associated modinfo, toolinfo, mods, and tools entries"
|
|
15
19
|
def repos(repo)
|
|
16
20
|
repo_name = repo.gsub(%r{https?://.*github\.com/}, "")
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
"Repository"
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
|
|
22
|
+
# Check if repository exists
|
|
23
|
+
unless firestore.repositories.include?(repo_name)
|
|
24
|
+
warn "Repository not found: #{repo_name}"
|
|
25
|
+
exit 1
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
puts Paint["Removing repository: #{repo_name}", :black] if verbose?
|
|
29
|
+
|
|
30
|
+
if options[:cascade]
|
|
31
|
+
cascade_delete_repo(repo_name)
|
|
32
|
+
else
|
|
33
|
+
# Just remove from repositories list
|
|
34
|
+
remove_item(
|
|
35
|
+
:repositories,
|
|
36
|
+
repo_name,
|
|
37
|
+
"Repository",
|
|
38
|
+
"Repository not found: #{repo_name}",
|
|
39
|
+
"Successfully removed repository: #{repo_name}",
|
|
40
|
+
"Failed to remove repository: #{repo_name}"
|
|
41
|
+
)
|
|
42
|
+
end
|
|
25
43
|
end
|
|
26
44
|
|
|
27
45
|
desc "modinfo ITEM", "Removes an entry from 'meta/modinfo/list'"
|
|
@@ -48,6 +66,30 @@ module Icarus
|
|
|
48
66
|
)
|
|
49
67
|
end
|
|
50
68
|
|
|
69
|
+
desc "mod MOD_ID", "Removes a mod from the 'mods' collection"
|
|
70
|
+
def mod(mod_id)
|
|
71
|
+
remove_entity(
|
|
72
|
+
:mod,
|
|
73
|
+
mod_id,
|
|
74
|
+
"Mod",
|
|
75
|
+
"Mod not found with ID: #{mod_id}",
|
|
76
|
+
"Successfully removed mod: #{mod_id}",
|
|
77
|
+
"Failed to remove mod: #{mod_id}"
|
|
78
|
+
)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
desc "tool TOOL_ID", "Removes a tool from the 'tools' collection"
|
|
82
|
+
def tool(tool_id)
|
|
83
|
+
remove_entity(
|
|
84
|
+
:tool,
|
|
85
|
+
tool_id,
|
|
86
|
+
"Tool",
|
|
87
|
+
"Tool not found with ID: #{tool_id}",
|
|
88
|
+
"Successfully removed tool: #{tool_id}",
|
|
89
|
+
"Failed to remove tool: #{tool_id}"
|
|
90
|
+
)
|
|
91
|
+
end
|
|
92
|
+
|
|
51
93
|
private
|
|
52
94
|
|
|
53
95
|
def remove_item(type, item, display_name, not_found_msg, success_msg, failure_msg)
|
|
@@ -78,6 +120,205 @@ module Icarus
|
|
|
78
120
|
end
|
|
79
121
|
end
|
|
80
122
|
|
|
123
|
+
def remove_entity(type, entity_id, display_name, not_found_msg, success_msg, failure_msg)
|
|
124
|
+
# Find the entity by ID
|
|
125
|
+
collection = type == :mod ? firestore.mods : firestore.tools
|
|
126
|
+
entity = collection.find { |e| e.id == entity_id }
|
|
127
|
+
|
|
128
|
+
unless entity
|
|
129
|
+
warn not_found_msg
|
|
130
|
+
exit 1
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
puts Paint["Removing #{display_name.downcase}: #{entity.name} (ID: #{entity_id})", :black] if verbose?
|
|
134
|
+
|
|
135
|
+
if options[:dry_run]
|
|
136
|
+
puts Paint["Dry run; no changes will be made", :yellow]
|
|
137
|
+
return
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
if firestore.delete(type, entity)
|
|
141
|
+
puts Paint[success_msg, :green]
|
|
142
|
+
else
|
|
143
|
+
warn Paint[failure_msg, :red]
|
|
144
|
+
exit 1
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def cascade_delete_repo(repo_name)
|
|
149
|
+
@delete_failures = []
|
|
150
|
+
|
|
151
|
+
# Find all modinfo URLs belonging to this repository
|
|
152
|
+
# Match full "owner/repo" path component, not substring
|
|
153
|
+
repo_pattern = %r{/#{Regexp.escape(repo_name)}(?=/|$)}
|
|
154
|
+
modinfo_urls = firestore.modinfo.select { |url| url.match?(repo_pattern) }
|
|
155
|
+
toolinfo_urls = firestore.toolinfo.select { |url| url.match?(repo_pattern) }
|
|
156
|
+
|
|
157
|
+
puts Paint["Found #{modinfo_urls.size} modinfo entries and #{toolinfo_urls.size} toolinfo entries", :cyan] if verbose?
|
|
158
|
+
|
|
159
|
+
if options[:dry_run]
|
|
160
|
+
puts Paint["Dry run; no changes will be made", :yellow]
|
|
161
|
+
preview = preview_cascade_deletions(repo_name, modinfo_urls, toolinfo_urls)
|
|
162
|
+
display_cascade_preview(repo_name, preview)
|
|
163
|
+
return
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# Delete modinfo URLs and their associated mods
|
|
167
|
+
modinfo_urls.each do |url|
|
|
168
|
+
puts Paint[" Removing modinfo: #{url}", :black] if verbose?
|
|
169
|
+
track_delete(:modinfo, url) { firestore.delete(:modinfo, url) }
|
|
170
|
+
|
|
171
|
+
# Find and delete associated mods
|
|
172
|
+
delete_entities_from_url(url, :mod)
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# Delete toolinfo URLs and their associated tools
|
|
176
|
+
toolinfo_urls.each do |url|
|
|
177
|
+
puts Paint[" Removing toolinfo: #{url}", :black] if verbose?
|
|
178
|
+
track_delete(:toolinfo, url) { firestore.delete(:toolinfo, url) }
|
|
179
|
+
|
|
180
|
+
# Find and delete associated tools
|
|
181
|
+
delete_entities_from_url(url, :tool)
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# Report any failures
|
|
185
|
+
report_delete_failures
|
|
186
|
+
report_fetch_failures
|
|
187
|
+
|
|
188
|
+
# Finally, remove the repository
|
|
189
|
+
if firestore.delete(:repositories, repo_name)
|
|
190
|
+
puts Paint["Successfully removed repository and all associated entries: #{repo_name}", :green]
|
|
191
|
+
else
|
|
192
|
+
warn Paint["Failed to remove repository: #{repo_name}", :red]
|
|
193
|
+
exit 1
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def delete_entities_from_url(url, type)
|
|
198
|
+
# Fetch the modinfo/toolinfo JSON
|
|
199
|
+
begin
|
|
200
|
+
data = retrieve_from_url(url)
|
|
201
|
+
entities = data[type == :mod ? :mods : :tools] || []
|
|
202
|
+
|
|
203
|
+
entities.each do |entity_data|
|
|
204
|
+
# Find ALL matching entities in Firestore by name and author
|
|
205
|
+
collection = type == :mod ? firestore.mods : firestore.tools
|
|
206
|
+
matching_entities = collection.select do |e|
|
|
207
|
+
e.name == entity_data[:name] && e.author == entity_data[:author]
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
next if matching_entities.empty?
|
|
211
|
+
|
|
212
|
+
if matching_entities.size > 1 && verbose?
|
|
213
|
+
warn Paint[" Note: Found #{matching_entities.size} entities matching '#{entity_data[:name]}' by #{entity_data[:author]}", :yellow]
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
matching_entities.each do |entity|
|
|
217
|
+
puts Paint[" Removing #{type}: #{entity.name} (ID: #{entity.id})", :black] if verbose?
|
|
218
|
+
track_delete(type, "#{entity.name} (#{entity.id})") { firestore.delete(type, entity) }
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
rescue SocketError, IOError, SystemCallError, Timeout::Error, JSON::ParserError => e
|
|
222
|
+
@failed_entity_fetches ||= []
|
|
223
|
+
@failed_entity_fetches << { url: url, error: e.class.name, message: e.message }
|
|
224
|
+
warn Paint["Warning: Could not fetch #{url} to remove entities: #{e.message}", :yellow]
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def preview_cascade_deletions(repo_name, modinfo_urls, toolinfo_urls)
|
|
229
|
+
mods = []
|
|
230
|
+
tools = []
|
|
231
|
+
|
|
232
|
+
modinfo_urls.each do |url|
|
|
233
|
+
entities = fetch_entities_from_url(url, :mod)
|
|
234
|
+
mods.concat(entities) if entities
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
toolinfo_urls.each do |url|
|
|
238
|
+
entities = fetch_entities_from_url(url, :tool)
|
|
239
|
+
tools.concat(entities) if entities
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
{
|
|
243
|
+
modinfo_urls: modinfo_urls,
|
|
244
|
+
toolinfo_urls: toolinfo_urls,
|
|
245
|
+
mods: mods,
|
|
246
|
+
tools: tools
|
|
247
|
+
}
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
def fetch_entities_from_url(url, type)
|
|
251
|
+
data = retrieve_from_url(url)
|
|
252
|
+
entity_data_list = data[type == :mod ? :mods : :tools] || []
|
|
253
|
+
|
|
254
|
+
collection = type == :mod ? firestore.mods : firestore.tools
|
|
255
|
+
entities = []
|
|
256
|
+
|
|
257
|
+
entity_data_list.each do |entity_data|
|
|
258
|
+
matching_entities = collection.select do |e|
|
|
259
|
+
e.name == entity_data[:name] && e.author == entity_data[:author]
|
|
260
|
+
end
|
|
261
|
+
entities.concat(matching_entities)
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
entities
|
|
265
|
+
rescue SocketError, IOError, SystemCallError, Timeout::Error, JSON::ParserError => e
|
|
266
|
+
warn Paint["Warning: Could not fetch #{url}: #{e.message}", :yellow] if verbose?
|
|
267
|
+
nil
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
def display_cascade_preview(repo_name, preview)
|
|
271
|
+
puts "Would remove:"
|
|
272
|
+
puts " - Repository: #{repo_name}"
|
|
273
|
+
|
|
274
|
+
if preview[:modinfo_urls].any?
|
|
275
|
+
puts " - Modinfo URLs: #{preview[:modinfo_urls].size}"
|
|
276
|
+
preview[:modinfo_urls].each { |url| puts " • #{url}" }
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
if preview[:mods].any?
|
|
280
|
+
puts " - Mods: #{preview[:mods].size}"
|
|
281
|
+
preview[:mods].each { |mod| puts " • #{mod.name} by #{mod.author} (ID: #{mod.id})" }
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
if preview[:toolinfo_urls].any?
|
|
285
|
+
puts " - Toolinfo URLs: #{preview[:toolinfo_urls].size}"
|
|
286
|
+
preview[:toolinfo_urls].each { |url| puts " • #{url}" }
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
if preview[:tools].any?
|
|
290
|
+
puts " - Tools: #{preview[:tools].size}"
|
|
291
|
+
preview[:tools].each { |tool| puts " • #{tool.name} by #{tool.author} (ID: #{tool.id})" }
|
|
292
|
+
end
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
def track_delete(type, identifier)
|
|
296
|
+
success = yield
|
|
297
|
+
unless success
|
|
298
|
+
@delete_failures ||= []
|
|
299
|
+
@delete_failures << { type: type, identifier: identifier.to_s }
|
|
300
|
+
end
|
|
301
|
+
success
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
def report_delete_failures
|
|
305
|
+
return unless @delete_failures&.any?
|
|
306
|
+
|
|
307
|
+
warn Paint["\nWarning: #{@delete_failures.size} delete operation(s) failed:", :red]
|
|
308
|
+
@delete_failures.each do |failure|
|
|
309
|
+
warn Paint[" • #{failure[:type]}: #{failure[:identifier]}", :red]
|
|
310
|
+
end
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
def report_fetch_failures
|
|
314
|
+
return unless @failed_entity_fetches&.any?
|
|
315
|
+
|
|
316
|
+
warn Paint["\nWarning: Failed to fetch #{@failed_entity_fetches.size} URL(s) - some entities may not have been deleted:", :yellow]
|
|
317
|
+
@failed_entity_fetches.each do |failure|
|
|
318
|
+
warn Paint[" • #{failure[:url]} (#{failure[:error]})", :yellow]
|
|
319
|
+
end
|
|
320
|
+
end
|
|
321
|
+
|
|
81
322
|
def firestore
|
|
82
323
|
$firestore ||= Firestore.new
|
|
83
324
|
end
|
data/lib/icarus/mod/firestore.rb
CHANGED
|
@@ -66,6 +66,12 @@ module Icarus
|
|
|
66
66
|
case type.to_sym
|
|
67
67
|
when :mod, :tool
|
|
68
68
|
response = @client.doc("#{collections.send(pluralize(type))}/#{payload.id}").delete
|
|
69
|
+
# Invalidate cache to prevent stale data
|
|
70
|
+
if response.is_a?(Google::Cloud::Firestore::CommitResponse::WriteResult)
|
|
71
|
+
cache_var = type == :mod ? :@mods : :@tools
|
|
72
|
+
cached_collection = instance_variable_get(cache_var)
|
|
73
|
+
cached_collection&.delete_if { |item| item.id == payload.id }
|
|
74
|
+
end
|
|
69
75
|
when :modinfo, :toolinfo, :repositories
|
|
70
76
|
update_array = (send(type) - [payload]).flatten.uniq
|
|
71
77
|
|
data/lib/icarus/mod/github.rb
CHANGED
|
@@ -36,14 +36,16 @@ module Icarus
|
|
|
36
36
|
if use_cache
|
|
37
37
|
@resources.each { |file| block.call(file) } if block
|
|
38
38
|
else
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
begin
|
|
40
|
+
@client.contents(repository, path:).each do |entry|
|
|
41
|
+
if entry[:type] == "dir"
|
|
42
|
+
all_files(path: entry[:path], cache: false, recursive: true, &block) if recursive
|
|
43
|
+
next # we don't need directories in our output
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
block&.call(entry)
|
|
47
|
+
@resources << entry # cache the file
|
|
43
48
|
end
|
|
44
|
-
|
|
45
|
-
block&.call(entry)
|
|
46
|
-
@resources << entry # cache the file
|
|
47
49
|
rescue Octokit::NotFound
|
|
48
50
|
warn "WARNING: Could not access #{repository}: 404 - not found"
|
|
49
51
|
end
|
|
@@ -9,6 +9,15 @@ module Icarus
|
|
|
9
9
|
|
|
10
10
|
HASHKEYS = %i[name author version compatibility description files imageURL readmeURL].freeze
|
|
11
11
|
|
|
12
|
+
# Match github.com URLs with /blob/ or /raw/ and convert to raw.githubusercontent.com
|
|
13
|
+
GITHUB_BLOB_RAW_URL_PATTERN = %r{
|
|
14
|
+
(https?)://(?:www\.)?github\.com/ # Protocol and GitHub domain (capture group 1)
|
|
15
|
+
([^/]+)/ # owner (capture group 2)
|
|
16
|
+
([^/]+)/ # repo (capture group 3)
|
|
17
|
+
(?:blob|raw)/ # /blob/ or /raw/ to remove
|
|
18
|
+
(.+) # branch and file path (capture group 4)
|
|
19
|
+
}x
|
|
20
|
+
|
|
12
21
|
def initialize(data, id: nil, created: nil, updated: nil)
|
|
13
22
|
@id = id
|
|
14
23
|
@created_at = created
|
|
@@ -26,6 +35,8 @@ module Icarus
|
|
|
26
35
|
|
|
27
36
|
def read(data)
|
|
28
37
|
@data = data.is_a?(String) ? JSON.parse(data, symbolize_names: true) : data
|
|
38
|
+
normalize_github_urls_in_data
|
|
39
|
+
@data
|
|
29
40
|
end
|
|
30
41
|
|
|
31
42
|
def errors
|
|
@@ -147,6 +158,26 @@ module Icarus
|
|
|
147
158
|
@warnings << "Version should be a version string" unless /^\d+[.\d+]*/.match?(version)
|
|
148
159
|
end
|
|
149
160
|
end
|
|
161
|
+
|
|
162
|
+
def normalize_github_url(url)
|
|
163
|
+
return url if url.nil? || url.empty?
|
|
164
|
+
|
|
165
|
+
if url.match?(GITHUB_BLOB_RAW_URL_PATTERN)
|
|
166
|
+
normalized = url.gsub(GITHUB_BLOB_RAW_URL_PATTERN, '\1://raw.githubusercontent.com/\2/\3/\4')
|
|
167
|
+
@warnings << "GitHub URL converted to raw.githubusercontent.com format for direct download. Auto-fixed: #{url}"
|
|
168
|
+
normalized
|
|
169
|
+
else
|
|
170
|
+
url
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def normalize_github_urls_in_data
|
|
175
|
+
# Skip normalization if data is frozen (e.g., from Firestore)
|
|
176
|
+
return if @data.frozen?
|
|
177
|
+
|
|
178
|
+
@data[:imageURL] = normalize_github_url(@data[:imageURL])
|
|
179
|
+
@data[:readmeURL] = normalize_github_url(@data[:readmeURL])
|
|
180
|
+
end
|
|
150
181
|
end
|
|
151
182
|
end
|
|
152
183
|
end
|
|
@@ -21,6 +21,22 @@ module Icarus
|
|
|
21
21
|
|
|
22
22
|
super
|
|
23
23
|
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def normalize_github_urls_in_data
|
|
28
|
+
super # Handle imageURL and readmeURL
|
|
29
|
+
|
|
30
|
+
# Skip normalization if data is frozen (e.g., from Firestore)
|
|
31
|
+
return if @data.frozen?
|
|
32
|
+
|
|
33
|
+
# Normalize each file URL in the files hash
|
|
34
|
+
return unless @data[:files].is_a?(Hash)
|
|
35
|
+
|
|
36
|
+
@data[:files].transform_values! do |url|
|
|
37
|
+
normalize_github_url(url)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
24
40
|
end
|
|
25
41
|
end
|
|
26
42
|
end
|
|
@@ -21,6 +21,16 @@ module Icarus
|
|
|
21
21
|
|
|
22
22
|
private
|
|
23
23
|
|
|
24
|
+
def normalize_github_urls_in_data
|
|
25
|
+
super # Handle imageURL and readmeURL
|
|
26
|
+
|
|
27
|
+
# Skip normalization if data is frozen (e.g., from Firestore)
|
|
28
|
+
return if @data.frozen?
|
|
29
|
+
|
|
30
|
+
# Normalize fileURL
|
|
31
|
+
@data[:fileURL] = normalize_github_url(@data[:fileURL])
|
|
32
|
+
end
|
|
33
|
+
|
|
24
34
|
def filetype_pattern
|
|
25
35
|
/(zip|exe)/i
|
|
26
36
|
end
|
data/lib/icarus/mod/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: Icarus-Mod-Tools
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Donovan Young
|
|
@@ -73,7 +73,7 @@ executables:
|
|
|
73
73
|
extensions: []
|
|
74
74
|
extra_rdoc_files: []
|
|
75
75
|
files:
|
|
76
|
-
- ".claude
|
|
76
|
+
- ".claude-context.md"
|
|
77
77
|
- ".rspec"
|
|
78
78
|
- ".ruby-version"
|
|
79
79
|
- CHANGELOG.md
|