forked from MinecraftForge/MinecraftForge
-
Notifications
You must be signed in to change notification settings - Fork 0
/
release.py
416 lines (350 loc) · 15.6 KB
/
release.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
import os, os.path, sys, glob
import shutil, fnmatch, time, json
import logging, zipfile, re, subprocess
from pprint import pformat, pprint
from optparse import OptionParser
from urllib2 import HTTPError
from contextlib import closing
from datetime import datetime
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
forge_dir = os.path.dirname(os.path.abspath(__file__))
from forge import reset_logger, load_version, zip_folder, zip_create, inject_version, build_forge_dev
from changelog import make_changelog
zip = None
zip_name = None
zip_base = None
version_str = None
version_mc = None
def main():
global version_str
global version_mc
parser = OptionParser()
parser.add_option('-m', '--mcp-dir', action='store', dest='mcp_dir', help='MCP Path', default=None)
parser.add_option('-b', '--build', action='store', dest='build', help='Build number', default=None)
parser.add_option('-s', '--skipchangelog', action='store_true', dest='skip_changelog', help='Skip Changelog', default=False)
parser.add_option('-j', '--sign-jar', action='store', dest='sign_jar', help='Path to jar signer command', default=None)
options, _ = parser.parse_args()
build_num = 0
if not options.build is None:
try:
build_num = int(options.build)
except:
pass
mcp_dir = os.path.join(forge_dir, 'mcp')
if not options.mcp_dir is None:
mcp_dir = os.path.abspath(options.mcp_dir)
ret = 0
fml_dir = os.path.join(forge_dir, 'fml')
ret = build_forge_dev(mcp_dir, forge_dir, fml_dir, build_num)
if ret != 0:
sys.exit(ret)
temp_dir = os.path.join(forge_dir, 'temp')
src_dir = os.path.join(mcp_dir, 'src')
reobf_dir = os.path.join(mcp_dir, 'reobf')
client_dir = os.path.join(reobf_dir, 'minecraft')
fml_dir = os.path.join(temp_dir, 'fml')
print '=================================== Release Start ================================='
fml = glob.glob(os.path.join(forge_dir, 'fml', 'target', 'fml-src-*.%d-*.zip' % build_num))
if not len(fml) == 1:
if len(fml) == 0:
print 'Missing FML source zip, should be named fml-src-*.zip inside ./fml/target/ created when running setup'
else:
print 'To many FML source zips found, we should only have one. Check the Forge Git for the latest FML version supported'
sys.exit(1)
if os.path.isdir(fml_dir):
shutil.rmtree(fml_dir)
print 'Extracting: %s' % os.path.basename(fml[0])
zf = zipfile.ZipFile(fml[0])
zf.extractall(temp_dir)
zf.close()
if os.path.isfile('MANIFEST.MF'):
os.remove('MANIFEST.MF')
fml_name = os.path.basename(fml[0]).replace('src', 'universal').replace('.zip', '.jar').replace('-master.', '.')
print('Extracting %s MANIFEST.MF' % fml_name)
with closing(zipfile.ZipFile(os.path.join(forge_dir, 'fml', 'target', fml_name), mode='r')) as zip_in:
with closing(open('MANIFEST.MF', 'wb')) as out:
out.write(zip_in.read('META-INF/MANIFEST.MF'))
error_level = 0
try:
sys.path.append(mcp_dir)
from runtime.reobfuscate import reobfuscate
os.chdir(mcp_dir)
reset_logger()
reobfuscate(None, False, True, True, True, False, False)
reset_logger()
os.chdir(forge_dir)
except SystemExit, e:
print 'Reobfusicate Exception: %d ' % e.code
error_level = e.code
extract_fml_obfed(fml_dir, mcp_dir, reobf_dir, client_dir)
gen_bin_patches(mcp_dir, os.path.join(forge_dir, 'fml'), build_num, client_dir)
version = load_version(build_num)
version_forge = '%d.%d.%d.%d' % (version['major'], version['minor'], version['revision'], version['build'])
version_mc = load_mc_version(fml_dir)
branch = get_branch_name()
version_str = '%s-%s' % (version_mc, version_forge)
if not branch == "":
version_str = '%s-%s' % (version_str, branch)
out_folder = os.path.join(forge_dir, 'target')
if not os.path.isdir(out_folder):
os.makedirs(out_folder)
for f in ['minecraftforge-changelog-%s.txt', 'minecraftforge-universal-%s.jar', 'minecraftforge-installer-%s.jar']:
fn = os.path.join(out_folder, f % version_str)
if os.path.isfile(fn):
os.remove(fn)
if not options.skip_changelog:
changelog_file = 'target/minecraftforge-changelog-%s.txt' % (version_str)
try:
make_changelog("http://ci.jenkins.minecraftforge.net/job/minecraftforge/", build_num, changelog_file, version_str)
except HTTPError, e:
print 'Changelog failed to generate: %s' % e
options.skip_changelog = True
version_file = 'forgeversion.properties'
if os.path.exists(version_file):
os.remove(version_file)
with open(version_file, 'wb') as fh:
fh.write('forge.major.number=%d\n' % version['major'])
fh.write('forge.minor.number=%d\n' % version['minor'])
fh.write('forge.revision.number=%d\n' % version['revision'])
fh.write('forge.build.number=%d\n' % version['build'])
json_data = gather_json(forge_dir, version_mc, version_forge, version_str)
if not options.sign_jar is None:
sign_jar(forge_dir, options.sign_jar, client_dir, 'minecraftforge-universal-%s.jar' % version_str)
else:
zip_start('minecraftforge-universal-%s.jar' % version_str)
zip_folder(client_dir, '', zip)
zip_add('MANIFEST.MF','META-INF/MANIFEST.MF')
zip_add('client/forge_logo.png')
zip_add('install/MinecraftForge-Credits.txt')
zip_add('install/MinecraftForge-License.txt')
zip_add('install/Paulscode IBXM Library License.txt')
zip_add('install/Paulscode SoundSystem CodecIBXM License.txt')
zip_add('common/forge_at.cfg')
zip_add(version_file)
if not options.skip_changelog:
zip_add(changelog_file, 'MinecraftForge-Changelog.txt')
print(' version.json')
zip.writestr('version.json', json.dumps(json_data['versionInfo'], indent=4, separators=(',', ': ')))
#Add dependancy and licenses from FML
FML_FILES = [
'CREDITS-fml.txt',
'LICENSE-fml.txt',
'README-fml.txt',
'common/fml_at.cfg',
'common/fml_marker.cfg',
'common/fmlversion.properties',
'common/mcpmod.info',
'client/mcplogo.png',
'common/deobfuscation_data-%s.lzma' % version_mc
]
for file in FML_FILES:
zip_add(os.path.join(fml_dir, file))
zip_end()
build_installer(forge_dir, version_str, version_forge, version_mc, out_folder, json.dumps(json_data, indent=4, separators=(',', ': ')))
inject_version(os.path.join(forge_dir, 'common/net/minecraftforge/common/ForgeVersion.java'.replace('/', os.sep)), build_num)
zip_start('minecraftforge-src-%s.zip' % version_str, 'forge')
zip_add('client', 'client')
zip_add('common', 'common')
zip_add('patches', 'patches')
zip_add(fml_dir, 'fml')
zip_add('install', '')
zip_add('forge.py')
zip_add(version_file)
if not options.skip_changelog:
zip_add(changelog_file, 'MinecraftForge-Changelog.txt')
zip_end()
inject_version(os.path.join(forge_dir, 'common/net/minecraftforge/common/ForgeVersion.java'.replace('/', os.sep)), 0)
if os.path.exists(version_file):
os.remove(version_file)
shutil.rmtree(temp_dir)
if os.path.isfile('MANIFEST.MF'):
os.remove('MANIFEST.MF')
print '=================================== Release Finished %d =================================' % error_level
sys.exit(error_level)
def gather_json(forge_dir, version_mc, version_forge, version_str):
def getTZ():
ret = '-'
t = time.timezone
if (t < 0):
ret = '+'
t *= -1
h = int(t/60/60)
t -= (h*60*60)
m = int(t/60)
return '%s%02d%02d' % (ret, h, m)
timestamp = datetime.now().replace(microsecond=0).isoformat() + getTZ()
json_data = {}
with closing(open(os.path.join(forge_dir, 'fml', 'jsons', '%s-rel.json' % version_mc), 'r')) as fh:
data = fh.read()
data = data.replace('@version@', version_forge)
data = data.replace('@timestamp@', timestamp)
data = data.replace('@minecraft_version@', version_mc)
data = data.replace('@universal_jar@', 'minecraftforge-universal-%s.jar' % version_str)
data = data.replace('FMLTweaker', 'F_M_L_Tweaker')
data = data.replace('FML', 'Forge')
data = data.replace('F_M_L_Tweaker', 'FMLTweaker')
data = data.replace('cpw.mods:fml:', 'net.minecraftforge:minecraftforge:')
json_data = json.loads(data)
pprint(json_data)
return json_data
def build_installer(forge_dir, version_str, version_forge, version_minecraft, out_folder, json_data):
file_name = 'minecraftforge-installer-%s.jar' % version_str
universal_name = 'minecraftforge-universal-%s.jar' % version_str
print '================== %s Start ==================' % file_name
with closing(zipfile.ZipFile(os.path.join(forge_dir, 'fml', 'installer_base.jar'), mode='a')) as zip_in:
with closing(zipfile.ZipFile(os.path.join(out_folder, file_name), 'w', zipfile.ZIP_DEFLATED)) as zip_out:
# Copy everything over
for i in zip_in.filelist:
if not i.filename in ['install_profile.json', 'big_logo.png']:
#print(' %s' % i.filename)
zip_out.writestr(i.filename, zip_in.read(i.filename))
print(' %s' % universal_name)
zip_out.write(os.path.join(out_folder, universal_name), universal_name)
print(' big_logo.png')
zip_out.write(os.path.join(forge_dir, 'client', 'forge_logo.png'), 'big_logo.png')
print(' install_profile.json')
zip_out.writestr('install_profile.json', json_data)
print '================== %s Finished ==================' % file_name
def zip_add(file, key=None):
if key == None:
key = os.path.basename(file)
else:
key = key.replace('/', os.sep)
if not zip_base is None:
key = os.path.join(zip_base, key)
file = os.path.join(forge_dir, file.replace('/', os.sep))
if os.path.isdir(file):
zip_folder(file, key, zip)
else:
if os.path.isfile(file):
print ' ' + key
zip.write(file, key)
def zip_start(name, base=None):
global zip, zip_name, zip_base
zip_name = name
print '================== %s Start ==================' % zip_name
zip_file = os.path.join(forge_dir, 'target', name)
zip = zipfile.ZipFile(zip_file, 'w', zipfile.ZIP_DEFLATED)
zip_base = base
def zip_end():
global zip, zip_name, zip_base
zip.close()
print '================== %s Finished ==================' % zip_name
zip_name = None
zip_base = None
def load_mc_version(fml_dir):
props = os.path.join(fml_dir, 'common', 'fmlversion.properties')
if not os.path.isfile(props):
print 'Could not load fmlversion.properties, build failed'
sys.exit(1)
with open(props, 'r') as fh:
for line in fh:
line = line.strip()
if line.startswith('fmlbuild.mcversion'):
return line.split('=')[1].strip()
print 'Could not load fmlversion.properties, build failed'
sys.exit(1)
def extract_fml_obfed(fml_dir, mcp_dir, reobf_dir, client_dir):
fml_file = os.path.join(fml_dir, 'difflist.txt')
if not os.path.isfile(fml_file):
print 'Could not find Forge ModLoader\'s DiffList, looking for it at: %s' % fml_file
sys.exit(1)
with open(fml_file, 'r') as fh:
lines = fh.readlines()
client = zipfile.ZipFile(os.path.join(mcp_dir, 'temp', 'client_reobf.jar'))
print 'Extracting Reobfed Forge ModLoader classes'
for line in lines:
line = line.replace('\n', '').replace('\r', '').replace('/', os.sep)
if not os.path.isfile(os.path.join(reobf_dir, line)):
print ' %s' % line
side = line.split(os.sep)[0]
if side == 'minecraft':
client.extract(line[10:].replace(os.sep, '/'), client_dir)
client.close()
def get_branch_name():
from subprocess import Popen, PIPE, STDOUT
branch = ''
if os.getenv("GIT_BRANCH") is None:
try:
process = Popen(["git", "rev-parse", "--abbrev-ref", "HEAD"], stdout=PIPE, stderr=STDOUT, bufsize=-1)
branch, _ = process.communicate()
branch = branch.rstrip('\r\n')
except OSError:
print "Git not found"
else:
branch = os.getenv("GIT_BRANCH").rpartition('/')[2]
branch = branch.replace('master', '')
branch = branch.replace('HEAD', '')
print 'Detected Branch as \'%s\'' % branch
return branch
def sign_jar(forge_dir, command, files, dest_zip):
from subprocess import Popen, PIPE, STDOUT
global zip
zip_file = os.path.join(forge_dir, 'tmp.jar')
if os.path.isfile(zip_file):
os.remove(zip_file)
print '============== Creating tmp zip to sign ====================='
zf = zipfile.ZipFile(zip_file, 'w', zipfile.ZIP_DEFLATED)
zf.write(os.path.join(forge_dir, 'MANIFEST.MF'), 'META-INF/MANIFEST.MF')
zip_folder_filter(files, '', zf, 'cpw/mods/'.replace('/', os.sep))
zip_folder_filter(files, '', zf, 'net/minecraftforge/'.replace('/', os.sep))
zf.close()
print '================ End tmp zip to sign ========================'
try:
process = Popen([command, zip_file, "forge"], stdout=PIPE, stderr=STDOUT, bufsize=-1)
out, _ = process.communicate()
print out
except OSError as e:
print "Error creating signed tmp jar: %s" % e.strerror
sys.exit(1)
tmp_dir = os.path.join(forge_dir, 'tmp')
if os.path.isdir(tmp_dir):
shutil.rmtree(tmp_dir)
zf = zipfile.ZipFile(zip_file)
zf.extractall(tmp_dir)
zf.close()
os.remove(zip_file)
zip_start(dest_zip)
zip_folder(tmp_dir, '', zip)
zip_folder(files, '', zip)
if os.path.isdir(tmp_dir):
shutil.rmtree(tmp_dir)
def zip_folder_filter(path, key, zip, filter):
files = os.listdir(path)
for file in files:
file_path = os.path.join(path, file)
file_key = os.path.join(key, file)
if os.path.isdir(file_path):
zip_folder_filter(file_path, file_key, zip, filter)
else:
if file_key.startswith(filter):
print file_key
zip.write(file_path, file_key)
def gen_bin_patches(mcp_dir, fml_dir, build_num, client_dir):
print('Creating Binary patches')
os.environ['WORKSPACE'] = os.path.join(mcp_dir, '..')
os.environ['BUILD_NUMBER'] = str(build_num)
BUILD = ['ant', 'makebinpatches']
if sys.platform.startswith('win'):
BUILD = ['cmd', '/C'] + BUILD
if not run_command(BUILD, cwd=fml_dir):
print('Could not crate binary patches')
sys.exit(1)
fml_lzma = os.path.join(fml_dir, 'binpatches.pack.lzma')
obf_lzma = os.path.join(client_dir, 'binpatches.pack.lzma')
shutil.move(fml_lzma, obf_lzma)
def run_command(command, cwd='.', verbose=True):
print('Running command: ')
print(pformat(command))
process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, bufsize=1, cwd=cwd)
while process.poll() is None:
line = process.stdout.readline()
if line:
line = line.rstrip()
print(line)
if process.returncode:
print "failed: {0}".format(process.returncode)
return False
return True
if __name__ == '__main__':
main()