def handle_custom(custom_dir):
  """Download custom resources and build the package."""
  print "Downloading Munki client resources."
  updatecheck.download_client_resources()
  # Client Resoures are stored in
  #   /Library/Managed Installs/client_resources/custom.zip
  resource_dir = os.path.join(
    pref('ManagedInstallDir'), 'client_resources')
  resource_file = os.path.join(resource_dir, 'custom.zip')
  if os.path.isfile(resource_file):
    destination_path = custom_dir
    pkg_output_file = os.path.join(CACHE, 'munki_custom.pkg')
    success = build_pkg(
      resource_dir,
      'munki_custom',
      'com.facebook.cpe.munki_custom',
      destination_path,
      CACHE,
      'Creating the Munki custom resources package.'
    )
    if success:
      return pkg_output_file
    else:
      print >> sys.stderr, "Failed to build Munki custom resources package!"
      return None
Exemple #2
0
def handle_custom():
    """Download custom resources and build the package."""
    print("Downloading Munki client resources.")
    # Set the local directory to the AutoDMG cache
    old_managed = prefs.pref('ManagedInstallDir')
    prefs.set_pref('ManagedInstallDir', CACHE)
    # Downloads client resources into the AutoDMG cache
    download.download_client_resources()
    # Put the actual Munki cache dir back
    prefs.set_pref('ManagedInstallDir', old_managed)
    resource_dir = os.path.join(
        CACHE, 'client_resources')
    resource_file = os.path.join(resource_dir, 'custom.zip')
    if os.path.isfile(resource_file):
            # Client Resources are stored in
            # /Library/Managed Installs/client_resources/custom.zip
        destination_path = '/Library/Managed Installs/client_resources'
        pkg_output_file = os.path.join(CACHE, 'munki_custom.pkg')
        success = build_pkg(
            resource_dir,
            'munki_custom',
            'com.facebook.cpe.munki_custom',
            destination_path,
            CACHE,
            'Creating the Munki custom resources package.'
        )
        if success:
            return pkg_output_file
        else:
            print(
                "Failed to build Munki custom resources package!",
                file=sys.stderr
            )
            return None
def handle_icons(icon_dir, installinfo):
  """Download icons and build the package."""
  print "Downloading icons."
  pkg_output_file = os.path.join(CACHE, 'munki_icons.pkg')
  icon_list = installinfo['optional_installs']
  icon_list.extend(installinfo['managed_installs'])
  icon_list.extend(installinfo['removals'])
  # Downloads all icons into the icon directory in the Munki cache
  download_icons(icon_list, icon_dir)

  # Build a package of optional Munki icons, so we don't need to cache
  success = build_pkg(
    icon_dir,
    'munki_icons',
    'com.facebook.cpe.munki_icons',
    '/Library/Managed Installs/icons',
    CACHE,
    'Creating the icon package.'
  )
  # Add the icon package to the additional packages list for the template.
  if success:
    return pkg_output_file
  else:
    print >> sys.stderr, "Failed to build icon package!"
    return None
Exemple #4
0
def handle_icons(itemlist):
    """Download icons and build the package."""
    print("Downloading icons.")
    pkg_output_file = os.path.join(CACHE, 'munki_icons.pkg')
    # Set the local directory to the AutoDMG cache
    old_managed = prefs.pref('ManagedInstallDir')
    prefs.set_pref('ManagedInstallDir', CACHE)
    # Downloads all icons into the icon directory in the Munki cache
    download.download_icons(itemlist)
    # Put the actual Munki cache dir back
    prefs.set_pref('ManagedInstallDir', old_managed)
    # Build a package of optional Munki icons, so we don't need to cache
    success = build_pkg(
        os.path.join(CACHE, 'icons'),
        'munki_icons',
        'com.facebook.cpe.munki_icons',
        '/Library/Managed Installs/icons',
        CACHE,
        'Creating the icon package.'
    )
    # Add the icon package to the additional packages list for the template.
    if success:
        return pkg_output_file
    else:
        print("Failed to build icon package!", file=sys.stderr)
        return None
Exemple #5
0
def handle_custom():
    """Download custom resources and build the package."""
    print("Downloading Munki client resources.")
    # Set the local directory to the AutoDMG cache
    old_managed = prefs.pref('ManagedInstallDir')
    prefs.set_pref('ManagedInstallDir', CACHE)
    # Downloads client resources into the AutoDMG cache
    download.download_client_resources()
    # Put the actual Munki cache dir back
    prefs.set_pref('ManagedInstallDir', old_managed)
    resource_dir = os.path.join(CACHE, 'client_resources')
    resource_file = os.path.join(resource_dir, 'custom.zip')
    if os.path.isfile(resource_file):
        # Client Resources are stored in
        # /Library/Managed Installs/client_resources/custom.zip
        destination_path = '/Library/Managed Installs/client_resources'
        pkg_output_file = os.path.join(CACHE, 'munki_custom.pkg')
        success = build_pkg(resource_dir, 'munki_custom',
                            'com.facebook.cpe.munki_custom', destination_path,
                            CACHE,
                            'Creating the Munki custom resources package.')
        if success:
            return pkg_output_file
        else:
            print("Failed to build Munki custom resources package!",
                  file=sys.stderr)
            return None
def handle_custom(custom_dir):
    """Download custom resources and build the package."""
    print "Downloading Munki client resources."
    updatecheck.download_client_resources()
    # Client Resoures are stored in
    #   /Library/Managed Installs/client_resources/custom.zip
    resource_dir = os.path.join(pref('ManagedInstallDir'), 'client_resources')
    resource_file = os.path.join(resource_dir, 'custom.zip')
    if os.path.isfile(resource_file):
        destination_path = custom_dir
        pkg_output_file = os.path.join(CACHE, 'munki_custom.pkg')
        success = build_pkg(resource_dir, 'munki_custom',
                            'com.facebook.cpe.munki_custom', destination_path,
                            CACHE,
                            'Creating the Munki custom resources package.')
        if success:
            return pkg_output_file
        else:
            print >> sys.stderr, "Failed to build Munki custom resources package!"
            return None
Exemple #7
0
def build_exceptions(cache_dir):
    """Build a package for each exception."""
    exceptions_pkg_list = []
    exceptions_dir = os.path.join(cache_dir, 'exceptions')
    exceptions_pkgs_dir = os.path.join(cache_dir, 'exceptions_pkgs')
    # Empty out existing exceptions packages first, to avoid cruft
    for file in os.listdir(exceptions_pkgs_dir):
        os.unlink(os.path.join(exceptions_pkgs_dir, file))
    counter = 1
    tmp_cache_dir = '/tmp/individ_except/Library/Managed Installs/Cache'
    try:
        os.makedirs(tmp_cache_dir)
    except OSError:
        # Path likely exists
        pass
    # Now begin our building
    for exception in os.listdir(exceptions_dir):
        split_name = re.split('_|-|\s', exception)[0]
        output_name = "munki_cache_%s-%s" % (split_name, str(counter))
        receipt_name = os.path.splitext(exception)[0]
        counter += 1
        # Copy each one to a temporary location
        shutil.copy2(
            os.path.join(cache_dir, 'exceptions', exception),
            tmp_cache_dir
        )
        output_exception_pkg = build_pkg(
            tmp_cache_dir,
            output_name,
            'com.facebook.cpe.munki_exceptions.%s' % receipt_name,
            '/Library/Managed Installs/Cache',
            exceptions_pkgs_dir,
            'Building exception pkg for %s' % exception
        )
        if output_exception_pkg:
            exceptions_pkg_list.append(output_exception_pkg)
        if not output_exception_pkg:
            print("Failed to build exceptions package for %s!" % exception)
        # Delete the copied file
        os.unlink(os.path.join(tmp_cache_dir, exception))
    return exceptions_pkg_list
def handle_icons(icon_dir, installinfo):
    """Download icons and build the package."""
    print "Downloading icons."
    pkg_output_file = os.path.join(CACHE, 'munki_icons.pkg')
    icon_list = installinfo['optional_installs']
    icon_list.extend(installinfo['managed_installs'])
    icon_list.extend(installinfo['removals'])
    # Downloads all icons into the icon directory in the Munki cache
    download_icons(icon_list, icon_dir)

    # Build a package of optional Munki icons, so we don't need to cache
    success = build_pkg(icon_dir, 'munki_icons',
                        'com.facebook.cpe.munki_icons',
                        '/Library/Managed Installs/icons', CACHE,
                        'Creating the icon package.')
    # Add the icon package to the additional packages list for the template.
    if success:
        return pkg_output_file
    else:
        print >> sys.stderr, "Failed to build icon package!"
        return None
Exemple #9
0
def handle_icons(itemlist):
    """Download icons and build the package."""
    print("Downloading icons.")
    pkg_output_file = os.path.join(CACHE, 'munki_icons.pkg')
    # Set the local directory to the AutoDMG cache
    old_managed = prefs.pref('ManagedInstallDir')
    prefs.set_pref('ManagedInstallDir', CACHE)
    # Downloads all icons into the icon directory in the Munki cache
    download.download_icons(itemlist)
    # Put the actual Munki cache dir back
    prefs.set_pref('ManagedInstallDir', old_managed)
    # Build a package of optional Munki icons, so we don't need to cache
    success = build_pkg(os.path.join(CACHE, 'icons'), 'munki_icons',
                        'com.facebook.cpe.munki_icons',
                        '/Library/Managed Installs/icons', CACHE,
                        'Creating the icon package.')
    # Add the icon package to the additional packages list for the template.
    if success:
        return pkg_output_file
    else:
        print("Failed to build icon package!", file=sys.stderr)
        return None
Exemple #10
0
def build_exceptions(cache_dir):
    """Build a package for each exception."""
    exceptions_pkg_list = []
    exceptions_dir = os.path.join(cache_dir, 'exceptions')
    exceptions_pkgs_dir = os.path.join(cache_dir, 'exceptions_pkgs')
    # Empty out existing exceptions packages first, to avoid cruft
    for file in os.listdir(exceptions_pkgs_dir):
        os.unlink(os.path.join(exceptions_pkgs_dir, file))
    counter = 1
    tmp_cache_dir = '/tmp/individ_except/Library/Managed Installs/Cache'
    try:
        os.makedirs(tmp_cache_dir)
    except OSError:
        # Path likely exists
        pass
    # Now begin our building
    for exception in os.listdir(exceptions_dir):
        split_name = re.split('_|-|\s', exception)[0]
        output_name = "munki_cache_%s-%s" % (split_name, str(counter))
        receipt_name = os.path.splitext(exception)[0]
        counter += 1
        # Copy each one to a temporary location
        shutil.copy2(os.path.join(cache_dir, 'exceptions', exception),
                     tmp_cache_dir)
        output_exception_pkg = build_pkg(
            tmp_cache_dir, output_name,
            'com.facebook.cpe.munki_exceptions.%s' % receipt_name,
            '/Library/Managed Installs/Cache', exceptions_pkgs_dir,
            'Building exception pkg for %s' % exception)
        if output_exception_pkg:
            exceptions_pkg_list.append(output_exception_pkg)
        if not output_exception_pkg:
            print("Failed to build exceptions package for %s!" % exception)
        # Delete the copied file
        os.unlink(os.path.join(tmp_cache_dir, exception))
    return exceptions_pkg_list
def main():
    """Main function."""
    wait_for_network()
    if not os.path.exists('/Applications/AutoDMG.app/Contents/MacOS/AutoDMG'):
        print "AutoDMG not at expected path in /Applications, quitting!"
        sys.exit(1)
    parser = argparse.ArgumentParser(
        description='Built a precached AutoDMG image.')
    parser.add_argument('-c',
                        '--catalog',
                        help='Catalog name. Defaults to "prod".',
                        default='prod')
    parser.add_argument('-m',
                        '--manifest',
                        help='Manifest name. Defaults to "prod".',
                        default='prod')
    parser.add_argument('-o',
                        '--output',
                        help='Path to DMG to create.',
                        default='AutoDMG_full.hfs.dmg')
    parser.add_argument('--cache',
                        help='Path to local cache to store files.'
                        ' Defaults to "/Library/AutoDMG"',
                        default='/Library/AutoDMG')
    parser.add_argument('-d',
                        '--download',
                        help='Force a redownload of all files.',
                        action='store_true',
                        default=False)
    parser.add_argument('-l',
                        '--logpath',
                        help='Path to log files for AutoDMG.',
                        default='/Library/AutoDMG/logs/')
    parser.add_argument('--custom',
                        help='Path to place custom resources. Defaults to '
                        '/Library/Managed Installs/client_resources/.',
                        default='/Library/Managed Installs/client_resources/')
    parser.add_argument('-s',
                        '--source',
                        help='Path to base OS installer.',
                        default='/Applications/Install OS X El Capitan.app')
    parser.add_argument('-v',
                        '--volumename',
                        help='Name of volume after imaging. '
                        'Defaults to "Macintosh HD."',
                        default='Macintosh HD')
    parser.add_argument('--loglevel',
                        help='Set loglevel between 1 and 7. Defaults to 6.',
                        choices=range(1, 8),
                        default=6,
                        type=int)
    parser.add_argument('--dsrepo', help='Path to DeployStudio repo. ')
    parser.add_argument('--noicons',
                        help="Don't cache icons.",
                        action='store_true',
                        default=False)
    parser.add_argument('-u',
                        '--update',
                        help='Update the profiles plist.',
                        action='store_true',
                        default=False)
    parser.add_argument('--disableupdates',
                        help='Disable updates to built image via AutoDMG',
                        action='store_false',
                        default=True)
    parser.add_argument('--movefile',
                        help="Path to move file to after building.")
    parser.add_argument('--extras',
                        help='Path to JSON file containing additions '
                        ' and exceptions lists.')
    args = parser.parse_args()

    print "Using Munki repo: %s" % MUNKI_URL
    global CACHE
    CACHE = args.cache

    if "https" in MUNKI_URL and not BASIC_AUTH:
        print >> sys.stderr, "Error: HTTPS was used but no auth provided."
        sys.exit(2)

    print time.strftime("%c")
    print "Starting run..."
    # Create the local cache directories
    dir_struct = {
        'additions': os.path.join(CACHE, 'additions'),
        'catalogs': os.path.join(CACHE, 'catalogs'),
        'downloads': os.path.join(CACHE, 'downloads'),
        'exceptions': os.path.join(CACHE, 'exceptions'),
        'manifests': os.path.join(CACHE, 'manifests'),
        'icons': os.path.join(CACHE, 'icons'),
        'logs': os.path.join(CACHE, 'logs')
    }
    path_creation = prepare_local_paths(dir_struct.values())
    if path_creation > 0:
        print "Error setting up local cache directories."
        sys.exit(-1)

    # These are necessary to populate the globals used in updatecheck
    keychain_obj = keychain.MunkiKeychain()
    manifestpath = updatecheck.getPrimaryManifest(args.manifest)
    updatecheck.getPrimaryManifestCatalogs(args.manifest)
    updatecheck.getCatalogs([args.catalog])

    installinfo = {}
    installinfo['processed_installs'] = []
    installinfo['processed_uninstalls'] = []
    installinfo['managed_updates'] = []
    installinfo['optional_installs'] = []
    installinfo['managed_installs'] = []
    installinfo['removals'] = []
    updatecheck.processManifestForKey(manifestpath, 'managed_installs',
                                      installinfo)
    # installinfo['managed_installs'] now contains a list of all managed_installs
    install_list = []
    for item in installinfo['managed_installs']:
        detail = updatecheck.getItemDetail(item['name'], [args.catalog])
        if detail:
            install_list.append(detail)

    # Prior to downloading anything, populate the lists
    additions_list = list()
    item_list = list()
    except_list = list()
    exceptions = list()
    # exceptions[] is a list of exceptions specified by the extras file
    # except_list is a list of files downloaded into the exceptions dir
    if args.extras:
        # Additions are downloaded & added to the additions_list
        # Exceptions are added to the exceptions list,
        # Downloaded exceptions are added to the except_list list.
        handle_extras(args.extras, dir_struct['exceptions'],
                      dir_struct['additions'], args.download, exceptions,
                      except_list, additions_list)

    # Check for managed_install items and download them
    process_managed_installs(install_list, exceptions, except_list, item_list,
                             dir_struct['exceptions'], dir_struct['downloads'],
                             args.download)

    # Icon handling
    if not args.noicons:
        # Get icons for Managed Updates, Optional Installs and removals
        updatecheck.processManifestForKey(manifestpath, 'managed_updates',
                                          installinfo)
        updatecheck.processManifestForKey(manifestpath, 'managed_uninstalls',
                                          installinfo)
        updatecheck.processManifestForKey(manifestpath, 'optional_installs',
                                          installinfo)
        icon_pkg_file = handle_icons(dir_struct['icons'], installinfo)
    if icon_pkg_file:
        additions_list.extend([icon_pkg_file])

    # Munki custom resources handling
    custom_pkg_file = handle_custom(args.custom)
    if custom_pkg_file:
        additions_list.extend([custom_pkg_file])

    # Clean up cache of items we don't recognize
    cleanup_local_cache(item_list, dir_struct['downloads'])
    cleanup_local_cache(except_list, dir_struct['exceptions'])

    # Build the package of exceptions, if any
    if except_list:
        pkg_output_file = os.path.join(CACHE, 'munki_cache.pkg')
        success = build_pkg(dir_struct['exceptions'], 'munki_cache',
                            'com.facebook.cpe.munki_exceptions',
                            '/Library/Managed Installs/Cache', CACHE,
                            'Building exceptions package')
        if success:
            additions_list.extend([pkg_output_file])
        else:
            print "Failed to build exceptions package!"

    loglevel = str(args.loglevel)

    # Run any extra code or package builds
    sys.stdout.flush()
    pkg_list = autodmg_org.run_unique_code(args)
    additions_list.extend(pkg_list)

    # Now that cache is downloaded, let's add it to the AutoDMG template.
    print "Creating AutoDMG-full.adtmpl."
    templatepath = os.path.join(CACHE, 'AutoDMG-full.adtmpl')

    plist = dict()
    plist["ApplyUpdates"] = args.disableupdates
    plist["SourcePath"] = args.source
    plist["TemplateFormat"] = "1.0"
    plist["VolumeName"] = args.volumename
    plist["AdditionalPackages"] = [
        os.path.join(dir_struct['downloads'], f)
        for f in os.listdir(dir_struct['downloads'])
        if (not f == '.DS_Store') and (f not in additions_list)
    ]

    if additions_list:
        plist["AdditionalPackages"].extend(additions_list)

    # Complete the AutoDMG-full.adtmpl template
    plistlib.writePlist(plist, templatepath)
    autodmg_cmd = ['/Applications/AutoDMG.app/Contents/MacOS/AutoDMG']
    if os.getuid() == 0:
        # We are running as root
        print "Running as root."
        autodmg_cmd.append('--root')
    if args.update:
        # Update the profiles plist too
        print "Updating UpdateProfiles.plist..."
        cmd = autodmg_cmd + ['update']
        run(cmd)

    logfile = os.path.join(args.logpath, 'build.log')
    # Now kick off the AutoDMG build
    dmg_output_path = os.path.join(CACHE, args.output)
    sys.stdout.flush()
    print "Building disk image..."
    if os.path.isfile(dmg_output_path):
        os.remove(dmg_output_path)
    cmd = autodmg_cmd + [
        '-L', loglevel, '-l', logfile, 'build', templatepath,
        '--download-updates', '-o', dmg_output_path
    ]
    print "Full command: %s" % cmd
    run(cmd)
    if not os.path.isfile(dmg_output_path):
        print >> sys.stderr, "Failed to create disk image!"
        sys.exit(1)

    # Check the Deploystudio masters to see if this image already exists
    sys.stdout.flush()
    if args.dsrepo:
        populate_ds_repo(dmg_output_path, args.dsrepo)

    if args.movefile:
        move_file(dmg_output_path, args.movefile)

    print "Ending run."
    print time.strftime("%c")
def main():
  """Main function."""
  wait_for_network()
  if not os.path.exists('/Applications/AutoDMG.app/Contents/MacOS/AutoDMG'):
    print "AutoDMG not at expected path in /Applications, quitting!"
    sys.exit(1)
  parser = argparse.ArgumentParser(
    description='Built a precached AutoDMG image.')
  parser.add_argument(
    '-c', '--catalog', help='Catalog name. Defaults to "prod".',
    default='prod')
  parser.add_argument(
    '-m', '--manifest', help='Manifest name. Defaults to "prod".',
    default='prod')
  parser.add_argument(
    '-o', '--output', help='Path to DMG to create.',
    default='AutoDMG_full.hfs.dmg')
  parser.add_argument(
    '--cache', help='Path to local cache to store files.'
                    ' Defaults to "/Library/AutoDMG"',
    default='/Library/AutoDMG')
  parser.add_argument(
    '-d', '--download', help='Force a redownload of all files.',
    action='store_true', default=False)
  parser.add_argument(
    '-l', '--logpath', help='Path to log files for AutoDMG.',
    default='/Library/AutoDMG/logs/')
  parser.add_argument(
    '--custom', help='Path to place custom resources. Defaults to '
                     '/Library/Managed Installs/client_resources/.',
    default='/Library/Managed Installs/client_resources/')
  parser.add_argument(
    '-s', '--source', help='Path to base OS installer.',
    default='/Applications/Install OS X El Capitan.app')
  parser.add_argument(
    '-v', '--volumename', help='Name of volume after imaging. '
                               'Defaults to "Macintosh HD."',
    default='Macintosh HD')
  parser.add_argument(
    '--loglevel', help='Set loglevel between 1 and 7. Defaults to 6.',
    choices=range(1, 8), default=6, type=int)
  parser.add_argument(
    '--dsrepo', help='Path to DeployStudio repo. ')
  parser.add_argument(
    '--noicons', help="Don't cache icons.",
    action='store_true', default=False)
  parser.add_argument(
    '-u', '--update', help='Update the profiles plist.',
    action='store_true', default=False)
  parser.add_argument(
    '--disableupdates', help='Disable updates to built image via AutoDMG',
    action='store_false', default=True)
  parser.add_argument(
    '--movefile', help="Path to move file to after building.")
  parser.add_argument(
    '--extras', help='Path to JSON file containing additions '
                     ' and exceptions lists.')
  args = parser.parse_args()

  print "Using Munki repo: %s" % MUNKI_URL
  global CACHE
  CACHE = args.cache

  if "https" in MUNKI_URL and not BASIC_AUTH:
    print >> sys.stderr, "Error: HTTPS was used but no auth provided."
    sys.exit(2)

  print time.strftime("%c")
  print "Starting run..."
  # Create the local cache directories
  dir_struct = {
    'additions': os.path.join(CACHE, 'additions'),
    'catalogs': os.path.join(CACHE, 'catalogs'),
    'downloads': os.path.join(CACHE, 'downloads'),
    'exceptions': os.path.join(CACHE, 'exceptions'),
    'manifests': os.path.join(CACHE, 'manifests'),
    'icons': os.path.join(CACHE, 'icons'),
    'logs': os.path.join(CACHE, 'logs')
  }
  path_creation = prepare_local_paths(dir_struct.values())
  if path_creation > 0:
    print "Error setting up local cache directories."
    sys.exit(-1)

  # These are necessary to populate the globals used in updatecheck
  keychain_obj = keychain.MunkiKeychain()
  manifestpath = updatecheck.getPrimaryManifest(args.manifest)
  updatecheck.getPrimaryManifestCatalogs(args.manifest)
  updatecheck.getCatalogs([args.catalog])

  installinfo = {}
  installinfo['processed_installs'] = []
  installinfo['processed_uninstalls'] = []
  installinfo['managed_updates'] = []
  installinfo['optional_installs'] = []
  installinfo['managed_installs'] = []
  installinfo['removals'] = []
  updatecheck.processManifestForKey(manifestpath, 'managed_installs',
                                    installinfo)
  # installinfo['managed_installs'] now contains a list of all managed_installs
  install_list = []
  for item in installinfo['managed_installs']:
    detail = updatecheck.getItemDetail(item['name'], [args.catalog])
    if detail:
      install_list.append(detail)

  # Prior to downloading anything, populate the lists
  additions_list = list()
  item_list = list()
  except_list = list()
  exceptions = list()
  # exceptions[] is a list of exceptions specified by the extras file
  # except_list is a list of files downloaded into the exceptions dir
  if args.extras:
    # Additions are downloaded & added to the additions_list
    # Exceptions are added to the exceptions list,
    # Downloaded exceptions are added to the except_list list.
    handle_extras(
      args.extras,
      dir_struct['exceptions'],
      dir_struct['additions'],
      args.download,
      exceptions,
      except_list,
      additions_list
    )

  # Check for managed_install items and download them
  process_managed_installs(install_list, exceptions,
                           except_list, item_list,
                           dir_struct['exceptions'],
                           dir_struct['downloads'],
                           args.download)

  # Icon handling
  if not args.noicons:
    # Get icons for Managed Updates, Optional Installs and removals
    updatecheck.processManifestForKey(manifestpath, 'managed_updates',
                                    installinfo)
    updatecheck.processManifestForKey(manifestpath, 'managed_uninstalls',
                                    installinfo)
    updatecheck.processManifestForKey(manifestpath, 'optional_installs',
                                    installinfo)
    icon_pkg_file = handle_icons(dir_struct['icons'], installinfo)
  if icon_pkg_file:
    additions_list.extend([icon_pkg_file])

  # Munki custom resources handling
  custom_pkg_file = handle_custom(args.custom)
  if custom_pkg_file:
    additions_list.extend([custom_pkg_file])

  # Clean up cache of items we don't recognize
  cleanup_local_cache(item_list, dir_struct['downloads'])
  cleanup_local_cache(except_list, dir_struct['exceptions'])

  # Build the package of exceptions, if any
  if except_list:
    pkg_output_file = os.path.join(CACHE, 'munki_cache.pkg')
    success = build_pkg(
      dir_struct['exceptions'],
      'munki_cache',
      'com.facebook.cpe.munki_exceptions',
      '/Library/Managed Installs/Cache',
      CACHE,
      'Building exceptions package'
    )
    if success:
      additions_list.extend([pkg_output_file])
    else:
      print "Failed to build exceptions package!"

  loglevel = str(args.loglevel)

  # Run any extra code or package builds
  sys.stdout.flush()
  pkg_list = autodmg_org.run_unique_code(args)
  additions_list.extend(pkg_list)

  # Now that cache is downloaded, let's add it to the AutoDMG template.
  print "Creating AutoDMG-full.adtmpl."
  templatepath = os.path.join(CACHE, 'AutoDMG-full.adtmpl')

  plist = dict()
  plist["ApplyUpdates"] = args.disableupdates
  plist["SourcePath"] = args.source
  plist["TemplateFormat"] = "1.0"
  plist["VolumeName"] = args.volumename
  plist["AdditionalPackages"] = [
    os.path.join(
      dir_struct['downloads'], f
    ) for f in os.listdir(
      dir_struct['downloads']
    ) if (not f == '.DS_Store') and (f not in additions_list)
  ]

  if additions_list:
    plist["AdditionalPackages"].extend(additions_list)

  # Complete the AutoDMG-full.adtmpl template
  plistlib.writePlist(plist, templatepath)
  autodmg_cmd = [
    '/Applications/AutoDMG.app/Contents/MacOS/AutoDMG'
  ]
  if os.getuid() == 0:
    # We are running as root
    print "Running as root."
    autodmg_cmd.append('--root')
  if args.update:
    # Update the profiles plist too
    print "Updating UpdateProfiles.plist..."
    cmd = autodmg_cmd + ['update']
    run(cmd)

  logfile = os.path.join(args.logpath, 'build.log')
  # Now kick off the AutoDMG build
  dmg_output_path = os.path.join(CACHE, args.output)
  sys.stdout.flush()
  print "Building disk image..."
  if os.path.isfile(dmg_output_path):
    os.remove(dmg_output_path)
  cmd = autodmg_cmd + [
    '-L', loglevel,
    '-l', logfile,
    'build', templatepath,
    '--download-updates',
    '-o', dmg_output_path]
  print "Full command: %s" % cmd
  run(cmd)
  if not os.path.isfile(dmg_output_path):
    print >> sys.stderr, "Failed to create disk image!"
    sys.exit(1)

  # Check the Deploystudio masters to see if this image already exists
  sys.stdout.flush()
  if args.dsrepo:
    populate_ds_repo(dmg_output_path, args.dsrepo)

  if args.movefile:
    move_file(dmg_output_path, args.movefile)

  print "Ending run."
  print time.strftime("%c")