This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
usbipd-mac is a macOS USB/IP protocol implementation for sharing USB devices over IP networks. The project is built using Swift Package Manager and targets macOS 11+.
Main Repository: https://github.com/beriberikix/usbipd-mac
Homebrew Tap Repository: https://github.com/beriberikix/homebrew-usbipd-mac
Works, verified against hardware. Devices macOS has not bound a driver to are served end to end: enumeration, string descriptors, control transfers, and bidirectional bulk transfers. Three device classes have been driven: a SEGGER J-Link with probe-rs, which read the probe's VTref over the wire and behaved exactly as it does connected directly; a Pixel 10a in ADB mode, which answered a CNXN with its AUTH challenge; and a Raspberry Pi Debug Probe, where probe-rs ran ~930 CMSIS-DAP bulk exchanges and stopped only at chip detection — the same error it gives with the probe plugged straight into the Mac, because no target is wired to its SWD pins. No System Extension and no entitlement are involved.
bind and unbind take effect on a running daemon. They ask it directly, over a Unix
socket at ~/.usbipd/control.sock, so the command reports what the daemon actually did
rather than what it hoped would happen. When no daemon is listening they write
~/.usbipd/bound-devices.json themselves and say so; the daemon reads it at startup.
The control channel is deliberately not a command on port 3240. That port is exposed to the network, so a bind reachable there would let any host that can open a TCP connection share any USB device on this machine. The socket is mode 0600 in the user's own directory.
The daemon still checks the file's timestamp when it consults the list. That is not redundant: it is how a change made over the control socket, or by a CLI that ran while no daemon was up, reaches the request path — the file stays the single source of truth.
State and configuration are separate files, and only state is ever written by the tool.
~/.usbipd/usbipd-config.json holds the port, log level and tuning; it is read at
startup and needs a restart to change. ~/.usbipd/bound-devices.json holds the bind
list, guarded by a lock file so two bind commands cannot lose each other's device.
The two used to share one file, so bind rewrote the whole configuration to append a
busid — and because the CLI falls back to defaults when that file will not parse, one
malformed character turned the next bind into a silent reset of the port and log
level. A allowedDevices key left in an old config is migrated once and then ignored.
Clients that cancel a transfer are supported, which matters more than it sounds: libusb cancels any read it has timed out, and probe-rs drains the IN endpoint that way before its first command. A cancelled request is aborted at the pipe and answered by its RET_UNLINK alone.
A caveat that is not about claiming. A client whose protocol keys off USB
connection or reset events may not work even on a claimable device. adb reports a
Pixel offline because the phone announces itself once per connection and macOS
already received that announcement; attaching from a client causes no bus reset the
phone can observe. Request/response devices are unaffected. See
Documentation/development/android-adb-validation.md.
Does not work, and cannot be made to. Devices whose interfaces macOS actually
holds — HID, mass storage, audio, cameras. bind refuses these up front with an
explanation naming the owner. This was measured, not assumed: USBInterfaceOpenSeize
returns the same kIOReturnExclusiveAccess as a plain open, and neither unmounting
nor ejecting releases a device. Only the DriverKit USB transport entitlements would
change it, and Apple has to grant those.
"USB-serial" is not one answer, and this file gave the wrong one twice — first that none of it works, then that all of it does. It depends on which driver macOS attaches:
- FTDI and CP210x carry
IOUserSerial, which does not take exclusive access. They open and serve normally, no entitlement involved. Verified against an FTDI Quad RS232-HS and a CP2102N. - CDC-ACM splits. Measured on a Raspberry Pi Debug Probe with
Scripts/validate-usb-entitlements.sh --only 2e8a:000c: the data interface (class 10,AppleUSBACMData) opens withkIOReturnSuccess, while the control interface (class 2,AppleUSBACMControl) returnskIOReturnExclusiveAccess. The data path carries the bytes; the control path carriesSET_LINE_CODINGandSET_CONTROL_LINE_STATE— baud rate, DTR and RTS. So a CDC-ACM device cannot be served usefully: a client can be handed the bytes but can never set the line up. This is the class most dev boards with native USB present, and it is the one case where a DriverKit entitlement would actually change the answer. Note also thatAppleUSBACMDatapublishes/dev/cu.*throughIOSerialBSDClientwhile that interface is simultaneously openable — the conflict hazard, confirmed. - CH340, PL2303 and the rest have not been measured. Do not assume either way.
Which is why ownership is decided by attempting the open rather than by reading driver names: a driver being attached settles nothing, and the chip alone does not tell you.
Untested. Interrupt endpoints (no unbound interrupt device has been available).
Isochronous is not merely untested but structurally incomplete: alternate settings are
never selected and pipes are discovered once at open, so a UVC device's isochronous
endpoints would never appear. See Documentation/development/probe-rs-validation.md.
The SystemExtension subsystem was removed in 0.7.0 — some 24,000 lines that no
shipping path could reach. OSSystemExtensionRequest resolves extensions inside the
calling process's own bundle and requires that bundle to live in /Applications, so a
Homebrew install could never activate one; the claiming strategy underneath was measured
not to unbind anything either. DeviceClaimManager survives as the seam a real
implementation would slot into, satisfied by UserspaceDeviceClaimManager, which tracks
intent and says so. See
Documentation/development/system-extension-archive/why-it-was-removed.md.
Serving the devices macOS does hold — HID, mass storage, audio, cameras, and CDC-ACM
control interfaces — needs a DriverKit extension, which needs an app bundle in
/Applications to activate it, which needs a capability Apple has not granted. That
work is planned but not started: see
Documentation/development/app-bundle-phase-2.md, which records the measurements, the
chosen non-breaking distribution shape, and the two questions to settle first.
The project is structured as a multi-target Swift package:
- USBIPDCore: Core USB/IP protocol implementation and device management
Device/: IOKit-based USB device discovery and monitoringNetwork/: TCP server and client connection handlingProtocol/: USB/IP message encoding/decoding and request processing
- USBIPDCLI: Command-line interface executable (
usbipdbinary) - Common: Shared utilities (logging, error handling)
- QEMUTestServer: QEMU validation test server
Package.swift declares two test targets, and they are the whole suite:
- Tests/USBIPDCoreTests/ — core protocol, device, and network tests
- Tests/USBIPDCLITests/ — CLI behaviour
Both list Tests/SharedUtilities/ in their sources:, but SwiftPM silently ignores
source paths outside the target directory, so it is not compiled. Proof beyond the
build plan: AssertionHelpers.swift references config.autoBindDevices, which no
longer exists. Do not add tests there expecting them to run.
Three further targets are declared but commented out as "temporarily disabled":
IntegrationTests, SystemExtensionTests, QEMUIntegrationTests. Alongside them
Tests/TestMocks/, Tests/ProductionTests/ and Tests/PerformanceTests/ are compiled
by nothing. None of it has built in about a year — do not cite coverage from it without
reviving the target first. See Documentation/development/testing-strategy.md.
# Standard build
swift build
# Build specific product
swift build --product QEMUTestServer
# Xcode build
xcodebuild -scheme usbipd-mac build# Run the test suite
swift test --parallel
# Run one target or one test
swift test --filter USBIPDCoreTests
swift test --filter USBIPDCoreTests.USBIPProtocolTestsOnly two test targets exist in Package.swift: USBIPDCLITests and
USBIPDCoreTests. There is no tiered development/CI/production test system — the
scripts that claimed to provide one filtered on target names that were never declared,
so they matched nothing and exited 0. They were removed in 2026-08 along with the CI
step that called them. swift test is the whole story.
Note that swift test needs XCTest, which ships with Xcode rather than the Command Line
Tools — a CLT-only machine fails with error: no such module 'XCTest' and has to use CI.
The current development Mac has full Xcode, so the whole gate runs locally.
# Run SwiftLint (strict mode like CI)
swiftlint lint --strict
# Auto-fix violations
swiftlint --fix# Complete validation sequence (matches consolidated CI pipeline)
swiftlint lint --strict # Code quality validation
swift build --verbose # Build validation
swift test --parallel # Test suite
# Full production validation for release preparation
swiftlint lint --strict # Code quality validation
swift build --verbose # Build validation
# Validate specific workflow components locally
# (These match the consolidated CI workflow jobs)
# 1. Code Quality Job validation
swiftlint lint --strict --reporter xcode # Matches CI swiftlint-validation action
# 2. Build Validation Job validation
swift package resolve # Dependency resolution
swift build --verbose # Project compilation
# 3. Test Suite Job validation (environment-specific)
# 4. Release Validation (when preparing releases)
swift build --configuration release # Release build validation
# Note: Full release validation includes version checks and artifact validationWhen making changes that might affect CI workflows:
# Test changes against CI workflow locally before pushing
swiftlint lint --strict && swift build --verbose && swift test --parallel
# For release-related changes, test with production environment
swiftlint lint --strict && swift build --verbose && swift test --parallel
# Check if changes affect security scanning
# (Security workflow runs on Package.swift, Package.resolved, and Sources/ changes)
find Sources -name "*.swift" -exec grep -l "secret\|password\|key" {} +
# Validate test environment setup before CI runsWhen working with the consolidated CI system:
- Code Quality: Always run
swiftlint lint --strictbefore committing to catch issues early - Build Validation: Use
swift build --verboseto get detailed build information - Test Execution: Use environment-specific test scripts that match CI workflow job matrix
- Release Preparation: Use production environment tests for release validation
- Security Awareness: Be mindful of changes to dependencies and source code that trigger security scans
- Workflow Monitoring: Monitor GitHub Actions for CI status and investigate failures promptly
The consolidated architecture reduces complexity while maintaining comprehensive validation coverage.
The IOKit-based device discovery system in Sources/USBIPDCore/Device/ handles USB device enumeration and monitoring. Key files:
IOKitDeviceDiscovery.swift: Main discovery interfaceDeviceMonitor.swift: Device state change monitoring
TCP server implementation in Sources/USBIPDCore/Network/ manages client connections and protocol communication.
Protocol implementation in Sources/USBIPDCore/Protocol/ handles message encoding/decoding according to USB/IP specification.
The project uses a comprehensive SwiftLint configuration (.swiftlint.yml) with:
- Strict enforcement in CI (warnings treated as errors)
- Many formatting rules disabled to focus on core issues
- Extensive opt-in rules for code quality
- Test-specific rule relaxations
swift test --parallel is the whole suite — 429 tests across USBIPDCoreTests and
USBIPDCLITests. There is no development/CI/production tier system; the scripts that
claimed to provide one filtered on target names that were never declared, matched
nothing, and exited 0. See Documentation/development/testing-strategy.md.
CI runs the same command. It additionally builds with -Xswiftc -warnings-as-errors,
which local builds do not, so a clean local build can still fail CI — most recently on
NSLock.lock() being unavailable from async contexts under Swift 6. Reproduce CI's
strictness with:
swift build --build-tests -Xswiftc -warnings-as-errorsValidate release builds, not debug. swift test and swift build are debug, and
the verification scripts talk to a daemon over TCP so they cannot tell which binary is
behind the socket. v0.5.0 shipped with control transfers broken for exactly this reason:
performControlTransfer read a pointer that escaped a withUnsafeBytes closure, which
debug builds tolerated and optimised builds did not.
./Scripts/verify-hardware.sh # builds release, binds, runs the checks
./Scripts/verify-hardware.sh --configuration debug
./Scripts/verify-hardware.sh --busid 1-17It picks the first bindable non-hub device, polls until the daemon listens, runs a control transfer and — for a J-Link — a bulk exchange, then unbinds and stops the daemon. It exits non-zero when a transfer fails, verified by reintroducing the v0.5.0 bug and confirming it is caught.
Unit tests cannot reach the IOKit transfer path, and mocks of it have been wrong in ways the suite could not detect. Three scripts exercise real hardware:
Scripts/verify-jlink-bulk.py— writesEMU_CMD_VERSIONto a J-Link and reads the reply, proving bidirectional bulk traffic over USB/IPScripts/verify-usb-transfer.py— a raw USB/IP client that issues a control transfer, bypassing kernel enumerationScripts/validate-usb-entitlements.sh— measures which devices can be claimed and which are owned;--only VID:PIDscopes it, which matters with--seize
Real interop is validated with a Linux client in Docker, not with QEMU. Docker
Desktop's LinuxKit kernel has vhci_hcd built in, so --privileged plus
-v /dev/bus/usb:/dev/bus/usb is enough to run usbip attach and then probe-rs. See
Documentation/development/probe-rs-validation.md.
Located in Scripts/ directory. This list is exhaustive — several scripts named in
earlier revisions of this file (release-health-check.sh,
validate-release-environment.sh, generate-release-diagnostics.sh,
validate-release-artifacts.sh, and the per-environment test runners) have never
existed in the repository.
validate-usb-entitlements.sh: measures which devices can be claimed and which are owned, across five entitlement variants.--only VID:PIDrestricts it to one device, which matters with--seizesince that flag otherwise targets everything attached. SeeDocumentation/development/entitlement-validation.mdentitlement-validation/USBClaimProbe.swift: the probe the above compiles and signsverify-jlink-bulk.py: J-Link protocol exchange over USB/IP, proving bulk transfersverify-hardware.sh: builds a chosen configuration, binds a device and runs the checks below against it. Defaults to release, which is the pointverify-usb-transfer.py: raw USB/IP control transfer, bypassing kernel enumerationverify-concurrent-transfers.py: posts a bulk IN read that will block, then a control transfer, and times the second. Catches transfers being serialized against each other, which deadlocked every read/write protocol until 2026-08. Takes about a minute, since it waits out the blocked readverify-adb-protocol.py: sends an ADB CNXN to an Android device and reads its reply, which tests the transport without depending on adb's connection state machine
-
qemu/test-orchestrator.sh,qemu/vm-manager.sh,qemu/validate-environment.sh,qemu/cleanup.sh,qemu/create-test-image.sh,qemu/setup-usb-testing.sh,qemu/portable-timeout.sh,qemu-test.sh,qemu-test-validation.shTreat the QEMU harness with suspicion. The orchestrator starts a local test server and inspects its log; it does not boot a VM and runs no
usbipclient, so a green run says nothing about interop. Docker is what actually validates interop.
prepare-release.sh: release preparation and validationrollback-release.sh: release rollback and cleanupupdate-changelog.sh,generate-completions.shgenerate-homebrew-metadata.sh,validate-homebrew-metadata.sh
# Quick development feedback
swift test --parallel
# Validate environment before testing
# Generate comprehensive test report
swift test --parallel
# Test repository dispatch workflow
# Prepare and validate release
./Scripts/prepare-release.sh --dry-run v1.2.3
# Validate release artifactsThe project includes comprehensive QEMU-based testing infrastructure for end-to-end validation of USB/IP protocol implementation.
- QEMUTestServer: Test server executable for protocol validation
- Scripts/qemu/: QEMU testing infrastructure and utilities
- Tests/QEMUIntegrationTests/: Integration tests for QEMU workflows
# QEMU test orchestration (main entry point)
./Scripts/qemu/test-orchestrator.sh <scenario>
# Available test scenarios:
./Scripts/qemu/test-orchestrator.sh basic # Basic connectivity testing
./Scripts/qemu/test-orchestrator.sh protocol # USB/IP protocol validation
./Scripts/qemu/test-orchestrator.sh stress # Load testing (production only)
./Scripts/qemu/test-orchestrator.sh full # Complete test suite
# Environment-specific QEMU testing
TEST_ENVIRONMENT=development ./Scripts/qemu/test-orchestrator.sh basic
TEST_ENVIRONMENT=ci ./Scripts/qemu/test-orchestrator.sh protocol
TEST_ENVIRONMENT=production ./Scripts/qemu/test-orchestrator.sh full
# QEMU test configuration and status
./Scripts/qemu/test-orchestrator.sh --info # Show environment config
./Scripts/qemu/test-orchestrator.sh --dry-run full # Preview test execution# Environment validation and setup
./Scripts/qemu/validate-environment.sh # Check QEMU prerequisites
./Scripts/qemu/validate-environment.sh install-help # Installation guidance
# VM lifecycle management
./Scripts/qemu/vm-manager.sh create test-vm # Create VM
./Scripts/qemu/vm-manager.sh start test-vm # Start VM
./Scripts/qemu/vm-manager.sh stop test-vm # Stop VM
./Scripts/qemu/vm-manager.sh status test-vm # Check VM status
# QEMU test maintenance
./Scripts/qemu/cleanup.sh status # Show environment status
./Scripts/qemu/cleanup.sh full # Complete cleanup
./Scripts/qemu/cleanup.sh processes # Clean up processes only
./Scripts/qemu/cleanup.sh files --max-age 3 # Clean files older than 3 daysQEMU testing is integrated with the main test execution scripts:
# Development tests with QEMU (optional)
swift test --parallel
# CI tests with QEMU mocking
swift test --parallel
# Production tests with full QEMU integration
swift test --parallel # Automatically includes QEMU testsEnvironment variables for QEMU testing:
QEMU_TEST_MODE: Set tomockorvm(default: auto-detect)QEMU_TIMEOUT: Test timeout in seconds (environment-specific default)ENABLE_QEMU_TESTS: Enable QEMU tests in development environmentQEMU_VM_MEMORY: VM memory allocation (e.g., 512M)QEMU_CPU_CORES: VM CPU core count (e.g., 2)
# Generate QEMU test reports
./Scripts/qemu/test-orchestrator.sh --report-only
# Integration with main test reporting
swift test --parallel # Includes QEMU resultsThe project includes comprehensive automated release workflows with GitHub Actions integration, artifact building, code signing, and distribution management.
The release system uses a multi-stage automated pipeline:
- Release Preparation (
Scripts/prepare-release.sh) - GitHub Actions Workflows (
.github/workflows/) - Rollback Utilities (
Scripts/rollback-release.sh) - Monitoring and Alerting (Automated workflow monitoring)
Use the release preparation script to validate and prepare releases locally:
# Prepare a release (validates environment, runs tests, creates tags)
./Scripts/prepare-release.sh v1.2.3
# Dry run to preview release preparation
./Scripts/prepare-release.sh --dry-run v1.2.3
# Prepare release with custom options
./Scripts/prepare-release.sh --skip-tests --force v1.2.3-beta
# Emergency release preparation (skips validation)
./Scripts/prepare-release.sh --force --skip-tests --skip-lint v1.2.4The project uses a streamlined GitHub Actions architecture with three consolidated workflows:
- Purpose: Main continuous integration validation for all code changes
- Triggers: Push to main, pull requests, workflow calls from release workflows, manual dispatch
- Jobs: Code quality (SwiftLint), build validation, comprehensive test suite, release validation (conditional)
- Features: Parallel execution, environment-specific testing, reusable composite actions
- Duration: ~5-8 minutes for typical CI run
# Manual CI trigger with options
gh workflow run ci.yml -f test_environment=ci -f enable_qemu_tests=false
# Manual CI trigger for production testing
gh workflow run ci.yml -f test_environment=production -f enable_qemu_tests=true
# Manual release validation mode
gh workflow run ci.yml -f release_validation=true -f test_environment=ci- Purpose: Automated release process from validation to publication
- Triggers: Git tags (
v*) or manual dispatch - Jobs: Release validation, CI validation (via workflow_call), artifact building, release creation, post-release validation
- Features: Reuses CI workflow for validation, code signing, multi-architecture builds, GitHub release creation
- Duration: ~15-20 minutes for full release
# Manual release trigger (via GitHub web interface or gh CLI)
gh workflow run release.yml -f version=v1.2.3 -f prerelease=false
# Emergency release (skips CI validation)
gh workflow run release.yml -f version=v1.2.3-hotfix -f skip_tests=true- Purpose: Comprehensive security monitoring without blocking development
- Triggers: Daily schedule (6 AM UTC), push/PR on security-relevant files, manual dispatch
- Jobs: Dependency vulnerability scanning, static security analysis, security summary
- Features: Configurable scan types and severity thresholds, detailed security reporting
- Duration: ~3-5 minutes for comprehensive scan
# Manual security scan with options
gh workflow run security.yml -f scan_type=comprehensive -f severity_threshold=high
# Quick dependency-only scan
gh workflow run security.yml -f scan_type=dependency-only -f severity_threshold=criticalReusable workflow components that eliminate duplication:
- setup-swift-environment: Swift environment setup with caching, SwiftLint installation, dependency resolution
- swiftlint-validation: Standardized code quality validation with configurable options
- run-test-suite: Parameterized test execution across different environments
Benefits of Consolidated Architecture:
- Reduced workflow maintenance (7 workflows → 3 workflows)
- Eliminated duplication through composite actions
- Consistent environment setup and validation
- Improved caching and performance
- Enhanced reusability through workflow_call interface
The project uses a repository dispatch pattern to automatically update the Homebrew tap repository when new releases are published.
The formula in the tap is Formula/usbip.rb (class Usbip, installed with brew install usbip) — not usbipd-mac.rb. Several documents under Documentation/ still use the wrong filename; the tap's own scripts are the authority.
The main repository (usbipd-mac) triggers updates to the tap repository (homebrew-usbipd-mac) using GitHub's repository dispatch events:
- Release Workflow Trigger: When a new release is created, the release workflow sends a
repository_dispatchevent - Tap Repository Response: The tap repository receives the event and updates the formula file
- Automated Validation: Binary download, checksum verification, and formula syntax validation
- Error Handling: Automatic issue creation for failed updates with detailed diagnostics
# Manual repository dispatch trigger (for testing)
gh api repos/beriberikix/homebrew-usbipd-mac/dispatches \
--method POST \
--field event_type=formula_update \
--field client_payload='{"version":"v1.2.3","binary_url":"https://github.com/beriberikix/usbipd-mac/releases/download/v1.2.3/usbipd-v1.2.3-macos","sha256":"abc123..."}'
# Test repository dispatch workflow validation
cd ~/path/to/homebrew-usbipd-mac
./Scripts/test-formula-update.shThe tap repository workflow (homebrew-usbipd-mac/.github/workflows/formula-update.yml) handles the following steps:
- Payload Validation: Verify required fields (version, binary_url, sha256)
- Binary Download: Download and validate the binary against expected checksum
- Formula Update: Update version, URL, and SHA256 in the formula file
- Syntax Validation: Ensure Ruby syntax is correct using
ruby -c - Atomic Operations: Rollback on failure to maintain repository integrity
In case of automated update failures:
# Manual formula update (emergency procedure)
cd ~/path/to/homebrew-usbipd-mac
./Scripts/manual-update.sh v1.2.3 https://github.com/beriberikix/usbipd-mac/releases/download/v1.2.3/usbipd-v1.2.3-macos abc123...
# Force update with validation bypass (emergency only)
./Scripts/manual-update.sh --force --skip-validation v1.2.3-hotfix
# Check update status and logs
gh run list --repo beriberikix/homebrew-usbipd-mac
gh run view --repo beriberikix/homebrew-usbipd-mac [run-id]Common issues and solutions:
-
Repository Dispatch Failures:
- Verify
HOMEBREW_TAP_DISPATCH_TOKENsecret is configured - Check token permissions (requires
repositoryscope) - Validate payload structure and required fields
- Verify
-
Binary Download Issues:
- Confirm binary is accessible at the provided URL
- Verify SHA256 checksum matches the expected value
- Check network connectivity and GitHub release availability
-
Formula Syntax Errors:
- Review Ruby syntax using
ruby -c Formula/usbip.rb - Check for proper escaping of special characters
- Validate version format and URL structure
- Review Ruby syntax using
-
Rollback Scenarios:
- Repository automatically rolls back to previous formula on failure
- Manual rollback:
git checkout HEAD~1 -- Formula/usbip.rb - Issue creation provides detailed failure context for investigation
Available testing utilities in the tap repository:
# Validate tap repository workflow
./Scripts/test-formula-update.sh
# Test binary validation process
./Scripts/validate-binary.sh [binary_url] [expected_sha256]
# Test formula update with mock data
./Scripts/update-formula-from-dispatch.sh # Uses GITHUB_EVENT_PATH
# Create test issue (dry run)
DRY_RUN=true ./Scripts/create-update-issue.sh "Test error" "validation" "v1.2.3" "Test details"Validate release artifacts for integrity, signatures, and compatibility:
# Validate all release artifacts
# Validate specific version artifacts
# Skip signature validation (development/testing)
# Comprehensive validation with verbose outputHandle failed releases and cleanup incomplete artifacts:
# Rollback failed release (removes tags, cleans artifacts)
./Scripts/rollback-release.sh v1.2.3
# Rollback with different strategies
./Scripts/rollback-release.sh --type failed-release v1.2.3 # Full Git rollback
./Scripts/rollback-release.sh --type incomplete-build # Build artifacts only
./Scripts/rollback-release.sh --type artifacts-only # Preserve Git state
# Cleanup old artifacts and temporary files
./Scripts/rollback-release.sh --cleanup-only --max-age 30
# Preview rollback actions without changes
./Scripts/rollback-release.sh --dry-run v1.2.3Release behaviour is validated by running the workflows themselves, not by asserting on
them from XCTest. Tests/Integration/, Tests/ReleaseWorkflowTests/,
Tests/ReleaseValidation/ and Tests/Distribution/ previously held ~8,300 lines of
Swift that inspected YAML, shell scripts and brew output through the act framework.
They were compiled by no target, had never run, and were removed in 2026-08.
The release system includes comprehensive code signing and security validation:
Configure Apple Developer certificates and GitHub Secrets:
DEVELOPER_ID_CERTIFICATE: Base64-encoded Developer ID Application certificateDEVELOPER_ID_CERTIFICATE_PASSWORD: Certificate passwordNOTARIZATION_USERNAME: Apple ID for notarizationNOTARIZATION_PASSWORD: App-specific password for notarization
Automated security scanning is integrated into release workflows:
- Dependency vulnerability scanning
- Code signature validation
- Binary security analysis
- Supply chain verification
Monitor release workflow performance and identify optimization opportunities:
# Benchmark release workflow performance
# Generate performance optimization reportTrack release success rates, performance metrics, and infrastructure health:
- Success Rate Monitoring: Track release success/failure rates over time
- Performance Metrics: Build times, test execution duration, artifact sizes
- Infrastructure Health: Workflow availability, dependency status, environment validation
For emergency releases or hotfixes:
- Immediate Release: Use force flags to bypass non-critical validation
- Hotfix Process: Create hotfix branches with accelerated testing
- Rollback Strategy: Automated rollback with preserved backup capabilities
- Recovery Procedures: Comprehensive cleanup and state restoration
# Emergency release preparation
./Scripts/prepare-release.sh --force --skip-lint v1.2.4-hotfix
# Emergency GitHub Actions trigger
gh workflow run release.yml -f version=v1.2.4-hotfix -f skip_tests=true
# Emergency rollback if needed
./Scripts/rollback-release.sh --type failed-release v1.2.4-hotfix- Build Failures: Check SwiftLint compliance, dependency resolution, environment setup
- Test Failures: Validate test environment, check QEMU integration, review test logs
- Code Signing Issues: Verify certificate validity, check secret configuration, validate entitlements
- Artifact Problems: Run artifact validation, check checksums, verify file permissions
- Workflow Failures: Review GitHub Actions logs, check secret access, validate branch protection
# Preview a release without changing anything
./Scripts/prepare-release.sh --dry-run v1.2.3
# Inspect a run that failed
gh run list --workflow=ci.yml --limit 5
gh run view <run-id> --log-failedEarlier revisions listed release-health-check.sh, validate-release-environment.sh
and generate-release-diagnostics.sh here. None of them exist.
When working with release automation:
- Always validate environment before making release-related changes
- Run comprehensive tests before triggering release workflows
- Use dry-run mode to preview changes before execution
- Monitor workflow execution and be prepared to rollback if issues occur
- Follow security best practices for code signing and artifact handling
- Document any manual interventions and update automation accordingly
The release automation system is designed for reliability, security, and minimal manual intervention while providing comprehensive monitoring and rollback capabilities for production deployments.