Skip to content

Commit

Permalink
Repository Bootstrapper version 1.2.0
Browse files Browse the repository at this point in the history
  • Loading branch information
Twilight0 committed Nov 27, 2018
1 parent 4e7ad0a commit c11a53a
Show file tree
Hide file tree
Showing 4 changed files with 189 additions and 49 deletions.
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@

_forked from BartOtten with the additions of:_

- Ignore .idea folders in addons' subfolders
- Ignore .idea, .git and __MACOSX folders in addons' subfolders
- Ignore various files like .gitignore, .gitattributes
- Copy changelogs (& rename to version number), icons, fanarts to your repository
- Changed from deprecated md5 module to hashlib

Expand All @@ -13,3 +14,5 @@ _forked from BartOtten with the additions of:_
- ZIP-file your addons with version numbers.
- Copy changelogs and rename with version numbers.
- Copy icons & fanarts, if any.

Tested Python versions: 2.7.12+, 3.6.5
218 changes: 171 additions & 47 deletions _tools/generate_repo.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,25 @@
- by Rodrigo@TVADDONS: Zip plugins/repositories to a "zip" folder
- BartOtten: Create a repository addon, skip folders without addon.xml, user config file
- Twilight0@TVADDONS: Ignore .idea subdirectories in addons' directories, changed from md5 module to hashlib
copy changelogs, icons and fanarts
- Twilight0: See changelog from version 1.1.2+
This file is provided "as is", without any warranty whatsoever. Use at your own risk
"""
from __future__ import print_function, absolute_import

from re import sub
import os
import hashlib
import zipfile
import shutil
import datetime
from xml.dom import minidom
from ConfigParser import SafeConfigParser
try:
from ConfigParser import SafeConfigParser
except ImportError:
from configparser import SafeConfigParser

__version__ = '1.2.0'

# Load the configuration:
config = SafeConfigParser()
Expand All @@ -33,8 +38,18 @@
description = config.get('addon', 'description')
url = config.get('locations', 'url')

# Script settings:
ask_for_exit_input = True # asks user to press enter to exit the information window (stdout)
overwrite_existing = True # this will overwrite existing zip files in output directory
rename_old = True # will rename older zip files in output directory
copy_additional = True # will copy additional files such as changelog.txt, icon.png, fanart.jpg
replace_ampersand = True # will replace solo ampersands (&) with & in order to pass xml validation
compress = True # Setting this to True will compress with ZIP_DEFLATED method, if False it will use ZIP_STORED
ignored_dirs = ['.git', '.idea', '__MACOSX', '.svn', output_path, tools_path]
ignored_files = ['.gitignore', 'gitattributes']

class Generator:

"""
Generates a new addons.xml file from each addons addon.xml file
and a new addons.xml.md5 hash file. Must be run from a subdirectory (eg. _tools) of
Expand Down Expand Up @@ -64,10 +79,10 @@ def _generate_repo_files(self):
if os.path.isfile(addonid + os.path.sep + "addon.xml"):
return

print "Create repository addon"
print("Create repository addon")

with open(tools_path + os.path.sep + "template.xml", "r") as template:
template_xml = template.read()
with open(tools_path + os.path.sep + "template.xml", "r") as f:
template_xml = f.read()

repo_xml = template_xml.format(
addonid=addonid,
Expand All @@ -77,68 +92,110 @@ def _generate_repo_files(self):
summary=summary,
description=description,
url=url,
output_path=output_path)
output_path=output_path
)

# save file
if not os.path.exists(addonid):
os.makedirs(addonid)

self._save_file(repo_xml.encode("utf-8"), file=addonid + os.path.sep + "addon.xml")
self._save_file(repo_xml, file=addonid + os.path.sep + "addon.xml")

def _generate_zip_files(self):

global version, addonid

addons = os.listdir(".")

# loop thru and add each addons addon.xml file
for addon in addons:

# create path
_path = os.path.join(addon, "addon.xml")

# skip path if it has no addon.xml
if not os.path.isfile(_path):
continue

try:

# skip any file or .git folder
if not (os.path.isdir(addon) or addon == ".idea" or addon == ".git" or addon == ".svn" or addon == output_path or addon == tools_path):
if not (os.path.isdir(addon) or addon in ignored_dirs):
continue

# create path
_path = os.path.join(addon, "addon.xml")

# split lines for stripping
document = minidom.parse(_path)

for parent in document.getElementsByTagName("addon"):
version = parent.getAttribute("version")
addonid = parent.getAttribute("id")

self._generate_zip_file(addon, version, addonid)
except Exception, e:
print e

except Exception as e:

print(e)

def _generate_zip_file(self, path, version, addonid):
print "Generate zip file for " + addonid + " " + version

print("Generate zip file for " + addonid + " " + version)

filename = path + "-" + version + ".zip"

compression = zipfile.ZIP_DEFLATED if compress else zipfile.ZIP_STORED

try:
zip = zipfile.ZipFile(filename, 'w', compression=zipfile.ZIP_DEFLATED)

zip = zipfile.ZipFile(filename, 'w', compression=compression)

for root, dirs, files in os.walk(path + os.path.sep):
if '.idea' in dirs:
dirs.remove('.idea')
if '.git' in dirs:
dirs.remove('.git')

for ignored_dir in ignored_dirs:
if ignored_dir in dirs:
dirs.remove(ignored_dir)

for ignored_file in ignored_files:
if ignored_file in files:
files.remove(ignored_file)

zip.write(os.path.join(root))

for file in files:

zip.write(os.path.join(root, file))

zip.close()

if not os.path.exists(output_path + addonid):
os.makedirs(output_path + addonid)

if os.path.isfile(output_path + addonid + os.path.sep + filename):
# pass #uncomment to overwrite existing zip file, then comment or remove the next two lines below
os.rename(output_path + addonid + os.path.sep + filename,
output_path + addonid + os.path.sep + filename + "." + datetime.datetime.now().strftime("%Y%m%d%H%M%S"))
shutil.move(filename, output_path + addonid + os.path.sep + filename)
except Exception, e:
print e
destination = output_path + addonid + os.path.sep + filename
output_exists = os.path.isfile(destination)

if output_exists and rename_old:
os.rename(destination, destination + "." + datetime.datetime.now().strftime("%Y%m%d%H%M%S"))

if output_exists and overwrite_existing:

shutil.move(filename, destination)

elif output_exists and not overwrite_existing:

os.remove(filename)

else:

shutil.move(filename, destination)

except Exception as e:

print(e)

def _generate_addons_file(self):
print "Generating addons.xml file"
print("Generating addons.xml file")
# addon list
addons = os.listdir(".")
# final addons text
Expand All @@ -152,85 +209,152 @@ def _generate_addons_file(self):
continue
try:
# split lines for stripping
xml_lines = open(_path, "r").read().splitlines()
try:
with open(_path, "r") as f:
xml_lines = f.read().splitlines()
except UnicodeDecodeError:
# noinspection PyArgumentList
with open(_path, "r", encoding='utf-8') as f:
xml_lines = f.read().splitlines()
# new addon
addon_xml = ""
# loop thru cleaning each line
for line in xml_lines:
# skip encoding format line
if (line.find("<?xml") >= 0):
if line.find("<?xml") >= 0:
continue
# replace "&" with &amp; in order to pass xml validation:
if replace_ampersand:
line = sub(r'&(?!amp;)', r'&amp;', line)
# add line
addon_xml += unicode(line.rstrip() + "\n", "utf-8")
try:
addon_xml += unicode(line.rstrip() + "\n", "utf-8")
except NameError:
addon_xml += line.rstrip() + "\n"
# we succeeded so add to our final addons.xml text
addons_xml += addon_xml.rstrip() + "\n\n"
except Exception, e:
except Exception as e:
# missing or poorly formatted addon.xml
print "Excluding %s for %s" % (_path, e,)
print("Excluding %s for %s" % (_path, e,))
# clean and add closing tag
addons_xml = addons_xml.strip() + u"\n</addons>\n"
# save file
self._save_file(addons_xml.encode("utf-8"), file=output_path + "addons.xml")
self._save_file(addons_xml, file=output_path + "addons.xml")

def _generate_md5_file(self):
print "Generating addons.xml.md5 file"

print("Generating addons.xml.md5 file")

try:

# create a new md5 hash
m = hashlib.md5(open(output_path + "addons.xml").read()).hexdigest()
try:
# noinspection PyArgumentList
with open(output_path + "addons.xml", encoding='utf-8') as f:
addons_xml = f.read()
except TypeError:
with open(output_path + "addons.xml") as f:
addons_xml = f.read()

try:
m = hashlib.md5(addons_xml).hexdigest()
except TypeError:
# noinspection PyArgumentList
m = hashlib.md5(bytes(addons_xml, encoding='utf-8')).hexdigest()

# save file
self._save_file(m, file=output_path + "addons.xml.md5")
except Exception, e:
except Exception as e:
# oops
print "An error occurred creating addons.xml.md5 file!\n%s" % (e,)
print("An error occurred creating addons.xml.md5 file!\n%s" % (e,))

def _save_file(self, data, file):

try:

# write data to the file
open(file, "w").write(data)
except Exception, e:
try:
with open(file, "w") as f:
f.write(data)
except UnicodeEncodeError:
with open(file, "wb") as f:
try:
# noinspection PyArgumentList
f.write(bytes(data, encoding='utf-8'))
except TypeError:
f.write(data.encode('utf-8'))

except Exception as e:
# oops
print "An error occurred saving %s file!\n%s" % (file, e,)
print("An error occurred saving %s file!\n%s" % (file, e,))


class Copier:

def __init__(self):

self._copy_additional_files()

def _copy_additional_files(self):

os.chdir(os.path.abspath(os.path.join(tools_path, os.pardir)))
addons = os.listdir(".")

for addon in addons:

xml_file = os.path.join(addon, "addon.xml")

if not os.path.isfile(xml_file):
continue
if not (os.path.isdir(addon) or addon == ".idea" or addon == ".git" or addon == ".svn" or addon == output_path or addon == tools_path):
if not (os.path.isdir(addon) or addon in ignored_dirs):
continue

document = minidom.parse(xml_file)

for parent in document.getElementsByTagName("addon"):

version = parent.getAttribute("version")

# Changelog.txt
try:
if os.path.isfile(output_path + addon + os.path.sep + "changelog-" + version + ".txt"):
pass
if os.path.isfile(output_path + addon + os.path.sep + "changelog-" + version + ".txt") and overwrite_existing:
shutil.copy(addon + os.path.sep + "changelog.txt", output_path + addon + os.path.sep + "changelog-" + version + ".txt")
else:
shutil.copy(addon + os.path.sep + "changelog.txt", output_path + addon + os.path.sep + "changelog-" + version + ".txt")
except IOError:
pass

# Icon.png
try:
if os.path.isfile(output_path + addon + os.path.sep + "icon.png"):
pass
if os.path.isfile(output_path + addon + os.path.sep + "icon.png") and overwrite_existing:
shutil.copy(addon + os.path.sep + "icon.png", output_path + addon + os.path.sep + "icon.png")
else:
shutil.copy(addon + os.path.sep + "icon.png", output_path + addon + os.path.sep + "icon.png")
except IOError:
pass

# Fanart.jpg
try:
if os.path.isfile(output_path + addon + os.path.sep + "fanart.jpg"):
pass
if os.path.isfile(output_path + addon + os.path.sep + "fanart.jpg") and overwrite_existing:
shutil.copy(addon + os.path.sep + "fanart.jpg", output_path + addon + os.path.sep + "fanart.jpg")
else:
shutil.copy(addon + os.path.sep + "fanart.jpg", output_path + addon + os.path.sep + "fanart.jpg")
except IOError:
pass


if __name__ == "__main__":

print('Repository bootstrapper script, version: {0}'.format(__version__))

Generator()
Copier()
print "Finished updating addons xml & md5 files, zipping addons and copying additional files"
if copy_additional:
Copier()

print("Process completed")

if ask_for_exit_input:
try:
input("Press enter to exit")
except SyntaxError:
pass
1 change: 0 additions & 1 deletion _tools/version.txt

This file was deleted.

Loading

0 comments on commit c11a53a

Please sign in to comment.