commit
57f1532dd1
@ -0,0 +1,26 @@
|
||||
---
|
||||
name: Issue Running Docker-OSX
|
||||
about: OS related issued, please help us identify the issue by posting the output
|
||||
of this
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
# OS related issued, please help us identify the issue by posting the output of this
|
||||
uname -a \
|
||||
; echo "${DISPLAY}" \
|
||||
; echo 1 | sudo tee /sys/module/kvm/parameters/ignore_msrs \
|
||||
; grep NAME /etc/os-release \
|
||||
; df -h . \
|
||||
; qemu-system-x86_64 --version \
|
||||
; libvirtd --version \
|
||||
; free -mh \
|
||||
; nproc \
|
||||
; egrep -c '(svm|vmx)' /proc/cpuinfo \
|
||||
; ls -lha /dev/kvm \
|
||||
; ls -lha /tmp/.X11-unix/ \
|
||||
; ps aux | grep dockerd \
|
||||
; docker ps | grep osx \
|
||||
; grep "docker\|kvm\|virt" /etc/group
|
@ -0,0 +1,10 @@
|
||||
---
|
||||
name: Open an Issue
|
||||
about: About anything!
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
|
@ -0,0 +1,3 @@
|
||||
[submodule "osx-serial-generator"]
|
||||
path = osx-serial-generator
|
||||
url = https://github.com/sickcodes/osx-serial-generator.git
|
@ -0,0 +1,7 @@
|
||||
# OSX Serial Generator
|
||||
|
||||
This folder has been moved to its own repository :)
|
||||
|
||||
This is a temporary copy for hardlinks.
|
||||
|
||||
See [https://github.com/sickcodes/osx-serial-generator](https://github.com/sickcodes/osx-serial-generator)
|
@ -0,0 +1,447 @@
|
||||
#!/usr/bin/env python3
|
||||
# encoding: utf-8
|
||||
#
|
||||
# https://github.com/munki/macadmin-scripts/blob/master/installinstallmacos.py
|
||||
#
|
||||
# Copyright 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
|
||||
#
|
||||
# http://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.
|
||||
#
|
||||
# Thanks to Tim Sutton for ideas, suggestions, and sample code.
|
||||
#
|
||||
# Updated in May of 2019 by Dhiru Kholia.
|
||||
|
||||
'''installinstallmacos.py
|
||||
A tool to download the parts for an Install macOS app from Apple's
|
||||
softwareupdate servers and install a functioning Install macOS app onto an
|
||||
empty disk image'''
|
||||
|
||||
# https://github.com/foxlet/macOS-Simple-KVM/blob/master/tools/FetchMacOS/fetch-macos.py
|
||||
# is pretty similar.
|
||||
|
||||
|
||||
# Bad hack
|
||||
import warnings
|
||||
|
||||
warnings.filterwarnings("ignore", category=DeprecationWarning)
|
||||
|
||||
import os
|
||||
import gzip
|
||||
import argparse
|
||||
import plistlib
|
||||
import subprocess
|
||||
|
||||
from xml.dom import minidom
|
||||
from xml.parsers.expat import ExpatError
|
||||
|
||||
|
||||
import sys
|
||||
|
||||
if sys.version_info[0] < 3:
|
||||
import urlparse as urlstuff
|
||||
else:
|
||||
import urllib.parse as urlstuff
|
||||
# Quick fix for python 3.9 and above
|
||||
if sys.version_info[0] == 3 and sys.version_info[1] >= 9:
|
||||
from types import MethodType
|
||||
|
||||
def readPlist(self,filepath):
|
||||
with open(filepath, 'rb') as f:
|
||||
p = plistlib._PlistParser(dict)
|
||||
rootObject = p.parse(f)
|
||||
return rootObject
|
||||
# adding the method readPlist() to plistlib
|
||||
plistlib.readPlist = MethodType(readPlist, plistlib)
|
||||
|
||||
# https://github.com/foxlet/macOS-Simple-KVM/blob/master/tools/FetchMacOS/fetch-macos.py (unused)
|
||||
# https://github.com/munki/macadmin-scripts
|
||||
catalogs = {
|
||||
"CustomerSeed": "https://swscan.apple.com/content/catalogs/others/index-10.16customerseed-10.16-10.15-10.14-10.13-10.12-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog",
|
||||
"DeveloperSeed": "https://swscan.apple.com/content/catalogs/others/index-10.16seed-10.16-10.15-10.14-10.13-10.12-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog",
|
||||
"PublicSeed": "https://swscan.apple.com/content/catalogs/others/index-10.16beta-10.16-10.15-10.14-10.13-10.12-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog",
|
||||
"PublicRelease": "https://swscan.apple.com/content/catalogs/others/index-10.16-10.15-10.14-10.13-10.12-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog",
|
||||
"20": "https://swscan.apple.com/content/catalogs/others/index-11-10.15-10.14-10.13-10.12-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog"
|
||||
}
|
||||
|
||||
|
||||
def get_default_catalog():
|
||||
'''Returns the default softwareupdate catalog for the current OS'''
|
||||
return catalogs["20"]
|
||||
# return catalogs["PublicRelease"]
|
||||
# return catalogs["DeveloperSeed"]
|
||||
|
||||
|
||||
class ReplicationError(Exception):
|
||||
'''A custom error when replication fails'''
|
||||
pass
|
||||
|
||||
|
||||
def cmd_exists(cmd):
|
||||
return subprocess.call("type " + cmd, shell=True,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE) == 0
|
||||
|
||||
|
||||
def replicate_url(full_url,
|
||||
root_dir='/tmp',
|
||||
show_progress=False,
|
||||
ignore_cache=False,
|
||||
attempt_resume=False, installer=False, product_title=""):
|
||||
'''Downloads a URL and stores it in the same relative path on our
|
||||
filesystem. Returns a path to the replicated file.'''
|
||||
|
||||
# hack
|
||||
print("[+] Fetching %s" % full_url)
|
||||
if installer and "BaseSystem.dmg" not in full_url and "Big Sur" not in product_title:
|
||||
return
|
||||
if "Big Sur" in product_title and "InstallAssistant.pkg" not in full_url:
|
||||
return
|
||||
attempt_resume = True
|
||||
# path = urllib.parse.urlsplit(full_url)[2]
|
||||
path = urlstuff.urlsplit(full_url)[2]
|
||||
relative_url = path.lstrip('/')
|
||||
relative_url = os.path.normpath(relative_url)
|
||||
# local_file_path = os.path.join(root_dir, relative_url)
|
||||
local_file_path = relative_url
|
||||
# print("Downloading %s..." % full_url)
|
||||
|
||||
if cmd_exists('wget'):
|
||||
if not installer:
|
||||
download_cmd = ['wget', "-c", "--quiet", "-x", "-nH", full_url]
|
||||
# this doesn't work as there are multiple metadata files with the same name!
|
||||
# download_cmd = ['wget', "-c", "--quiet", full_url]
|
||||
else:
|
||||
download_cmd = ['wget', "-c", full_url]
|
||||
else:
|
||||
if not installer:
|
||||
download_cmd = ['curl', "--silent", "--show-error", "-o", local_file_path, "--create-dirs", full_url]
|
||||
else:
|
||||
local_file_path = os.path.basename(local_file_path)
|
||||
download_cmd = ['curl', "-o", local_file_path, full_url]
|
||||
|
||||
try:
|
||||
subprocess.check_call(download_cmd)
|
||||
except subprocess.CalledProcessError as err:
|
||||
raise ReplicationError(err)
|
||||
return local_file_path
|
||||
|
||||
|
||||
def parse_server_metadata(filename):
|
||||
'''Parses a softwareupdate server metadata file, looking for information
|
||||
of interest.
|
||||
Returns a dictionary containing title, version, and description.'''
|
||||
title = ''
|
||||
vers = ''
|
||||
try:
|
||||
md_plist = plistlib.readPlist(filename)
|
||||
except (OSError, IOError, ExpatError) as err:
|
||||
print('Error reading %s: %s' % (filename, err), file=sys.stderr)
|
||||
return {}
|
||||
vers = md_plist.get('CFBundleShortVersionString', '')
|
||||
localization = md_plist.get('localization', {})
|
||||
preferred_localization = (localization.get('English') or
|
||||
localization.get('en'))
|
||||
if preferred_localization:
|
||||
title = preferred_localization.get('title', '')
|
||||
|
||||
metadata = {}
|
||||
metadata['title'] = title
|
||||
metadata['version'] = vers
|
||||
|
||||
"""
|
||||
{'title': 'macOS Mojave', 'version': '10.14.5'}
|
||||
{'title': 'macOS Mojave', 'version': '10.14.6'}
|
||||
"""
|
||||
return metadata
|
||||
|
||||
|
||||
def get_server_metadata(catalog, product_key, workdir, ignore_cache=False):
|
||||
'''Replicate ServerMetaData'''
|
||||
try:
|
||||
url = catalog['Products'][product_key]['ServerMetadataURL']
|
||||
try:
|
||||
smd_path = replicate_url(
|
||||
url, root_dir=workdir, ignore_cache=ignore_cache)
|
||||
return smd_path
|
||||
except ReplicationError as err:
|
||||
print('Could not replicate %s: %s' % (url, err), file=sys.stderr)
|
||||
return None
|
||||
except KeyError:
|
||||
# print('Malformed catalog.', file=sys.stderr)
|
||||
return None
|
||||
|
||||
|
||||
def parse_dist(filename):
|
||||
'''Parses a softwareupdate dist file, returning a dict of info of
|
||||
interest'''
|
||||
dist_info = {}
|
||||
try:
|
||||
dom = minidom.parse(filename)
|
||||
except ExpatError:
|
||||
print('Invalid XML in %s' % filename, file=sys.stderr)
|
||||
return dist_info
|
||||
except IOError as err:
|
||||
print('Error reading %s: %s' % (filename, err), file=sys.stderr)
|
||||
return dist_info
|
||||
|
||||
titles = dom.getElementsByTagName('title')
|
||||
if titles:
|
||||
dist_info['title_from_dist'] = titles[0].firstChild.wholeText
|
||||
|
||||
auxinfos = dom.getElementsByTagName('auxinfo')
|
||||
if not auxinfos:
|
||||
return dist_info
|
||||
auxinfo = auxinfos[0]
|
||||
key = None
|
||||
value = None
|
||||
children = auxinfo.childNodes
|
||||
# handle the possibility that keys from auxinfo may be nested
|
||||
# within a 'dict' element
|
||||
dict_nodes = [n for n in auxinfo.childNodes
|
||||
if n.nodeType == n.ELEMENT_NODE and
|
||||
n.tagName == 'dict']
|
||||
if dict_nodes:
|
||||
children = dict_nodes[0].childNodes
|
||||
for node in children:
|
||||
if node.nodeType == node.ELEMENT_NODE and node.tagName == 'key':
|
||||
key = node.firstChild.wholeText
|
||||
if node.nodeType == node.ELEMENT_NODE and node.tagName == 'string':
|
||||
value = node.firstChild.wholeText
|
||||
if key and value:
|
||||
dist_info[key] = value
|
||||
key = None
|
||||
value = None
|
||||
return dist_info
|
||||
|
||||
|
||||
def download_and_parse_sucatalog(sucatalog, workdir, ignore_cache=False):
|
||||
'''Downloads and returns a parsed softwareupdate catalog'''
|
||||
try:
|
||||
localcatalogpath = replicate_url(
|
||||
sucatalog, root_dir=workdir, ignore_cache=ignore_cache)
|
||||
except ReplicationError as err:
|
||||
print('Could not replicate %s: %s' % (sucatalog, err), file=sys.stderr)
|
||||
exit(-1)
|
||||
if os.path.splitext(localcatalogpath)[1] == '.gz':
|
||||
with gzip.open(localcatalogpath) as the_file:
|
||||
content = the_file.read()
|
||||
try:
|
||||
catalog = plistlib.readPlistFromString(content)
|
||||
return catalog
|
||||
except ExpatError as err:
|
||||
print('Error reading %s: %s' % (localcatalogpath, err), file=sys.stderr)
|
||||
exit(-1)
|
||||
else:
|
||||
try:
|
||||
catalog = plistlib.readPlist(localcatalogpath)
|
||||
return catalog
|
||||
except (OSError, IOError, ExpatError) as err:
|
||||
print('Error reading %s: %s' % (localcatalogpath, err), file=sys.stderr)
|
||||
exit(-1)
|
||||
|
||||
|
||||
def find_mac_os_installers(catalog):
|
||||
'''Return a list of product identifiers for what appear to be macOS
|
||||
installers'''
|
||||
mac_os_installer_products = []
|
||||
if 'Products' in catalog:
|
||||
for product_key in catalog['Products'].keys():
|
||||
product = catalog['Products'][product_key]
|
||||
try:
|
||||
if product['ExtendedMetaInfo'][
|
||||
'InstallAssistantPackageIdentifiers']:
|
||||
mac_os_installer_products.append(product_key)
|
||||
except KeyError:
|
||||
continue
|
||||
|
||||
return mac_os_installer_products
|
||||
|
||||
|
||||
def os_installer_product_info(catalog, workdir, ignore_cache=False):
|
||||
'''Returns a dict of info about products that look like macOS installers'''
|
||||
product_info = {}
|
||||
installer_products = find_mac_os_installers(catalog)
|
||||
for product_key in installer_products:
|
||||
product_info[product_key] = {}
|
||||
filename = get_server_metadata(catalog, product_key, workdir)
|
||||
if filename:
|
||||
product_info[product_key] = parse_server_metadata(filename)
|
||||
else:
|
||||
# print('No server metadata for %s' % product_key)
|
||||
product_info[product_key]['title'] = None
|
||||
product_info[product_key]['version'] = None
|
||||
|
||||
product = catalog['Products'][product_key]
|
||||
product_info[product_key]['PostDate'] = product['PostDate']
|
||||
distributions = product['Distributions']
|
||||
dist_url = distributions.get('English') or distributions.get('en')
|
||||
try:
|
||||
dist_path = replicate_url(
|
||||
dist_url, root_dir=workdir, ignore_cache=ignore_cache)
|
||||
except ReplicationError as err:
|
||||
print('Could not replicate %s: %s' % (dist_url, err),
|
||||
file=sys.stderr)
|
||||
else:
|
||||
dist_info = parse_dist(dist_path)
|
||||
product_info[product_key]['DistributionPath'] = dist_path
|
||||
product_info[product_key].update(dist_info)
|
||||
if not product_info[product_key]['title']:
|
||||
product_info[product_key]['title'] = dist_info.get('title_from_dist')
|
||||
if not product_info[product_key]['version']:
|
||||
product_info[product_key]['version'] = dist_info.get('VERSION')
|
||||
|
||||
return product_info
|
||||
|
||||
|
||||
def replicate_product(catalog, product_id, workdir, ignore_cache=False, product_title=""):
|
||||
'''Downloads all the packages for a product'''
|
||||
product = catalog['Products'][product_id]
|
||||
for package in product.get('Packages', []):
|
||||
# TO-DO: Check 'Size' attribute and make sure
|
||||
# we have enough space on the target
|
||||
# filesystem before attempting to download
|
||||
if 'URL' in package:
|
||||
try:
|
||||
replicate_url(
|
||||
package['URL'], root_dir=workdir,
|
||||
show_progress=True, ignore_cache=ignore_cache,
|
||||
attempt_resume=(not ignore_cache), installer=True, product_title=product_title)
|
||||
except ReplicationError as err:
|
||||
print('Could not replicate %s: %s' % (package['URL'], err), file=sys.stderr)
|
||||
exit(-1)
|
||||
if 'MetadataURL' in package:
|
||||
try:
|
||||
replicate_url(package['MetadataURL'], root_dir=workdir,
|
||||
ignore_cache=ignore_cache, installer=True)
|
||||
except ReplicationError as err:
|
||||
print('Could not replicate %s: %s' % (package['MetadataURL'], err), file=sys.stderr)
|
||||
exit(-1)
|
||||
|
||||
|
||||
def find_installer_app(mountpoint):
|
||||
'''Returns the path to the Install macOS app on the mountpoint'''
|
||||
applications_dir = os.path.join(mountpoint, 'Applications')
|
||||
for item in os.listdir(applications_dir):
|
||||
if item.endswith('.app'):
|
||||
return os.path.join(applications_dir, item)
|
||||
return None
|
||||
|
||||
|
||||
def determine_version(version, product_info):
|
||||
if version:
|
||||
if version == 'latest':
|
||||
from distutils.version import StrictVersion
|
||||
latest_version = StrictVersion('0.0.0')
|
||||
for index, product_id in enumerate(product_info):
|
||||
d = product_info[product_id]['version']
|
||||
if d > latest_version:
|
||||
latest_version = d
|
||||
|
||||
if latest_version == StrictVersion("0.0.0"):
|
||||
print("Could not find latest version {}")
|
||||
exit(1)
|
||||
|
||||
version = str(latest_version)
|
||||
|
||||
for index, product_id in enumerate(product_info):
|
||||
v = product_info[product_id]['version']
|
||||
if v == version:
|
||||
return product_id, product_info[product_id]['title']
|
||||
|
||||
print("Could not find version {}. Versions available are:".format(version))
|
||||
for _, pid in enumerate(product_info):
|
||||
print("- {}".format(product_info[pid]['version']))
|
||||
|
||||
exit(1)
|
||||
|
||||
# display a menu of choices (some seed catalogs have multiple installers)
|
||||
print('%2s %12s %10s %11s %s' % ('#', 'ProductID', 'Version',
|
||||
'Post Date', 'Title'))
|
||||
for index, product_id in enumerate(product_info):
|
||||
print('%2s %12s %10s %11s %s' % (
|
||||
index + 1,
|
||||
product_id,
|
||||
product_info[product_id]['version'],
|
||||
product_info[product_id]['PostDate'].strftime('%Y-%m-%d'),
|
||||
product_info[product_id]['title']
|
||||
))
|
||||
|
||||
answer = input(
|
||||
'\nChoose a product to download (1-%s): ' % len(product_info))
|
||||
try:
|
||||
index = int(answer) - 1
|
||||
if index < 0:
|
||||
raise ValueError
|
||||
product_id = list(product_info.keys())[index]
|
||||
return product_id, product_info[product_id]['title']
|
||||
except (ValueError, IndexError):
|
||||
pass
|
||||
|
||||
print('Invalid input provided.')
|
||||
exit(0)
|
||||
|
||||
|
||||
def main():
|
||||
'''Do the main thing here'''
|
||||
"""
|
||||
if os.getuid() != 0:
|
||||
sys.exit('This command requires root (to install packages), so please '
|
||||
'run again with sudo or as root.')
|
||||
"""
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--workdir', metavar='path_to_working_dir',
|
||||
default='.',
|
||||
help='Path to working directory on a volume with over '
|
||||
'10G of available space. Defaults to current working '
|
||||
'directory.')
|
||||
parser.add_argument('--version', metavar='version',
|
||||
default=None,
|
||||
help='The version to download in the format of '
|
||||
'"$major.$minor.$patch", e.g. "10.15.4". Can '
|
||||
'be "latest" to download the latest version.')
|
||||
parser.add_argument('--compress', action='store_true',
|
||||
help='Output a read-only compressed disk image with '
|
||||
'the Install macOS app at the root. This is now the '
|
||||
'default. Use --raw to get a read-write sparse image '
|
||||
'with the app in the Applications directory.')
|
||||
parser.add_argument('--raw', action='store_true',
|
||||
help='Output a read-write sparse image '
|
||||
'with the app in the Applications directory. Requires '
|
||||
'less available disk space and is faster.')
|
||||
parser.add_argument('--ignore-cache', action='store_true',
|
||||
help='Ignore any previously cached files.')
|
||||
args = parser.parse_args()
|
||||
|
||||
su_catalog_url = get_default_catalog()
|
||||
if not su_catalog_url:
|
||||
print('Could not find a default catalog url for this OS version.', file=sys.stderr)
|
||||
exit(-1)
|
||||
|
||||
# download sucatalog and look for products that are for macOS installers
|
||||
catalog = download_and_parse_sucatalog(
|
||||
su_catalog_url, args.workdir, ignore_cache=args.ignore_cache)
|
||||
product_info = os_installer_product_info(
|
||||
catalog, args.workdir, ignore_cache=args.ignore_cache)
|
||||
|
||||
if not product_info:
|
||||
print('No macOS installer products found in the sucatalog.', file=sys.stderr)
|
||||
exit(-1)
|
||||
|
||||
product_id, product_title = determine_version(args.version, product_info)
|
||||
print(product_id, product_title)
|
||||
|
||||
# download all the packages for the selected product
|
||||
replicate_product(catalog, product_id, args.workdir, ignore_cache=args.ignore_cache, product_title=product_title)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -0,0 +1 @@
|
||||
Subproject commit 1d7425a7fa929423d965334cd78e9c75aeff2ad0
|
@ -0,0 +1,301 @@
|
||||
#!/usr/bin/docker
|
||||
# ____ __ ____ ______ __
|
||||
# / __ \____ _____/ /_____ _____/ __ \/ ___/ |/ /
|
||||
# / / / / __ \/ ___/ //_/ _ \/ ___/ / / /\__ \| /
|
||||
# / /_/ / /_/ / /__/ ,< / __/ / / /_/ /___/ / |
|
||||
# /_____/\____/\___/_/|_|\___/_/ \____//____/_/|_| TESTS
|
||||
#
|
||||
# Title: Docker-OSX (Mac on Docker)
|
||||
# Author: Sick.Codes https://twitter.com/sickcodes
|
||||
# Version: 4.2
|
||||
# License: GPLv3+
|
||||
# Repository: https://github.com/sickcodes/Docker-OSX
|
||||
# Website: https://sick.codes
|
||||
#
|
||||
# Status: Work in progress.
|
||||
#
|
||||
|
||||
help_text="Usage: ./test.sh --branch <string> --repo <string>
|
||||
|
||||
General options:
|
||||
--branch, -b <string> Git branch, default is master
|
||||
--repo, -r <url> Alternative link to build
|
||||
--mirror-country, -m <SS> Two letter country code for Arch mirrors
|
||||
--docker-username, -u <string> Docker hub username
|
||||
--docker-password, -p <string> Docker hub password
|
||||
--vnc-password, -v <string> Choose a VNC passwd.
|
||||
|
||||
Flags
|
||||
--no-cache, -n Enable --no-cache (default already)
|
||||
--no-no-cache, -nn Disable --no-cache docker builds
|
||||
--help, -h, help Display this help and exit
|
||||
"
|
||||
|
||||
# set -xeuf -o pipefail
|
||||
|
||||
|
||||
# gather arguments
|
||||
while (( "$#" )); do
|
||||
case "${1}" in
|
||||
|
||||
--help | -h | h | help )
|
||||
echo "${help_text}" && exit 0
|
||||
;;
|
||||
|
||||
--branch=* | -b=* )
|
||||
export BRANCH="${1#*=}"
|
||||
shift
|
||||
;;
|
||||
--branch* | -b* )
|
||||
export BRANCH="${2}"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
--repo=* | -r=* )
|
||||
export REPO="${1#*=}"
|
||||
shift
|
||||
;;
|
||||
--repo* | -r* )
|
||||
export REPO="${2}"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
--mirror-country=* | -m=* )
|
||||
export MIRROR_COUNTRY="${1#*=}"
|
||||
shift
|
||||
;;
|
||||
--mirror-country* | -m* )
|
||||
export MIRROR_COUNTRY="${2}"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
--vnc-password=* | -v=* | --vnc-passwd=* )
|
||||
export VNC_PASSWORD="${1#*=}"
|
||||
shift
|
||||
;;
|
||||
--vnc-password* | -v* | --vnc-passwd* )
|
||||
export VNC_PASSWORD="${2}"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
--docker-username=* | -u=* )
|
||||
export DOCKER_USERNAME="${1#*=}"
|
||||
shift
|
||||
;;
|
||||
--docker-username* | -u* )
|
||||
export DOCKER_USERNAME="${2}"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
--docker-password=* | -p=* )
|
||||
export DOCKER_PASSWORD="${1#*=}"
|
||||
shift
|
||||
;;
|
||||
--docker-password* | -p* )
|
||||
export DOCKER_PASSWORD="${2}"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
--no-cache | -n )
|
||||
export NO_CACHE='--no-cache'
|
||||
shift
|
||||
;;
|
||||
--no-no-cache | -nn )
|
||||
export NO_CACHE=
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
echo "Invalid option: ${1}"
|
||||
exit 1
|
||||
;;
|
||||
|
||||
esac
|
||||
done
|
||||
|
||||
BRANCH="${BRANCH:=master}"
|
||||
REPO="${REPO:=https://github.com/sickcodes/Docker-OSX.git}"
|
||||
VNC_PASSWORD="${VNC_PASSWORD:=testing}"
|
||||
MIRROR_COUNTRY="${MIRROR_COUNTRY:=US}"
|
||||
NO_CACHE="${NO_CACHE:=--no-cache}"
|
||||
|
||||
|
||||
TEST_BUILDS=(
|
||||
'docker-osx:latest'
|
||||
'docker-osx:naked'
|
||||
'docker-osx:big-sur'
|
||||
'docker-osx:auto'
|
||||
#'docker-osx:auto-big-sur'
|
||||
)
|
||||
|
||||
install_docker () {
|
||||
apt remove docker docker-engine docker.io containerd runc -y \
|
||||
; apt install apt-transport-https ca-certificates curl gnupg-agent software-properties-common -y \
|
||||
&& curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add - \
|
||||
&& apt-key fingerprint 0EBFCD88 \
|
||||
&& add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" \
|
||||
&& apt update -y \
|
||||
&& apt install docker-ce docker-ce-cli containerd.io -y \
|
||||
&& usermod -aG docker "${USER}" \
|
||||
&& su hook docker run --rm hello-world
|
||||
}
|
||||
|
||||
install_vnc () {
|
||||
apt update -y \
|
||||
&& apt install xorg openbox tigervnc-standalone-server tigervnc-common tigervnc-xorg-extension tigervnc-viewer -y \
|
||||
&& mkdir -p ${HOME}/.vnc \
|
||||
&& touch ~/.vnc/config \
|
||||
&& tee -a ~/.vnc/config <<< 'geometry=1920x1080' \
|
||||
&& tee -a ~/.vnc/config <<< 'localhost' \
|
||||
&& tee -a ~/.vnc/config <<< 'alwaysshared' \
|
||||
&& touch ./vnc.sh \
|
||||
&& printf '\n%s\n' \
|
||||
'sudo rm -f /tmp/.X99-lock' \
|
||||
'export DISPLAY=:99' \
|
||||
'/usr/bin/Xvnc -geometry 1920x1080 -rfbauth ~/.vnc/passwd :99 &' > ./vnc.sh \
|
||||
&& tee vncpasswd_file <<< "${VNC_PASSWORD:=testing}" && echo "${VNC_PASSWORD:="$(tr -dc '[:graph:]' </dev/urandom | head -c8)"}" \
|
||||
&& vncpasswd -f < vncpasswd_file > ${HOME}/.vnc/passwd \
|
||||
&& chmod 600 ~/.vnc/passwd \
|
||||
&& apt install qemu qemu-kvm libvirt-clients libvirt-daemon-system bridge-utils virt-manager -y \
|
||||
&& sudo systemctl enable libvirtd.service \
|
||||
&& sudo systemctl enable virtlogd.service \
|
||||
&& echo 1 | sudo tee /sys/module/kvm/parameters/ignore_msrs \
|
||||
&& sudo modprobe kvm \
|
||||
&& echo 'export DISPLAY=:99' >> ~/.bashrc \
|
||||
&& printf '\n\n\n\n%s\n%s\n\n\n\n' '===========VNC_PASSWORD========== ' "$(<vncpasswd_file)"
|
||||
# ufw allow 5999
|
||||
}
|
||||
|
||||
install_scrotcat () {
|
||||
apt update -y
|
||||
apt install git curl wget vim xvfb scrot build-essential sshpass -y
|
||||
git clone https://github.com/stolk/imcat.git
|
||||
make -C ./imcat
|
||||
sudo cp ./imcat/imcat /usr/bin/imcat
|
||||
touch /usr/bin/scrotcat
|
||||
tee -a /usr/bin/scrotcat <<< '/usr/bin/imcat <(scrot -o /dev/stdout)'
|
||||
chmod +x /usr/bin/scrotcat
|
||||
}
|
||||
|
||||
export_display_99 () {
|
||||
touch ~/.bashrc
|
||||
tee -a ~/.bashrc <<< 'export DISPLAY=:99'
|
||||
export DISPLAY=:99
|
||||
}
|
||||
|
||||
start_xvfb () {
|
||||
nohup Xvfb :99 -screen 0 1920x1080x16 &
|
||||
}
|
||||
|
||||
start_vnc () {
|
||||
nohup bash vnc.sh &
|
||||
}
|
||||
|
||||
enable_kvm () {
|
||||
echo 1 | tee /sys/module/kvm/parameters/ignore_msrs
|
||||
}
|
||||
|
||||
|
||||
clone_repo () {
|
||||
git clone --branch="${1}" "${2}" Docker-OSX
|
||||
}
|
||||
|
||||
|
||||
docker-osx:latest () {
|
||||
docker build ${NO_CACHE} \
|
||||
--build-arg BRANCH="${BRANCH}" \
|
||||
--build-arg RANKMIRRORS=true \
|
||||
--build-arg MIRROR_COUNTRY="${MIRROR_COUNTRY}" \
|
||||
-f ./Dockerfile \
|
||||
-t docker-osx:latest .
|
||||
docker tag docker-osx:latest sickcodes/docker-osx:latest
|
||||
}
|
||||
|
||||
|
||||
docker-osx:naked () {
|
||||
docker build ${NO_CACHE} \
|
||||
--build-arg RANKMIRRORS=true \
|
||||
--build-arg MIRROR_COUNTRY="${MIRROR_COUNTRY}" \
|
||||
-f ./Dockerfile.naked \
|
||||
-t docker-osx:naked .
|
||||
docker tag docker-osx:naked sickcodes/docker-osx:naked
|
||||
}
|
||||
|
||||
|
||||
docker-osx:big-sur () {
|
||||
docker build ${NO_CACHE} \
|
||||
--build-arg VERSION=11 \
|
||||
--build-arg RANKMIRRORS=true \
|
||||
--build-arg MIRROR_COUNTRY="${MIRROR_COUNTRY}" \
|
||||
-f ./Dockerfile \
|
||||
-t docker-osx:big-sur .
|
||||
docker tag docker-osx:big-sur sickcodes/docker-osx:big-sur
|
||||
}
|
||||
|
||||
docker-osx:auto () {
|
||||
docker build ${NO_CACHE} \
|
||||
--build-arg RANKMIRRORS=true \
|
||||
--build-arg MIRROR_COUNTRY="${MIRROR_COUNTRY}" \
|
||||
-f ./Dockerfile.auto \
|
||||
-t docker-osx:auto .
|
||||
docker tag docker-osx:auto sickcodes/docker-osx:auto
|
||||
}
|
||||
|
||||
docker-osx:auto-big-sur () {
|
||||
docker build ${NO_CACHE} \
|
||||
--build-arg RANKMIRRORS=true \
|
||||
--build-arg MIRROR_COUNTRY="${MIRROR_COUNTRY}" \
|
||||
--build-arg IMAGE_URL='https://images.sick.codes/mac_hdd_ng_auto_big_sur.img' \
|
||||
-f ./Dockerfile.auto \
|
||||
-t docker-osx:auto-big-sur .
|
||||
docker tag docker-osx:auto-big-sur sickcodes/docker-osx:auto-big-sur
|
||||
}
|
||||
|
||||
reset_docker_hard () {
|
||||
systemctl disable --now docker
|
||||
systemctl disable --now docker.socket
|
||||
systemctl stop docker
|
||||
systemctl stop docker.socket
|
||||
rm -rf /var/lib/docker
|
||||
systemctl enable --now docker
|
||||
}
|
||||
|
||||
tee -a ~/.bashrc <<EOF ; true &&
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
export TZ=UTC
|
||||
EOF
|
||||
export DEBIAN_FRONTEND=noninteractive \
|
||||
; export TZ=UTC \
|
||||
; ln -snf "/usr/share/zoneinfo/${TZ}" /etc/localtime \
|
||||
; tee -a /etc/timezone <<< "${TZ}" \
|
||||
; apt update -y \
|
||||
; apt-get install keyboard-configuration -y \
|
||||
; docker -v | grep '\ 20\.\|\ 19\.' || install_docker \
|
||||
; yes | apt install -y --no-install-recommends tzdata -y \
|
||||
; install_scrotcat \
|
||||
; yes | install_vnc \
|
||||
; export_display_99 \
|
||||
; echo start_xvfb \
|
||||
; start_vnc \
|
||||
; enable_kvm \
|
||||
; echo reset_docker_hard \
|
||||
; echo killall Xvfb \
|
||||
; clone_repo "${BRANCH}" "${REPO}" \
|
||||
; cd Docker-OSX \
|
||||
; for TEST_BUILD in "${TEST_BUILDS[@]}"; do
|
||||
"${TEST_BUILD}"
|
||||
done \
|
||||
&& touch COMPLETED
|
||||
|
||||
if [[ "${DOCKER_USERNAME}" ]] && [[ "${DOCKER_PASSWORD}" ]]; then
|
||||
docker login --username "${DOCKER_USERNAME}" --password "${DOCKER_PASSWORD}" \
|
||||
&& docker push sickcodes/docker-osx:latest \
|
||||
&& docker push sickcodes/docker-osx:big-sur \
|
||||
&& docker push sickcodes/docker-osx:naked \
|
||||
&& docker push sickcodes/docker-osx:auto \
|
||||
&& docker push sickcodes/docker-osx:auto-big-sur \
|
||||
&& touch PUSHED
|
||||
fi
|
||||
|
||||
# connect remotely to your server to use VNC
|
||||
# ssh -N root@1.1.1.1 -L 5999:127.0.0.1:5999
|
||||
|
@ -0,0 +1,208 @@
|
||||
#!/usr/bin/docker
|
||||
# ____ __ ____ ______ __
|
||||
# / __ \____ _____/ /_____ _____/ __ \/ ___/ |/ /
|
||||
# / / / / __ \/ ___/ //_/ _ \/ ___/ / / /\__ \| /
|
||||
# / /_/ / /_/ / /__/ ,< / __/ / / /_/ /___/ / |
|
||||
# /_____/\____/\___/_/|_|\___/_/ \____//____/_/|_| :NAKED
|
||||
#
|
||||
# Title: Docker-OSX (Mac on Docker)
|
||||
# Author: Sick.Codes https://twitter.com/sickcodes
|
||||
# Version: 4.3
|
||||
# License: GPLv3+
|
||||
# Repository: https://github.com/sickcodes/Docker-OSX
|
||||
# Website: https://sick.codes
|
||||
#
|
||||
# This image won't run unless you supply a disk image using:
|
||||
# -v ${PWD}/mac_hdd_ng.img:/image
|
||||
#
|
||||
# Take screenshots in the Arch container and display in terminal: scrotcat
|
||||
#
|
||||
# Build:
|
||||
#
|
||||
# docker build -t docker-osx:naked -f Dockerfile.naked .
|
||||
#
|
||||
# Run headless:
|
||||
#
|
||||
# docker run -it --device /dev/kvm -p 50922:10022 -v ${PWD}/mac_hdd_ng.img:/image docker-osx:naked
|
||||
#
|
||||
# Run with display:
|
||||
#
|
||||
# docker run -it --device /dev/kvm -p 50922:10022 -v ${PWD}/mac_hdd_ng.img:/image -e "DISPLAY=${DISPLAY:-:0.0}" -v /tmp/.X11-unix:/tmp/.X11-unix docker-osx:naked
|
||||
#
|
||||
|
||||
FROM sickcodes/docker-osx:latest
|
||||
|
||||
MAINTAINER 'https://twitter.com/sickcodes' <https://sick.codes>
|
||||
|
||||
USER root
|
||||
|
||||
WORKDIR /root
|
||||
|
||||
RUN rm -f /home/arch/OSX-KVM/mac_hdd_ng.img
|
||||
|
||||
# OPTIONAL: Arch Linux server mirrors for super fast builds
|
||||
# set RANKMIRRORS to any value other that nothing, e.g. -e RANKMIRRORS=true
|
||||
ARG RANKMIRRORS
|
||||
ARG MIRROR_COUNTRY=US
|
||||
ARG MIRROR_COUNT=10
|
||||
RUN if [[ "${RANKMIRRORS}" ]]; then { pacman -Sy wget --noconfirm || pacman -Syu wget --noconfirm ; } \
|
||||
; wget -O ./rankmirrors "https://raw.githubusercontent.com/sickcodes/Docker-OSX/master/rankmirrors" \
|
||||
; wget -O- "https://www.archlinux.org/mirrorlist/?country=${MIRROR_COUNTRY:-US}&protocol=https&use_mirror_status=on" \
|
||||
| sed -e 's/^#Server/Server/' -e '/^#/d' \
|
||||
| head -n "$((${MIRROR_COUNT:-10}+1))" \
|
||||
| bash ./rankmirrors --verbose --max-time 5 - > /etc/pacman.d/mirrorlist \
|
||||
&& tee -a /etc/pacman.d/mirrorlist <<< 'Server = http://mirrors.evowise.com/archlinux/$repo/os/$arch' \
|
||||
&& tee -a /etc/pacman.d/mirrorlist <<< 'Server = http://mirror.rackspace.com/archlinux/$repo/os/$arch' \
|
||||
&& tee -a /etc/pacman.d/mirrorlist <<< 'Server = https://mirror.rackspace.com/archlinux/$repo/os/$arch' \
|
||||
&& cat /etc/pacman.d/mirrorlist ; fi
|
||||
|
||||
# TEMP-FIX for pacman issue
|
||||
RUN patched_glibc=glibc-linux4-2.33-4-x86_64.pkg.tar.zst \
|
||||
&& curl -LO "https://raw.githubusercontent.com/sickcodes/Docker-OSX/master/${patched_glibc}" \
|
||||
&& bsdtar -C / -xvf "${patched_glibc}" || echo "Everything is fine."
|
||||
# TEMP-FIX for pacman issue
|
||||
|
||||
# For taking screenshots of the Xfvb screen, useful during development.
|
||||
ARG SCROT
|
||||
|
||||
RUN pacman -Syu xorg-server-xvfb wget xterm xorg-xhost xorg-xrandr sshpass --noconfirm \
|
||||
&& if [[ "${SCROT}" ]]; then \
|
||||
pacman -Syu scrot base-devel --noconfirm \
|
||||
&& git clone --recurse-submodules --depth 1 https://github.com/stolk/imcat.git \
|
||||
&& cd imcat \
|
||||
&& make \
|
||||
&& sudo cp imcat /usr/bin/imcat \
|
||||
&& touch /usr/bin/scrotcat \
|
||||
&& tee -a /usr/bin/scrotcat <<< '/usr/bin/imcat <(scrot -o /dev/stdout)' \
|
||||
&& chmod +x /usr/bin/scrotcat \
|
||||
; else \
|
||||
touch /usr/bin/scrotcat \
|
||||
&& echo echo >> /usr/bin/scrotcat \
|
||||
&& chmod +x /usr/bin/scrotcat \
|
||||
; fi \
|
||||
; yes | pacman -Scc
|
||||
|
||||
# TEMP-FIX for pacman issue
|
||||
RUN patched_glibc=glibc-linux4-2.33-4-x86_64.pkg.tar.zst \
|
||||
&& curl -LO "https://raw.githubusercontent.com/sickcodes/Docker-OSX/master/${patched_glibc}" \
|
||||
&& bsdtar -C / -xvf "${patched_glibc}" || echo "Everything is fine."
|
||||
# TEMP-FIX for pacman issue
|
||||
|
||||
USER arch
|
||||
|
||||
WORKDIR /home/arch/OSX-KVM
|
||||
|
||||
RUN mkdir -p ~/.ssh \
|
||||
&& touch ~/.ssh/authorized_keys \
|
||||
&& touch ~/.ssh/config \
|
||||
&& chmod 700 ~/.ssh \
|
||||
&& chmod 600 ~/.ssh/config \
|
||||
&& chmod 600 ~/.ssh/authorized_keys \
|
||||
&& tee -a ~/.ssh/config <<< 'Host *' \
|
||||
&& tee -a ~/.ssh/config <<< ' StrictHostKeyChecking no' \
|
||||
&& tee -a ~/.ssh/config <<< ' UserKnownHostsFile=/dev/null'
|
||||
|
||||
#### SPECIAL RUNTIME ARGUMENTS BELOW
|
||||
|
||||
ENV ADDITIONAL_PORTS=
|
||||
|
||||
ENV BOOTDISK=
|
||||
|
||||
ENV DISPLAY=:99
|
||||
|
||||
ENV HEADLESS=false
|
||||
|
||||
ENV ENV=/env
|
||||
|
||||
# Boolean for generating a bootdisk with new random serials.
|
||||
ENV GENERATE_UNIQUE=false
|
||||
|
||||
# Boolean for generating a bootdisk with specific serials.
|
||||
ENV GENERATE_SPECIFIC=false
|
||||
|
||||
ENV IMAGE_PATH=/image
|
||||
ENV IMAGE_FORMAT=qcow2
|
||||
|
||||
ENV KVM='accel=kvm:tcg'
|
||||
|
||||
# ENV MASTER_PLIST_URL="https://raw.githubusercontent.com/sickcodes/osx-serial-generator/master/config-custom.plist"
|
||||
|
||||
# ENV NETWORKING=e1000-82545em
|
||||
ENV NETWORKING=vmxnet3
|
||||
|
||||
ENV NOPICKER=true
|
||||
|
||||
# dynamic RAM options for runtime
|
||||
ENV RAM=8
|
||||
# ENV RAM=max
|
||||
# ENV RAM=half
|
||||
|
||||
# The x and y coordinates for resolution.
|
||||
# Must be used with either -e GENERATE_UNIQUE=true or -e GENERATE_SPECIFIC=true.
|
||||
ENV WIDTH=1920
|
||||
ENV HEIGHT=1080
|
||||
|
||||
CMD sudo touch /dev/kvm /dev/snd "${IMAGE_PATH}" "${BOOTDISK}" "${ENV}" || true \
|
||||
; sudo chown -R $(id -u):$(id -g) /dev/kvm /dev/snd "${IMAGE_PATH}" "${BOOTDISK}" "${ENV}" || true \
|
||||
; { [[ "${DISPLAY}" = ':99' ]] || [[ "${HEADLESS}" == true ]] ; } && { \
|
||||
nohup Xvfb :99 -screen 0 1920x1080x16 \
|
||||
& until [[ "$(xrandr --query 2>/dev/null)" ]]; do sleep 1 ; done \
|
||||
; } \
|
||||
; [[ "${NOPICKER}" == true ]] && { \
|
||||
sed -i '/^.*InstallMedia.*/d' Launch.sh \
|
||||
&& export BOOTDISK="${BOOTDISK:=/home/arch/OSX-KVM/OpenCore-Catalina/OpenCore-nopicker.qcow2}" \
|
||||
; } \
|
||||
|| export BOOTDISK="${BOOTDISK:=/home/arch/OSX-KVM/OpenCore-Catalina/OpenCore.qcow2}" \
|
||||
; [[ "${GENERATE_UNIQUE}" == true ]] && { \
|
||||
./Docker-OSX/osx-serial-generator/generate-unique-machine-values.sh \
|
||||
--master-plist-url="${MASTER_PLIST_URL}" \
|
||||
--count 1 \
|
||||
--tsv ./serial.tsv \
|
||||
--bootdisks \
|
||||
--width "${WIDTH:-1920}" \
|
||||
--height "${HEIGHT:-1080}" \
|
||||
--output-bootdisk "${BOOTDISK:=/home/arch/OSX-KVM/OpenCore-Catalina/OpenCore.qcow2}" \
|
||||
--output-env "${ENV:=/env}" \
|
||||
; } \
|
||||
; [[ "${GENERATE_SPECIFIC}" == true ]] && { \
|
||||
source "${ENV:=/env}" 2>/dev/null \
|
||||
; ./Docker-OSX/osx-serial-generator/generate-specific-bootdisk.sh \
|
||||
--master-plist-url="${MASTER_PLIST_URL}" \
|
||||
--model "${DEVICE_MODEL}" \
|
||||
--serial "${SERIAL}" \
|
||||
--board-serial "${BOARD_SERIAL}" \
|
||||
--uuid "${UUID}" \
|
||||
--mac-address "${MAC_ADDRESS}" \
|
||||
--width "${WIDTH:-1920}" \
|
||||
--height "${HEIGHT:-1080}" \
|
||||
--output-bootdisk "${BOOTDISK:=/home/arch/OSX-KVM/OpenCore-Catalina/OpenCore.qcow2}" \
|
||||
; } \
|
||||
# ; ./enable-ssh.sh && /bin/bash -c ./Launch.sh
|
||||
|
||||
WORKDIR /home/arch/OSX-KVM
|
||||
|
||||
RUN sudo pacman -Syyuu --noconfirm \
|
||||
&& sudo pacman -S tigervnc xterm xorg-xhost xdotool ufw --noconfirm \
|
||||
&& mkdir -p ${HOME}/.vnc \
|
||||
&& touch ~/.vnc/config \
|
||||
&& tee -a ~/.vnc/config <<< 'geometry=1920x1080' \
|
||||
&& tee -a ~/.vnc/config <<< 'localhost' \
|
||||
&& tee -a ~/.vnc/config <<< 'alwaysshared'
|
||||
|
||||
RUN printf '\n%s\n' \
|
||||
'sudo rm -f /tmp/.X99-lock' \
|
||||
'export DISPLAY=:99' \
|
||||
'/usr/bin/Xvnc -geometry 1920x1080 -rfbauth "${HOME}/.vnc/passwd" :99 &' > vnc.sh
|
||||
|
||||
RUN cat vnc.sh Launch.sh > Launch_custom.sh
|
||||
|
||||
RUN chmod +x Launch_custom.sh
|
||||
|
||||
RUN tee vncpasswd_file <<< "${VNC_PASSWORD:="$(tr -dc '[:graph:]' </dev/urandom | head -c8)"}"
|
||||
RUN vncpasswd -f < vncpasswd_file > ${HOME}/.vnc/passwd
|
||||
|
||||
RUN chmod 600 ~/.vnc/passwd
|
||||
RUN printf '\n\n\n\n%s\n%s\n\n\n\n' '===========VNC_PASSWORD========== ' "$(<vncpasswd_file)"
|
||||
|
||||
CMD ./enable-ssh.sh && envsubst < ./Launch_custom.sh | bash
|
||||
|
Loading…
Reference in new issue