Skip to content

CI

CI #209

Workflow file for this run

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..."
# Expected commands that should be in completion scripts
# attach/detach were removed: usbipd is the USB/IP server, and both only ever
# threw operationNotSupported while appearing in help and completions.
EXPECTED_COMMANDS=("help" "list" "bind" "unbind" "daemon" "install-system-extension" "diagnose" "completion")
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
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