mirror of
https://github.com/munki/munki.git
synced 2025-12-16 20:14:48 -06:00
* gitattributes: add .gitattributes for munki-python Signed-off-by: Koichi Shiraishi <zchee.io@gmail.com> * code/client: workaronud for munkiimport highlight Signed-off-by: Koichi Shiraishi <zchee.io@gmail.com>
609 lines
24 KiB
Python
Executable File
609 lines
24 KiB
Python
Executable File
#!/usr/local/munki/munki-python
|
|
# -*- python -*-
|
|
# encoding: utf-8
|
|
#
|
|
# Copyright 2010-2021 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.
|
|
|
|
"""
|
|
munkiimport
|
|
|
|
Created by Greg Neagle on 2010-09-29.
|
|
|
|
Assists with importing installer items into the munki repo
|
|
"""
|
|
from __future__ import absolute_import, print_function
|
|
|
|
# std lib imports
|
|
import optparse
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
|
|
# our lib imports
|
|
from munkilib.cliutils import ConfigurationSaveError
|
|
from munkilib.cliutils import configure as _configure
|
|
from munkilib.cliutils import pref, path2url
|
|
from munkilib.cliutils import get_input_with_default
|
|
from munkilib.wrappers import get_input
|
|
|
|
from munkilib import info
|
|
from munkilib import dmgutils
|
|
from munkilib import munkihash
|
|
from munkilib import munkirepo
|
|
from munkilib import osutils
|
|
from munkilib import pkgutils
|
|
from munkilib import FoundationPlist
|
|
|
|
from munkilib.admin import makecatalogslib
|
|
from munkilib.admin import munkiimportlib
|
|
from munkilib.admin import pkginfolib
|
|
|
|
|
|
def make_dmg(pkgpath):
|
|
"""Wraps a non-flat package into a disk image.
|
|
Returns path to newly-created disk image."""
|
|
|
|
pkgname = os.path.basename(pkgpath)
|
|
print('Making disk image containing %s...' % pkgname)
|
|
diskimagename = os.path.splitext(pkgname)[0] + '.dmg'
|
|
diskimagepath = os.path.join(osutils.tmpdir(), diskimagename)
|
|
cmd = ['/usr/bin/hdiutil', 'create', '-fs', 'HFS+',
|
|
'-srcfolder', pkgpath, diskimagepath]
|
|
proc = subprocess.Popen(cmd, shell=False, bufsize=-1,
|
|
stdin=subprocess.PIPE, stdout=subprocess.PIPE,
|
|
stderr=subprocess.STDOUT)
|
|
while True:
|
|
output = proc.stdout.readline().decode('UTF-8')
|
|
if not output and (proc.poll() != None):
|
|
break
|
|
line = output.rstrip('\n')
|
|
if line:
|
|
print(line)
|
|
sys.stdout.flush()
|
|
retcode = proc.poll()
|
|
if retcode:
|
|
print('Disk image creation failed.', file=sys.stderr)
|
|
return ''
|
|
print('Disk image created at: %s' % diskimagepath)
|
|
return diskimagepath
|
|
|
|
|
|
def edit_pkginfo_in_editor(pkginfo):
|
|
"""Opens pkginfo list in the user's chosen editor."""
|
|
editor = pref('editor')
|
|
if editor:
|
|
answer = get_input('Edit pkginfo before upload? [y/N]: ')
|
|
if answer.lower().startswith('y'):
|
|
filedesc, filepath = tempfile.mkstemp()
|
|
# we just want the path; close the file descriptor
|
|
os.close(filedesc)
|
|
try:
|
|
FoundationPlist.writePlist(pkginfo, filepath)
|
|
except FoundationPlist.FoundationPlistException as err:
|
|
print(u'Could not save pkginfo to temp file: %s'
|
|
% err, file=sys.stderr)
|
|
return pkginfo
|
|
|
|
if editor.endswith('.app'):
|
|
cmd = ['/usr/bin/open', '-a', editor, filepath]
|
|
else:
|
|
cmd = [editor, filepath]
|
|
try:
|
|
dummy_returncode = subprocess.check_call(cmd)
|
|
except (OSError, subprocess.CalledProcessError) as err:
|
|
print('Problem running editor %s: %s.' % (editor, err),
|
|
file=sys.stderr)
|
|
os.remove(filepath)
|
|
return pkginfo
|
|
else:
|
|
if editor.endswith('.app'):
|
|
# wait for user to finish with GUI editor.
|
|
answer = 'no'
|
|
while not answer.lower().startswith('y'):
|
|
answer = get_input('Pkginfo editing complete? [y/N]: ')
|
|
try:
|
|
edited_pkginfo = FoundationPlist.readPlist(filepath)
|
|
except FoundationPlist.FoundationPlistException as err:
|
|
print(u'Problem reading edited pkginfo: %s' % err,
|
|
file=sys.stderr)
|
|
os.remove(filepath)
|
|
return pkginfo
|
|
os.remove(filepath)
|
|
return edited_pkginfo
|
|
return pkginfo
|
|
|
|
|
|
def prompt_for_subdirectory(repo, subdirectory):
|
|
"""Prompts the user for a subdirectory for the pkg and pkginfo"""
|
|
try:
|
|
pkgsinfo_list = munkiimportlib.list_items_of_kind(repo, 'pkgsinfo')
|
|
except munkirepo.RepoError as err:
|
|
raise munkiimportlib.RepoCopyError(
|
|
u'Unable to get list of current pkgsinfo: %s' % err)
|
|
# filter the list of pkgsinfo to a list of subdirectories
|
|
subdir_set = set()
|
|
for item in pkgsinfo_list:
|
|
subdir_set.add(os.path.dirname(item))
|
|
|
|
while True:
|
|
newdir = get_input(
|
|
'Upload item to subdirectory path [%s]: ' % subdirectory)
|
|
if newdir:
|
|
destination_path = os.path.join('pkgsinfo', newdir)
|
|
if destination_path not in subdir_set:
|
|
answer = get_input('Path %s doesn\'t exist. Create it? [y/N] '
|
|
% destination_path)
|
|
if answer.lower().startswith('y'):
|
|
break
|
|
else:
|
|
break
|
|
else:
|
|
return subdirectory
|
|
return newdir
|
|
|
|
|
|
def make_catalogs(repo, options):
|
|
"""Rebuild our catalogs"""
|
|
if not options.verbose:
|
|
print('Rebuilding catalogs at %s...' % options.repo_url)
|
|
output_fn = None
|
|
else:
|
|
output_fn = print
|
|
|
|
errors = makecatalogslib.makecatalogs(repo, {}, output_fn=output_fn)
|
|
if errors:
|
|
print('\nThe following issues occurred while building catalogs:\n')
|
|
for error in errors:
|
|
print(error)
|
|
|
|
|
|
def cleanup_and_exit(exitcode):
|
|
"""Unmounts the repo if we mounted it, then exits"""
|
|
result = 0
|
|
# TODO: reimplement this
|
|
#if repo and repo.mounted and repo.WE_MOUNTED_THE_REPO:
|
|
# if not NOINTERACTIVE:
|
|
# answer = raw_input('Unmount the repo fileshare? [y/N] ')
|
|
# if answer.lower().startswith('y'):
|
|
# result = repo.unmount()
|
|
# else:
|
|
# result = repo.unmount()
|
|
# clean up tmpdir
|
|
osutils.cleanUpTmpDir()
|
|
exit(exitcode or result)
|
|
|
|
|
|
def configure():
|
|
"""Configures munkiimport for use"""
|
|
prompt_list = [
|
|
#('repo_path', 'Path to munki repo (example: /Volumes/repo)'),
|
|
('repo_url', 'Repo URL (example: afp://munki.example.com/repo)'),
|
|
('pkginfo_extension', 'pkginfo extension (Example: .plist)'),
|
|
('editor', 'pkginfo editor (examples: /usr/bin/vi or TextMate.app; '
|
|
'leave empty to not open an editor after import)'),
|
|
('default_catalog', 'Default catalog to use (example: testing)'),
|
|
('plugin', 'Repo access plugin (defaults to FileRepo)')
|
|
]
|
|
try:
|
|
_configure(prompt_list)
|
|
except ConfigurationSaveError:
|
|
pass
|
|
|
|
|
|
def main():
|
|
"""Main routine"""
|
|
|
|
usage = """usage: %prog [options] /path/to/installer_item
|
|
Imports an installer item into a munki repo.
|
|
Installer item can be a pkg, mpkg, dmg, mobileconfig, or app.
|
|
Bundle-style pkgs and apps are wrapped in a dmg file before upload.
|
|
Example:
|
|
munkiimport --subdirectory apps /path/to/installer_item
|
|
"""
|
|
|
|
parser = optparse.OptionParser(usage=usage)
|
|
|
|
parser.add_option('--configure', action='store_true',
|
|
help='Configure munkiimport with details about your '
|
|
'munki repo, preferred editor, and the like. Any '
|
|
'other options and arguments are ignored.')
|
|
parser.add_option('--subdirectory', default='',
|
|
help='When importing an installer item, item will be '
|
|
'uploaded to this subdirectory path in the repo '
|
|
'pkgs directory, and the pkginfo file will be '
|
|
'stored under this subdirectory under the pkgsinfo '
|
|
'directory.')
|
|
parser.add_option('--nointeractive', '-n', action='store_true',
|
|
help='No interactive prompts.')
|
|
parser.add_option('--repo_url', '--repo-url', default=pref('repo_url'),
|
|
help='Optional repo URL. If specified, overrides any '
|
|
'repo_url specified via --configure.')
|
|
parser.add_option('--plugin', default=pref('plugin'),
|
|
help='Optional plugin to connect to repo. If specified, '
|
|
'overrides any plugin specified via --configure.')
|
|
parser.add_option('--icon_path', '--icon-path', default='', type='string',
|
|
help='Path to an icns file for the package. Will be '
|
|
'converted to PNG and uploaded, and will overwrite '
|
|
'any existing PNG.')
|
|
parser.add_option('--extract_icon', '--extract-icon', action='store_true',
|
|
help='Attempt to extract and upload a product icon from '
|
|
'the installer item')
|
|
parser.add_option('--version', '-V', action='store_true',
|
|
help='Print the version of the munki tools and exit.')
|
|
parser.add_option('--verbose', '-v', action='store_true',
|
|
help='Print more output.')
|
|
|
|
# add a header for Pkginfo Options
|
|
pkginfo_options = optparse.OptionGroup(
|
|
parser, 'Pkginfo Options',
|
|
'The following options are shared with makepkginfo and affect the '
|
|
'generated pkginfo data.')
|
|
parser.add_option_group(pkginfo_options)
|
|
# now add all the shared makepkginfo options
|
|
pkginfolib.add_option_groups(parser)
|
|
|
|
#sys.argv = [unicode(item, 'utf-8') for item in sys.argv]
|
|
options, arguments = parser.parse_args()
|
|
|
|
# there are a lot of valid combinations of option flags and arguments
|
|
# but if there are no option flags or arguments we should print usage
|
|
if len(sys.argv) == 1:
|
|
parser.print_usage()
|
|
exit(0)
|
|
|
|
if options.version:
|
|
print(info.get_version())
|
|
exit(0)
|
|
|
|
if options.configure:
|
|
configure()
|
|
exit(0)
|
|
|
|
if not options.repo_url:
|
|
repo_path = pref('repo_path')
|
|
if repo_path:
|
|
options.repo_url = path2url(repo_path)
|
|
|
|
if not options.repo_url:
|
|
print('No repo URL found. Please run this tool with the --configure '
|
|
'option, or use the --repo-url option.', file=sys.stderr)
|
|
parser.print_help()
|
|
exit(-1)
|
|
|
|
if not options.plugin:
|
|
options.plugin = 'FileRepo'
|
|
|
|
if options.icon_path and not os.path.isfile(options.icon_path):
|
|
print('The specified icon file does not exist.', file=sys.stderr)
|
|
exit(-1)
|
|
|
|
if (options.apple_update and arguments) or len(arguments) > 1:
|
|
parser.print_usage()
|
|
exit(0)
|
|
|
|
installer_item = None
|
|
if arguments:
|
|
installer_item = arguments[0]
|
|
uninstaller_item = options.uninstalleritem
|
|
is_applemetadata = options.apple_update
|
|
|
|
if not installer_item and not is_applemetadata:
|
|
cleanup_and_exit(-1)
|
|
|
|
if not is_applemetadata:
|
|
# Strip trailing '/' from installer_item
|
|
installer_item = installer_item.rstrip('/')
|
|
|
|
# Check if the item is a mount point for a disk image
|
|
if dmgutils.pathIsVolumeMountPoint(installer_item):
|
|
# Get the disk image path for the mount point
|
|
# and use that instead of the original item
|
|
installer_item = dmgutils.diskImageForMountPoint(installer_item)
|
|
|
|
if (not pkgutils.hasValidInstallerItemExt(installer_item) and
|
|
not pkgutils.isApplication(installer_item)):
|
|
print('Unknown installer item type: "%s"' % installer_item,
|
|
file=sys.stderr)
|
|
exit(-1)
|
|
|
|
if not os.path.exists(installer_item):
|
|
print('%s does not exist!' % installer_item, file=sys.stderr)
|
|
exit(-1)
|
|
|
|
try:
|
|
repo = munkirepo.connect(options.repo_url, options.plugin)
|
|
except munkirepo.RepoError as err:
|
|
print(u'Could not connect to munki repo: %s' % err,
|
|
file=sys.stderr)
|
|
exit(-1)
|
|
|
|
if not is_applemetadata:
|
|
if os.path.isdir(installer_item):
|
|
if pkgutils.hasValidDiskImageExt(installer_item):
|
|
# a directory named foo.dmg or foo.iso!
|
|
print('%s is an unknown type.' % installer_item,
|
|
file=sys.stderr)
|
|
cleanup_and_exit(-1)
|
|
else:
|
|
# we need to convert to dmg
|
|
dmg_path = make_dmg(installer_item)
|
|
if dmg_path:
|
|
installer_item = dmg_path
|
|
else:
|
|
print('Could not convert %s to a disk image.'
|
|
% installer_item, file=sys.stderr)
|
|
cleanup_and_exit(-1)
|
|
|
|
if uninstaller_item:
|
|
# Strip trailing '/' from uninstaller_item
|
|
uninstaller_item = uninstaller_item.rstrip('/')
|
|
|
|
if os.path.isdir(uninstaller_item):
|
|
if pkgutils.hasValidDiskImageExt(uninstaller_item):
|
|
# a directory named foo.dmg or foo.iso!
|
|
print('%s is an unknown type.' % uninstaller_item,
|
|
file=sys.stderr)
|
|
cleanup_and_exit(-1)
|
|
else:
|
|
# we need to convert to dmg
|
|
dmg_path = make_dmg(uninstaller_item)
|
|
if dmg_path:
|
|
uninstaller_item = dmg_path
|
|
else:
|
|
print('Could not convert %s to a disk image.'
|
|
% uninstaller_item, file=sys.stderr)
|
|
cleanup_and_exit(-1)
|
|
options.uninstalleritem = uninstaller_item
|
|
|
|
# if catalog/catalogs have not been explicitly specified via command-line,
|
|
# add our default catalog
|
|
if not options.catalog:
|
|
default_catalog = pref('default_catalog') or 'testing'
|
|
options.catalog = [default_catalog]
|
|
|
|
# make a pkginfo!
|
|
try:
|
|
pkginfo = pkginfolib.makepkginfo(installer_item, options)
|
|
except pkginfolib.PkgInfoGenerationError as err:
|
|
# makepkginfo returned an error
|
|
print('Getting package info failed.', file=sys.stderr)
|
|
print(err, file=sys.stderr)
|
|
cleanup_and_exit(-1)
|
|
|
|
if not options.nointeractive:
|
|
# try to find existing pkginfo items that match this one
|
|
matchingpkginfo = munkiimportlib.find_matching_pkginfo(repo, pkginfo)
|
|
exactmatch = False
|
|
if matchingpkginfo:
|
|
if ('installer_item_hash' in matchingpkginfo and
|
|
matchingpkginfo['installer_item_hash'] ==
|
|
pkginfo.get('installer_item_hash')):
|
|
exactmatch = True
|
|
print ('***This item is identical to an existing item in '
|
|
'the repo***:')
|
|
else:
|
|
print('This item is similar to an existing item in the repo:')
|
|
fields = (('Item name', 'name'),
|
|
('Display name', 'display_name'),
|
|
('Description', 'description'),
|
|
('Version', 'version'),
|
|
('Installer item path', 'installer_item_location'))
|
|
for (name, key) in fields:
|
|
print('%21s: %s' % (name, matchingpkginfo.get(key, '')))
|
|
print()
|
|
if exactmatch:
|
|
answer = get_input('Import this item anyway? [y/N] ')
|
|
if not answer.lower().startswith('y'):
|
|
cleanup_and_exit(0)
|
|
|
|
answer = get_input('Use existing item as a template? [y/N] ')
|
|
if answer.lower().startswith('y'):
|
|
pkginfo['name'] = matchingpkginfo['name']
|
|
pkginfo['display_name'] = (
|
|
matchingpkginfo.get('display_name') or
|
|
pkginfo.get('display_name') or
|
|
matchingpkginfo['name'])
|
|
pkginfo['description'] = pkginfo.get('description') or \
|
|
matchingpkginfo.get('description', '')
|
|
if (options.subdirectory == '' and
|
|
matchingpkginfo.get('installer_item_location')):
|
|
options.subdirectory = os.path.dirname(
|
|
matchingpkginfo['installer_item_location'])
|
|
for key in ['blocking_applications',
|
|
'forced_install',
|
|
'forced_uninstall',
|
|
'unattended_install',
|
|
'unattended_uninstall',
|
|
'requires',
|
|
'update_for',
|
|
'category',
|
|
'developer',
|
|
'icon_name',
|
|
'unused_software_removal_info',
|
|
'localized_strings',
|
|
'featured']:
|
|
if key in matchingpkginfo:
|
|
print('Copying %s: %s' % (key, matchingpkginfo[key]))
|
|
pkginfo[key] = matchingpkginfo[key]
|
|
|
|
# now let user do some basic editing
|
|
editfields = (('Item name', 'name', 'str'),
|
|
('Display name', 'display_name', 'str'),
|
|
('Description', 'description', 'str'),
|
|
('Version', 'version', 'str'),
|
|
('Category', 'category', 'str'),
|
|
('Developer', 'developer', 'str'),
|
|
('Unattended install', 'unattended_install', 'bool'),
|
|
('Unattended uninstall', 'unattended_uninstall', 'bool'),
|
|
)
|
|
for (name, key, kind) in editfields:
|
|
prompt = '%20s: ' % name
|
|
if kind == 'bool':
|
|
default = str(pkginfo.get(key, False))
|
|
else:
|
|
default = pkginfo.get(key, '')
|
|
pkginfo[key] = get_input_with_default(prompt, default)
|
|
if kind == 'bool':
|
|
value = pkginfo[key].lower().strip()
|
|
pkginfo[key] = value.startswith(('y', 't'))
|
|
|
|
# special handling for catalogs array
|
|
prompt = '%20s: ' % 'Catalogs'
|
|
default = ', '.join(pkginfo['catalogs'])
|
|
newvalue = get_input_with_default(prompt, default)
|
|
pkginfo['catalogs'] = [item.strip()
|
|
for item in newvalue.split(',')]
|
|
|
|
if (not is_applemetadata and
|
|
not pkginfo.get(
|
|
'installer_type') in ['profile', 'startosinstall']):
|
|
if 'receipts' not in pkginfo and 'installs' not in pkginfo:
|
|
print('WARNING: There are no receipts and no \'installs\' '
|
|
'items for this installer item. You will need to add at '
|
|
'least one item to the \'installs\' list.',
|
|
file=sys.stderr)
|
|
|
|
print()
|
|
#for (name, key, kind) in editfields:
|
|
# if kind == 'bool':
|
|
# print '%20s: %s' % (name, pkginfo.get(key, False))
|
|
# else:
|
|
# print '%20s: %s' % (name, pkginfo.get(key, ''))
|
|
#print '%20s: %s' % 'Catalogs', ', '.join(pkginfo['catalogs'])
|
|
#print
|
|
answer = get_input('Import this item? [y/N] ')
|
|
if not answer.lower().startswith('y'):
|
|
cleanup_and_exit(0)
|
|
|
|
if options.subdirectory == '':
|
|
if (not is_applemetadata and
|
|
isinstance(repo, munkirepo.FileRepo)):
|
|
repo_pkgs_path = os.path.join(repo.root, 'pkgs')
|
|
installer_item_abspath = os.path.abspath(installer_item)
|
|
if installer_item_abspath.startswith(repo_pkgs_path):
|
|
# special case: We're using a file repo and the item being
|
|
# "imported" is actually already in the repo -- we're just
|
|
# creating a pkginfo item and copying it to the repo.
|
|
# In this case, we want to use the same subdirectory for
|
|
# the pkginfo that corresponds to the one the pkg is
|
|
# already in.
|
|
# We aren't handling the case of alternate implementations
|
|
# FileRepo.
|
|
installer_item_dirpath = os.path.dirname(
|
|
installer_item_abspath)
|
|
options.subdirectory = installer_item_dirpath[
|
|
len(repo_pkgs_path)+1:]
|
|
options.subdirectory = prompt_for_subdirectory(
|
|
repo, options.subdirectory)
|
|
|
|
# if we have an icon, upload it
|
|
if options.icon_path:
|
|
try:
|
|
munkiimportlib.convert_and_install_icon(repo, pkginfo,
|
|
options.icon_path)
|
|
except munkiimportlib.RepoCopyError as err:
|
|
print(err, file=sys.stderr)
|
|
elif options.extract_icon:
|
|
pass
|
|
elif (not options.nointeractive and
|
|
not munkiimportlib.icon_exists_in_repo(repo, pkginfo) and
|
|
not is_applemetadata and
|
|
not pkginfo.get('installer_type') == 'profile'):
|
|
print('No existing product icon found.')
|
|
answer = get_input('Attempt to create a product icon? [y/N] ')
|
|
if answer.lower().startswith('y'):
|
|
print('Attempting to extract and upload icon...')
|
|
options.extract_icon = True
|
|
|
|
if options.extract_icon:
|
|
try:
|
|
imported_paths = munkiimportlib.extract_and_copy_icon(
|
|
repo, installer_item, pkginfo)
|
|
if imported_paths:
|
|
print('Imported %s.' % imported_paths)
|
|
else:
|
|
print('No icons found for import.')
|
|
except munkiimportlib.RepoCopyError as err:
|
|
print(err, file=sys.stderr)
|
|
|
|
# fix in case user accidentally starts subdirectory with a slash
|
|
if options.subdirectory.startswith('/'):
|
|
options.subdirectory = options.subdirectory[1:]
|
|
|
|
if not is_applemetadata:
|
|
try:
|
|
print('Copying %s to repo...' % os.path.basename(installer_item))
|
|
uploaded_pkgpath = munkiimportlib.copy_item_to_repo(
|
|
repo, installer_item, pkginfo.get('version'),
|
|
options.subdirectory)
|
|
print('Copied %s to %s.'
|
|
% (os.path.basename(installer_item), uploaded_pkgpath))
|
|
except munkiimportlib.RepoCopyError as errmsg:
|
|
print(errmsg, file=sys.stderr)
|
|
cleanup_and_exit(-1)
|
|
|
|
# adjust the installer_item_location to match
|
|
# the actual location and name
|
|
pkginfo['installer_item_location'] = uploaded_pkgpath.partition('/')[2]
|
|
|
|
if uninstaller_item:
|
|
try:
|
|
print('Copying %s to repo...' % os.path.basename(
|
|
uninstaller_item))
|
|
uploaded_pkgpath = munkiimportlib.copy_item_to_repo(
|
|
repo, uninstaller_item, pkginfo.get('version'),
|
|
options.subdirectory)
|
|
print('Copied %s to %s.' % (
|
|
os.path.basename(uninstaller_item), uploaded_pkgpath))
|
|
except munkiimportlib.RepoCopyError as errmsg:
|
|
print(errmsg, file=sys.stderr)
|
|
cleanup_and_exit(-1)
|
|
|
|
# adjust the uninstaller_item_location to match
|
|
# the actual location and name; update size and hash
|
|
pkginfo['uninstaller_item_location'] = (
|
|
uploaded_pkgpath.partition('/')[2])
|
|
itemsize = int(os.path.getsize(uninstaller_item))
|
|
itemhash = munkihash.getsha256hash(uninstaller_item)
|
|
pkginfo['uninstaller_item_size'] = int(itemsize/1024) # pylint: disable=old-division
|
|
pkginfo['uninstaller_item_hash'] = itemhash
|
|
|
|
# add icon to pkginfo if in repository
|
|
munkiimportlib.add_icon_hash_to_pkginfo(pkginfo)
|
|
|
|
# installer_item upload was successful. so upload pkginfo to repo
|
|
if not options.nointeractive:
|
|
# possibly edit the pkginfo file in the user's editor
|
|
pkginfo = edit_pkginfo_in_editor(pkginfo)
|
|
try:
|
|
pkginfo_path = munkiimportlib.copy_pkginfo_to_repo(
|
|
repo, pkginfo, options.subdirectory)
|
|
print('Saved pkginfo to %s.' % pkginfo_path)
|
|
except munkiimportlib.RepoCopyError as errmsg:
|
|
print(errmsg, file=sys.stderr)
|
|
cleanup_and_exit(-1)
|
|
|
|
if not options.nointeractive:
|
|
answer = get_input('Rebuild catalogs? [y/N] ')
|
|
if answer.lower().startswith('y'):
|
|
make_catalogs(repo, options)
|
|
|
|
cleanup_and_exit(0)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|