mirror of
https://github.com/munki/munki.git
synced 2026-05-09 05:49:32 -05:00
257 lines
9.0 KiB
Python
Executable File
257 lines
9.0 KiB
Python
Executable File
#!/usr/bin/python
|
|
# encoding: utf-8
|
|
#
|
|
# Copyright 2010-2017 Greg Neagle.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the 'License');
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# https://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an 'AS IS' BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
"""
|
|
iconimporter
|
|
|
|
Created by Greg Neagle on 2014-03-03.
|
|
|
|
Converts and imports icons as png files for Munki repo
|
|
"""
|
|
# standard libs
|
|
import sys
|
|
import os
|
|
from optparse import OptionParser
|
|
|
|
# our libs
|
|
from munkilib import dmgutils
|
|
from munkilib import iconutils
|
|
from munkilib import osutils
|
|
from munkilib import pkgutils
|
|
|
|
from munkilib import FoundationPlist
|
|
from munkilib import Repo
|
|
|
|
# PyLint cannot properly find names inside Cocoa libraries, so issues bogus
|
|
# No name 'Foo' in module 'Bar' warnings. Disable them.
|
|
# pylint: disable=E0611
|
|
from Foundation import CFPreferencesCopyAppValue
|
|
# pylint: enable=E0611
|
|
|
|
def generate_png_from_dmg_item(install_item, repo):
|
|
'''Generate a PNG from a disk image containing an application'''
|
|
dmgpath = repo.join('pkgs', install_item['installer_item_location'])
|
|
mountpoints = dmgutils.mountdmg(dmgpath)
|
|
if mountpoints:
|
|
mountpoint = mountpoints[0]
|
|
apps = [item for item in install_item.get('items_to_copy', [])
|
|
if item.get('source_item', '').endswith('.app')]
|
|
if len(apps):
|
|
app_path = os.path.join(mountpoint, apps[0]['source_item'])
|
|
icon_path = iconutils.findIconForApp(app_path)
|
|
if icon_path:
|
|
png_path = repo.join(u'icons', install_item['name'] + u'.png')
|
|
png_handle = repo.open(png_path, 'w')
|
|
result = iconutils.convertIconToPNG(
|
|
icon_path, png_handle.local_path)
|
|
if result:
|
|
print_utf8(u'\tWrote: %s' % png_path)
|
|
else:
|
|
print_err_utf8(u'\tError converting %s to png.' % icon_path)
|
|
else:
|
|
print_utf8(u'\tNo application icons found.')
|
|
else:
|
|
print_utf8(u'\tNo application icons found.')
|
|
dmgutils.unmountdmg(mountpoint)
|
|
|
|
|
|
def generate_pngs_from_pkg(install_item, repo):
|
|
'''Generate PNGS from applications inside a pkg'''
|
|
icon_paths = []
|
|
mountpoint = None
|
|
pkg_path = None
|
|
pkg_repo = None
|
|
item_path = repo.join(u'pkgs', install_item['installer_item_location'])
|
|
if pkgutils.hasValidDiskImageExt(item_path):
|
|
dmg_path = item_path
|
|
mountpoints = dmgutils.mountdmg(dmg_path)
|
|
if mountpoints:
|
|
mountpoint = mountpoints[0]
|
|
if install_item.get('package_path'):
|
|
pkg_path = os.path.join(
|
|
mountpoint, install_item['package_path'])
|
|
else:
|
|
# find first item that appears to be a pkg at the root
|
|
for fileitem in osutils.listdir(mountpoints[0]):
|
|
if pkgutils.hasValidPackageExt(fileitem):
|
|
pkg_path = os.path.join(mountpoint, fileitem)
|
|
break
|
|
elif pkgutils.hasValidPackageExt(item_path):
|
|
pkg_path = item_path
|
|
pkg_repo = repo
|
|
if pkg_path:
|
|
if ((pkg_repo and pkg_repo.isdir(pkg_path)) or
|
|
(not repo and os.path.isdir(pkg_path))):
|
|
icon_paths = iconutils.extractAppIconsFromBundlePkg(
|
|
pkg_path, pkg_repo)
|
|
else:
|
|
handle = None
|
|
if pkg_repo:
|
|
handle = pkg_repo.open(pkg_path, 'r')
|
|
pkg_path = handle.local_path
|
|
icon_paths = iconutils.extractAppIconsFromFlatPkg(pkg_path)
|
|
|
|
if mountpoint:
|
|
dmgutils.unmountdmg(mountpoint)
|
|
|
|
if len(icon_paths) == 1:
|
|
png_path = repo.join(u'icons', install_item['name'] + u'.png')
|
|
handle = repo.open(png_path, 'w')
|
|
result = iconutils.convertIconToPNG(icon_paths[0], handle.local_path)
|
|
if result:
|
|
print_utf8(u'\tWrote: %s' % png_path)
|
|
elif len(icon_paths) > 1:
|
|
index = 1
|
|
for icon_path in icon_paths:
|
|
png_path = repo.join(
|
|
u'icons', install_item['name'] + '_' + str(index) + u'.png')
|
|
handle = repo.open(png_path, 'w')
|
|
result = iconutils.convertIconToPNG(icon_path, handle.local_path)
|
|
if result:
|
|
print_utf8(u'\tWrote: %s' % png_path)
|
|
index += 1
|
|
else:
|
|
print_utf8(u'\tNo application icons found.')
|
|
|
|
|
|
def find_items_to_check(repo, itemlist=None):
|
|
'''Builds a list of items to check; only the latest version
|
|
of an item is retained. If itemlist is given, include items
|
|
only on that list.'''
|
|
all_catalog_path = repo.join('catalogs', 'all')
|
|
handle = repo.open(all_catalog_path)
|
|
catalogitems = FoundationPlist.readPlist(handle.local_path)
|
|
itemdb = {}
|
|
for catalogitem in catalogitems:
|
|
if itemlist and catalogitem['name'] not in itemlist:
|
|
continue
|
|
name = catalogitem['name']
|
|
if name not in itemdb:
|
|
itemdb[name] = catalogitem
|
|
elif (pkgutils.MunkiLooseVersion(catalogitem['version'])
|
|
> pkgutils.MunkiLooseVersion(itemdb[name]['version'])):
|
|
itemdb[name] = catalogitem
|
|
pkg_list = []
|
|
for key in itemdb:
|
|
pkg_list.append(itemdb[key])
|
|
return pkg_list
|
|
|
|
|
|
def generate_pngs_from_munki_items(repo, force=False, itemlist=None):
|
|
'''Generate PNGs from either pkgs or disk images containing applications'''
|
|
itemlist = find_items_to_check(repo, itemlist=itemlist)
|
|
icons_dir = repo.join(u'icons')
|
|
if not repo.exists(icons_dir):
|
|
repo.mkdir(icons_dir)
|
|
for item in itemlist:
|
|
print_utf8(u'Processing %s...' % item['name'])
|
|
icon_name = item.get('icon_name') or item['name']
|
|
if not os.path.splitext(icon_name)[1]:
|
|
icon_name += u'.png'
|
|
icon_path = repo.join(u'icons', icon_name)
|
|
if repo.exists(icon_path) and not force:
|
|
print_utf8(u'Found existing icon at %s' % icon_name)
|
|
continue
|
|
installer_type = item.get('installer_type')
|
|
if installer_type == 'copy_from_dmg':
|
|
generate_png_from_dmg_item(item, repo)
|
|
elif installer_type in [None, '']:
|
|
generate_pngs_from_pkg(item, repo)
|
|
else:
|
|
print_utf8(u'\tCan\'t process installer_type: %s' % installer_type)
|
|
|
|
|
|
def print_utf8(text):
|
|
'''Print Unicode text as UTF-8'''
|
|
print text.encode('UTF-8')
|
|
|
|
|
|
def print_err_utf8(text):
|
|
'''Print Unicode text to stderr as UTF-8'''
|
|
print >> sys.stderr, text.encode('UTF-8')
|
|
|
|
|
|
BUNDLE_ID = 'com.googlecode.munki.munkiimport'
|
|
def pref(prefname):
|
|
"""Return a preference. Since this uses CFPreferencesCopyAppValue,
|
|
Preferences can be defined several places. Precedence is:
|
|
- MCX/Configuration Profile
|
|
- ~/Library/Preferences/ByHost/com.googlecode.munki.munkiimport.XX.plist
|
|
- ~/Library/Preferences/com.googlecode.munki.munkiimport.plist
|
|
- /Library/Preferences/com.googlecode.munki.munkiimport.plist
|
|
"""
|
|
return CFPreferencesCopyAppValue(prefname, BUNDLE_ID)
|
|
|
|
def main():
|
|
'''Main'''
|
|
usage = "usage: %prog [options] [/path/to/repo_root] [repo_url] [plugin]"
|
|
parser = OptionParser(usage=usage)
|
|
parser.add_option(
|
|
'--force', '-f', action='store_true', dest='force',
|
|
help='Create pngs even if there is an existing icon in the repo.')
|
|
parser.add_option(
|
|
'--item', '-i', action='append', type='string', dest='items',
|
|
help='Only run for given pkginfo item name(s).')
|
|
parser.set_defaults(force=False)
|
|
options, arguments = parser.parse_args()
|
|
|
|
# Make sure we have a path to work with
|
|
repo_path = None
|
|
repo_url = None
|
|
plugin = None
|
|
if len(arguments) == 0:
|
|
repo_path = pref('repo_path')
|
|
if not repo_path:
|
|
print_err_utf8("Need to specify a path to the repo root!")
|
|
exit(-1)
|
|
else:
|
|
print_utf8("Using repo path: %s" % repo_path)
|
|
else:
|
|
repo_path = arguments[0].rstrip("/")
|
|
|
|
# Make sure the repo path exists
|
|
if len(arguments) > 1:
|
|
repo_url = arguments[1].rstrip("/")
|
|
else:
|
|
repo_url = pref('repo_url')
|
|
if not repo_url:
|
|
print_err_utf8("Need to specify a URL for the repo!")
|
|
exit(-1)
|
|
else:
|
|
print_utf8("Using repo url: %s" % repo_url)
|
|
|
|
if len(arguments) > 2:
|
|
plugin = arguments[2]
|
|
else:
|
|
plugin = pref('plugin')
|
|
|
|
# Make sure the repo exists
|
|
repo = Repo.Open(repo_path, repo_url, plugin)
|
|
if not repo.available():
|
|
print_err_utf8("Repo root path %s doesn't exist!" % repo_path)
|
|
exit(-1)
|
|
|
|
# generate icons!
|
|
generate_pngs_from_munki_items(
|
|
repo, force=options.force, itemlist=options.items)
|
|
|
|
# clean up
|
|
osutils.cleanUpTmpDir()
|
|
|
|
if __name__ == '__main__':
|
|
main()
|