Skip to content

Commit d2ece06

Browse files
committed
Merge branch 'dev'
2 parents 566b06c + 8fe6efe commit d2ece06

File tree

20 files changed

+336
-107
lines changed

20 files changed

+336
-107
lines changed

.github/workflows/build.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ jobs:
9696
- name: Setup Linux
9797
if: runner.os == 'Linux'
9898
run: |
99+
sudo apt-get update
99100
sudo apt-get install -y libsystemd-dev
100101
if [[ '${{ matrix.os }}' == 'ubuntu-20.04' ]]; then
101102
echo "CC=gcc-10" >> $GITHUB_ENV
@@ -105,7 +106,11 @@ jobs:
105106
- name: Build
106107
run: |
107108
cmake -E make_directory out
108-
cmake -S . -B out -DCMAKE_BUILD_TYPE=RelWithDebInfo "-DCMAKE_OSX_ARCHITECTURES=x86_64;arm64" -DFETCHCONTENT_QUIET=OFF
109+
cmake -S . -B out \
110+
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
111+
"-DCMAKE_OSX_ARCHITECTURES=x86_64;arm64" \
112+
-DWSDDN_MACOS_BUILD_WRAPPER=ON \
113+
-DFETCHCONTENT_QUIET=OFF
109114
installers/${{ matrix.installer }}/build.py . out
110115
111116

.github/workflows/publish.yml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ jobs:
101101
- name: Setup Linux
102102
if: runner.os == 'Linux'
103103
run: |
104+
sudo apt-get update
104105
sudo apt-get install -y libsystemd-dev
105106
if [[ '${{ matrix.os }}' == 'ubuntu-20.04' ]]; then
106107
echo "CC=gcc-10" >> $GITHUB_ENV
@@ -110,8 +111,11 @@ jobs:
110111
- name: Configure
111112
run: |
112113
cmake -E make_directory out
113-
cd out
114-
cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo "-DCMAKE_OSX_ARCHITECTURES=x86_64;arm64" -DFETCHCONTENT_QUIET=OFF ..
114+
cmake -S . -B out \
115+
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
116+
"-DCMAKE_OSX_ARCHITECTURES=x86_64;arm64" \
117+
-DWSDDN_MACOS_BUILD_WRAPPER=ON \
118+
-DFETCHCONTENT_QUIET=OFF
115119
116120
117121
- name: Make Distribution

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ __pycache__/
66

77
LLDBInitFile
88
.DS_Store
9+
env.cmake
910

.vscode/settings.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"cmake.buildDirectory": "${workspaceFolder}/out/vscode/${buildKit}",
44
"cmake.configureSettings": {
55
"CMAKE_OSX_ARCHITECTURES": ["x86_64", "arm64"],
6+
"WSDDN_MACOS_BUILD_WRAPPER": "ON",
67
"FETCHCONTENT_QUIET": "OFF"
78
},
89
"cmake.configureOnOpen": true,

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55

66
## Unreleased
77

8+
### Fixed:
9+
- macOS: Corrected `_wsddn` group definition so it is no longer removed from `_wsddn` user on macOS upgrade and no longer shown as available user group in Settings.
10+
- macOS: Made the macOS warning about startup software say "wsddn.app" rather than "Eugene Gershnik" (my developer account name).
11+
812
## [1.3] - 2023-05-27
913

1014
### Added

CMakeLists.txt

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ if (${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
3636
set(WSDDN_BUNDLE_IDENTIFIER "io.github.gershnik.wsddn" CACHE STRING "macOS bundle identifier")
3737
endif()
3838

39-
39+
option(WSDDN_MACOS_BUILD_WRAPPER "whether to build a macos wrapper bundle" OFF)
4040

4141
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
4242

@@ -101,7 +101,6 @@ PRIVATE
101101

102102
target_compile_options(wsddn
103103
PRIVATE
104-
$<$<CXX_COMPILER_ID:MSVC>:/utf-8;/W4;/WX>
105104
$<$<CXX_COMPILER_ID:Clang>:-Wall;-Wextra;-pedantic;-ftemplate-backtrace-limit=0>
106105
$<$<CXX_COMPILER_ID:AppleClang>:-Wall;-Wextra;-pedantic;-fpch-instantiate-templates;-ftemplate-backtrace-limit=0>
107106
$<$<CXX_COMPILER_ID:GNU>:-Wall;-Wextra;-pedantic>
@@ -110,6 +109,8 @@ PRIVATE
110109
target_link_options(wsddn
111110
PRIVATE
112111
"$<$<CXX_COMPILER_ID:AppleClang>:-Wl,-object_path_lto,${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/lto.o>"
112+
"$<$<CXX_COMPILER_ID:AppleClang>:-Wl,-cache_path_lto,${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/LTOCache>"
113+
"$<$<CXX_COMPILER_ID:AppleClang>:-Wl,-no_adhoc_codesign;-Wl,-reproducible>"
113114
)
114115

115116
target_compile_definitions(wsddn
@@ -282,5 +283,10 @@ if (GROFF_PATH)
282283
)
283284
endif()
284285

286+
if (${CMAKE_SYSTEM_NAME} STREQUAL "Darwin" AND WSDDN_MACOS_BUILD_WRAPPER)
287+
288+
add_subdirectory(installers/mac/wrapper wrapper)
289+
290+
endif()
285291

286292
include(cmake/install.cmake)

installers/mac/build.py

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
import os
88
import subprocess
99
import shutil
10+
import plistlib
11+
import re
1012
from pathlib import Path
1113

1214
IDENTIFIER='io.github.gershnik.wsddn'
@@ -18,8 +20,8 @@
1820
from common import VERSION, parseCommandLine, buildCode, installCode, copyTemplated, uploadResults
1921

2022
args = parseCommandLine()
21-
srcdir = args.srcdir
22-
builddir = args.builddir
23+
srcdir: Path = args.srcdir
24+
builddir: Path = args.builddir
2325

2426
buildCode(builddir)
2527

@@ -30,14 +32,17 @@
3032

3133
installCode(builddir, stagedir / 'usr/local')
3234

33-
if args.sign:
34-
subprocess.run(['codesign', '--force', '--sign', 'Developer ID Application', '-o', 'runtime', '--timestamp',
35-
stagedir / 'usr/local/bin/wsddn'], check=True)
36-
3735
ignoreCrap = shutil.ignore_patterns('.DS_Store')
3836

39-
shutil.copytree(srcdir / 'config/mac', stagedir, dirs_exist_ok=True, ignore=ignoreCrap)
37+
supdir = stagedir / "Library/Application Support/wsddn"
38+
supdir.mkdir(parents=True)
39+
40+
appDir = supdir / "wsddn.app"
41+
shutil.copytree(builddir / "wrapper/wsddn.app", appDir, ignore=ignoreCrap)
4042

43+
subprocess.run(['/usr/bin/strip', '-u', '-r', appDir/ 'Contents/MacOS/wsddn'], check=True)
44+
45+
(stagedir / 'usr/local/bin').mkdir(parents=True, exist_ok=True)
4146
shutil.copy(mydir / 'wsddn-uninstall', stagedir / 'usr/local/bin')
4247

4348
copyTemplated(mydir.parent / 'wsddn.conf', stagedir / 'etc/wsddn.conf.sample', {
@@ -52,11 +57,38 @@
5257
'VERSION': VERSION
5358
})
5459

60+
resdir = appDir / "Contents/Resources"
61+
if args.sign:
62+
subprocess.run(['codesign', '--force', '--sign', 'Developer ID Application', '-o', 'runtime', '--timestamp',
63+
stagedir / 'usr/local/bin/wsddn'], check=True)
64+
subprocess.run(['codesign', '--force', '--sign', 'Developer ID Application', '-o', 'runtime', '--timestamp',
65+
'--preserve-metadata=entitlements',
66+
appDir], check=True)
67+
else:
68+
subprocess.run(['codesign', '--force', '--sign', '-', '-o', 'runtime', '--timestamp=none',
69+
stagedir / 'usr/local/bin/wsddn'], check=True)
70+
subprocess.run(['codesign', '--force', '--sign', '-', '-o', 'runtime', '--timestamp=none',
71+
'--preserve-metadata=entitlements',
72+
appDir], check=True)
73+
5574

5675
packagesdir = workdir / 'packages'
5776
packagesdir.mkdir()
77+
78+
subprocess.run(['pkgbuild',
79+
'--analyze',
80+
'--root', str(stagedir),
81+
str(packagesdir/'component.plist')
82+
], check=True)
83+
with open(packagesdir/'component.plist', "rb") as src:
84+
components = plistlib.load(src, fmt=plistlib.FMT_XML)
85+
for component in components:
86+
component['BundleIsRelocatable'] = False
87+
with open(packagesdir/'component.plist', "wb") as dest:
88+
plistlib.dump(components, dest, fmt=plistlib.FMT_XML)
5889
subprocess.run(['pkgbuild',
5990
'--root', str(stagedir),
91+
'--component-plist', str(packagesdir/'component.plist'),
6092
'--scripts', str(mydir / 'scripts'),
6193
'--identifier', IDENTIFIER,
6294
'--version', VERSION,
@@ -76,7 +108,19 @@
76108

77109
if args.sign:
78110
subprocess.run(['productsign', '--sign', 'Developer ID Installer', workdir / 'wsddn.pkg', installer], check=True)
79-
subprocess.run([mydir / 'notarize', '--user', os.environ['NOTARIZE_USER'], '--password', '@env:NOTARIZE_PWD', installer], check=True)
111+
pattern = re.compile(r'^\s*1. Developer ID Installer: .*\(([0-9A-Z]{10})\)$')
112+
teamId = None
113+
for line in subprocess.run(['pkgutil', '--check-signature', installer],
114+
check=True, stdout=subprocess.PIPE).stdout.decode('utf-8').splitlines():
115+
m = pattern.match(line)
116+
if m:
117+
teamId = m.group(1)
118+
break
119+
if teamId is None:
120+
print('Unable to find team ID from signature', file=sys.stderr)
121+
sys.exit(1)
122+
subprocess.run([mydir / 'notarize', '--user', os.environ['NOTARIZE_USER'], '--password', os.environ['NOTARIZE_PWD'],
123+
'--team', teamId, installer], check=True)
80124
print('Signature Info')
81125
res1 = subprocess.run(['pkgutil', '--check-signature', installer])
82126
print('\nAssesment')

installers/mac/distribution.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
Copyright (c) 2022, Eugene Gershnik
44
SPDX-License-Identifier: BSD-3-Clause
55
-->
6-
<installer-gui-script minSpecVersion="1">
6+
<installer-gui-script minSpecVersion="2">
77
<title>WS-Discovery Host Daemon</title>
88
<organization>io.github.gershnik</organization>
99
<domains enable_localSystem="true"/>

installers/mac/notarize

Lines changed: 24 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,118 +1,54 @@
11
#!/usr/bin/env -S python3 -u
22

33
import sys
4-
5-
MIN_PYTHON = (3, 7)
6-
if sys.version_info < MIN_PYTHON:
7-
print("This script requires Python version " + {'.'.join([str(n) for n in MIN_PYTHON])} + " or greater")
8-
sys.exit(1)
9-
10-
114
import argparse
125
import subprocess
13-
import time
6+
import json
147
from pathlib import Path
15-
from uuid import uuid4 as uuid
16-
import plistlib
17-
188

199

20-
def callAlTool(cmd, outPlistPath):
21-
fullCmd = ['xcrun', 'altool'] + cmd + ['--output-format', 'xml']
22-
with open(outPlistPath, 'wt') as outPlist:
23-
callRes = subprocess.call(fullCmd, stdout=outPlist)
24-
with open(outPlistPath, 'rb') as outPlist:
25-
try:
26-
resp = plistlib.load(outPlist)
27-
except plistlib.InvalidFileException:
28-
if callRes:
29-
print(f"Command '{fullCmd}' returned non-zero exit status {callRes}", file = sys.stderr)
30-
else:
31-
print(f"output file {outPlistPath} is invalid", file = sys.stderr)
32-
sys.exit(1)
33-
return resp
10+
def callNotaryTool(cmd):
11+
fullCmd = ['xcrun', 'notarytool'] + cmd + ['-f', 'json']
12+
output = subprocess.run(fullCmd, check=True, stdout=subprocess.PIPE).stdout.decode('utf-8')
13+
print(output)
14+
return json.loads(output)
3415

35-
def getAlToolErrors(resp):
36-
errors = resp.get('product-errors', [])
37-
if len(errors) == 0:
38-
return None
39-
return errors
4016

41-
def abortOnAlTooolErrors(errors):
42-
if not errors:
43-
return
44-
for err in errors:
45-
print(f"{err['code']}: {err['message']}", file = sys.stderr)
46-
sys.exit(1)
47-
48-
49-
def notarize(package, username, password):
17+
def notarize(package, username, team, password):
5018
print("Starting...")
5119
workDir = package.parent
52-
uploadId = str(uuid())
53-
print(f"Uploading to Apple to notarize with Bundle ID {uploadId}")
54-
uploadPlistPath = workDir/'upload-info.plist'
55-
uploadResponse = callAlTool([ '--notarize-app',
56-
'--primary-bundle-id', uploadId, '--username', username, '--password', password,
57-
'--file', str(package)
58-
],
59-
uploadPlistPath)
60-
errors = getAlToolErrors(uploadResponse)
61-
abortOnAlTooolErrors(errors)
62-
requestId = uploadResponse['notarization-upload']['RequestUUID']
63-
print(f"Uploading succeeded, Request ID: {requestId}")
64-
65-
success = False
66-
startTime = time.time()
67-
while True:
68-
time.sleep(30)
69-
print("Checking progress...")
70-
statusPath = workDir/'upload-status.plist'
71-
infoResponse = callAlTool(['--notarization-info', requestId, '-u', username, '-p', password],
72-
statusPath)
73-
errors = getAlToolErrors(infoResponse)
74-
if errors and errors[0]['code'] == 1519: #Could not find the RequestUUID
75-
elapsedTime = time.time() - startTime
76-
if elapsedTime > 60 * 10:
77-
print('Timeout waiting for request ID to become valid')
78-
sys.exit(1)
79-
continue
80-
abortOnAlTooolErrors(errors)
81-
status = infoResponse['notarization-info']['Status']
82-
print(f"Status: {status}")
83-
if status == 'in progress':
84-
continue
85-
if status == 'success':
86-
success = True
87-
break
20+
print(f"Uploading to Apple to notarize")
21+
submission = callNotaryTool(['submit', str(package),
22+
'--apple-id', username, '--team-id', team, '--password', password,
23+
'--wait'])
24+
success = (submission['status'] == 'Accepted')
25+
submissionId = submission['id']
8826
print("Downloading log file")
89-
subprocess.check_call(['curl', '-s', '-L', infoResponse['notarization-info']['LogFileURL'], '-o', workDir/'notarization-log.json'])
90-
if not success:
91-
log = (workDir/'notarization-log.json').read_text()
92-
print(f"Notarization log:\n{log}")
27+
callNotaryTool(['log', submissionId,
28+
'--apple-id', username, '--team-id', team, '--password', password,
29+
workDir/'notarization-log.json'])
30+
log = (workDir/'notarization-log.json').read_text()
31+
print(f"Notarization log:\n{log}")
32+
if not success:
9333
sys.exit(1)
9434
print("Stapling")
9535
subprocess.check_call(['xcrun', 'stapler', 'staple', f"{package}"])
9636
print("Done")
9737

9838
def main():
9939
parser = argparse.ArgumentParser(description='''
100-
Notarize Mac app
40+
Notarize Mac software
10141
''')
10242
parser.add_argument(dest='package',
10343
help=f'Package to notarize')
10444
parser.add_argument('--user', dest='username', type=str, required = True,
10545
help='Username')
10646
parser.add_argument('--password', dest='password', type=str, required = True,
107-
help='''
108-
Application password configured for your Apple ID (not your Apple ID password)
109-
Alternatively to entering <password> in plaintext, it may also be specified using a '@keychain:'
110-
or '@env:' prefix followed by a keychain password item name or environment variable name.
111-
Example: '-p @keychain:<name>' uses the password stored in the keychain password item named <name>.
112-
Example: '-p @env:<variable>' uses the value in the environment variable named <variable>
113-
''')
47+
help='Application password configured for your Apple ID (not your Apple ID password)')
48+
parser.add_argument('--team', dest='team', type=str, required = True,
49+
help='Team ID')
11450
args = parser.parse_args()
115-
notarize(Path(args.package), args.username, args.password)
51+
notarize(Path(args.package), args.username, args.team, args.password)
11652

11753
if __name__ == "__main__":
11854
main()

installers/mac/scripts/postinstall

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,6 @@ if [[ ! -f "$CONF" ]]; then
77
cp $CONF.sample $CONF
88
fi
99

10-
/bin/launchctl load -w "/Library/LaunchDaemons/io.github.gershnik.wsddn.plist"
11-
10+
/Library/Application\ Support/wsddn/wsddn.app/Contents/MacOS/wsddn -i
1211

12+
/bin/launchctl load -w "/Library/LaunchDaemons/io.github.gershnik.wsddn.plist"

0 commit comments

Comments
 (0)