6
6
import requests
7
7
import shutil
8
8
import logging
9
- from gm4 .utils import run
9
+ from gm4 .utils import run , Version , NoneAttribute
10
+ import gm4 .plugins .manifest # for ManifestCacheModel; a runtime circular dependency
10
11
11
12
parent_logger = logging .getLogger ("gm4.output" )
12
13
19
20
20
21
class ModrinthConfig (PluginOptions ):
21
22
project_id : Optional [str ]
23
+ minecraft : list [str ] = SUPPORTED_GAME_VERSIONS
22
24
23
25
class SmithedConfig (PluginOptions ):
24
26
pack_id : Optional [str ]
27
+ minecraft : list [str ] = SUPPORTED_GAME_VERSIONS
25
28
26
29
class PMCConfig (PluginOptions ):
27
30
uid : Optional [int ]
@@ -55,7 +58,10 @@ def release(ctx: Context):
55
58
"""
56
59
version_dir = os .getenv ("VERSION" , "1.20" )
57
60
release_dir = Path ("release" ) / version_dir
58
- file_name = f"{ ctx .project_id } _{ version_dir .replace ('.' , '_' )} .zip"
61
+
62
+ corrected_project_id = stem if (stem := ctx .directory .stem ).startswith ("lib" ) else ctx .project_id
63
+
64
+ file_name = f"{ corrected_project_id } _{ version_dir .replace ('.' , '_' )} .zip"
59
65
60
66
yield # wait for exit phase, after other plugins cleanup
61
67
@@ -70,37 +76,36 @@ def release(ctx: Context):
70
76
pack_icon_dir = generated_dir / "pack_icons"
71
77
os .makedirs (pack_icon_dir , exist_ok = True )
72
78
if "pack.png" in ctx .data .extra :
73
- ctx .data .extra ["pack.png" ].dump (pack_icon_dir , f"{ ctx . project_id } .png" )
79
+ ctx .data .extra ["pack.png" ].dump (pack_icon_dir , f"{ corrected_project_id } .png" )
74
80
75
81
smithed_readme_dir = generated_dir / "smithed_readmes"
76
82
os .makedirs (smithed_readme_dir , exist_ok = True )
77
83
if "smithed_readme" in ctx .meta :
78
- ctx .meta ['smithed_readme' ].dump (smithed_readme_dir , f"{ ctx . project_id } .md" )
84
+ ctx .meta ['smithed_readme' ].dump (smithed_readme_dir , f"{ corrected_project_id } .md" )
79
85
80
86
# publish to download platforms
81
87
publish_modrinth (ctx , release_dir , file_name )
82
- publish_smithed (ctx , release_dir , file_name )
88
+ publish_smithed (ctx , file_name )
89
+
83
90
84
91
def publish_modrinth (ctx : Context , release_dir : Path , file_name : str ):
85
92
'''Attempts to publish pack to modrinth'''
86
- modrinth = ctx .meta . get ("modrinth" , None )
93
+ opts = ctx .validate ("modrinth" , ModrinthConfig )
87
94
auth_token = os .getenv (MODRINTH_AUTH_KEY , None )
88
95
logger = parent_logger .getChild (f"modrinth.{ ctx .project_id } " )
89
- if modrinth and auth_token :
90
- modrinth_id = modrinth ["project_id" ]
91
-
96
+ if opts .project_id and auth_token :
92
97
# update page description
93
- res = requests .get (f"{ MODRINTH_API } /project/{ modrinth_id } " , headers = {'Authorization' : auth_token , 'User-Agent' : USER_AGENT })
98
+ res = requests .get (f"{ MODRINTH_API } /project/{ opts . project_id } " , headers = {'Authorization' : auth_token , 'User-Agent' : USER_AGENT })
94
99
if not (200 <= res .status_code < 300 ):
95
100
if res .status_code == 404 :
96
- logger .warning (f"Cannot edit description of modrinth project { modrinth_id } as it doesn't exist." )
101
+ logger .warning (f"Cannot edit description of modrinth project { opts . project_id } as it doesn't exist." )
97
102
else :
98
103
logger .warning (f"Failed to get project: { res .status_code } { res .text } " )
99
104
return
100
105
existing_readme = res .json ()["body" ]
101
106
if existing_readme != (d := ctx .meta ['modrinth_readme' ].text ):
102
107
logger .debug ("Readme and modrinth-page content differ. Updating webpage body" )
103
- res = requests .patch (f"{ MODRINTH_API } /project/{ modrinth_id } " , headers = {'Authorization' : auth_token , 'User-Agent' : USER_AGENT }, json = {"body" : d })
108
+ res = requests .patch (f"{ MODRINTH_API } /project/{ opts . project_id } " , headers = {'Authorization' : auth_token , 'User-Agent' : USER_AGENT }, json = {"body" : d })
104
109
if not (200 <= res .status_code < 300 ):
105
110
logger .warning (f"Failed to update description: { res .status_code } { res .text } " )
106
111
logger .info (f"Successfully updated description of { ctx .project_name } " )
@@ -112,16 +117,16 @@ def publish_modrinth(ctx: Context, release_dir: Path, file_name: str):
112
117
logger .warning ("Full version number not available in ctx.meta. Skipping publishing" )
113
118
return
114
119
115
- res = requests .get (f"{ MODRINTH_API } /project/{ modrinth_id } /version" , headers = {'Authorization' : auth_token , 'User-Agent' : USER_AGENT })
120
+ res = requests .get (f"{ MODRINTH_API } /project/{ opts . project_id } /version" , headers = {'Authorization' : auth_token , 'User-Agent' : USER_AGENT })
116
121
if not (200 <= res .status_code < 300 ):
117
122
if res .status_code == 404 :
118
- logger .warning (f"Cannot publish to modrinth project { modrinth_id } as it doesn't exist." )
123
+ logger .warning (f"Cannot publish to modrinth project { opts . project_id } as it doesn't exist." )
119
124
else :
120
125
logger .warning (f"Failed to get project versions: { res .status_code } { res .text } " )
121
126
return
122
127
project_data = res .json ()
123
128
matching_version = next ((v for v in project_data if v ["version_number" ] == str (version )), None )
124
- game_versions = modrinth . get ( " minecraft" , SUPPORTED_GAME_VERSIONS )
129
+ game_versions = opts . minecraft
125
130
if matching_version is not None : # patch version already exists
126
131
# update mc versions if necessary
127
132
if not set (matching_version ["game_versions" ]) == set (game_versions ):
@@ -148,7 +153,7 @@ def publish_modrinth(ctx: Context, release_dir: Path, file_name: str):
148
153
"version_type" : "release" ,
149
154
"loaders" : ["datapack" ],
150
155
"featured" : False ,
151
- "project_id" : modrinth_id ,
156
+ "project_id" : opts . project_id ,
152
157
"file_parts" : [file_name ],
153
158
}),
154
159
file_name : file_bytes ,
@@ -158,54 +163,58 @@ def publish_modrinth(ctx: Context, release_dir: Path, file_name: str):
158
163
return
159
164
logger .info (f"Successfully published { res .json ()['name' ]} " )
160
165
161
- def publish_smithed (ctx : Context , release_dir : Path , file_name : str ):
166
+ def publish_smithed (ctx : Context , file_name : str ):
162
167
"""Attempts to publish pack to smithed"""
163
- smithed = ctx .meta . get ("smithed" , None )
168
+ opts = ctx .validate ("smithed" , SmithedConfig )
164
169
auth_token = os .getenv (SMITHED_AUTH_KEY , None )
165
170
logger = parent_logger .getChild (f"smithed.{ ctx .project_id } " )
166
171
mc_version_dir = os .getenv ("VERSION" , "1.20" )
167
- if smithed and auth_token and ctx .project_version :
168
- version = ctx .cache ["gm4_manifest" ].json ["modules" ].get (ctx .project_id , {}).get ("version" , None )
169
- smithed_id = smithed ["pack_id" ]
172
+ manifest = gm4 .plugins .manifest .ManifestCacheModel .parse_obj (ctx .cache ["gm4_manifest" ].json )
173
+ project_id = stem if (stem := ctx .directory .stem ).startswith ("lib" ) else ctx .project_id
174
+
175
+ if opts .pack_id and auth_token :
176
+ version = (manifest .modules | manifest .libraries ).get (project_id , NoneAttribute ()).version or ""
170
177
171
178
# get project data and existing versions
172
- res = requests .get (f"{ SMITHED_API } /packs/{ smithed_id } " )
179
+ res = requests .get (f"{ SMITHED_API } /packs/{ opts . pack_id } " )
173
180
if not (200 <= res .status_code < 300 ):
174
181
if res .status_code == 404 :
175
- logger .warning (f"Cannot publish to smithed project { smithed_id } as it doesn't exist." )
182
+ logger .warning (f"Cannot publish to smithed project { opts . pack_id } as it doesn't exist." )
176
183
else :
177
184
logger .warning (f"Failed to get project: { res .status_code } { res .text } " )
178
185
return
179
186
180
187
project_data = res .json ()
181
-
188
+
182
189
# update description and pack image
183
190
# ensures they point to the most up-to-date mc version branch
184
- project_display = project_data ["display" ]
185
- current_icon = f"https://raw.githubusercontent.com/Gamemode4Dev/GM4_Datapacks/release/{ mc_version_dir } /generated/pack_icons/{ ctx .project_id } .png"
186
- current_readme = f"https://raw.githubusercontent.com/Gamemode4Dev/GM4_Datapacks/release/{ mc_version_dir } /generated/smithed_readmes/{ ctx .project_id } .md"
187
-
188
- if project_display ["icon" ] != current_icon or project_display ["webPage" ] != current_readme :
189
- logger .debug ("Pack Icon or Readme hyperlink is incorrect. Updating project" )
190
- res = requests .patch (f"{ SMITHED_API } /packs/{ smithed_id } " , params = {'token' : auth_token },
191
- json = {"data" : {
192
- "display" : {
193
- "icon" : current_icon ,
194
- "webPage" : current_readme ,
195
- },
196
- }})
197
- if not (200 <= res .status_code < 300 ):
198
- logger .warning (f"Failed to update descripion: { res .status_code } { res .text } " )
199
- logger .info (f"{ ctx .project_name } { res .text } " )
200
-
201
191
project_versions = project_data ["versions" ]
192
+ newest_version = sorted ([Version (v ["name" ]) for v in project_versions ])[- 1 ]
193
+ if Version (version ) > newest_version : # only update the description if we're not patching an old version
194
+ project_display = project_data ["display" ]
195
+ current_icon = f"https://raw.githubusercontent.com/Gamemode4Dev/GM4_Datapacks/release/{ mc_version_dir } /generated/pack_icons/{ project_id } .png"
196
+ current_readme = f"https://raw.githubusercontent.com/Gamemode4Dev/GM4_Datapacks/release/{ mc_version_dir } /generated/smithed_readmes/{ project_id } .md"
197
+
198
+ if project_display ["icon" ] != current_icon or project_display ["webPage" ] != current_readme :
199
+ logger .debug ("Pack Icon or Readme hyperlink is incorrect. Updating project" )
200
+ res = requests .patch (f"{ SMITHED_API } /packs/{ opts .pack_id } " , params = {'token' : auth_token },
201
+ json = {"data" : {
202
+ "display" : {
203
+ "icon" : current_icon ,
204
+ "webPage" : current_readme ,
205
+ },
206
+ }})
207
+ if not (200 <= res .status_code < 300 ):
208
+ logger .warning (f"Failed to update descripion: { res .status_code } { res .text } " )
209
+ logger .info (f"{ ctx .project_name } { res .text } " )
210
+
202
211
matching_version = next ((v for v in project_versions if v ["name" ] == str (version )), None )
203
- game_versions = smithed . get ( " minecraft" , SUPPORTED_GAME_VERSIONS )
212
+ game_versions = opts . minecraft
204
213
if matching_version is not None : # patch version already exists
205
214
# update MC version if necessary
206
215
if not set (matching_version ["supports" ]) == set (game_versions ):
207
216
logger .debug ("Additional MC version support has been added to an existing patch version. Updating existing smithed version data" )
208
- res = requests .patch (f"{ SMITHED_API } /packs/{ smithed_id } /versions/{ matching_version ['name' ]} " , params = {'token' : auth_token }, json = {
217
+ res = requests .patch (f"{ SMITHED_API } /packs/{ opts . pack_id } /versions/{ matching_version ['name' ]} " , params = {'token' : auth_token }, json = {
209
218
"data" : {
210
219
"supports" : game_versions
211
220
}
@@ -214,17 +223,27 @@ def publish_smithed(ctx: Context, release_dir: Path, file_name: str):
214
223
logger .warning (f"Failed to patch project versions: { res .status_code } { res .text } " )
215
224
return
216
225
217
- # remove other existing versions for that mc version
218
- mc_version_matching_version = (v ["name" ] for v in project_versions if set (v ['supports' ]) & set (game_versions ))
219
- for v in mc_version_matching_version :
220
- res = requests .delete (f"{ SMITHED_API } /packs/{ smithed_id } /versions/{ v } " , params = {'token' : auth_token })
226
+ # permalink previous version (in that MC version) to the git history
227
+ commit_hash = run ("cd release && git log -1 --format=%H" )
228
+ matching_mc_versions = sorted ((Version (v ["name" ]) for v in project_versions if set (v ['supports' ]) & set (game_versions )))
229
+ prior_version_in_mc_version = matching_mc_versions [- 1 ] if len (matching_mc_versions ) > 0 else None # newest version number, with any MC overlap
230
+ prior_url : str = next ((v ["downloads" ]["datapack" ] for v in project_versions if Version (v ["name" ]) == prior_version_in_mc_version ), "" )
231
+ if "https://github.com/Gamemode4Dev/GM4_Datapacks/blob/" not in prior_url and prior_version_in_mc_version :
232
+ res = requests .patch (f"{ SMITHED_API } /packs/{ opts .pack_id } /versions/{ prior_version_in_mc_version } " , params = {'token' : auth_token }, json = {
233
+ "data" :{
234
+ "downloads" : {
235
+ "datapack" : f"https://github.com/Gamemode4Dev/GM4_Datapacks/blob/{ commit_hash } /{ mc_version_dir } /{ file_name } ?raw=true" ,
236
+ "resourcepack" : ""
237
+ }
238
+ }
239
+ })
221
240
if not (200 <= res .status_code < 300 ):
222
- logger .warning (f"Failed to delete { ctx . project_name } version { v } : { res .status_code } { res .text } " )
241
+ logger .warning (f"Failed to permalink { project_id } version { prior_version_in_mc_version } : { res .status_code } { res .text } " )
223
242
else :
224
- logger .info (f"{ ctx . project_name } { res .text } " )
243
+ logger .info (f"Permalinked { project_id } { prior_version_in_mc_version } to git history: { res .text } " )
225
244
226
245
# post new version
227
- res = requests .post (f"{ SMITHED_API } /packs/{ smithed_id } /versions" ,
246
+ res = requests .post (f"{ SMITHED_API } /packs/{ opts . pack_id } /versions" ,
228
247
params = {'token' : auth_token , 'version' : version },
229
248
json = {"data" :{
230
249
"downloads" :{
0 commit comments