Notice a bind made while the daemon is running #233
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| push: | |
| branches: [main, develop] | |
| paths-ignore: ['**/*.md', 'LICENSE', '.gitignore'] | |
| pull_request: | |
| branches: [main, develop] | |
| paths-ignore: ['**/*.md', 'LICENSE', '.gitignore'] | |
| workflow_call: | |
| inputs: | |
| test_environment: | |
| description: 'Test environment to run (development, ci, production)' | |
| required: false | |
| default: 'ci' | |
| type: string | |
| enable_qemu_tests: | |
| description: 'Enable QEMU integration tests' | |
| required: false | |
| default: false | |
| type: boolean | |
| release_validation: | |
| description: 'Enable release validation tests' | |
| required: false | |
| default: false | |
| type: boolean | |
| workflow_dispatch: | |
| inputs: | |
| test_environment: | |
| description: 'Test environment to run' | |
| required: false | |
| default: 'ci' | |
| type: choice | |
| options: | |
| - 'development' | |
| - 'ci' | |
| - 'production' | |
| enable_qemu_tests: | |
| description: 'Enable QEMU integration tests' | |
| required: false | |
| default: false | |
| type: boolean | |
| release_validation: | |
| description: 'Enable release validation tests' | |
| required: false | |
| default: false | |
| type: boolean | |
| env: | |
| TEST_ENVIRONMENT: ${{ inputs.test_environment || 'ci' }} | |
| ENABLE_QEMU_TESTS: ${{ inputs.enable_qemu_tests || 'false' }} | |
| SKIP_OPTIONAL_TESTS: ${{ vars.SKIP_OPTIONAL_TESTS || 'false' }} | |
| jobs: | |
| code-quality: | |
| name: Code Quality | |
| runs-on: macos-latest | |
| steps: | |
| - name: π₯ Checkout Repository | |
| uses: actions/checkout@v4 | |
| - name: π SwiftLint Validation | |
| uses: ./.github/actions/swiftlint-validation | |
| with: | |
| strict-mode: true | |
| build-validation: | |
| name: Build Validation | |
| runs-on: macos-latest | |
| needs: code-quality | |
| steps: | |
| - name: π₯ Checkout Repository | |
| uses: actions/checkout@v4 | |
| - name: π οΈ Setup Swift Environment | |
| uses: ./.github/actions/setup-swift-environment | |
| - name: π¨ Build Project | |
| run: | | |
| echo "Building main targets..." | |
| swift build --verbose | |
| echo "Building System Extension target..." | |
| swift build --product USBIPDSystemExtension --verbose | |
| echo "Building test server..." | |
| swift build --product QEMUTestServer --verbose | |
| test-suite: | |
| name: Test Suite | |
| runs-on: macos-latest | |
| needs: build-validation | |
| steps: | |
| - name: π₯ Checkout Repository | |
| uses: actions/checkout@v4 | |
| - name: π οΈ Setup Swift Environment | |
| uses: ./.github/actions/setup-swift-environment | |
| - name: π§ͺ Run Test Suite | |
| uses: ./.github/actions/run-test-suite | |
| with: | |
| test-environment: ${{ env.TEST_ENVIRONMENT }} | |
| enable-qemu-tests: ${{ env.ENABLE_QEMU_TESTS }} | |
| completion-validation: | |
| name: Completion Scripts Validation | |
| runs-on: macos-latest | |
| needs: build-validation | |
| steps: | |
| - name: π₯ Checkout Repository | |
| uses: actions/checkout@v4 | |
| - name: π οΈ Setup Swift Environment | |
| uses: ./.github/actions/setup-swift-environment | |
| - name: π Setup Shell Environments | |
| run: | | |
| echo "Setting up shell environments for completion testing..." | |
| # Install bash-completion if needed | |
| if ! command -v bash >/dev/null 2>&1; then | |
| echo "π¦ Installing bash..." | |
| brew install bash | |
| fi | |
| # Install zsh (should be available on macOS) | |
| if ! command -v zsh >/dev/null 2>&1; then | |
| echo "π¦ Installing zsh..." | |
| brew install zsh | |
| fi | |
| # Install fish shell | |
| if ! command -v fish >/dev/null 2>&1; then | |
| echo "π¦ Installing fish shell..." | |
| brew install fish | |
| fi | |
| echo "β Shell environments ready for completion testing" | |
| - name: π¨ Build CLI for Completion Testing | |
| run: | | |
| echo "Building CLI for completion testing..." | |
| swift build --product usbipd --configuration release | |
| # Verify the built CLI works | |
| if ./.build/release/usbipd --help > /dev/null; then | |
| echo "β CLI built successfully and functional" | |
| else | |
| echo "β CLI build verification failed" | |
| exit 1 | |
| fi | |
| - name: β¨ Generate Completion Scripts | |
| run: | | |
| echo "Generating shell completion scripts..." | |
| # Create temporary directory for completions | |
| COMPLETION_DIR="$(mktemp -d)" | |
| echo "Using completion directory: $COMPLETION_DIR" | |
| # Generate completion scripts using the CLI | |
| ./.build/release/usbipd completion generate --output "$COMPLETION_DIR" | |
| # Verify all expected files were created | |
| echo "Verifying generated completion files..." | |
| if [ ! -f "$COMPLETION_DIR/usbipd" ]; then | |
| echo "β Bash completion file not generated" | |
| exit 1 | |
| fi | |
| if [ ! -f "$COMPLETION_DIR/_usbipd" ]; then | |
| echo "β Zsh completion file not generated" | |
| exit 1 | |
| fi | |
| if [ ! -f "$COMPLETION_DIR/usbipd.fish" ]; then | |
| echo "β Fish completion file not generated" | |
| exit 1 | |
| fi | |
| echo "β All completion files generated successfully" | |
| # Store completion directory for subsequent steps | |
| echo "COMPLETION_DIR=$COMPLETION_DIR" >> $GITHUB_ENV | |
| - name: π Validate Completion Script Syntax | |
| run: | | |
| echo "Validating completion script syntax..." | |
| # Test bash completion syntax | |
| echo "Testing bash completion syntax..." | |
| if bash -n "$COMPLETION_DIR/usbipd"; then | |
| echo "β Bash completion syntax valid" | |
| else | |
| echo "β Bash completion syntax invalid" | |
| exit 1 | |
| fi | |
| # Test zsh completion syntax | |
| echo "Testing zsh completion syntax..." | |
| if zsh -n "$COMPLETION_DIR/_usbipd"; then | |
| echo "β Zsh completion syntax valid" | |
| else | |
| echo "β Zsh completion syntax invalid" | |
| exit 1 | |
| fi | |
| # Test fish completion syntax | |
| echo "Testing fish completion syntax..." | |
| if fish -n "$COMPLETION_DIR/usbipd.fish"; then | |
| echo "β Fish completion syntax valid" | |
| else | |
| echo "β Fish completion syntax invalid" | |
| exit 1 | |
| fi | |
| echo "β All completion script syntax validation passed" | |
| - name: π§ͺ Test Completion Functionality | |
| run: | | |
| echo "Testing completion functionality in shell environments..." | |
| # Test bash completion | |
| echo "Testing bash completion functionality..." | |
| bash -c " | |
| source '$COMPLETION_DIR/usbipd' | |
| if declare -F _usbipd >/dev/null; then | |
| echo 'β Bash completion function loaded successfully' | |
| else | |
| echo 'β Bash completion function not loaded' | |
| exit 1 | |
| fi | |
| " | |
| # Test zsh completion | |
| echo "Testing zsh completion functionality..." | |
| zsh -c " | |
| fpath=('$COMPLETION_DIR' \$fpath) | |
| autoload -U compinit | |
| compinit -u | |
| if [[ -n \${_comps[usbipd]} ]]; then | |
| echo 'β Zsh completion loaded successfully' | |
| else | |
| echo 'β Zsh completion available (completion system ready)' | |
| fi | |
| " | |
| # Test fish completion | |
| echo "Testing fish completion functionality..." | |
| fish -c " | |
| complete -c usbipd --erase | |
| source '$COMPLETION_DIR/usbipd.fish' | |
| if complete -C 'usbipd ' | grep -q help | |
| echo 'β Fish completion loaded and functional' | |
| else | |
| echo 'β οΈ Fish completion loaded (basic validation passed)' | |
| end | |
| " | |
| echo "β Completion functionality tests passed" | |
| - name: π Test Completion Validation Command | |
| run: | | |
| echo "Testing completion validation command..." | |
| # Test the CLI's own validation functionality | |
| ./.build/release/usbipd completion validate --output "$COMPLETION_DIR" | |
| echo "β CLI completion validation passed" | |
| - name: π Verify Completion Content | |
| run: | | |
| echo "Verifying completion script content..." | |
| # Must match the commands the CLI registers. Completions are generated from | |
| # the parser's registry, so this list only has to track that. | |
| # | |
| # attach/detach were removed: usbipd is the USB/IP server, and both only ever | |
| # threw operationNotSupported while appearing in help and completions. | |
| # install-system-extension could not succeed from a Homebrew prefix, and | |
| # diagnose did not complete; both were removed too. | |
| EXPECTED_COMMANDS=("help" "list" "bind" "unbind" "daemon" "status" "completion") | |
| REMOVED_COMMANDS=("attach" "detach" "install-system-extension" "diagnose") | |
| for cmd in "${EXPECTED_COMMANDS[@]}"; do | |
| echo "Checking for command: $cmd" | |
| # Check bash completion | |
| if grep -q "$cmd" "$COMPLETION_DIR/usbipd"; then | |
| echo " β Found in bash completion" | |
| else | |
| echo " β Missing from bash completion" | |
| exit 1 | |
| fi | |
| # Check zsh completion | |
| if grep -q "$cmd" "$COMPLETION_DIR/_usbipd"; then | |
| echo " β Found in zsh completion" | |
| else | |
| echo " β Missing from zsh completion" | |
| exit 1 | |
| fi | |
| # Check fish completion | |
| if grep -q "$cmd" "$COMPLETION_DIR/usbipd.fish"; then | |
| echo " β Found in fish completion" | |
| else | |
| echo " β Missing from fish completion" | |
| exit 1 | |
| fi | |
| done | |
| # A removed command reappearing means a hardcoded list drifted back, which is | |
| # how attach and detach lingered in the fish script after deletion. | |
| for cmd in "${REMOVED_COMMANDS[@]}"; do | |
| for script in "$COMPLETION_DIR"/*; do | |
| if grep -q -- "$cmd" "$script"; then | |
| echo " β Removed command '$cmd' still present in $(basename "$script")" | |
| exit 1 | |
| fi | |
| done | |
| done | |
| echo "β No removed commands present in completion scripts" | |
| echo "β All expected commands found in completion scripts" | |
| - name: π§Ή Cleanup Completion Artifacts | |
| if: always() | |
| run: | | |
| echo "Cleaning up completion test artifacts..." | |
| rm -rf "$COMPLETION_DIR" || true | |
| echo "β Cleanup completed" | |
| entitlement-harness-validation: | |
| name: Entitlement Harness Validation | |
| runs-on: macos-latest | |
| needs: build-validation | |
| steps: | |
| - name: π₯ Checkout Repository | |
| uses: actions/checkout@v4 | |
| - name: π¬ Self-test the USB entitlement harness | |
| run: | | |
| echo "::group::Entitlement harness self-test" | |
| # Validates the instrument, not the hardware. CI runners have no USB | |
| # devices attached, so this cannot measure whether a device can be | |
| # claimed β that needs a maintainer's Mac with real hardware. What it | |
| # does catch, on every push rather than once someone gets around to it: | |
| # the probe failing to compile, codesign rejecting an entitlement set, | |
| # a variant dying at launch, or malformed results. | |
| # | |
| # It also guards the shipped entitlement files against regressing to | |
| # the misspelled and over-broad keys that were removed. | |
| ./Scripts/validate-usb-entitlements.sh --self-test | |
| echo "::endgroup::" | |
| - name: π€ Upload Harness Results | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: entitlement-harness-results | |
| path: | | |
| .build/entitlement-validation/*.json | |
| .build/entitlement-validation/*.log | |
| .build/entitlement-validation/*.xml | |
| .build/entitlement-validation/report.md | |
| retention-days: 14 | |
| if-no-files-found: warn | |
| homebrew-metadata-validation: | |
| name: Homebrew Metadata Validation | |
| runs-on: macos-latest | |
| needs: build-validation | |
| if: contains(github.event.head_commit.message, 'homebrew') || contains(github.event.head_commit.message, 'metadata') || github.event_name == 'workflow_dispatch' | |
| steps: | |
| - name: π₯ Checkout Repository | |
| uses: actions/checkout@v4 | |
| - name: π οΈ Setup Swift Environment | |
| uses: ./.github/actions/setup-swift-environment | |
| - name: π§ Setup Metadata Generation Environment | |
| run: | | |
| echo "Setting up metadata generation environment..." | |
| # Install jq if not available | |
| if ! command -v jq >/dev/null 2>&1; then | |
| echo "π¦ Installing jq for JSON processing..." | |
| brew install jq | |
| fi | |
| echo "β Environment ready for metadata generation testing" | |
| - name: πΊ Test Metadata Generation Script | |
| run: | | |
| echo "Testing homebrew metadata generation script..." | |
| # Check script exists and is executable | |
| if [ ! -f "Scripts/generate-homebrew-metadata.sh" ]; then | |
| echo "β Metadata generation script not found" | |
| exit 1 | |
| fi | |
| if [ ! -x "Scripts/generate-homebrew-metadata.sh" ]; then | |
| echo "β Metadata generation script is not executable" | |
| exit 1 | |
| fi | |
| echo "β Metadata generation script found and executable" | |
| # Test script help output | |
| if ./Scripts/generate-homebrew-metadata.sh --help > /dev/null; then | |
| echo "β Script help option works" | |
| else | |
| echo "β Script help option failed" | |
| exit 1 | |
| fi | |
| # Test dry run mode with dummy data | |
| echo "Testing metadata generation in dry-run mode..." | |
| LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.1") | |
| if ./Scripts/generate-homebrew-metadata.sh \ | |
| --version "$LATEST_TAG" \ | |
| --checksum "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" \ | |
| --archive-url "https://github.com/beriberikix/usbipd-mac/archive/$LATEST_TAG.tar.gz" \ | |
| --release-notes "Test release notes for CI validation" \ | |
| --dry-run \ | |
| --skip-validation; then | |
| echo "β Metadata generation dry-run succeeded" | |
| else | |
| echo "β Metadata generation dry-run failed" | |
| exit 1 | |
| fi | |
| - name: π Test Metadata Validation Script | |
| run: | | |
| echo "Testing homebrew metadata validation script..." | |
| # Check script exists and is executable | |
| if [ ! -f "Scripts/validate-homebrew-metadata.sh" ]; then | |
| echo "β Metadata validation script not found" | |
| exit 1 | |
| fi | |
| if [ ! -x "Scripts/validate-homebrew-metadata.sh" ]; then | |
| echo "β Metadata validation script is not executable" | |
| exit 1 | |
| fi | |
| echo "β Metadata validation script found and executable" | |
| # Test script help output | |
| if ./Scripts/validate-homebrew-metadata.sh --help > /dev/null; then | |
| echo "β Validation script help option works" | |
| else | |
| echo "β Validation script help option failed" | |
| exit 1 | |
| fi | |
| - name: π§ͺ Test Complete Metadata Workflow | |
| run: | | |
| echo "Testing complete metadata generation and validation workflow..." | |
| # Generate test metadata | |
| LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.1") | |
| TEST_CHECKSUM="e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" | |
| TEST_URL="https://github.com/beriberikix/usbipd-mac/archive/$LATEST_TAG.tar.gz" | |
| echo "Generating test metadata..." | |
| ./Scripts/generate-homebrew-metadata.sh \ | |
| --version "$LATEST_TAG" \ | |
| --checksum "$TEST_CHECKSUM" \ | |
| --archive-url "$TEST_URL" \ | |
| --release-notes "Test release for CI validation" \ | |
| --skip-validation \ | |
| --force | |
| # Check if metadata was generated | |
| METADATA_FILE=".build/homebrew-metadata/homebrew-metadata.json" | |
| if [ ! -f "$METADATA_FILE" ]; then | |
| echo "β Metadata file was not generated" | |
| exit 1 | |
| fi | |
| echo "β Metadata file generated successfully" | |
| # Validate the generated metadata | |
| echo "Validating generated metadata..." | |
| if ./Scripts/validate-homebrew-metadata.sh \ | |
| --file "$METADATA_FILE" \ | |
| --skip-network \ | |
| --skip-checksum \ | |
| --verbose; then | |
| echo "β Generated metadata validation passed" | |
| else | |
| echo "β Generated metadata validation failed" | |
| exit 1 | |
| fi | |
| # Verify metadata content | |
| echo "Verifying metadata content structure..." | |
| # Check required fields exist | |
| if ! jq -e '.schema_version' "$METADATA_FILE" > /dev/null; then | |
| echo "β Missing schema_version field" | |
| exit 1 | |
| fi | |
| if ! jq -e '.metadata.version' "$METADATA_FILE" > /dev/null; then | |
| echo "β Missing metadata.version field" | |
| exit 1 | |
| fi | |
| if ! jq -e '.metadata.sha256' "$METADATA_FILE" > /dev/null; then | |
| echo "β Missing metadata.sha256 field" | |
| exit 1 | |
| fi | |
| if ! jq -e '.formula_updates' "$METADATA_FILE" > /dev/null; then | |
| echo "β Missing formula_updates field" | |
| exit 1 | |
| fi | |
| echo "β All required fields found in metadata" | |
| # Verify content values | |
| METADATA_VERSION=$(jq -r '.metadata.version' "$METADATA_FILE") | |
| if [ "$METADATA_VERSION" != "$LATEST_TAG" ]; then | |
| echo "β Version mismatch in metadata: expected $LATEST_TAG, got $METADATA_VERSION" | |
| exit 1 | |
| fi | |
| METADATA_CHECKSUM=$(jq -r '.metadata.sha256' "$METADATA_FILE") | |
| if [ "$METADATA_CHECKSUM" != "$TEST_CHECKSUM" ]; then | |
| echo "β Checksum mismatch in metadata: expected $TEST_CHECKSUM, got $METADATA_CHECKSUM" | |
| exit 1 | |
| fi | |
| echo "β Metadata content verification passed" | |
| # Display metadata summary | |
| echo "π Generated Metadata Summary:" | |
| echo " β’ Schema Version: $(jq -r '.schema_version' "$METADATA_FILE")" | |
| echo " β’ Version: $(jq -r '.metadata.version' "$METADATA_FILE")" | |
| echo " β’ Archive URL: $(jq -r '.metadata.archive_url' "$METADATA_FILE")" | |
| echo " β’ SHA256: $(jq -r '.metadata.sha256' "$METADATA_FILE" | head -c 16)..." | |
| echo " β’ Timestamp: $(jq -r '.metadata.timestamp' "$METADATA_FILE")" | |
| echo " β’ File Size: $(wc -c < "$METADATA_FILE") bytes" | |
| echo "β Complete metadata workflow test passed" | |
| - name: π§Ή Cleanup Test Artifacts | |
| if: always() | |
| run: | | |
| echo "Cleaning up test artifacts..." | |
| rm -rf .build/homebrew-metadata/ || true | |
| echo "β Cleanup completed" | |
| ci-summary: | |
| name: CI Summary | |
| runs-on: ubuntu-latest | |
| if: always() | |
| needs: [code-quality, build-validation, test-suite, completion-validation, homebrew-metadata-validation, entitlement-harness-validation] | |
| steps: | |
| - name: π Check Overall CI Status | |
| run: | | |
| echo "CI Summary:" | |
| echo "Code Quality: ${{ needs.code-quality.result }}" | |
| echo "Build Validation: ${{ needs.build-validation.result }}" | |
| echo "Test Suite: ${{ needs.test-suite.result }}" | |
| echo "Completion Validation: ${{ needs.completion-validation.result }}" | |
| echo "Homebrew Metadata Validation: ${{ needs.homebrew-metadata-validation.result }}" | |
| echo "Entitlement Harness Validation: ${{ needs.entitlement-harness-validation.result }}" | |
| if [[ "${{ needs.code-quality.result }}" == "success" && | |
| "${{ needs.build-validation.result }}" == "success" && | |
| ("${{ needs.test-suite.result }}" == "success" || "${{ needs.test-suite.result }}" == "skipped") && | |
| ("${{ needs.completion-validation.result }}" == "success" || "${{ needs.completion-validation.result }}" == "skipped") && | |
| ("${{ needs.entitlement-harness-validation.result }}" == "success" || "${{ needs.entitlement-harness-validation.result }}" == "skipped") ]]; then | |
| echo "β CI passed successfully" | |
| else | |
| echo "β CI failed" | |
| exit 1 | |
| fi |