Beispiel #1
0
    def CreateDEB(self, bundle_id, recorded_version):
        """
        Creates a DEB from information stored in the "temp" folder.

        String bundle_id: The bundle id of the package to compress.
        """
        # TODO: Find a Python-based method to safely delete all DS_Store files.
        call(["find", ".", "-name", ".DS_Store", "-delete"],
             cwd=self.root + "temp/" +
             bundle_id)  # Remove .DS_Store. Kinda finicky.
        for file_name in os.listdir(self.root + "temp/" + bundle_id):
            if file_name.endswith(".deb"):
                # Check if the DEB is a newer version
                deb = Dpkg(self.root + "temp/" + bundle_id + "/" + file_name)
                if Dpkg.compare_versions(recorded_version, deb.version) == -1:
                    # Update package stuff
                    package_name = PackageLister.BundleIdToDirName(
                        self, bundle_id)
                    with open(
                            self.root + "Packages/" + package_name +
                            "/silica_data/index.json", "r") as content_file:
                        update_json = json.load(content_file)
                        update_json['version'] = deb.version
                        changelog_entry = input(
                            "The DEB provided for \"" + update_json['name'] +
                            "\" has a new version available (" +
                            recorded_version + " -> " + deb.version +
                            "). What changed in this version?\n(Add multiple lines"
                            +
                            " by using a newline character [\\n] and use valid Markdown syntax.): "
                        )
                        try:
                            update_json['changelog'].append({
                                "version":
                                deb.version,
                                "changes":
                                changelog_entry
                            })
                        except Exception:
                            update_json['changelog'] = {
                                "version": deb.version,
                                "changes": changelog_entry
                            }
                        return_str = json.dumps(update_json)
                        print("Updating package index.json...")
                        PackageLister.CreateFile(
                            self, "Packages/" + package_name +
                            "/silica_data/index.json", return_str)
                    pass
                DpkgPy.extract(
                    self, self.root + "temp/" + bundle_id + "/" + file_name,
                    self.root + "temp/" + bundle_id)
                os.remove(self.root + "temp/" + bundle_id + "/" + file_name)
                os.remove(self.root + "temp/" + bundle_id + "/control")
        else:
            # TODO: Update DpkgPy to generate DEB files without dependencies (for improved win32 support)
            call(["dpkg-deb", "-b", "-Zgzip", self.root + "temp/" + bundle_id],
                 cwd=self.root + "temp/")  # Compile DEB
    def NativeFeaturedCarousel(self, tweak_release):
        """
        Generate a sileo-featured.json file for featured packages.

        Object carousel_entry_list: A "tweak release" object.
        """
        repo_settings = PackageLister.GetRepoSettings(self)
        subfolder = PackageLister.FullPathCname(self, repo_settings)
        banners = []
        for package in tweak_release:
            try:
                if package['featured'].lower() == "true":
                    ar_el = {
                        "package":
                        package['bundle_id'],
                        "title":
                        package['name'],
                        "url":
                        "https://" + repo_settings['cname'] + subfolder +
                        "/assets/" + package['bundle_id'] + "/banner.png",
                        "hideShadow":
                        "false"
                    }
                    banners.append(ar_el)
            except Exception:
                pass
        if len(banners) == 0:
            try:
                featured_int = random.randint(0, (len(tweak_release) - 1))
            except Exception:
                PackageLister.ErrorReporter(
                    self, "Configuration Error!",
                    "You have no packages added to this repo. "
                    "Make sure a folder is created at \"" + self.version +
                    "/Packages\" that contains folders with "
                    "package data inside of them and run Silica again.")
            featured_package = tweak_release[featured_int]
            ar_el = {
                "package":
                featured_package['bundle_id'],
                "title":
                featured_package['name'],
                "url":
                "https://" + repo_settings['cname'] + subfolder + "/assets/" +
                featured_package['bundle_id'] + "/banner.png",
                "hideShadow":
                "false"
            }
            banners.append(ar_el)

        featured_json = {
            "class": "FeaturedBannersView",
            "itemSize": "{263, 148}",
            "itemCornerRadius": 8,
            "banners": banners
        }
        return json.dumps(featured_json, separators=(',', ':'))
 def RenderIndexHTML(self):
     """
     Renders the home page (index.html).
     """
     repo_settings = PackageLister.GetRepoSettings(self)
     with open(self.root + "Styles/index.mustache", "r") as content_file:
         index = content_file.read()
         replacements = DepictionGenerator.RenderDataHTML(self)
         replacements['tint_color'] = repo_settings['tint']
         replacements['footer'] = DepictionGenerator.RenderFooter(self)
         replacements['tweak_release'] = PackageLister.GetTweakRelease(self)
         return pystache.render(index, replacements)
    def NativeFeaturedCarousel(self, tweak_release):
        """
        Generate a sileo-featured.json file for featured packages.

        Object carousel_entry_list: A "tweak release" object.
        """
        repo_settings = PackageLister.GetRepoSettings(self)
        subfolder = PackageLister.FullPathCname(self, repo_settings)
        banners = []
        for package in tweak_release:
            try:
                if package['featured'].lower() == "true":
                    ar_el = {
                        "package":
                        package['bundle_id'],
                        "title":
                        package['name'],
                        "url":
                        "https://" + repo_settings['cname'] + subfolder +
                        "/assets/" + package['bundle_id'] + "/banner.png",
                        "hideShadow":
                        "false"
                    }
                    banners.append(ar_el)
            except Exception:
                pass
        if len(banners) == 0:
            featured_int = random.randint(0, (len(tweak_release) - 1))
            featured_package = tweak_release[featured_int]
            ar_el = {
                "package":
                featured_package['bundle_id'],
                "title":
                featured_package['name'],
                "url":
                "https://" + repo_settings['cname'] + subfolder + "/assets/" +
                featured_package['bundle_id'] + "/banner.png",
                "hideShadow":
                "false"
            }
            banners.append(ar_el)

        featured_json = {
            "class": "FeaturedBannersView",
            "itemSize": "{263, 148}",
            "itemCornerRadius": 8,
            "banners": banners
        }
        return json.dumps(featured_json)
    def CarouselEntryList(self, tweak_release):
        """
        Generate a carousel of featured packages on the repo.

        Object tweak_release: A "tweak release" object.
        """
        list_el = ""
        for package in tweak_release:
            try:
                if package['featured'].lower() == "true":
                    list_el += DepictionGenerator.CarouselEntry(
                        self, package['name'],
                        "assets/" + package['bundle_id'] + "/banner.png",
                        package['bundle_id'])
            except Exception:
                pass
        if list_el == "":
            try:
                featured_int = random.randint(0, (len(tweak_release) - 1))
            except Exception:
                PackageLister.ErrorReporter(
                    self, "Configuration Error!",
                    "You have no packages added to this repo."
                    " Make sure a folder is created at \"" + self.version +
                    "/Packages\" that contains folders with package data inside of them and run Silica again."
                )

            featured_package = tweak_release[featured_int]
            list_el += DepictionGenerator.CarouselEntry(
                self, featured_package['name'],
                "assets/" + featured_package['bundle_id'] + "/banner.png",
                featured_package['bundle_id'])
        return list_el
 def SignRelease(self):
     """
     Signs Release to create Release.gpg. Also adds hash for Packages.bz2 in Release.
     """
     with open(self.root + "docs/Packages.bz2", "rb") as content_file,\
         open(self.root + "docs/Packages.xz", "rb") as content_file_xz:
         bzip_raw = content_file.read()
         bzip_sha256_hash = hashlib.sha256(bzip_raw).hexdigest()
         bzip_size = os.path.getsize(self.root + "docs/Packages.bz2")
         xz_raw = content_file_xz.read()
         xz_sha256_hash = hashlib.sha256(xz_raw).hexdigest()
         xz_size = os.path.getsize(self.root + "docs/Packages.xz")
         with open(self.root + "docs/Release", "a") as text_file:
             text_file.write("\nSHA256:\n " + str(bzip_sha256_hash) + " " +
                             str(bzip_size) + " Packages.bz2"
                             "\n " + str(xz_sha256_hash) + " " +
                             str(xz_size) + " Packages.xz\n")
             repo_settings = PackageLister.GetRepoSettings(self)
             try:
                 if repo_settings['enable_gpg'].lower() == "true":
                     print("Signing repository with GPG...")
                     key = "Silica MobileAPT Repository"  # Most of the time, this is acceptable.
                     call([
                         "gpg", "-abs", "-u", key, "-o", "Release.gpg",
                         "Release"
                     ],
                          cwd=self.root + "docs/")
                     print("Generated Release.gpg!")
             except Exception:
                 pass
 def RenderDataBasic(self):
     """
     Gets the value of basic repo data to pass to Pystache.
     """
     repo_settings = PackageLister.GetRepoSettings(self)
     with open(self.root + "Styles/settings.json", "r") as content_file:
         data = json.load(content_file)
         date = datetime.datetime.now().strftime("%Y-%m-%d")
         subfolder = PackageLister.FullPathCname(self, repo_settings)
         return {
             "silica_version": self.version,
             "silica_compile_date": date,
             "repo_name": data['name'],
             "repo_url": data['cname'] + subfolder,
             "repo_desc": data['description'],
             "repo_tint": data['tint']
         }
    def RenderDataHTML(self):
        data = DepictionGenerator.RenderDataBasic(self)

        tweak_release = PackageLister.GetTweakRelease(self)

        data['repo_packages'] = DepictionGenerator.PackageEntryList(self, tweak_release)

        data['repo_carousel'] = DepictionGenerator.CarouselEntryList(self, tweak_release)

        return data
Beispiel #9
0
    def RenderPackageHTML(self, tweak_data):
        """
        Renders a package's depiction.

        Object tweak_data: A single index of a "tweak release" object.
        """
        with open(self.root + "Styles/tweak.mustache", "r") as content_file:
            index = content_file.read()
            replacements = DepictionGenerator.RenderDataHTML(self)
            replacements['tweak_name'] = tweak_data['name']
            replacements['tweak_developer'] = tweak_data['developer']['name']
            replacements['tweak_compatibility'] = "iOS " + tweak_data[
                'works_min'] + " to " + tweak_data['works_max']
            replacements['tweak_version'] = tweak_data['version']
            replacements['tweak_section'] = tweak_data['section']
            replacements['tweak_bundle_id'] = tweak_data['bundle_id']
            replacements['works_min'] = tweak_data['works_min']
            replacements['works_max'] = tweak_data['works_max']
            replacements[
                'tweak_carousel'] = DepictionGenerator.ScreenshotCarousel(
                    self, tweak_data)
            replacements[
                'tweak_changelog'] = DepictionGenerator.RenderChangelogHTML(
                    self, tweak_data)
            replacements['tweak_tagline'] = tweak_data['tagline']
            replacements['footer'] = DepictionGenerator.RenderFooter(self)
            try:
                if tweak_data['source'] != "":
                    replacements['source'] = tweak_data['source']
            except Exception:
                pass

            try:
                replacements['tint_color'] = tweak_data['tint']
            except Exception:
                try:
                    repo_settings = PackageLister.GetRepoSettings(self)
                    replacements['tint_color'] = repo_settings['tint']
                except Exception:
                    replacements['tint_color'] = "#2cb1be"

            try:
                with open(
                        self.root + "repo/assets/" + tweak_data['bundle_id'] +
                        "/description.md", "r") as md_file:
                    raw_md = md_file.read()
                    desc_md = mistune.markdown(raw_md)
                    replacements['tweak_description'] = desc_md
            except Exception:
                replacements['tweak_description'] = tweak_data['tagline']

            # tweak_carousel

            return pystache.render(index, replacements)
 def RenderFooter(self):
     """
     Renders the footer.
     """
     repo_settings = PackageLister.GetRepoSettings(self)
     data = DepictionGenerator.RenderDataHTML(self)
     try:
         footer = pystache.render(repo_settings['footer'], data)
     except Exception:
         footer = pystache.render("Silica {{silica_version}} – Updated {{silica_compile_date}}", data)
     return footer
Beispiel #11
0
    def ScreenshotCarousel(self, tweak_data):
        """
        Generates a screenshot div.

        Object tweak_data: A single index of a "tweak release" object.
        """
        repo_settings = PackageLister.GetRepoSettings(self)
        screenshot_div = "<div class=\"scroll_view\">"
        image_list = self.PackageLister.GetScreenshots(tweak_data)
        if (len(image_list) > 0):
            for image in image_list:
                screenshot_div += '''<img class="img_card" src="../../assets/{1}/screenshot/{2}">'''.format(
                    repo_settings['cname'], tweak_data['bundle_id'], image)
            screenshot_div += "</div>"
        else:
            screenshot_div = ""
        return screenshot_div
Beispiel #12
0
    ###########
    # Step 9: Make API endpoints
    ###########

    PackageLister.CreateFile("repo/api/tweak_release.json",
                             json.dumps(tweak_release))
    PackageLister.CreateFile("repo/api/repo_settings.json",
                             json.dumps(repo_settings))
    PackageLister.CreateFile("repo/api/about.json",
                             json.dumps(DepictionGenerator.SilicaAbout()))

    ###########
    # Step 10: Push to GitHub
    ###########

    shutil.rmtree(root + "temp/")  # Clean-up the now-unneeded temp folder.

    try:
        if repo_settings['automatic_git'].lower() == "true":
            DebianPackager.PushToGit(
            )  # Push the repo to GitHub automatically.
    except Exception:
        pass


if __name__ == '__main__':
    DepictionGenerator = DepictionGenerator(version)
    PackageLister = PackageLister(version)
    DebianPackager = DebianPackager(version)
    main()
Beispiel #13
0
def main():
    print("Silica Compiler {0}".format(version))

    ###########
    # Step 0: Clean up "repo" and "temp" folder
    ###########

    root = os.path.dirname(os.path.abspath(__file__)) + "/"

    try:
        shutil.rmtree(root + "repo/")
    except Exception:
        pass

    try:
        shutil.rmtree(root + "temp/")
    except Exception:
        pass

    ###########
    # Step 1: Generate folders, files, and variables needed (including temp)
    ###########
    PackageLister.CreateFolder("repo")
    PackageLister.CreateFolder("repo/web")
    PackageLister.CreateFolder("repo/depiction")
    PackageLister.CreateFolder("repo/depiction/web")
    PackageLister.CreateFolder("repo/depiction/native")
    PackageLister.CreateFolder("repo/depiction/native/help")
    PackageLister.CreateFolder("repo/pkg")
    PackageLister.CreateFolder("repo/assets")
    PackageLister.CreateFolder("repo/api")

    # Make sure all index.json files are generated (if using DEBs)
    DebianPackager.CheckForSilicaData()

    tweak_release = PackageLister.GetTweakRelease()
    repo_settings = PackageLister.GetRepoSettings()

    # Create folder for each tweak
    for tweak in tweak_release:
        PackageLister.CreateFolder("repo/assets/" + tweak['bundle_id'])

    ###########
    # Step 2: Copy all images
    ###########
    for package_name in PackageLister.ListDirNames():
        package_bundle_id = PackageLister.DirNameToBundleID(package_name)

        try:
            shutil.copy(
                root + "Packages/" + package_name + "/silica_data/icon.png",
                root + "repo/assets/" + package_bundle_id + "/icon.png")
        except Exception:
            category = PackageLister.ResolveCategory(tweak_release,
                                                     package_bundle_id)
            category = re.sub(r'\([^)]*\)', '', category).strip()
            try:
                shutil.copy(
                    root + "Styles/Generic/Icon/" + category + ".png",
                    root + "repo/assets/" + package_bundle_id + "/icon.png")
            except Exception:
                try:
                    shutil.copy(
                        root + "Styles/Generic/Icon/Generic.png", root +
                        "repo/assets/" + package_bundle_id + "/icon.png")
                except Exception:
                    PackageLister.ErrorReporter(
                        "Configuration Error!",
                        "You are missing a file at " + root +
                        "Styles/Generic/Icon/Generic.png. Please place an icon here to be the repo's default."
                    )

        try:
            shutil.copy(
                root + "Packages/" + package_name + "/silica_data/banner.png",
                root + "repo/assets/" + package_bundle_id + "/banner.png")
        except Exception:
            category = PackageLister.ResolveCategory(tweak_release,
                                                     package_bundle_id)
            category = re.sub(r'\([^)]*\)', '', category).strip()
            try:
                shutil.copy(
                    root + "Styles/Generic/Banner/" + category + ".png",
                    root + "repo/assets/" + package_bundle_id + "/banner.png")
            except Exception:
                try:
                    shutil.copy(
                        root + "Styles/Generic/Banner/Generic.png", root +
                        "repo/assets/" + package_bundle_id + "/banner.png")
                except Exception:
                    PackageLister.ErrorReporter(
                        "Configuration Error!",
                        "You are missing a file at " + root +
                        "Styles/Generic/Banner/Generic.png. Please place a banner here to be the repo's default."
                    )

        try:
            shutil.copy(
                root + "Packages/" + package_name +
                "/silica_data/description.md",
                root + "repo/assets/" + package_bundle_id + "/description.md")
        except Exception:
            pass

        try:
            shutil.copytree(
                root + "Packages/" + package_name + "/silica_data/screenshots",
                root + "repo/assets/" + package_bundle_id + "/screenshot")
        except Exception:
            pass
    try:
        shutil.copy(root + "Styles/icon.png", root + "repo/CydiaIcon.png")
    except Exception:
        PackageLister.ErrorReporter(
            "Configuration Error!",
            "You are missing a file at " + root + "Styles/icon.png. "
            "Please add a PNG here to act as the repo's icon.")

    ###########
    # Step 3: Generate HTML depictions and copy stylesheet
    ###########

    # Copy CSS and JS over
    shutil.copy(root + "Styles/index.css", root + "repo/web/index.css")
    shutil.copy(root + "Styles/index.js", root + "repo/web/index.js")

    # Generate index.html
    index_html = DepictionGenerator.RenderIndexHTML()
    PackageLister.CreateFile("repo/index.html", index_html)
    PackageLister.CreateFile("repo/404.html", index_html)

    # Generate per-tweak depictions
    for tweak_data in tweak_release:
        tweak_html = DepictionGenerator.RenderPackageHTML(tweak_data)
        PackageLister.CreateFile(
            "repo/depiction/web/" + tweak_data['bundle_id'] + ".html",
            tweak_html)

    PackageLister.CreateFile("repo/CNAME", repo_settings['cname'])

    ###########
    # Step 4: Generate Sileo depictions and featured JSON
    ###########

    # Generate sileo-featured.json
    carousel_obj = DepictionGenerator.NativeFeaturedCarousel(tweak_release)
    PackageLister.CreateFile("repo/sileo-featured.json", carousel_obj)

    # Generate per-tweak depictions
    for tweak_data in tweak_release:
        tweak_json = DepictionGenerator.RenderPackageNative(tweak_data)
        PackageLister.CreateFile(
            "repo/depiction/native/" + tweak_data['bundle_id'] + ".json",
            tweak_json)
        help_depiction = DepictionGenerator.RenderNativeHelp(tweak_data)
        PackageLister.CreateFile(
            "repo/depiction/native/help/" + tweak_data['bundle_id'] + ".json",
            help_depiction)

    ###########
    # Step 5: Generate Release file from settings.json.
    ###########

    release_file = DebianPackager.CompileRelease(repo_settings)
    PackageLister.CreateFile("repo/Release", release_file)

    ###########
    # Step 6: Copy packages to temp
    ###########

    # You should remove .DS_Store files AFTER copying to temp.
    PackageLister.CreateFolder("temp")
    for package_name in PackageLister.ListDirNames():
        bundle_id = PackageLister.DirNameToBundleID(package_name)
        try:
            shutil.copytree(root + "Packages/" + package_name,
                            root + "temp/" + bundle_id)
            shutil.rmtree(root + "temp/" + bundle_id + "/silica_data")
        except Exception:
            try:
                shutil.rmtree(root + "temp/" + bundle_id + "/silica_data")
            except Exception:
                pass

        script_check = Path(root + "Packages/" + package_name +
                            "/silica_data/scripts/")
        if script_check.is_dir():
            shutil.copytree(
                root + "Packages/" + package_name + "/silica_data/scripts",
                root + "temp/" + bundle_id + "/DEBIAN")
        else:
            PackageLister.CreateFolder("temp/" + bundle_id + "/DEBIAN")

    ###########
    # Step 7: Generate CONTROL and DEB files and move them to repo/
    ###########

    for tweak_data in tweak_release:
        control_file = DebianPackager.CompileControl(tweak_data, repo_settings)
        #input();
        PackageLister.CreateFile(
            "temp/" + tweak_data['bundle_id'] + "/DEBIAN/control",
            control_file)
        DebianPackager.CreateDEB(tweak_data['bundle_id'],
                                 tweak_data['version'])
        shutil.copy(root + "temp/" + tweak_data['bundle_id'] + ".deb",
                    root + "repo/pkg/" + tweak_data['bundle_id'] + ".deb")

    ###########
    # Step 8: Generate Package file and hash/sign the Release file.
    ###########

    DebianPackager.CompilePackages()
    DebianPackager.SignRelease()

    ###########
    # Step 9: Make API endpoints
    ###########

    PackageLister.CreateFile("repo/api/tweak_release.json",
                             json.dumps(tweak_release))
    PackageLister.CreateFile("repo/api/repo_settings.json",
                             json.dumps(repo_settings))
    PackageLister.CreateFile("repo/api/about.json",
                             json.dumps(DepictionGenerator.SilicaAbout()))

    ###########
    # Step 10: Push to GitHub
    ###########

    shutil.rmtree(root + "temp/")  # Clean-up the now-unneeded temp folder.

    try:
        if repo_settings['automatic_git'].lower() == "true":
            DebianPackager.PushToGit(
            )  # Push the repo to GitHub automatically.
    except Exception:
        pass
Beispiel #14
0
    def CheckForSilicaData(self):
        """
        Ensures that a silica_data file exists and if it doesn't, try to create one with as much data as we have.
        If there is a DEB file, it will take data from its CONTROL file. It will also auto-update the version number.
        If there is no DEB file, it will use the name of the folder, version 1.0.0, try to guess some dependencies,
            and add some placeholder data.
        :return:
        """
        for folder in os.listdir(self.root + "Packages"):
            if folder.lower() != ".ds_store":
                if not os.path.isdir(self.root + "Packages/" + folder + "/silica_data"):
                    print("It seems like the package \"" + folder + "\" is not configured. Let's set it up!")
                    is_deb = False
                    deb_path = ""
                    try:
                        for file_name in os.listdir(self.root + "Packages/" + folder):
                            if file_name.endswith(".deb"):
                                is_deb = True
                                deb_path = self.root + "Packages/" + folder + "/" + file_name
                    except Exception:
                        PackageLister.ErrorReporter(self, "Configuration Error!", "Please put your .deb file inside of "
                            "its own folder. The \"Packages\" directory should be made of multiple folders that each "
                            "contain data for a single package.\n Please fix this issue and try again.")

                    # This will be the default scaffolding for our package. Eventually I'll neuter it to only be the
                    # essential elements; it's also kinda a reference to me.
                    output = {
                        "bundle_id": "co.shuga.silica.unknown",
                        "name": "Unknown Package",
                        "version": "1.0.0",
                        "tagline": "An unknown package.",
                        "homepage": "https://shuga.co/",
                        "developer": {
                            "name": "Unknown",
                            "email": "*****@*****.**"
                        },
                        "maintainer": {
                            "name": "Unknown",
                            "email": "*****@*****.**"
                        },
                        "section": "Themes",

                        "works_min": "8.0",
                        "works_max": "13.0",
                        "featured": "false"
                    }

                    if is_deb:
                        print("Extracting data from DEB...")
                        deb = Dpkg(deb_path)
                        output['name'] = deb.headers['Name']
                        output['bundle_id'] = deb.headers['Package']
                        try:
                            output['tagline'] = deb.headers['Description']
                        except Exception:
                            output['tagline'] = input("What is a brief description of the package? ")
                        try:
                            output['homepage'] = deb.headers['Homepage']
                        except Exception:
                            pass
                        try:
                            remove_email_regex = re.compile('<.*?>')
                            output['developer']['name'] = remove_email_regex.sub("", deb.headers['Author'])
                        except Exception:
                            output['developer']['name'] = input("Who originally made this package? This may be"
                                                                " your name. ")
                        output['developer']['email'] = input("What is the original author's email address? ")
                        try:
                            remove_email_regex = re.compile('<.*?>')
                            output['maintainer']['name'] = remove_email_regex.sub("", deb.headers['Maintainer'])
                        except Exception:
                            output['maintainer']['name'] = input("Who maintains this package now?"
                                                                 " This is likely your name. ")
                        output['maintainer']['email'] = input("What is the maintainer's email address? ")
                        try:
                            output['sponsor']['name'] = remove_email_regex.sub("", deb.headers['Sponsor'])
                        except Exception:
                            pass
                        try:
                            output['dependencies'] = deb.headers['Depends']
                        except Exception:
                            pass
                        try:
                            output['section'] = deb.headers['Section']
                        except Exception:
                            pass
                        try:
                            output['version'] = deb.headers['Version']
                        except Exception:
                            output['version'] = "1.0.0"
                        try:
                            output['conflicts'] = deb.headers['Conflicts']
                        except Exception:
                            pass
                        try:
                            output['replaces'] = deb.headers['Replaces']
                        except Exception:
                            pass
                        try:
                            output['provides'] = deb.headers['Provides']
                        except Exception:
                            pass
                        try:
                            output['build_depends'] = deb.headers['Build-Depends']
                        except Exception:
                            pass
                        try:
                            output['recommends'] = deb.headers['Recommends']
                        except Exception:
                            pass
                        try:
                            output['suggests'] = deb.headers['Suggests']
                        except Exception:
                            pass
                        try:
                            output['enhances'] = deb.headers['Enhances']
                        except Exception:
                            pass
                        try:
                            output['breaks'] = deb.headers['Breaks']
                        except Exception:
                            pass
                        try:
                            output['tags'] = deb.headers['Tag']
                        except Exception:
                            pass
                        try:
                            output['suggests'] = deb.headers['Suggests']
                        except Exception:
                            pass
                        # These still need data.
                        output['works_min'] = input("What is the lowest iOS version the package works on? ")
                        output['works_max'] = input("What is the highest iOS version the package works on? ")
                        output['featured'] = input("Should this package be featured on your repo? (true/false) ")
                        set_tint = input("What would you like this package's tint color to be? To keep it at"
                                         " the default, leave this blank: ")
                        if set_tint != "":
                            output['tint'] = set_tint
                        print("All done! Please look over the generated \"index.json\" file and consider populating the"
                              " \"silica_data\" folder with a description, screenshots, and an icon.")
                        # Extract Control file and scripts from DEB
                        DpkgPy.control_extract(self, deb_path, self.root + "Packages/" + folder +
                                               "/silica_data/scripts/")
                        # Remove the Control; it's not needed.
                        os.remove(self.root + "Packages/" + folder + "/silica_data/scripts/Control")
                        if not os.listdir(self.root + "Packages/" + folder + "/silica_data/scripts/"):
                            os.rmdir(self.root + "Packages/" + folder + "/silica_data/scripts/")
                    else:
                        print("Estimating dependencies...")
                        # Use the filesystem to see if Zeppelin, Anemone, LockGlyph, XenHTML, and similar.
                        # If one of these are found, set it as a dependency.
                        # If multiple of these are found, use a hierarchy system, with Anemone as the highest priority,
                        # for determining the category.
                        output['dependencies'] = ""
                        output['section'] = "Themes"

                        if os.path.isdir(self.root + "Packages/" + folder + "/Library/Zeppelin"):
                            output['section'] = "Themes (Zeppelin)"
                            output['dependencies'] += "com.alexzielenski.zeppelin, "

                        if os.path.isdir(self.root + "Packages/" + folder + "/Library/Application Support/LockGlyph"):
                            output['section'] = "Themes (LockGlyph)"
                            output['dependencies'] += "com.evilgoldfish.lockglypgh, "

                        if os.path.isdir(self.root + "Packages/" + folder + "/var/mobile/Library/iWidgets"):
                            output['section'] = "Widgets"
                            output['dependencies'] += "com.matchstic.xenhtml, "

                        if os.path.isdir(self.root + "Packages/" + folder + "/Library/Wallpaper"):
                            output['section'] = "Wallpapers"

                        if os.path.isdir(self.root + "Packages/" + folder + "/Library/Themes"):
                            output['section'] = "Themes"
                            output['dependencies'] += "com.anemonetheming.anemone, "

                        if output['dependencies'] != "":
                            output['dependencies'] = output['dependencies'][:-2]

                        repo_settings = PackageLister.GetRepoSettings(self)
                        # Ask for name
                        output['name'] = input("What should we name this package? ")
                        # Automatically generate a bundle ID from the package name.
                        domain_breakup = repo_settings['cname'].split(".")[::-1]
                        only_alpha_regex = re.compile('[^a-zA-Z]')
                        machine_safe_name = only_alpha_regex.sub("", output['name']).lower()
                        output['bundle_id'] = ".".join(str(x) for x in domain_breakup) + "." + machine_safe_name
                        output['tagline'] = input("What is a brief description of the package? ")
                        output['homepage'] = "https://" + repo_settings['cname']
                        # I could potentially default this to what is in settings.json but attribution may be an issue.
                        output['developer']['name'] = input("Who made this package? This is likely your name. ")
                        output['developer']['email'] = input("What is the author's email address? ")
                        output['works_min'] = input("What is the lowest iOS version the package works on? ")
                        output['works_max'] = input("What is the highest iOS version the package works on? ")
                        output['featured'] = input("Should this package be featured on your repo? (true/false) ")
                    PackageLister.CreateFolder(self, "Packages/" + folder + "/silica_data/")
                    PackageLister.CreateFile(self, "Packages/" + folder + "/silica_data/index.json", json.dumps(output))
Beispiel #15
0
    def CompileControl(self, tweak_data, repo_settings):
        """
        Compiles a CONTROL file from a tweak_data object

        Object tweak_data: A single index of a "tweak release" object.
        Object repo_settings: An object of repo settings.
        """
        subfolder = PackageLister.FullPathCname(self, repo_settings)

        control_file = "Architecture: iphoneos-arm\n"
        # Mandatory properties include name, bundle id, and version.
        control_file += "Package: " + tweak_data['bundle_id'] + "\n"
        control_file += "Name: " + tweak_data['name'] + "\n"
        control_file += "Version: " + tweak_data['version'] + "\n"
        # Known properties
        control_file += "Depiction: https://" + repo_settings['cname'] + subfolder + "/depiction/web/" + tweak_data[
            'bundle_id'] \
                        + ".html\n"
        control_file += "SileoDepiction: https://" + repo_settings['cname'] + subfolder + "/depiction/native/" \
                        + tweak_data['bundle_id'] + ".json\n"
        control_file += "ModernDepiction: https://" + repo_settings['cname'] + subfolder + "/depiction/native/" \
                        + tweak_data['bundle_id'] + ".json\n"
        control_file += "Icon: https://" + repo_settings['cname'] + subfolder + "/assets/" + tweak_data[
            'bundle_id'] + "/icon.png\n"

        # Optional properties
        try:
            if tweak_data['tagline']:
                # APT note: Multi-line descriptions are in the spec, but must be indicated with a leading space.
                control_file += "Description: " + tweak_data['tagline'].replace("\n\n", "\n .\n ").replace("\n", "\n ") + "\n"
        except Exception:
            control_file += "Description: An awesome package!\n"

        try:
            if tweak_data['homepage']:
                control_file += "Homepage: " + tweak_data['homepage'] + "\n"
        except Exception:
            pass

        try:
            if tweak_data['section']:
                control_file += "Section: " + tweak_data['section'] + "\n"
        except Exception:
            control_file += "Section: Unknown\n"

        try:
            if tweak_data['pre_dependencies']:
                control_file += "Pre-Depends: " + tweak_data['pre_dependencies'] + "\n"
        except Exception:
            pass

        try:
            if tweak_data['dependencies']:
                control_file += "Depends: firmware (>=" + tweak_data['works_min'] + "), " + tweak_data[
                    'dependencies'] + "\n"
        except Exception:
            control_file += "Depends: firmware (>=" + tweak_data['works_min'] + ")\n"

        try:
            if tweak_data['conflicts']:
                control_file += "Conflicts: " + tweak_data['conflicts'] + "\n"
        except Exception:
            pass

        try:
            if tweak_data['replaces']:
                control_file += "Replaces: " + tweak_data['replaces'] + "\n"
        except Exception:
            pass

        try:
            if tweak_data['provides']:
                control_file += "Provides: " + tweak_data['provides'] + "\n"
        except Exception:
            pass

        try:
            if tweak_data['build_depends']:
                control_file += "Build-Depends: " + tweak_data['build_depends'] + "\n"
        except Exception:
            pass

        try:
            if tweak_data['recommends']:
                control_file += "Recommends: " + tweak_data['recommends'] + "\n"
        except Exception:
            pass

        try:
            if tweak_data['suggests']:
                control_file += "Suggests: " + tweak_data['suggests'] + "\n"
        except Exception:
            pass

        try:
            if tweak_data['enhances']:
                control_file += "Enhances: " + tweak_data['enhances'] + "\n"
        except Exception:
            pass

        try:
            if tweak_data['breaks']:
                control_file += "Breaks: " + tweak_data['breaks'] + "\n"
        except Exception:
            pass
        try:
            if tweak_data['tags']:
                control_file += "Tags: compatible_min::ios" + tweak_data['works_min'] + ", compatible_max::ios" + tweak_data['works_max'] + ", " + tweak_data['tags'] + "\n"
        except Exception:
            control_file += "Tags: compatible_min::ios" + tweak_data['works_min'] + ", compatible_max::ios" + tweak_data['works_max'] + "\n"

        try:
            if tweak_data['developer']:
                try:
                    if tweak_data['developer']['email']:
                        control_file += "Author: " + tweak_data['developer']['name'] + " <" + tweak_data['developer'][
                            'email'] + ">\n"
                except Exception:
                    control_file += "Author: " + tweak_data['developer']['name'] + "\n"
        except Exception:
            control_file += "Author: Unknown\n"

        try:
            if tweak_data['maintainer']['email']:
                control_file += "Maintainer: " + tweak_data['maintainer']['name'] + " <" \
                                + tweak_data['maintainer']['email'] + ">\n"
        except Exception:
            try:
                control_file += "Maintainer: " + tweak_data['maintainer']['name'] + "\n"
            except Exception:
                try:
                    if tweak_data['developer']['email']:
                        control_file += "Maintainer: " + tweak_data['developer']['name'] + " <" \
                                        + tweak_data['developer']['email'] + ">\n"
                except Exception:
                    try:
                        control_file += "Maintainer: " + tweak_data['developer']['name'] + "\n"
                    except Exception:
                        control_file += "Maintainer: Unknown\n"

        try:
            if tweak_data['sponsor']:
                try:
                    if tweak_data['sponsor']['email']:
                        control_file += "Sponsor: " + tweak_data['sponsor']['name'] + " <" + tweak_data['sponsor'][
                            'email'] + ">\n"
                except Exception:
                    control_file += "Sponsor: " + tweak_data['sponsor']['name'] + "\n"
        except Exception:
            pass

        # other_control
        try:
            if tweak_data['other_control']:
                for line in tweak_data['other_control']:
                    control_file += line + "\n"
        except Exception:
            pass

        return control_file
Beispiel #16
0
 def __init__(self, version):
     super(DebianPackager, self).__init__()
     self.version = version
     self.root = os.path.dirname(os.path.abspath(__file__)) + "/../"
     self.PackageLister = PackageLister(self.version)
Beispiel #17
0
    def CreateDEB(self, bundle_id, recorded_version):
        """
        Creates a DEB from information stored in the "temp" folder.

        String bundle_id: The bundle id of the package to compress.
        String recorded_version: 
        Object tweak_release: A "tweak release" object.
        """
        # TODO: Find a Python-based method to safely delete all DS_Store files.
        call(["find", ".", "-name", ".DS_Store", "-delete"],
             cwd=self.root + "temp/" + bundle_id)  # Remove .DS_Store. Kinda finicky.
        for file_name in os.listdir(self.root + "temp/" + bundle_id):
            if file_name.endswith(".deb"):
                # Check if the DEB is a newer version
                deb = Dpkg(self.root + "temp/" + bundle_id + "/" + file_name)
                if Dpkg.compare_versions(recorded_version, deb.version) == -1:
                    # Update package stuff
                    package_name = PackageLister.BundleIdToDirName(self, bundle_id)
                    with open(self.root + "Packages/" + package_name + "/silica_data/index.json", "r") as content_file:
                        update_json = json.load(content_file)
                        update_json['version'] = deb.version
                        changelog_entry = input("The DEB provided for \"" + update_json['name'] +
                                                "\" has a new version available (" + recorded_version + " -> " +
                                                deb.version + "). What changed in this version?\n(Add multiple lines" +
                                                " by using newline characters [\\n\\n] and use valid Markdown syntax): "
                                                )

                        try:
                            update_json['changelog'].append(
                                {
                                    "version": deb.version,
                                    "changes": changelog_entry
                                }
                            )
                        except Exception:
                            # Make it a list!
                            update_json['changelog'] = []

                            update_json['changelog'].append(
                                {
                                    "version": deb.version,
                                    "changes": changelog_entry
                                }
                            )

                        # A small note: We already created the variables that contain the changelogs and, to
                        # make matters worse, all the web assets. The only way to mitigate this is to re-create the
                        # tweak_release variable again, which wrecks a lot of things (ie runtime).
                        # A Silica rewrite is required to properly fix this bug.
                        print("\nA small warning about adding changelogs mid-run:\n")
                        print("Due to some less-than-ideal design decisions with Silica, for the changelog to show")
                        print("up, you're going to have to run Silica again. Yes, I know this is annoying, and a proper")
                        print("solution is in the works, but the under-the-hood changes that'll be needed to fix")
                        print("it properly would require a rewrite [see issue #22].\n")
                        print("I'm deeply sorry about this.\n    - Shuga.\n")

                        # Get human-readable folder name
                        folder = PackageLister.BundleIdToDirName(self, bundle_id)
                        deb_path = self.root + "Packages/" + folder + "/" + file_name
                        # Extract Control file and scripts from DEB
                        DpkgPy.control_extract(self, deb_path, self.root + "Packages/" + folder +
                                               "/silica_data/scripts/")
                        # Remove the Control; it's not needed.
                        os.remove(self.root + "Packages/" + folder + "/silica_data/scripts/Control")
                        if not os.listdir(self.root + "Packages/" + folder + "/silica_data/scripts/"):
                            os.rmdir(self.root + "Packages/" + folder + "/silica_data/scripts/")

                        return_str = json.dumps(update_json)
                        print("Updating package index.json...")
                        PackageLister.CreateFile(self, "Packages/" + package_name +
                                                 "/silica_data/index.json", return_str)
                    pass
                DpkgPy.extract(self, self.root + "temp/" + bundle_id + "/" + file_name, self.root + "temp/" + bundle_id)
                try:
                    os.remove(self.root + "temp/" + bundle_id + "/" + file_name)
                except:
                    pass
                try:
                    os.remove(self.root + "temp/" + bundle_id + "/control")
                except:
                    pass
        else:
            # TODO: Update DpkgPy to generate DEB files without dependencies (for improved win32 support)
            # If the version is consistent, then assume the package is unchanged. Don't regenerate it.
            try:
                # Check for a DEB that already exists.
                docs_deb = Dpkg(self.root + "docs/pkg/" + bundle_id + ".deb")
                if docs_deb.version == recorded_version:
                    shutil.copy(self.root + "docs/pkg/" + bundle_id + ".deb", self.root + "temp/" + bundle_id + ".deb")
                    call_result = 0;
                else:
                    # Sneaky swap.
                    call_result = call(["dpkg-deb", "-b", "-Zgzip", self.root + "temp/" + bundle_id], cwd=self.root + "temp/")  # Compile DEB
            except:
                # Create the DEB again.
                call_result = call(["dpkg-deb", "-b", "-Zgzip", self.root + "temp/" + bundle_id], cwd=self.root + "temp/")  # Compile DEB
            if call_result != 0:
                # Did we run within WSL?
                if "Microsoft" in platform.release():
                    PackageLister.ErrorReporter(self, "Platform Error!", "dpkg-deb failed to run. "
                    "This is likely due to improper configuration of WSL. Please check the Silcia README for "
                    "how to set up WSL for dpkg-deb.")
                else:
                    PackageLister.ErrorReporter(self, "DPKG Error!", "dpkg-deb failed to run. "
                    "This could be due to a faulty system configuration.")
Beispiel #18
0
    def RenderPackageHTML(self, tweak_data):
        """
        Renders a package's depiction.

        Object tweak_data: A single index of a "tweak release" object.
        """
        with open(self.root + "Styles/tweak.mustache", "r") as content_file:
            index = content_file.read()
            replacements = DepictionGenerator.RenderDataHTML(self)
            try:
                replacements['tweak_name'] = tweak_data['name']
            except:
                PackageLister.ErrorReporter(
                    self, "Configuration Error!", "You are missing a package "
                    "name in its index.json. Make sure this and other required properties are set."
                )
            try:
                replacements['tweak_developer'] = tweak_data['developer'][
                    'name']
                replacements['tweak_compatibility'] = "iOS " + tweak_data[
                    'works_min'] + " to " + tweak_data['works_max']
                replacements['tweak_version'] = tweak_data['version']
                replacements['tweak_section'] = tweak_data['section']
                replacements['tweak_bundle_id'] = tweak_data['bundle_id']
                replacements['works_min'] = tweak_data['works_min']
                replacements['works_max'] = tweak_data['works_max']
                replacements['tweak_tagline'] = tweak_data['tagline']
            except:
                PackageLister.ErrorReporter(
                    self, "Configuration Error!",
                    "You are missing an essential "
                    "property in " + tweak_data['name'] +
                    "'s index.json. Make sure developer, version, section, "
                    "bundle id, and tagline are set properly.")
            replacements[
                'tweak_carousel'] = DepictionGenerator.ScreenshotCarousel(
                    self, tweak_data)
            replacements[
                'tweak_changelog'] = DepictionGenerator.RenderChangelogHTML(
                    self, tweak_data)
            replacements['footer'] = DepictionGenerator.RenderFooter(self)
            try:
                if tweak_data['source'] != "":
                    replacements['source'] = tweak_data['source']
            except Exception:
                pass

            try:
                replacements['tint_color'] = tweak_data['tint']
            except Exception:
                try:
                    repo_settings = PackageLister.GetRepoSettings(self)
                    replacements['tint_color'] = repo_settings['tint']
                except Exception:
                    replacements['tint_color'] = "#2cb1be"

            try:
                with open(
                        self.root + "docs/assets/" + tweak_data['bundle_id'] +
                        "/description.md", "r") as md_file:
                    raw_md = md_file.read()
                    desc_md = mistune.markdown(raw_md)
                    replacements['tweak_description'] = desc_md
            except Exception:
                replacements['tweak_description'] = tweak_data['tagline']

            # tweak_carousel

            return pystache.render(index, replacements)
Beispiel #19
0
    def RenderNativeHelp(self, tweak_data):
        """
        Generates a help view for Sileo users.

        Object tweak_data: A single index of a "tweak release" object.
        """

        repo_settings = PackageLister.GetRepoSettings(self)

        try:
            tint = tweak_data['tint']
        except Exception:
            try:
                tint = repo_settings['tint']
            except Exception:
                tint = "#2cb1be"

        view = []
        try:
            if tweak_data['developer']['email']:
                view.append({
                    "class":
                    "DepictionMarkdownView",
                    "markdown":
                    "If you need help with \"" + tweak_data['name'] +
                    "\", you can contact " + tweak_data['developer']['name'] +
                    ", the developer, via e-mail."
                })
                view.append({
                    "class":
                    "DepictionTableButtonView",
                    "title":
                    "Email Developer",
                    "action":
                    "mailto:" + tweak_data['developer']['email'],
                    "openExternal":
                    "true",
                    "tintColor":
                    tint
                })
        except Exception:
            try:
                view.append({
                    "class":
                    "DepictionMarkdownView",
                    "markdown":
                    "If you need help with \"" + tweak_data['name'] +
                    "\", you can contact " + tweak_data['developer']['name'] +
                    ", who is the developer. Sadly, we don't know their email."
                })
            except Exception:
                view.append({
                    "class":
                    "DepictionMarkdownView",
                    "markdown":
                    "The developer of the package \"" + tweak_data['name'] +
                    "\" is not known. Try contacting the repo owner for more information."
                })

        try:
            if tweak_data['social']:
                view.append({
                    "class":
                    "DepictionMarkdownView",
                    "markdown":
                    "You can also contact " + tweak_data['developer']['name'] +
                    " using the following" + " sites:"
                })
                for entry in tweak_data['social']:
                    view.append({
                        "class": "DepictionTableButtonView",
                        "title": entry['name'],
                        "action": entry['url'],
                        "openExternal": "true",
                        "tintColor": tint
                    })
        except Exception:
            pass

        try:
            if tweak_data['maintainer']['email']:
                view.append({
                    "class":
                    "DepictionMarkdownView",
                    "markdown":
                    tweak_data['maintainer']['name'] +
                    " is the maintainer of the package \"" +
                    tweak_data['name'] +
                    "\". Please contact them via email for any questions on this"
                    " version of the package."
                })
                view.append({
                    "class":
                    "DepictionTableButtonView",
                    "title":
                    "Email Maintainer",
                    "action":
                    "mailto:" + tweak_data['maintainer']['email'],
                    "openExternal":
                    "true",
                    "tintColor":
                    tint
                })
        except Exception:
            try:
                view.append({
                    "class":
                    "DepictionMarkdownView",
                    "markdown":
                    "If you need help with \"" + tweak_data['name'] +
                    "\", you should contact " +
                    tweak_data['maintainer']['name'] +
                    ", who is the package's current maintainer. Sadly, we don't know their email."
                })
            except Exception:
                pass

        view.append({
            "class":
            "DepictionMarkdownView",
            "markdown":
            "If you found a mistake in the depiction or cannot download the package, you can reach out"
            + " to the maintainer of the \"" + repo_settings['name'] +
            "\" repo, " + repo_settings['maintainer']['name'] + "."
        })
        view.append({
            "class": "DepictionTableButtonView",
            "title": "Email Repo Maintainer",
            "action": "mailto:" + repo_settings['maintainer']['email'],
            "openExternal": "true",
            "tintColor": tint
        })

        try:
            if repo_settings['social']:
                view.append({
                    "class":
                    "DepictionMarkdownView",
                    "markdown":
                    "You can also contact the repo owner via the following" +
                    " sites:"
                })
                for entry in repo_settings['social']:
                    view.append({
                        "class": "DepictionTableButtonView",
                        "title": entry['name'],
                        "action": entry['url'],
                        "openExternal": "true",
                        "tintColor": tint
                    })
        except Exception:
            pass

        return json.dumps(
            {
                "class": "DepictionStackView",
                "tintColor": tint,
                "title": "Contact Support",
                "views": view
            },
            separators=(',', ':'))
Beispiel #20
0
class DepictionGenerator:
    """
    DepictionGenerator deals with the rendering and generating of depictions.

    """
    def __init__(self, version):
        super(DepictionGenerator, self).__init__()
        self.version = version
        self.root = os.path.dirname(os.path.abspath(__file__)) + "/../"
        self.PackageLister = PackageLister(self.version)

    def CleanUp(self):
        """
        Cleans up some stuff.
        """
        # Remove all Silica-generated folders in docs/ except for docs/pkg/.
        try:
            shutil.rmtree(root + "docs/api")
        except Exception:
            pass

        try:
            shutil.rmtree(root + "docs/assets")
        except Exception:
            pass

        try:
            shutil.rmtree(root + "docs/depiction")
        except Exception:
            pass

        try:
            shutil.rmtree(root + "docs/web")
        except Exception:
            pass

        # Delete all Silica-generated files in root.
        try:
            os.remove(self.root + "docs/404.html")
        except Exception:
            pass
        try:
            os.remove(self.root + "docs/CNAME")
        except Exception:
            pass
        try:
            os.remove(self.root + "docs/CydiaIcon.png")
        except Exception:
            pass
        try:
            os.remove(self.root + "docs/index.html")
        except Exception:
            pass
        try:
            os.remove(self.root + "docs/Packages")
        except Exception:
            pass
        try:
            os.remove(self.root + "docs/Packages.bz2")
        except Exception:
            pass
        try:
            os.remove(self.root + "docs/Packages.xz")
        except Exception:
            pass
        try:
            os.remove(self.root + "docs/Release")
        except Exception:
            pass
        try:
            os.remove(self.root + "docs/sileo-featured.json")
        except Exception:
            pass

        # Clean up temp.
        try:
            shutil.rmtree(root + "temp/")
        except Exception:
            pass

    def RenderPackageHTML(self, tweak_data):
        """
        Renders a package's depiction.

        Object tweak_data: A single index of a "tweak release" object.
        """
        with open(self.root + "Styles/tweak.mustache", "r") as content_file:
            index = content_file.read()
            replacements = DepictionGenerator.RenderDataHTML(self)
            try:
                replacements['tweak_name'] = tweak_data['name']
            except:
                PackageLister.ErrorReporter(
                    self, "Configuration Error!", "You are missing a package "
                    "name in its index.json. Make sure this and other required properties are set."
                )
            try:
                replacements['tweak_developer'] = tweak_data['developer'][
                    'name']
                replacements['tweak_compatibility'] = "iOS " + tweak_data[
                    'works_min'] + " to " + tweak_data['works_max']
                replacements['tweak_version'] = tweak_data['version']
                replacements['tweak_section'] = tweak_data['section']
                replacements['tweak_bundle_id'] = tweak_data['bundle_id']
                replacements['works_min'] = tweak_data['works_min']
                replacements['works_max'] = tweak_data['works_max']
                replacements['tweak_tagline'] = tweak_data['tagline']
            except:
                PackageLister.ErrorReporter(
                    self, "Configuration Error!",
                    "You are missing an essential "
                    "property in " + tweak_data['name'] +
                    "'s index.json. Make sure developer, version, section, "
                    "bundle id, and tagline are set properly.")
            replacements[
                'tweak_carousel'] = DepictionGenerator.ScreenshotCarousel(
                    self, tweak_data)
            replacements[
                'tweak_changelog'] = DepictionGenerator.RenderChangelogHTML(
                    self, tweak_data)
            replacements['footer'] = DepictionGenerator.RenderFooter(self)
            try:
                if tweak_data['source'] != "":
                    replacements['source'] = tweak_data['source']
            except Exception:
                pass

            try:
                replacements['tint_color'] = tweak_data['tint']
            except Exception:
                try:
                    repo_settings = PackageLister.GetRepoSettings(self)
                    replacements['tint_color'] = repo_settings['tint']
                except Exception:
                    replacements['tint_color'] = "#2cb1be"

            try:
                with open(
                        self.root + "docs/assets/" + tweak_data['bundle_id'] +
                        "/description.md", "r") as md_file:
                    raw_md = md_file.read()
                    desc_md = mistune.markdown(raw_md)
                    replacements['tweak_description'] = desc_md
            except Exception:
                replacements['tweak_description'] = tweak_data['tagline']

            # tweak_carousel

            return pystache.render(index, replacements)

    def RenderPackageNative(self, tweak_data):
        """
        Renders a package's depiction using Sileo's "native depiction" format.

        Object tweak_data: A single index of a "tweak release" object.
        """
        repo_settings = PackageLister.GetRepoSettings(self)
        try:
            tint = tweak_data['tint']
        except Exception:
            try:
                tint = repo_settings['tint']
            except Exception:
                tint = "#2cb1be"

        try:
            with open(
                    self.root + "docs/assets/" + tweak_data['bundle_id'] +
                    "/description.md", "r") as md_file:
                md_txt = md_file.read()
        except Exception:
            md_txt = tweak_data['tagline']

        date = datetime.datetime.now().strftime("%Y-%m-%d")

        screenshot_obj = []
        image_list = self.PackageLister.GetScreenshots(tweak_data)
        subfolder = PackageLister.FullPathCname(self, repo_settings)
        if len(image_list) > 0:
            for image in image_list:
                screenshot_entry = {
                    "url":
                    "https://" + repo_settings['cname'] + subfolder +
                    "/assets/" + tweak_data['bundle_id'] + "/screenshot/" +
                    image,
                    "accessibilityText":
                    "Screenshot"
                }
                screenshot_obj.append(screenshot_entry)
            # The following code is evil, but is actually easier to maintain. My humblest apologies.
            screenshot_view_title = "DepictionHeaderView"
            screenshot_view_carousel = "DepictionScreenshotsView"
        else:
            # The following code is evil, but is actually easier to maintain. My humblest apologies.
            screenshot_view_title = "HiddenDepictionHeaderView"
            screenshot_view_carousel = "HiddenDepictionScreenshotsView"

        changelog = DepictionGenerator.RenderNativeChangelog(self, tweak_data)
        screenshot_size = PackageLister.GetScreenshotSize(self, tweak_data)

        depiction = {
            "minVersion":
            "0.1",
            "headerImage":
            "https://" + repo_settings['cname'] + subfolder + "/assets/" +
            tweak_data['bundle_id'] + "/banner.png",
            "tintColor":
            tint,
            "tabs": [{
                "tabname":
                "Details",
                "views": [{
                    "class": screenshot_view_carousel,
                    "screenshots": screenshot_obj,
                    "itemCornerRadius": 8,
                    "itemSize": screenshot_size
                }, {
                    "markdown": md_txt,
                    "useSpacing": "true",
                    "class": "DepictionMarkdownView"
                }, {
                    "class": "DepictionSpacerView"
                }, {
                    "class": "DepictionHeaderView",
                    "title": "Information",
                }, {
                    "class": "DepictionTableTextView",
                    "title": "Developer",
                    "text": tweak_data['developer']['name']
                }, {
                    "class": "DepictionTableTextView",
                    "title": "Version",
                    "text": tweak_data['version']
                }, {
                    "class":
                    "DepictionTableTextView",
                    "title":
                    "Compatibility",
                    "text":
                    "iOS " + tweak_data['works_min'] + " to " +
                    tweak_data['works_max']
                }, {
                    "class": "DepictionTableTextView",
                    "title": "Section",
                    "text": tweak_data['section']
                }, {
                    "class": "DepictionSpacerView"
                }, {
                    "class":
                    "DepictionTableButtonView",
                    "title":
                    "Contact Support",
                    "action":
                    "depiction-https://" + repo_settings['cname'] + subfolder +
                    "/depiction/native/help/" + tweak_data['bundle_id'] +
                    ".json",
                    "openExternal":
                    "true",
                    "tintColor":
                    tint
                }, {
                    "class": "DepictionLabelView",
                    "text": DepictionGenerator.RenderFooter(self),
                    "textColor": "#999999",
                    "fontSize": "10.0",
                    "alignment": 1
                }],
                "class":
                "DepictionStackView"
            }, {
                "tabname": "Changelog",
                "views": changelog,
                "class": "DepictionStackView"
            }],
            "class":
            "DepictionTabView"
        }

        blank = {"class": "DepictionSpacerView"}

        try:
            if tweak_data['source'] != "":
                source_btn = {
                    "class": "DepictionTableButtonView",
                    "title": "View Source Code",
                    "action": tweak_data['source'],
                    "openExternal": "true",
                    "tintColor": tint
                }
                depiction['tabs'][0]['views'].insert(8, source_btn)
                depiction['tabs'][0]['views'].insert(8, blank)
                pass
        except Exception:
            pass

        return json.dumps(depiction, separators=(',', ':'))

    def RenderNativeChangelog(self, tweak_data):
        """
        Generates a changelog for use in native depictions.

        Object tweak_data: A single index of a "tweak release" object.
        """
        try:
            changelog = []
            for version in tweak_data['changelog'][::-1]:
                ver_entry = {
                    "class":
                    "DepictionMarkdownView",
                    "markdown":
                    "#### Version {0}\n\n{1}".format(version['version'],
                                                     version['changes']),
                }
                changelog.append(ver_entry)
            changelog.append({
                "class": "DepictionLabelView",
                "text": DepictionGenerator.RenderFooter(self),
                "textColor": "#999999",
                "fontSize": "10.0",
                "alignment": 1
            })
            return changelog
        except Exception:
            return [{
                "class": "DepictionHeaderView",
                "title": "Changelog"
            }, {
                "class": "DepictionMarkdownView",
                "markdown": "This package has no changelog.",
            }, {
                "class": "DepictionLabelView",
                "text": DepictionGenerator.RenderFooter(self),
                "textColor": "#999999",
                "fontSize": "10.0",
                "alignment": 1
            }]

    def ChangelogEntry(self, version, raw_md):
        """
        Generates a div for changelog entries.

        String version: The version number.
        String raw_md: The changelog entry text (Markdown-compatible).
        """
        return '''<div class="changelog_entry">
                <h4>{0}</h4>
                <div class="md_view">{1}</div>
            </div>'''.format(version, mistune.markdown(raw_md))

    def RenderChangelogHTML(self, tweak_data):
        """
        Generates a div of changelog entries.

        Object tweak_data: A single index of a "tweak release" object.
        """
        element = ""
        try:
            for version in tweak_data['changelog'][::-1]:
                element += DepictionGenerator.ChangelogEntry(
                    self, version['version'], version['changes'])
            return element
        except Exception:
            return "This package has no changelog."

    def RenderIndexHTML(self):
        """
        Renders the home page (index.html).
        """
        repo_settings = PackageLister.GetRepoSettings(self)
        with open(self.root + "Styles/index.mustache", "r") as content_file:
            index = content_file.read()
            replacements = DepictionGenerator.RenderDataHTML(self)
            replacements['tint_color'] = repo_settings['tint']
            replacements['footer'] = DepictionGenerator.RenderFooter(self)
            replacements['tweak_release'] = PackageLister.GetTweakRelease(self)
            return pystache.render(index, replacements)

    def RenderFooter(self):
        """
        Renders the footer.
        """
        repo_settings = PackageLister.GetRepoSettings(self)
        data = DepictionGenerator.RenderDataHTML(self)
        try:
            footer = pystache.render(repo_settings['footer'], data)
        except Exception:
            footer = pystache.render(
                "Silica {{silica_version}} – Updated {{silica_compile_date}}",
                data)
        return footer

    def RenderDataBasic(self):
        """
        Gets the value of basic repo data to pass to Pystache.
        """
        repo_settings = PackageLister.GetRepoSettings(self)
        with open(self.root + "Styles/settings.json", "r") as content_file:
            data = json.load(content_file)
            date = datetime.datetime.now().strftime("%Y-%m-%d")
            subfolder = PackageLister.FullPathCname(self, repo_settings)
            return {
                "silica_version": self.version,
                "silica_compile_date": date,
                "repo_name": data['name'],
                "repo_url": data['cname'] + subfolder,
                "repo_desc": data['description'],
                "repo_tint": data['tint']
            }

    def RenderDataHTML(self):
        data = DepictionGenerator.RenderDataBasic(self)

        tweak_release = PackageLister.GetTweakRelease(self)

        data['repo_packages'] = DepictionGenerator.PackageEntryList(
            self, tweak_release)

        data['repo_carousel'] = DepictionGenerator.CarouselEntryList(
            self, tweak_release)

        return data

    def PackageEntry(self, name, author, icon, bundle_id):
        """
        Generates a package entry div.

        String name: The package's name
        String author: The author's name
        String (URL) icon: A URL to an image of the package icon.

        Scope: HTML > Generation > Helpers
        """

        if (bundle_id != "silica_do_not_hyperlink"):
            return '''<a class="subtle_link" href="depiction/web/{3}.html"><div class="package">
            <img src="{0}">
            <div class="package_info">
                <p class="package_name">{1}</p>
                <p class="package_caption">{2}</p>
            </div>
        </div></a>'''.format(icon, name, author, bundle_id)
        else:
            return '''<div class="package">
                <img src="{0}">
                <div class="package_info">
                    <p class="package_name">{1}</p>
                    <p class="package_caption">{2}</p>
                </div>
            </div>'''.format(icon, name, author)

    def ScreenshotCarousel(self, tweak_data):
        """
        Generates a screenshot div.

        Object tweak_data: A single index of a "tweak release" object.
        """
        repo_settings = PackageLister.GetRepoSettings(self)
        screenshot_div = "<div class=\"scroll_view\">"
        image_list = self.PackageLister.GetScreenshots(tweak_data)
        if (len(image_list) > 0):
            for image in image_list:
                screenshot_div += '''<img class="img_card" src="../../assets/{1}/screenshot/{2}">'''.format(
                    repo_settings['cname'], tweak_data['bundle_id'], image)
            screenshot_div += "</div>"
        else:
            screenshot_div = ""
        return screenshot_div

    def CarouselEntry(self, name, banner, bundle_id):
        """
        Generates a card to be used in Featured carousels.

        String name: The package's name
        String (URL) banner: A URL to an image of the package banner.
        """
        if len(name) > 18:
            name = name[:18] + "…"
        return '''<a href="depiction/web/{0}.html" style="background-image: url({1})" class="card">
                <p>{2}</p>
            </a>'''.format(bundle_id, banner, name)

    def NativeFeaturedCarousel(self, tweak_release):
        """
        Generate a sileo-featured.json file for featured packages.

        Object carousel_entry_list: A "tweak release" object.
        """
        repo_settings = PackageLister.GetRepoSettings(self)
        subfolder = PackageLister.FullPathCname(self, repo_settings)
        banners = []
        for package in tweak_release:
            try:
                if package['featured'].lower() == "true":
                    ar_el = {
                        "package":
                        package['bundle_id'],
                        "title":
                        package['name'],
                        "url":
                        "https://" + repo_settings['cname'] + subfolder +
                        "/assets/" + package['bundle_id'] + "/banner.png",
                        "hideShadow":
                        "false"
                    }
                    banners.append(ar_el)
            except Exception:
                pass
        if len(banners) == 0:
            try:
                featured_int = random.randint(0, (len(tweak_release) - 1))
            except Exception:
                PackageLister.ErrorReporter(
                    self, "Configuration Error!",
                    "You have no packages added to this repo. "
                    "Make sure a folder is created at \"" + self.version +
                    "/Packages\" that contains folders with "
                    "package data inside of them and run Silica again.")
            featured_package = tweak_release[featured_int]
            ar_el = {
                "package":
                featured_package['bundle_id'],
                "title":
                featured_package['name'],
                "url":
                "https://" + repo_settings['cname'] + subfolder + "/assets/" +
                featured_package['bundle_id'] + "/banner.png",
                "hideShadow":
                "false"
            }
            banners.append(ar_el)

        featured_json = {
            "class": "FeaturedBannersView",
            "itemSize": "{263, 148}",
            "itemCornerRadius": 8,
            "banners": banners
        }
        return json.dumps(featured_json, separators=(',', ':'))

    def PackageEntryList(self, tweak_release):
        """
        Generate a user-friendly list of packages on the repo.

        Object tweak_release: A "tweak release" object.
        """
        list_el = ""
        for package in tweak_release:
            list_el += DepictionGenerator.PackageEntry(
                self, package['name'], package['developer']['name'],
                "assets/" + package['bundle_id'] + "/icon.png",
                package['bundle_id'])
        return list_el

    def CarouselEntryList(self, tweak_release):
        """
        Generate a carousel of featured packages on the repo.

        Object tweak_release: A "tweak release" object.
        """
        list_el = ""
        for package in tweak_release:
            try:
                if package['featured'].lower() == "true":
                    list_el += DepictionGenerator.CarouselEntry(
                        self, package['name'],
                        "assets/" + package['bundle_id'] + "/banner.png",
                        package['bundle_id'])
            except Exception:
                pass
        if list_el == "":
            try:
                featured_int = random.randint(0, (len(tweak_release) - 1))
            except Exception:
                PackageLister.ErrorReporter(
                    self, "Configuration Error!",
                    "You have no packages added to this repo."
                    " Make sure a folder is created at \"" + self.version +
                    "/Packages\" that contains folders with package data inside of them and run Silica again."
                )

            featured_package = tweak_release[featured_int]
            list_el += DepictionGenerator.CarouselEntry(
                self, featured_package['name'],
                "assets/" + featured_package['bundle_id'] + "/banner.png",
                featured_package['bundle_id'])
        return list_el

    def SilicaAbout(self):
        """
        Returns a JSON object that describes information about the Silica install.
        """

        compile_date = datetime.datetime.now().isoformat()
        try:
            upstream_url = check_output(
                ["git", "config", "--get", "remote.origin.url"],
                cwd=self.root).decode("utf-8")
        except Exception:
            upstream_url = "undefined"
        return {
            "software": "Silica",
            "version": self.version,
            "compile_date": compile_date,
            "upstream_url": upstream_url
        }

    def RenderNativeHelp(self, tweak_data):
        """
        Generates a help view for Sileo users.

        Object tweak_data: A single index of a "tweak release" object.
        """

        repo_settings = PackageLister.GetRepoSettings(self)

        try:
            tint = tweak_data['tint']
        except Exception:
            try:
                tint = repo_settings['tint']
            except Exception:
                tint = "#2cb1be"

        view = []
        try:
            if tweak_data['developer']['email']:
                view.append({
                    "class":
                    "DepictionMarkdownView",
                    "markdown":
                    "If you need help with \"" + tweak_data['name'] +
                    "\", you can contact " + tweak_data['developer']['name'] +
                    ", the developer, via e-mail."
                })
                view.append({
                    "class":
                    "DepictionTableButtonView",
                    "title":
                    "Email Developer",
                    "action":
                    "mailto:" + tweak_data['developer']['email'],
                    "openExternal":
                    "true",
                    "tintColor":
                    tint
                })
        except Exception:
            try:
                view.append({
                    "class":
                    "DepictionMarkdownView",
                    "markdown":
                    "If you need help with \"" + tweak_data['name'] +
                    "\", you can contact " + tweak_data['developer']['name'] +
                    ", who is the developer. Sadly, we don't know their email."
                })
            except Exception:
                view.append({
                    "class":
                    "DepictionMarkdownView",
                    "markdown":
                    "The developer of the package \"" + tweak_data['name'] +
                    "\" is not known. Try contacting the repo owner for more information."
                })

        try:
            if tweak_data['social']:
                view.append({
                    "class":
                    "DepictionMarkdownView",
                    "markdown":
                    "You can also contact " + tweak_data['developer']['name'] +
                    " using the following" + " sites:"
                })
                for entry in tweak_data['social']:
                    view.append({
                        "class": "DepictionTableButtonView",
                        "title": entry['name'],
                        "action": entry['url'],
                        "openExternal": "true",
                        "tintColor": tint
                    })
        except Exception:
            pass

        try:
            if tweak_data['maintainer']['email']:
                view.append({
                    "class":
                    "DepictionMarkdownView",
                    "markdown":
                    tweak_data['maintainer']['name'] +
                    " is the maintainer of the package \"" +
                    tweak_data['name'] +
                    "\". Please contact them via email for any questions on this"
                    " version of the package."
                })
                view.append({
                    "class":
                    "DepictionTableButtonView",
                    "title":
                    "Email Maintainer",
                    "action":
                    "mailto:" + tweak_data['maintainer']['email'],
                    "openExternal":
                    "true",
                    "tintColor":
                    tint
                })
        except Exception:
            try:
                view.append({
                    "class":
                    "DepictionMarkdownView",
                    "markdown":
                    "If you need help with \"" + tweak_data['name'] +
                    "\", you should contact " +
                    tweak_data['maintainer']['name'] +
                    ", who is the package's current maintainer. Sadly, we don't know their email."
                })
            except Exception:
                pass

        view.append({
            "class":
            "DepictionMarkdownView",
            "markdown":
            "If you found a mistake in the depiction or cannot download the package, you can reach out"
            + " to the maintainer of the \"" + repo_settings['name'] +
            "\" repo, " + repo_settings['maintainer']['name'] + "."
        })
        view.append({
            "class": "DepictionTableButtonView",
            "title": "Email Repo Maintainer",
            "action": "mailto:" + repo_settings['maintainer']['email'],
            "openExternal": "true",
            "tintColor": tint
        })

        try:
            if repo_settings['social']:
                view.append({
                    "class":
                    "DepictionMarkdownView",
                    "markdown":
                    "You can also contact the repo owner via the following" +
                    " sites:"
                })
                for entry in repo_settings['social']:
                    view.append({
                        "class": "DepictionTableButtonView",
                        "title": entry['name'],
                        "action": entry['url'],
                        "openExternal": "true",
                        "tintColor": tint
                    })
        except Exception:
            pass

        return json.dumps(
            {
                "class": "DepictionStackView",
                "tintColor": tint,
                "title": "Contact Support",
                "views": view
            },
            separators=(',', ':'))
Beispiel #21
0
    def RenderPackageNative(self, tweak_data):
        """
        Renders a package's depiction using Sileo's "native depiction" format.

        Object tweak_data: A single index of a "tweak release" object.
        """
        repo_settings = PackageLister.GetRepoSettings(self)
        try:
            tint = tweak_data['tint']
        except Exception:
            try:
                tint = repo_settings['tint']
            except Exception:
                tint = "#2cb1be"

        try:
            with open(
                    self.root + "docs/assets/" + tweak_data['bundle_id'] +
                    "/description.md", "r") as md_file:
                md_txt = md_file.read()
        except Exception:
            md_txt = tweak_data['tagline']

        date = datetime.datetime.now().strftime("%Y-%m-%d")

        screenshot_obj = []
        image_list = self.PackageLister.GetScreenshots(tweak_data)
        subfolder = PackageLister.FullPathCname(self, repo_settings)
        if len(image_list) > 0:
            for image in image_list:
                screenshot_entry = {
                    "url":
                    "https://" + repo_settings['cname'] + subfolder +
                    "/assets/" + tweak_data['bundle_id'] + "/screenshot/" +
                    image,
                    "accessibilityText":
                    "Screenshot"
                }
                screenshot_obj.append(screenshot_entry)
            # The following code is evil, but is actually easier to maintain. My humblest apologies.
            screenshot_view_title = "DepictionHeaderView"
            screenshot_view_carousel = "DepictionScreenshotsView"
        else:
            # The following code is evil, but is actually easier to maintain. My humblest apologies.
            screenshot_view_title = "HiddenDepictionHeaderView"
            screenshot_view_carousel = "HiddenDepictionScreenshotsView"

        changelog = DepictionGenerator.RenderNativeChangelog(self, tweak_data)
        screenshot_size = PackageLister.GetScreenshotSize(self, tweak_data)

        depiction = {
            "minVersion":
            "0.1",
            "headerImage":
            "https://" + repo_settings['cname'] + subfolder + "/assets/" +
            tweak_data['bundle_id'] + "/banner.png",
            "tintColor":
            tint,
            "tabs": [{
                "tabname":
                "Details",
                "views": [{
                    "class": screenshot_view_carousel,
                    "screenshots": screenshot_obj,
                    "itemCornerRadius": 8,
                    "itemSize": screenshot_size
                }, {
                    "markdown": md_txt,
                    "useSpacing": "true",
                    "class": "DepictionMarkdownView"
                }, {
                    "class": "DepictionSpacerView"
                }, {
                    "class": "DepictionHeaderView",
                    "title": "Information",
                }, {
                    "class": "DepictionTableTextView",
                    "title": "Developer",
                    "text": tweak_data['developer']['name']
                }, {
                    "class": "DepictionTableTextView",
                    "title": "Version",
                    "text": tweak_data['version']
                }, {
                    "class":
                    "DepictionTableTextView",
                    "title":
                    "Compatibility",
                    "text":
                    "iOS " + tweak_data['works_min'] + " to " +
                    tweak_data['works_max']
                }, {
                    "class": "DepictionTableTextView",
                    "title": "Section",
                    "text": tweak_data['section']
                }, {
                    "class": "DepictionSpacerView"
                }, {
                    "class":
                    "DepictionTableButtonView",
                    "title":
                    "Contact Support",
                    "action":
                    "depiction-https://" + repo_settings['cname'] + subfolder +
                    "/depiction/native/help/" + tweak_data['bundle_id'] +
                    ".json",
                    "openExternal":
                    "true",
                    "tintColor":
                    tint
                }, {
                    "class": "DepictionLabelView",
                    "text": DepictionGenerator.RenderFooter(self),
                    "textColor": "#999999",
                    "fontSize": "10.0",
                    "alignment": 1
                }],
                "class":
                "DepictionStackView"
            }, {
                "tabname": "Changelog",
                "views": changelog,
                "class": "DepictionStackView"
            }],
            "class":
            "DepictionTabView"
        }

        blank = {"class": "DepictionSpacerView"}

        try:
            if tweak_data['source'] != "":
                source_btn = {
                    "class": "DepictionTableButtonView",
                    "title": "View Source Code",
                    "action": tweak_data['source'],
                    "openExternal": "true",
                    "tintColor": tint
                }
                depiction['tabs'][0]['views'].insert(8, source_btn)
                depiction['tabs'][0]['views'].insert(8, blank)
                pass
        except Exception:
            pass

        return json.dumps(depiction, separators=(',', ':'))
Beispiel #22
0
    def CreateDEB(self, bundle_id, recorded_version):
        """
        Creates a DEB from information stored in the "temp" folder.

        String bundle_id: The bundle id of the package to compress.
        String recorded_version: 
        Object tweak_release: A "tweak release" object.
        """
        # TODO: Find a Python-based method to safely delete all DS_Store files.
        call(["find", ".", "-name", ".DS_Store", "-delete"],
             cwd=self.root + "temp/" + bundle_id)  # Remove .DS_Store. Kinda finicky.
        for file_name in os.listdir(self.root + "temp/" + bundle_id):
            if file_name.endswith(".deb"):
                # Check if the DEB is a newer version
                deb = Dpkg(self.root + "temp/" + bundle_id + "/" + file_name)
                if Dpkg.compare_versions(recorded_version, deb.version) == -1:
                    # Update package stuff
                    package_name = PackageLister.BundleIdToDirName(self, bundle_id)
                    with open(self.root + "Packages/" + package_name + "/silica_data/index.json", "r") as content_file:
                        update_json = json.load(content_file)
                        update_json['version'] = deb.version
                        changelog_entry = input("The DEB provided for \"" + update_json['name'] +
                                                "\" has a new version available (" + recorded_version + " -> " +
                                                deb.version + "). What changed in this version?\n(Add multiple lines" +
                                                " by using newline characters [\\n\\n] and use valid Markdown syntax): "
                                                )
                        try:
                            update_json['changelog'].append({
                                "version": deb.version,
                                "changes": changelog_entry
                            })
                        except Exception:
                            update_json['changelog'] = {
                                "version": deb.version,
                                "changes": changelog_entry
                            }

                        # Get human-readable folder name
                        folder = PackageLister.BundleIdToDirName(self, bundle_id)
                        deb_path = self.root + "Packages/" + folder + "/" + file_name
                        # Extract Control file and scripts from DEB
                        DpkgPy.control_extract(self, deb_path, self.root + "Packages/" + folder +
                                               "/silica_data/scripts/")
                        # Remove the Control; it's not needed.
                        os.remove(self.root + "Packages/" + folder + "/silica_data/scripts/Control")
                        if not os.listdir(self.root + "Packages/" + folder + "/silica_data/scripts/"):
                            os.rmdir(self.root + "Packages/" + folder + "/silica_data/scripts/")

                        return_str = json.dumps(update_json)
                        print("Updating package index.json...")
                        PackageLister.CreateFile(self, "Packages/" + package_name +
                                                 "/silica_data/index.json", return_str)
                    pass
                DpkgPy.extract(self, self.root + "temp/" + bundle_id + "/" + file_name, self.root + "temp/" + bundle_id)
                os.remove(self.root + "temp/" + bundle_id + "/" + file_name)
                os.remove(self.root + "temp/" + bundle_id + "/control")
        else:
            # TODO: Update DpkgPy to generate DEB files without dependencies (for improved win32 support)
            # If the version is consistent, then assume the package is unchanged. Don't regenerate it.
            try:
                docs_deb = Dpkg(self.root + "docs/pkg/" + bundle_id + ".deb")
                if docs_deb.version == recorded_version:
                    shutil.copy(self.root + "docs/pkg/" + bundle_id + ".deb", self.root + "temp/" + bundle_id + ".deb")
                    call_result = 0;
                else:
                    # Sneaky swap.
                    call_result = call(["dpkg-deb", "-b", "-Zgzip", self.root + "temp/" + bundle_id], cwd=self.root + "temp/")  # Compile DEB
            except:
                call_result = call(["dpkg-deb", "-b", "-Zgzip", self.root + "temp/" + bundle_id], cwd=self.root + "temp/")  # Compile DEB
            if call_result != 0:
                # Did we run within WSL?
                if "Microsoft" in platform.release():
                    PackageLister.ErrorReporter(self, "Platform Error!", "dpkg-deb failed to run. "
                    "This is due to improper configuration of WSL. Please check the Silcia README for "
                    "how to set up WSL for dpkg-deb.")
                else:
                    PackageLister.ErrorReporter(self, "Platform Error!", "dpkg-deb failed to run. "
                    "This may be due to a faulty system configuration.")