This commit is contained in:
@@ -88,7 +88,8 @@ identity keeps the same sandbox container across updates.
|
||||
`.gitea/workflows/build.yml` builds universal macOS artifacts for pushes and pull
|
||||
requests to `main`. The repository-scoped `mac-mini-meetingnotes` runner uses the
|
||||
`macos-arm64` label. Smoke tests use a separate CI bundle identifier and temporary
|
||||
launch preferences. Release jobs import the original Developer ID certificate
|
||||
launch preferences, an internal-volume staging directory, and Launch Services
|
||||
so the GUI app runs in the logged-in Mac session. Release jobs import the original Developer ID certificate
|
||||
into a temporary keychain and keep the original Sparkle signing key in Gitea
|
||||
Actions secrets. They never publish from a development branch.
|
||||
|
||||
|
||||
+31
-10
@@ -1,6 +1,8 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Check the CI app's unauthenticated API without changing saved preferences."""
|
||||
import json
|
||||
import os
|
||||
import signal
|
||||
import socket
|
||||
import subprocess
|
||||
import sys
|
||||
@@ -20,11 +22,20 @@ with tempfile.TemporaryDirectory(prefix="smoke-", dir=cache) as directory:
|
||||
with socket.socket() as listener:
|
||||
listener.bind(("127.0.0.1", 0))
|
||||
port = listener.getsockname()[1]
|
||||
with tempfile.TemporaryFile() as log:
|
||||
with tempfile.NamedTemporaryFile() as log:
|
||||
process = subprocess.Popen(
|
||||
[str(app), "-muteDeckAPIEnabled", "YES", "-muteDeckAPIPort", str(port), "-hasCompletedOnboarding", "YES", "-hasAcceptedTerms", "YES", "-SUEnableAutomaticChecks", "NO"],
|
||||
["open", "-n", "-W", "-g", "-a", str(staged), "--stdout", log.name, "--stderr", log.name, "--args", "-ApplePersistenceIgnoreState", "YES", "-muteDeckAPIEnabled", "YES", "-muteDeckAPIPort", str(port), "-hasCompletedOnboarding", "YES", "-hasAcceptedTerms", "YES", "-SUEnableAutomaticChecks", "NO"],
|
||||
stdout=log, stderr=subprocess.STDOUT,
|
||||
)
|
||||
def app_pid():
|
||||
# The unique staged path identifies only this test's app instance.
|
||||
listing = subprocess.check_output(["ps", "-axo", "pid=,command="], text=True)
|
||||
for line in listing.splitlines():
|
||||
fields = line.strip().split(None, 1)
|
||||
if len(fields) == 2 and fields[1].startswith(str(app) + " "):
|
||||
return int(fields[0])
|
||||
return None
|
||||
|
||||
try:
|
||||
for attempt in range(30):
|
||||
if process.poll() is not None:
|
||||
@@ -46,17 +57,27 @@ with tempfile.TemporaryDirectory(prefix="smoke-", dir=cache) as directory:
|
||||
raise RuntimeError("Recording status allowed an unauthenticated request")
|
||||
print("Local API readiness and authentication checks passed")
|
||||
except Exception:
|
||||
if process.poll() is None:
|
||||
sample = subprocess.run(["sample", str(process.pid), "1", "1"], capture_output=True, text=True, timeout=10)
|
||||
pid = app_pid()
|
||||
if pid is not None:
|
||||
sample = subprocess.run(["sample", str(pid), "1", "1"], capture_output=True, text=True, timeout=10)
|
||||
print(sample.stdout[:14000], file=sys.stderr)
|
||||
log.seek(0)
|
||||
print(log.read().decode(errors="replace")[-8000:], file=sys.stderr)
|
||||
raise
|
||||
finally:
|
||||
if process.poll() is None:
|
||||
process.terminate()
|
||||
pid = app_pid()
|
||||
if pid is not None:
|
||||
try:
|
||||
process.wait(timeout=10)
|
||||
except subprocess.TimeoutExpired:
|
||||
process.kill()
|
||||
process.wait()
|
||||
os.kill(pid, signal.SIGTERM)
|
||||
except ProcessLookupError:
|
||||
pass
|
||||
try:
|
||||
process.wait(timeout=10)
|
||||
except subprocess.TimeoutExpired:
|
||||
if app_pid() == pid and pid is not None:
|
||||
try:
|
||||
os.kill(pid, signal.SIGKILL)
|
||||
except ProcessLookupError:
|
||||
pass
|
||||
process.terminate()
|
||||
process.wait(timeout=10)
|
||||
|
||||
Reference in New Issue
Block a user