def generate_recipes(facts, prefs):
    """Generate the selected types of recipes.

    Args:
        facts: A continually-updated dictionary containing all the information
            we know so far about the app associated with the input path.
        prefs: The dictionary containing a key/value pair for each preference.
    """
    recipes = facts["recipes"]
    if "app_name" in facts:
        if not facts["args"].ignore_existing:
            create_existing_recipe_list(facts)
    else:
        raise RoboError("I wasn't able to determine the name of this app, so I " "can't make any recipes.")

    preferred = [recipe for recipe in recipes if recipe["preferred"]]

    raise_if_recipes_cannot_be_generated(facts, preferred)

    # We have enough information to create a recipe set, but with assumptions.
    # TODO(Elliot): This code may not be necessary if inspections do their job.
    if "codesign_reqs" not in facts and "codesign_authorities" not in facts:
        facts["reminders"].append(
            "I can't tell whether this app is codesigned or not, so I'm "
            "going to assume it's not. You may want to verify that yourself "
            "and add the CodeSignatureVerifier processor if necessary."
        )
        facts["codesign_reqs"] = ""
        facts["codesign_authorities"] = []
    if "version_key" not in facts:
        facts["reminders"].append(
            "I can't tell whether to use CFBundleShortVersionString or "
            "CFBundleVersion for the version key of this app. Most apps use "
            "CFBundleShortVersionString, so that's what I'll use. You may "
            "want to verify that and modify the recipes if necessary."
        )
        facts["version_key"] = "CFBundleShortVersionString"

    # TODO(Elliot): Run `autopkg repo-list` once and store the resulting value
    # for future use when detecting missing required repos, rather than running
    # `autopkg repo-list` separately during each check. (For example, the
    # FileWaveImporter repo must be present to run created filewave recipes.)

    # Prepare the destination directory.
    # TODO (Shea): This JSS Recipe format code is repeated all over.
    # Smells like a refactor.
    if "developer" in facts and prefs.get("FollowOfficialJSSRecipesFormat", False) is not True:
        recipe_dest_dir = robo_join(prefs["RecipeCreateLocation"], facts["developer"].replace("/", "-"))
    else:
        recipe_dest_dir = robo_join(prefs["RecipeCreateLocation"], facts["app_name"].replace("/", "-"))
    facts["recipe_dest_dir"] = recipe_dest_dir
    create_dest_dirs(recipe_dest_dir)

    build_recipes(facts, preferred, prefs)

    # TODO (Shea): As far as I can tell, the only pref that changes is the
    # recipe created count. Move out from here!
    # Save preferences to disk for next time.
    FoundationPlist.writePlist(prefs, PREFS_FILE)
Beispiel #2
0
 def write(self, path):
     """Write the recipe to disk."""
     FoundationPlist.writePlist(self["keys"], path)
Beispiel #3
0
def write_report(report, report_file):
    FoundationPlist.writePlist(report, report_file)
Beispiel #4
0
def write_report(report, report_file):
    FoundationPlist.writePlist(report, report_file)
Beispiel #5
0
 def write(self, path):
     """Write the recipe to disk."""
     FoundationPlist.writePlist(self["keys"], path)
def generate_recipes(facts, prefs, recipes):
    """Generate the selected types of recipes.

    Args:
        facts: A continually-updated dictionary containing all the information
            we know so far about the app associated with the input path.
        prefs: The dictionary containing a key/value pair for each preference.
        recipes: The list of known recipe types, created by init_recipes().
    """
    preferred = [recipe for recipe in recipes if recipe["preferred"]]

    # No recipe types are preferred.
    if not preferred:
        robo_print("Sorry, no recipes available to generate.", LogLevel.ERROR)

    # We don't have enough information to create a recipe set.
    if (facts["is_from_app_store"] is False and
            "sparkle_feed" not in facts and
            "github_repo" not in facts and
            "sourceforge_id" not in facts and
            "download_url" not in facts):
        robo_print("Sorry, I don't know how to download this app. "
                   "Maybe try another angle? If you provided an app, try "
                   "providing the Sparkle feed for the app instead. Or maybe "
                   "the app's developers offer a direct download URL on their "
                   "website.", LogLevel.ERROR)
    if (facts["is_from_app_store"] is False and
            "download_format" not in facts):
        robo_print("Sorry, I can't tell what format to download this app in. "
                   "Maybe try another angle? If you provided an app, try "
                   "providing the Sparkle feed for the app instead. Or maybe "
                   "the app's developers offer a direct download URL on their "
                   "website.", LogLevel.ERROR)

    # We have enough information to create a recipe set, but with assumptions.
    # TODO(Elliot): This code may not be necessary if inspections do their job.
    if "codesign_reqs" not in facts and "codesign_authorities" not in facts:
        robo_print("I can't tell whether this app is codesigned or not, so "
                   "I'm going to assume it's not. You may want to verify that "
                   "yourself and add the CodeSignatureVerifier processor if "
                   "necessary.", LogLevel.REMINDER)
        facts["codesign_reqs"] = ""
        facts["codesign_authorities"] = []
    if "version_key" not in facts:
        robo_print("I can't tell whether to use CFBundleShortVersionString or "
                   "CFBundleVersion for the version key of this app. Most "
                   "apps use CFBundleShortVersionString, so that's what I'll "
                   "use. You may want to verify that and modify the recipes "
                   "if necessary.", LogLevel.REMINDER)
        facts["version_key"] = "CFBundleShortVersionString"

    # TODO(Elliot): Run `autopkg repo-list` once and store the resulting value for
    # future use when detecting missing required repos, rather than running
    # `autopkg repo-list` separately during each check. (For example, the
    # FileWaveImporter repo must be present to run created filewave recipes.)

    # Prepare the destination directory.
    if "developer" in facts and prefs.get("FollowOfficialJSSRecipesFormat", False) is not True:
        recipe_dest_dir = os.path.join(os.path.expanduser(prefs["RecipeCreateLocation"]), facts["developer"].replace("/", "-"))
    else:
        recipe_dest_dir = os.path.join(os.path.expanduser(prefs["RecipeCreateLocation"]), facts["app_name"].replace("/", "-"))
    create_dest_dirs(recipe_dest_dir)

    # Create a recipe for each preferred type we know about.
    for recipe in preferred:

        # TODO (Shea): This could be a global constant. Well, maybe.
        # Construct the default keys common to all recipes.
        recipe["keys"] = {
            "Identifier": "",
            "MinimumVersion": "0.5.0",
            "Input": {
                "NAME": facts["app_name"]
            },
            "Process": [],
            "Comment": "Created with Recipe Robot v%s "
                       "(https://github.com/homebysix/recipe-robot)"
                       % __version__
        }
        keys = recipe["keys"]

        # Set the recipe filename (spaces are OK).
        recipe["filename"] = "%s.%s.recipe" % (facts["app_name"], recipe["type"])

        # Set the recipe identifier.
        keys["Identifier"] = "%s.%s.%s" % (prefs["RecipeIdentifierPrefix"],
                                            recipe["type"],
                                            facts["app_name"].replace(" ", ""))

        # If the name of the app bundle differs from the name of the app
        # itself, we need another input variable for that.
        if "app_file" in facts:
            keys["Input"]["APP_FILENAME"] = facts["app_file"]
            facts["app_name_key"] = "%APP_FILENAME%"
        else:
            facts["app_name_key"] = "%NAME%"

        # Set keys specific to download recipes.
        if recipe["type"] == "download":
            generate_download_recipe(facts, prefs, recipe)
        # Set keys specific to App Store munki overrides.
        elif recipe["type"] == "munki" and facts["is_from_app_store"] is True:
            generate_app_store_munki_recipe(facts, prefs, recipe)
        # Set keys specific to non-App Store munki recipes.
        elif recipe["type"] == "munki" and facts["is_from_app_store"] is False:
            generate_munki_recipe(facts, prefs, recipe)
        # Set keys specific to App Store pkg overrides.
        elif recipe["type"] == "pkg" and facts["is_from_app_store"] is True:
            generate_app_store_pkg_recipe(facts, prefs, recipe)
        # Set keys specific to non-App Store pkg recipes.
        elif recipe["type"] == "pkg" and facts["is_from_app_store"] is False:
            generate_pkg_recipe(facts, prefs, recipe)
        # Set keys specific to install recipes.
        elif recipe["type"] == "install":
            generate_install_recipe(facts, prefs, recipe)
        # Set keys specific to jss recipes.
        elif recipe["type"] == "jss":
            generate_jss_recipe(facts, prefs, recipe)
        # Set keys specific to absolute recipes.
        elif recipe["type"] == "absolute":
            generate_absolute_recipe(facts, prefs, recipe)
        # Set keys specific to sccm recipes.
        elif recipe["type"] == "sccm":
            generate_sccm_recipe(facts, prefs, recipe)
        # Set keys specific to ds recipes.
        elif recipe["type"] == "ds":
            generate_ds_recipe(facts, prefs, recipe)
        # Set keys specific to filewave recipes.
        elif recipe["type"] == "filewave":
            generate_filewave_recipe(facts, prefs, recipe)
        else:
            # This shouldn't happen, if all the right recipe types are
            # specified in init_recipes() and also specified above.
            robo_print("Oops, I think my programmer messed up. I don't "
                        "yet know how to generate a %s recipe. Sorry about "
                        "that." % recipe["type"], LogLevel.WARNING)

        # Write the recipe to disk.
        if len(recipe["keys"]["Process"]) > 0:
            dest_path = os.path.join(recipe_dest_dir, recipe["filename"])
            if not os.path.exists(dest_path):
                # Keep track of the total number of unique recipes we've created.
                prefs["RecipeCreateCount"] += 1
            FoundationPlist.writePlist(recipe["keys"], dest_path)
            robo_print("%s" % os.path.join(recipe_dest_dir,
                                  recipe["filename"]), LogLevel.LOG, 4)
            facts["recipes"].append(dest_path)

    # Save preferences to disk for next time.
    FoundationPlist.writePlist(prefs, prefs_file)