mirror of
https://github.com/ksyasuda/dotfiles.git
synced 2026-02-27 12:22:43 -08:00
update
This commit is contained in:
316
.agents/skills/ios-simulator-skill/scripts/simctl_create.py
Executable file
316
.agents/skills/ios-simulator-skill/scripts/simctl_create.py
Executable file
@@ -0,0 +1,316 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Create iOS simulators dynamically.
|
||||
|
||||
This script creates new simulators with specified device type and iOS version.
|
||||
Useful for CI/CD pipelines that need on-demand test device provisioning.
|
||||
|
||||
Key features:
|
||||
- Create by device type (iPhone 16 Pro, iPad Air, etc.)
|
||||
- Specify iOS version (17.0, 18.0, etc.)
|
||||
- Custom device naming
|
||||
- Return newly created device UDID
|
||||
- List available device types and runtimes
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import subprocess
|
||||
import sys
|
||||
from typing import Optional
|
||||
|
||||
from common.device_utils import list_simulators
|
||||
|
||||
|
||||
class SimulatorCreator:
|
||||
"""Create iOS simulators with specified configurations."""
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize simulator creator."""
|
||||
pass
|
||||
|
||||
def create(
|
||||
self,
|
||||
device_type: str,
|
||||
ios_version: str | None = None,
|
||||
custom_name: str | None = None,
|
||||
) -> tuple[bool, str, str | None]:
|
||||
"""
|
||||
Create new iOS simulator.
|
||||
|
||||
Args:
|
||||
device_type: Device type (e.g., "iPhone 16 Pro", "iPad Air")
|
||||
ios_version: iOS version (e.g., "18.0"). If None, uses latest.
|
||||
custom_name: Custom device name. If None, uses default.
|
||||
|
||||
Returns:
|
||||
(success, message, new_udid) tuple
|
||||
"""
|
||||
# Get available device types and runtimes
|
||||
available_types = self._get_device_types()
|
||||
if not available_types:
|
||||
return False, "Failed to get available device types", None
|
||||
|
||||
# Normalize device type
|
||||
device_type_id = None
|
||||
for dt in available_types:
|
||||
if device_type.lower() in dt["name"].lower():
|
||||
device_type_id = dt["identifier"]
|
||||
break
|
||||
|
||||
if not device_type_id:
|
||||
return (
|
||||
False,
|
||||
f"Device type '{device_type}' not found. "
|
||||
f"Use --list-devices for available types.",
|
||||
None,
|
||||
)
|
||||
|
||||
# Get available runtimes
|
||||
available_runtimes = self._get_runtimes()
|
||||
if not available_runtimes:
|
||||
return False, "Failed to get available runtimes", None
|
||||
|
||||
# Resolve iOS version
|
||||
runtime_id = None
|
||||
if ios_version:
|
||||
for rt in available_runtimes:
|
||||
if ios_version in rt["name"]:
|
||||
runtime_id = rt["identifier"]
|
||||
break
|
||||
|
||||
if not runtime_id:
|
||||
return (
|
||||
False,
|
||||
f"iOS version '{ios_version}' not found. "
|
||||
f"Use --list-runtimes for available versions.",
|
||||
None,
|
||||
)
|
||||
# Use latest runtime
|
||||
elif available_runtimes:
|
||||
runtime_id = available_runtimes[-1]["identifier"]
|
||||
|
||||
if not runtime_id:
|
||||
return False, "No iOS runtime available", None
|
||||
|
||||
# Create device
|
||||
try:
|
||||
# Build device name
|
||||
device_name = (
|
||||
custom_name or f"{device_type_id.split('.')[-1]}-{ios_version or 'latest'}"
|
||||
)
|
||||
|
||||
cmd = [
|
||||
"xcrun",
|
||||
"simctl",
|
||||
"create",
|
||||
device_name,
|
||||
device_type_id,
|
||||
runtime_id,
|
||||
]
|
||||
|
||||
result = subprocess.run(cmd, check=False, capture_output=True, text=True, timeout=60)
|
||||
|
||||
if result.returncode != 0:
|
||||
error = result.stderr.strip() or result.stdout.strip()
|
||||
return False, f"Creation failed: {error}", None
|
||||
|
||||
# Extract UDID from output
|
||||
new_udid = result.stdout.strip()
|
||||
|
||||
return (
|
||||
True,
|
||||
f"Device created: {device_name} ({device_type}) iOS {ios_version or 'latest'} "
|
||||
f"UDID: {new_udid}",
|
||||
new_udid,
|
||||
)
|
||||
|
||||
except subprocess.TimeoutExpired:
|
||||
return False, "Creation command timed out", None
|
||||
except Exception as e:
|
||||
return False, f"Creation error: {e}", None
|
||||
|
||||
@staticmethod
|
||||
def _get_device_types() -> list[dict]:
|
||||
"""
|
||||
Get available device types.
|
||||
|
||||
Returns:
|
||||
List of device type dicts with "name" and "identifier" keys
|
||||
"""
|
||||
try:
|
||||
cmd = ["xcrun", "simctl", "list", "devicetypes", "-j"]
|
||||
result = subprocess.run(cmd, capture_output=True, text=True, check=True)
|
||||
|
||||
import json
|
||||
|
||||
data = json.loads(result.stdout)
|
||||
devices = []
|
||||
|
||||
for device in data.get("devicetypes", []):
|
||||
devices.append(
|
||||
{
|
||||
"name": device.get("name", ""),
|
||||
"identifier": device.get("identifier", ""),
|
||||
}
|
||||
)
|
||||
|
||||
return devices
|
||||
except Exception:
|
||||
return []
|
||||
|
||||
@staticmethod
|
||||
def _get_runtimes() -> list[dict]:
|
||||
"""
|
||||
Get available iOS runtimes.
|
||||
|
||||
Returns:
|
||||
List of runtime dicts with "name" and "identifier" keys
|
||||
"""
|
||||
try:
|
||||
cmd = ["xcrun", "simctl", "list", "runtimes", "-j"]
|
||||
result = subprocess.run(cmd, capture_output=True, text=True, check=True)
|
||||
|
||||
import json
|
||||
|
||||
data = json.loads(result.stdout)
|
||||
runtimes = []
|
||||
|
||||
for runtime in data.get("runtimes", []):
|
||||
# Only include iOS runtimes (skip watchOS, tvOS, etc.)
|
||||
identifier = runtime.get("identifier", "")
|
||||
if "iOS" in identifier or "iOS" in runtime.get("name", ""):
|
||||
runtimes.append(
|
||||
{
|
||||
"name": runtime.get("name", ""),
|
||||
"identifier": runtime.get("identifier", ""),
|
||||
}
|
||||
)
|
||||
|
||||
# Sort by version number (latest first)
|
||||
runtimes.sort(key=lambda r: r.get("identifier", ""), reverse=True)
|
||||
|
||||
return runtimes
|
||||
except Exception:
|
||||
return []
|
||||
|
||||
@staticmethod
|
||||
def list_device_types() -> list[dict]:
|
||||
"""
|
||||
List all available device types.
|
||||
|
||||
Returns:
|
||||
List of device types with name and identifier
|
||||
"""
|
||||
return SimulatorCreator._get_device_types()
|
||||
|
||||
@staticmethod
|
||||
def list_runtimes() -> list[dict]:
|
||||
"""
|
||||
List all available iOS runtimes.
|
||||
|
||||
Returns:
|
||||
List of runtimes with name and identifier
|
||||
"""
|
||||
return SimulatorCreator._get_runtimes()
|
||||
|
||||
|
||||
def main():
|
||||
"""Main entry point."""
|
||||
parser = argparse.ArgumentParser(description="Create iOS simulators dynamically")
|
||||
parser.add_argument(
|
||||
"--device",
|
||||
required=False,
|
||||
help="Device type (e.g., 'iPhone 16 Pro', 'iPad Air')",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--runtime",
|
||||
help="iOS version (e.g., '18.0', '17.0'). Defaults to latest.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--name",
|
||||
help="Custom device name. Defaults to auto-generated.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--list-devices",
|
||||
action="store_true",
|
||||
help="List all available device types",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--list-runtimes",
|
||||
action="store_true",
|
||||
help="List all available iOS runtimes",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--json",
|
||||
action="store_true",
|
||||
help="Output as JSON",
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
creator = SimulatorCreator()
|
||||
|
||||
# Handle info queries
|
||||
if args.list_devices:
|
||||
devices = creator.list_device_types()
|
||||
if args.json:
|
||||
import json
|
||||
|
||||
print(json.dumps({"devices": devices}))
|
||||
else:
|
||||
print(f"Available device types ({len(devices)}):")
|
||||
for dev in devices[:20]: # Show first 20
|
||||
print(f" - {dev['name']}")
|
||||
if len(devices) > 20:
|
||||
print(f" ... and {len(devices) - 20} more")
|
||||
sys.exit(0)
|
||||
|
||||
if args.list_runtimes:
|
||||
runtimes = creator.list_runtimes()
|
||||
if args.json:
|
||||
import json
|
||||
|
||||
print(json.dumps({"runtimes": runtimes}))
|
||||
else:
|
||||
print(f"Available iOS runtimes ({len(runtimes)}):")
|
||||
for rt in runtimes:
|
||||
print(f" - {rt['name']}")
|
||||
sys.exit(0)
|
||||
|
||||
# Create device
|
||||
if not args.device:
|
||||
print(
|
||||
"Error: Specify --device, --list-devices, or --list-runtimes",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
success, message, new_udid = creator.create(
|
||||
device_type=args.device,
|
||||
ios_version=args.runtime,
|
||||
custom_name=args.name,
|
||||
)
|
||||
|
||||
if args.json:
|
||||
import json
|
||||
|
||||
print(
|
||||
json.dumps(
|
||||
{
|
||||
"action": "create",
|
||||
"device_type": args.device,
|
||||
"runtime": args.runtime,
|
||||
"success": success,
|
||||
"message": message,
|
||||
"new_udid": new_udid,
|
||||
}
|
||||
)
|
||||
)
|
||||
else:
|
||||
print(message)
|
||||
|
||||
sys.exit(0 if success else 1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user