feat: add macOS Gatekeeper support for CLI tool #36
Workflow file for this run
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: Build and Release | |
| on: | |
| push: | |
| tags: | |
| - 'v*.*.*' # Official release (v1.0.0) | |
| - 'v*.*.*-beta*' # Beta release (v1.0.0-beta.1) | |
| - 'v*.*.*-alpha*' # Alpha release (v1.0.0-alpha.1) | |
| - 'v*.*.*-rc*' # Release Candidate (v1.0.0-rc.1) | |
| # Add manual trigger option for testing | |
| workflow_dispatch: | |
| inputs: | |
| version: | |
| description: 'Version number (e.g.: v1.0.0-test)' | |
| required: true | |
| default: 'v0.1.0-test' | |
| create_release: | |
| description: 'Whether to create GitHub Release' | |
| type: boolean | |
| default: false | |
| jobs: | |
| # Run comprehensive tests before building release packages | |
| test: | |
| name: Run Full Test Suite | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: true # Stop all jobs if any test fails | |
| matrix: | |
| include: | |
| - os: windows-latest | |
| name: Windows Tests | |
| test_type: both | |
| - os: ubuntu-latest | |
| name: Linux Tests | |
| test_type: both | |
| - os: ubuntu-latest | |
| name: Linux ARM64 Tests | |
| test_type: functional | |
| arch: arm64 | |
| - os: macos-latest | |
| name: macOS Tests | |
| test_type: both | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| submodules: recursive | |
| - name: Setup cross-compilation for ARM64 | |
| if: matrix.arch == 'arm64' | |
| shell: bash | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y gcc-aarch64-linux-gnu g++-aarch64-linux-gnu qemu-user qemu-user-static | |
| - name: Setup Visual Studio (Windows) | |
| if: matrix.os == 'windows-latest' | |
| uses: microsoft/setup-msbuild@v1.1 | |
| - name: Install Linux dependencies | |
| if: matrix.os == 'ubuntu-latest' | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y cmake build-essential gcc | |
| if [ "${{ matrix.test_type }}" = "both" ]; then | |
| sudo apt-get install -y libglfw3-dev | |
| fi | |
| - name: Run Tests | |
| shell: bash | |
| run: | | |
| cd scripts | |
| echo "=== Running ${{ matrix.name }} ===" | |
| if [ "${{ matrix.arch }}" = "arm64" ]; then | |
| # ARM64 cross-compilation tests | |
| echo "Running ARM64 functional tests..." | |
| ./run_tests.sh --functional --exit-when-failed | |
| elif [ "${{ matrix.test_type }}" = "both" ]; then | |
| # Full test suite (functional + performance) | |
| echo "Running full test suite..." | |
| ./run_tests.sh --functional --exit-when-failed | |
| ./run_tests.sh --performance --exit-when-failed | |
| else | |
| # Functional tests only | |
| echo "Running functional tests..." | |
| ./run_tests.sh --functional --exit-when-failed | |
| fi | |
| echo "✅ ${{ matrix.name }} completed successfully!" | |
| - name: Upload test results | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: test-results-${{ matrix.name }}-${{ github.run_id }} | |
| path: | | |
| build/**/test_results.xml | |
| build/**/*_results.xml | |
| retention-days: 7 | |
| if-no-files-found: ignore | |
| build: | |
| name: Build Release Packages | |
| needs: test | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| # Static library builds (updated naming) | |
| - os: macos-latest | |
| name: macOS Static | |
| artifact_name: ccap-macos-universal-static | |
| build_type: Release | |
| shared: false | |
| - os: windows-latest | |
| name: Windows Static | |
| artifact_name: ccap-msvc-x86_64-static | |
| build_type: Release | |
| shared: false | |
| - os: ubuntu-latest | |
| name: Linux Static | |
| artifact_name: ccap-linux-x86_64-static | |
| build_type: Release | |
| shared: false | |
| - os: ubuntu-latest | |
| name: Linux ARM64 Static | |
| artifact_name: ccap-linux-arm64-static | |
| build_type: Release | |
| arch: arm64 | |
| shared: false | |
| # Shared library builds (updated naming) | |
| - os: macos-latest | |
| name: macOS Shared | |
| artifact_name: ccap-macos-universal-shared | |
| build_type: Release | |
| shared: true | |
| - os: windows-latest | |
| name: Windows Shared | |
| artifact_name: ccap-msvc-x86_64-shared | |
| build_type: Release | |
| shared: true | |
| - os: ubuntu-latest | |
| name: Linux Shared | |
| artifact_name: ccap-linux-x86_64-shared | |
| build_type: Release | |
| shared: true | |
| - os: ubuntu-latest | |
| name: Linux ARM64 Shared | |
| artifact_name: ccap-linux-arm64-shared | |
| build_type: Release | |
| arch: arm64 | |
| shared: true | |
| runs-on: ${{ matrix.os }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup cross-compilation for ARM64 | |
| if: matrix.arch == 'arm64' | |
| shell: bash | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y gcc-aarch64-linux-gnu g++-aarch64-linux-gnu | |
| - name: Setup Visual Studio (Windows) | |
| if: matrix.os == 'windows-latest' | |
| uses: microsoft/setup-msbuild@v1.1 | |
| - name: Configure CMake | |
| shell: bash | |
| run: | | |
| # Set shared library flag based on matrix configuration | |
| SHARED_FLAG="" | |
| if [ "${{ matrix.shared }}" = "true" ]; then | |
| SHARED_FLAG="-DCCAP_BUILD_SHARED=ON" | |
| else | |
| SHARED_FLAG="-DCCAP_BUILD_SHARED=OFF" | |
| fi | |
| if [ "${{ matrix.os }}" = "windows-latest" ]; then | |
| # Windows: Use multi-config generator (Visual Studio) | |
| cmake -B build -G "Visual Studio 17 2022" -A x64 -DCCAP_BUILD_EXAMPLES=ON -DCCAP_BUILD_TESTS=OFF $SHARED_FLAG | |
| elif [ "${{ matrix.os }}" = "ubuntu-latest" ]; then | |
| # Linux: Use single-config generator | |
| if [ "${{ matrix.arch }}" = "arm64" ]; then | |
| # ARM64 cross-compilation | |
| cmake -B build -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} \ | |
| -DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc \ | |
| -DCMAKE_CXX_COMPILER=aarch64-linux-gnu-g++ \ | |
| -DCMAKE_SYSTEM_NAME=Linux \ | |
| -DCMAKE_SYSTEM_PROCESSOR=aarch64 \ | |
| -DCCAP_BUILD_EXAMPLES=ON -DCCAP_BUILD_TESTS=OFF $SHARED_FLAG | |
| else | |
| # Regular Linux x86_64 | |
| cmake -B build -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -DCCAP_BUILD_EXAMPLES=ON -DCCAP_BUILD_TESTS=OFF $SHARED_FLAG | |
| fi | |
| else | |
| # macOS: Use single-config generator with universal binary | |
| cmake -B build -DCMAKE_OSX_ARCHITECTURES='arm64;x86_64' -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -DCCAP_BUILD_EXAMPLES=ON -DCCAP_BUILD_TESTS=OFF $SHARED_FLAG | |
| fi | |
| - name: Build project | |
| shell: bash | |
| run: | | |
| if [ "${{ matrix.os }}" = "windows-latest" ]; then | |
| # Windows: Build both Debug and Release versions | |
| cmake --build build --config Debug --parallel | |
| cmake --build build --config Release --parallel | |
| else | |
| # Other platforms: Build specified version | |
| cmake --build build --config ${{ matrix.build_type }} --parallel | |
| fi | |
| - name: List build outputs (Windows Debug) | |
| if: matrix.os == 'windows-latest' | |
| shell: bash | |
| run: | | |
| echo "=== Debug build outputs ===" | |
| find build/Debug -name "*.lib" -o -name "*.dll" -o -name "*.exe" | head -20 | |
| echo "=== Release build outputs ===" | |
| find build/Release -name "*.lib" -o -name "*.dll" -o -name "*.exe" | head -20 | |
| - name: Prepare package directory | |
| shell: bash | |
| run: | | |
| mkdir -p package/lib | |
| mkdir -p package/include | |
| mkdir -p package/examples | |
| mkdir -p package/cmake | |
| - name: Copy libraries (Windows) | |
| if: matrix.os == 'windows-latest' | |
| shell: bash | |
| run: | | |
| # Copy Debug version library files (ccapd.lib generated through DEBUG_POSTFIX "d") | |
| cp build/Debug/ccapd.lib package/lib/ccapd.lib || echo "Debug static library not found" | |
| # Copy Release version library files, keeping ccap.lib name | |
| cp build/Release/ccap.lib package/lib/ccap.lib || echo "Release static library not found" | |
| # Copy shared libraries (if shared build) | |
| if [ "${{ matrix.shared }}" = "true" ]; then | |
| cp build/Debug/ccapd.dll package/lib/ccapd.dll || echo "Debug shared library not found" | |
| cp build/Release/ccap.dll package/lib/ccap.dll || echo "Release shared library not found" | |
| # Also copy import libraries for DLLs | |
| cp build/Debug/ccapd.lib package/lib/ccapd_import.lib || echo "Debug import library not found" | |
| cp build/Release/ccap.lib package/lib/ccap_import.lib || echo "Release import library not found" | |
| fi | |
| # Copy example programs (both versions in examples directory) | |
| mkdir -p package/examples/Debug | |
| mkdir -p package/examples/Release | |
| cp build/Debug/*.exe package/examples/Debug/ || echo "Debug examples not found" | |
| cp build/Release/*.exe package/examples/Release/ || echo "Release examples not found" | |
| - name: Copy libraries (macOS) | |
| if: matrix.os == 'macos-latest' | |
| shell: bash | |
| run: | | |
| if [ "${{ matrix.shared }}" = "true" ]; then | |
| # Copy shared library | |
| cp build/libccap.dylib package/lib/ || echo "Shared library not found" | |
| else | |
| # Copy static library | |
| cp build/libccap.a package/lib/ || echo "Static library not found" | |
| fi | |
| # Copy example programs | |
| find build -name "*-print_camera" -exec cp {} package/examples/ \; || echo "Examples not found" | |
| find build -name "*-minimal_example" -exec cp {} package/examples/ \; || echo "Examples not found" | |
| find build -name "*-capture_grab" -exec cp {} package/examples/ \; || echo "Examples not found" | |
| find build -name "*-capture_callback" -exec cp {} package/examples/ \; || echo "Examples not found" | |
| find build -name "*-example_with_glfw" -exec cp {} package/examples/ \; || echo "Examples not found" | |
| - name: Copy libraries (Linux) | |
| if: matrix.os == 'ubuntu-latest' && matrix.arch != 'arm64' | |
| shell: bash | |
| run: | | |
| if [ "${{ matrix.shared }}" = "true" ]; then | |
| # Copy shared library | |
| cp build/libccap.so package/lib/ || echo "Shared library not found" | |
| else | |
| # Copy static library | |
| cp build/libccap.a package/lib/ || echo "Static library not found" | |
| fi | |
| # Copy example programs | |
| find build -name "0-print_camera" -exec cp {} package/examples/ \; || echo "Examples not found" | |
| find build -name "1-minimal_example" -exec cp {} package/examples/ \; || echo "Examples not found" | |
| find build -name "2-capture_grab" -exec cp {} package/examples/ \; || echo "Examples not found" | |
| find build -name "3-capture_callback" -exec cp {} package/examples/ \; || echo "Examples not found" | |
| find build -name "4-example_with_glfw" -exec cp {} package/examples/ \; || echo "Examples not found" | |
| - name: Copy libraries (Linux ARM64) | |
| if: matrix.os == 'ubuntu-latest' && matrix.arch == 'arm64' | |
| shell: bash | |
| run: | | |
| if [ "${{ matrix.shared }}" = "true" ]; then | |
| # Copy shared library | |
| cp build/libccap.so package/lib/ || echo "Shared library not found" | |
| else | |
| # Copy static library | |
| cp build/libccap.a package/lib/ || echo "Static library not found" | |
| fi | |
| # Copy example programs (ARM64 cross-compiled) | |
| find build -name "0-print_camera" -exec cp {} package/examples/ \; || echo "Examples not found" | |
| find build -name "1-minimal_example" -exec cp {} package/examples/ \; || echo "Examples not found" | |
| find build -name "2-capture_grab" -exec cp {} package/examples/ \; || echo "Examples not found" | |
| find build -name "3-capture_callback" -exec cp {} package/examples/ \; || echo "Examples not found" | |
| find build -name "4-example_with_glfw" -exec cp {} package/examples/ \; || echo "Examples not found" | |
| - name: Copy headers and other files | |
| shell: bash | |
| run: | | |
| # Copy header files | |
| cp -r include/* package/include/ | |
| # Copy CMake configuration files | |
| cp build/ccap*.cmake package/cmake/ || echo "CMake config files not found" | |
| cp build/ccap.pc package/ || echo "pkg-config file not found" | |
| # Copy documentation | |
| cp README.md package/ || echo "README not found" | |
| cp README.zh-CN.md package/ || echo "Chinese README not found" | |
| cp LICENSE package/ || echo "LICENSE not found" | |
| cp BUILD_AND_INSTALL.md package/ || echo "Build instructions not found" | |
| cp PACKAGE_USAGE.md package/ || echo "Package usage guide not found" | |
| # Copy example source files | |
| cp examples/desktop/*.cpp package/examples/ || echo "Example source files not found" | |
| - name: Verify package contents (Windows) | |
| if: matrix.os == 'windows-latest' | |
| shell: bash | |
| run: | | |
| echo "=== Package contents ===" | |
| find package -name "*.lib" -o -name "*.dll" -o -name "*.exe" | sort | |
| echo "=== lib directory ===" | |
| ls -la package/lib/ || echo "lib directory not found" | |
| - name: Create archive | |
| shell: bash | |
| run: | | |
| cd package | |
| if [ "${{ matrix.os }}" = "windows-latest" ]; then | |
| # Windows: Create ZIP file | |
| 7z a ../${{ matrix.artifact_name }}.zip ./* | |
| else | |
| # macOS and Linux: Create tar.gz file | |
| tar -czf ../${{ matrix.artifact_name }}.tar.gz . | |
| fi | |
| cd .. | |
| - name: Upload build artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: ${{ matrix.artifact_name }} | |
| path: ${{ matrix.artifact_name }}.* | |
| retention-days: 5 | |
| build-cli: | |
| name: Build CLI Tool | |
| needs: test | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| # Static-only Release builds for CLI tool | |
| - os: macos-latest | |
| name: macOS CLI | |
| artifact_name: ccap-cli-macos-universal | |
| build_type: Release | |
| - os: windows-latest | |
| name: Windows CLI | |
| artifact_name: ccap-cli-msvc-x86_64 | |
| build_type: Release | |
| - os: ubuntu-latest | |
| name: Linux x86_64 CLI | |
| artifact_name: ccap-cli-linux-x86_64 | |
| build_type: Release | |
| - os: ubuntu-latest | |
| name: Linux ARM64 CLI | |
| artifact_name: ccap-cli-linux-arm64 | |
| build_type: Release | |
| arch: arm64 | |
| runs-on: ${{ matrix.os }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup cross-compilation for ARM64 | |
| if: matrix.arch == 'arm64' | |
| shell: bash | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y gcc-aarch64-linux-gnu g++-aarch64-linux-gnu | |
| - name: Setup Visual Studio (Windows) | |
| if: matrix.os == 'windows-latest' | |
| uses: microsoft/setup-msbuild@v1.1 | |
| - name: Install Linux dependencies | |
| if: matrix.os == 'ubuntu-latest' | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y cmake build-essential gcc libglfw3-dev | |
| - name: Configure CMake | |
| shell: bash | |
| run: | | |
| if [ "${{ matrix.os }}" = "windows-latest" ]; then | |
| # Windows: Use multi-config generator (Visual Studio) | |
| cmake -B build -G "Visual Studio 17 2022" -A x64 \ | |
| -DCCAP_BUILD_EXAMPLES=OFF \ | |
| -DCCAP_BUILD_TESTS=OFF \ | |
| -DCCAP_BUILD_SHARED=OFF \ | |
| -DBUILD_CCAP_CLI=ON | |
| elif [ "${{ matrix.os }}" = "ubuntu-latest" ]; then | |
| # Linux: Use single-config generator | |
| if [ "${{ matrix.arch }}" = "arm64" ]; then | |
| # ARM64 cross-compilation | |
| cmake -B build -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} \ | |
| -DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc \ | |
| -DCMAKE_CXX_COMPILER=aarch64-linux-gnu-g++ \ | |
| -DCMAKE_SYSTEM_NAME=Linux \ | |
| -DCMAKE_SYSTEM_PROCESSOR=aarch64 \ | |
| -DCCAP_BUILD_EXAMPLES=OFF \ | |
| -DCCAP_BUILD_TESTS=OFF \ | |
| -DCCAP_BUILD_SHARED=OFF \ | |
| -DBUILD_CCAP_CLI=ON | |
| else | |
| # Regular Linux x86_64 | |
| cmake -B build -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} \ | |
| -DCCAP_BUILD_EXAMPLES=OFF \ | |
| -DCCAP_BUILD_TESTS=OFF \ | |
| -DCCAP_BUILD_SHARED=OFF \ | |
| -DBUILD_CCAP_CLI=ON | |
| fi | |
| else | |
| # macOS: Use single-config generator with universal binary | |
| cmake -B build -DCMAKE_OSX_ARCHITECTURES='arm64;x86_64' \ | |
| -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} \ | |
| -DCCAP_BUILD_EXAMPLES=OFF \ | |
| -DCCAP_BUILD_TESTS=OFF \ | |
| -DCCAP_BUILD_SHARED=OFF \ | |
| -DBUILD_CCAP_CLI=ON | |
| fi | |
| - name: Build CLI | |
| shell: bash | |
| run: | | |
| if [ "${{ matrix.os }}" = "windows-latest" ]; then | |
| # Windows: Build Release version only | |
| cmake --build build --config Release --target ccap-cli --parallel | |
| else | |
| # Other platforms: Build specified version | |
| cmake --build build --config ${{ matrix.build_type }} --target ccap-cli --parallel | |
| fi | |
| - name: Prepare CLI package directory | |
| shell: bash | |
| run: | | |
| mkdir -p cli-package | |
| - name: Copy CLI executable (Windows) | |
| if: matrix.os == 'windows-latest' | |
| shell: bash | |
| run: | | |
| # Copy CLI executable | |
| cp build/Release/ccap.exe cli-package/ccap.exe || echo "CLI executable not found" | |
| # Copy required DLLs if any | |
| # Note: Static build should not need additional DLLs | |
| - name: Copy CLI executable (macOS) | |
| if: matrix.os == 'macos-latest' | |
| shell: bash | |
| run: | | |
| # Copy CLI executable | |
| cp build/ccap cli-package/ccap || echo "CLI executable not found" | |
| # Verify universal binary | |
| if [ -f "cli-package/ccap" ]; then | |
| file cli-package/ccap | |
| lipo -info cli-package/ccap | |
| fi | |
| - name: Copy CLI executable (Linux) | |
| if: matrix.os == 'ubuntu-latest' | |
| shell: bash | |
| run: | | |
| # Copy CLI executable | |
| cp build/ccap cli-package/ccap || echo "CLI executable not found" | |
| # Verify architecture | |
| if [ -f "cli-package/ccap" ]; then | |
| file cli-package/ccap | |
| fi | |
| - name: Copy documentation | |
| shell: bash | |
| run: | | |
| # Copy README files | |
| cp README.md cli-package/ || echo "README not found" | |
| cp README.zh-CN.md cli-package/ || echo "Chinese README not found" | |
| cp LICENSE cli-package/ || echo "LICENSE not found" | |
| # Create platform-specific usage guide for CLI | |
| if [ "${{ matrix.os }}" = "macos-latest" ]; then | |
| # macOS-specific USAGE.md with Gatekeeper instructions | |
| cat > cli-package/USAGE.md << 'EOF' | |
| # ccap CLI Tool Usage (macOS) | |
| ## 🚀 Quick Start | |
| ### First-time Setup (macOS Security) | |
| macOS Gatekeeper will block this executable because it's not signed by Apple. | |
| You have **two options**: | |
| **Option 1: Use the convenience script (recommended)** | |
| ```bash | |
| # Run ccap through the wrapper script (handles security automatically) | |
| ./run_ccap.sh --list-devices | |
| ./run_ccap.sh --help | |
| ``` | |
| **Option 2: Remove quarantine attribute manually** | |
| ```bash | |
| # One-time setup - remove the quarantine attribute | |
| xattr -d com.apple.quarantine ccap | |
| chmod +x ccap | |
| # Now you can run ccap directly | |
| ./ccap --list-devices | |
| ``` | |
| **Option 3: Use System Preferences** | |
| 1. Try to run `./ccap` | |
| 2. macOS will show a security warning | |
| 3. Go to **System Preferences** → **Security & Privacy** → **General** | |
| 4. Click **"Open Anyway"** next to the blocked message | |
| 5. Try running `./ccap` again and click **"Open"** | |
| --- | |
| ## Usage Examples | |
| ```bash | |
| # List all available cameras | |
| ./ccap --list-devices | |
| # Capture a single frame (saves as output.bmp by default) | |
| ./ccap | |
| # Capture with specific device | |
| ./ccap --device 0 | |
| # Capture with specific resolution | |
| ./ccap --width 1920 --height 1080 | |
| # Capture with specific pixel format | |
| ./ccap --format YUYV | |
| # Capture with internal format (camera native format) | |
| ./ccap --internal-format MJPEG | |
| # Capture multiple frames | |
| ./ccap --count 10 | |
| # Save to specific file | |
| ./ccap --output my-capture.bmp | |
| # Preview window (with GLFW support) | |
| ./ccap --preview | |
| ``` | |
| ## Available Options | |
| Run `./ccap --help` for complete list of options. | |
| ## 🔧 Troubleshooting | |
| ### "ccap cannot be opened because the developer cannot be verified" | |
| This is macOS Gatekeeper security feature. See the **First-time Setup** section above for solutions. | |
| ### Check if quarantine attribute is set | |
| ```bash | |
| xattr -l ccap | |
| # If you see "com.apple.quarantine", it's quarantined | |
| ``` | |
| ### Remove quarantine from all files in directory | |
| ```bash | |
| xattr -dr com.apple.quarantine . | |
| ``` | |
| ## System Requirements | |
| - **macOS**: 10.13 or higher | |
| - Universal Binary (supports both Intel and Apple Silicon) | |
| ## Notes | |
| - This CLI tool is statically linked and has no external dependencies | |
| - BMP format is the only supported output format | |
| - For more advanced usage, please refer to the ccap library documentation | |
| - The `run_ccap.sh` script automatically handles macOS security on first run | |
| EOF | |
| else | |
| # Generic USAGE.md for Windows and Linux | |
| cat > cli-package/USAGE.md << 'EOF' | |
| # ccap CLI Tool Usage | |
| ## Quick Start | |
| ```bash | |
| # List all available cameras | |
| ./ccap --list-devices | |
| # Capture a single frame (saves as output.bmp by default) | |
| ./ccap | |
| # Capture with specific device | |
| ./ccap --device 0 | |
| # Capture with specific resolution | |
| ./ccap --width 1920 --height 1080 | |
| # Capture with specific pixel format | |
| ./ccap --format YUYV | |
| # Capture with internal format (camera native format) | |
| ./ccap --internal-format MJPEG | |
| # Capture multiple frames | |
| ./ccap --count 10 | |
| # Save to specific file | |
| ./ccap --output my-capture.bmp | |
| # Preview window (if compiled with GLFW support) | |
| ./ccap --preview | |
| ``` | |
| ## Available Options | |
| Run `./ccap --help` for complete list of options. | |
| ## System Requirements | |
| - **Windows**: Windows 10 or higher | |
| - **Linux**: Modern Linux distribution with V4L2 support (kernel 2.6+) | |
| ## Notes | |
| - This CLI tool is statically linked and has no external dependencies | |
| - BMP format is the only supported output format | |
| - For more advanced usage, please refer to the ccap library documentation | |
| EOF | |
| fi | |
| - name: Create macOS convenience script | |
| if: matrix.os == 'macos-latest' | |
| shell: bash | |
| run: | | |
| # Create run_ccap.sh wrapper script for macOS | |
| cat > cli-package/run_ccap.sh << 'EOF' | |
| #!/bin/bash | |
| # ccap convenience wrapper for macOS | |
| # This script automatically removes the quarantine attribute on first run | |
| SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" | |
| CCAP_BIN="$SCRIPT_DIR/ccap" | |
| # Check if ccap executable exists | |
| if [ ! -f "$CCAP_BIN" ]; then | |
| echo "Error: ccap executable not found at: $CCAP_BIN" | |
| exit 1 | |
| fi | |
| # Check if quarantine attribute is set | |
| if xattr -l "$CCAP_BIN" 2>/dev/null | grep -q "com.apple.quarantine"; then | |
| echo "🔓 First run detected: Removing macOS quarantine attribute..." | |
| if xattr -d com.apple.quarantine "$CCAP_BIN" 2>/dev/null; then | |
| echo "✅ Quarantine attribute removed successfully" | |
| else | |
| echo "⚠️ Warning: Could not remove quarantine attribute" | |
| echo " You may need to run: xattr -d com.apple.quarantine ccap" | |
| echo "" | |
| fi | |
| fi | |
| # Ensure executable permission | |
| if [ ! -x "$CCAP_BIN" ]; then | |
| chmod +x "$CCAP_BIN" 2>/dev/null || true | |
| fi | |
| # Execute ccap with all passed arguments | |
| exec "$CCAP_BIN" "$@" | |
| EOF | |
| # Make the wrapper script executable | |
| chmod +x cli-package/run_ccap.sh | |
| - name: Create CLI archive | |
| shell: bash | |
| run: | | |
| cd cli-package | |
| if [ "${{ matrix.os }}" = "windows-latest" ]; then | |
| # Windows: Create ZIP file | |
| 7z a ../${{ matrix.artifact_name }}.zip ./* | |
| else | |
| # macOS and Linux: Create tar.gz file | |
| tar -czf ../${{ matrix.artifact_name }}.tar.gz . | |
| fi | |
| cd .. | |
| - name: Upload CLI artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: ${{ matrix.artifact_name }} | |
| path: ${{ matrix.artifact_name }}.* | |
| retention-days: 5 | |
| release: | |
| needs: [test, build, build-cli] | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Download all artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: artifacts | |
| - name: List downloaded files | |
| shell: bash | |
| run: | | |
| echo "Downloaded artifacts:" | |
| find artifacts -type f | |
| echo "" | |
| echo "Directory structure:" | |
| ls -la artifacts/ | |
| - name: Determine release type | |
| id: release_type | |
| shell: bash | |
| run: | | |
| # Get version number (from tag or manual input) | |
| if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then | |
| TAG_NAME="${{ github.event.inputs.version }}" | |
| echo "Manual trigger with version: $TAG_NAME" | |
| else | |
| TAG_NAME="${{ github.ref_name }}" | |
| echo "Tag trigger with version: $TAG_NAME" | |
| fi | |
| echo "tag_name=$TAG_NAME" >> $GITHUB_OUTPUT | |
| if [[ "$TAG_NAME" =~ -beta ]]; then | |
| echo "prerelease=true" >> $GITHUB_OUTPUT | |
| echo "release_name=Beta Release $TAG_NAME" >> $GITHUB_OUTPUT | |
| elif [[ "$TAG_NAME" =~ -alpha ]]; then | |
| echo "prerelease=true" >> $GITHUB_OUTPUT | |
| echo "release_name=Alpha Release $TAG_NAME" >> $GITHUB_OUTPUT | |
| elif [[ "$TAG_NAME" =~ -rc ]]; then | |
| echo "prerelease=true" >> $GITHUB_OUTPUT | |
| echo "release_name=Release Candidate $TAG_NAME" >> $GITHUB_OUTPUT | |
| elif [[ "$TAG_NAME" =~ -test ]]; then | |
| echo "prerelease=true" >> $GITHUB_OUTPUT | |
| echo "release_name=Test Build $TAG_NAME" >> $GITHUB_OUTPUT | |
| else | |
| echo "prerelease=false" >> $GITHUB_OUTPUT | |
| echo "release_name=Release $TAG_NAME" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Generate release notes | |
| id: release_notes | |
| shell: bash | |
| run: | | |
| cat > release_notes.md << 'EOF' | |
| ## 🚀 ccap ${{ steps.release_type.outputs.tag_name }} | |
| ### ✅ Quality Assurance | |
| **All packages have passed comprehensive unit tests before release:** | |
| - ✅ **Windows Tests**: Functional and performance tests on Windows platform | |
| - ✅ **Linux x86_64 Tests**: Functional and performance tests on Linux | |
| - ✅ **Linux ARM64 Tests**: Cross-compiled functional tests with QEMU validation | |
| - ✅ **macOS Tests**: Functional and performance tests on macOS platform | |
| > **Note**: This release was only created after ALL platform tests passed successfully. Any test failure would have prevented the release. | |
| ### 📦 Downloads | |
| **Library Packages:** | |
| **Static Library Packages (for static linking):** | |
| - **macOS** (Universal Binary - supports Intel & Apple Silicon): `ccap-macos-universal-static.tar.gz` | |
| - **Windows** (MSVC x64 - includes Debug and Release versions): `ccap-msvc-x86_64-static.zip` | |
| - **Linux x86_64** (compatible with most distributions): `ccap-linux-x86_64-static.tar.gz` | |
| - **Linux ARM64** (compatible with Raspberry Pi, ARM servers, and other ARM64 boards): `ccap-linux-arm64-static.tar.gz` | |
| **Shared Library Packages (for dynamic linking):** | |
| - **macOS** (Universal Binary - supports Intel & Apple Silicon): `ccap-macos-universal-shared.tar.gz` | |
| - **Windows** (MSVC x64 - includes Debug and Release versions): `ccap-msvc-x86_64-shared.zip` | |
| - **Linux x86_64** (compatible with most distributions): `ccap-linux-x86_64-shared.tar.gz` | |
| - **Linux ARM64** (compatible with Raspberry Pi, ARM servers, and other ARM64 boards): `ccap-linux-arm64-shared.tar.gz` | |
| **CLI Tool Packages (command-line tool, ready to run):** | |
| - **macOS** (Universal Binary): `ccap-cli-macos-universal.tar.gz` | |
| - **Windows** (x64): `ccap-cli-msvc-x86_64.zip` | |
| - **Linux x86_64**: `ccap-cli-linux-x86_64.tar.gz` | |
| - **Linux ARM64**: `ccap-cli-linux-arm64.tar.gz` | |
| > **CLI Tool**: These packages contain only the command-line executable (statically linked, no dependencies). Perfect for quick testing and simple capture tasks without requiring library integration. | |
| ### 📁 Package Contents | |
| **Static Library Packages:** | |
| - **Library Files**: Static library files for linking | |
| - Windows: `lib/ccap.lib` (Release version) and `lib/ccapd.lib` (Debug version) | |
| - macOS: `lib/libccap.a` (Universal Binary) | |
| - Linux x86_64: `lib/libccap.a` (x86_64) | |
| - Linux ARM64: `lib/libccap.a` (ARM64) | |
| **Shared Library Packages:** | |
| - **Library Files**: Shared library files for runtime linking | |
| - Windows: `lib/ccap.dll` + `lib/ccap_import.lib` (Release), `lib/ccapd.dll` + `lib/ccapd_import.lib` (Debug) | |
| - macOS: `lib/libccap.dylib` (Universal Binary) | |
| - Linux x86_64: `lib/libccap.so` (x86_64) | |
| - Linux ARM64: `lib/libccap.so` (ARM64) | |
| **All Packages Include:** | |
| - **Header Files**: Complete C++ API header files | |
| - **Example Programs**: 5 complete usage examples | |
| - Windows: `examples/Release/` and `examples/Debug/` directories contain corresponding versions | |
| - macOS: `examples/` directory contains executable files | |
| - Linux x86_64: `examples/` directory contains executable files | |
| - Linux ARM64: `examples/` directory contains executable files | |
| - **Example Source Code**: Ready-to-compile example code | |
| - **Documentation**: README and build instructions | |
| - **CMake Configuration**: Easy integration with other CMake projects | |
| ### 🔧 Usage | |
| **For Static Linking:** | |
| 1. Download the appropriate static library package | |
| 2. Extract to your project directory | |
| 3. Add the `include` directory to your compiler's include path | |
| 4. Link the corresponding library file: | |
| - **Windows Debug**: Link `lib/ccapd.lib` | |
| - **Windows Release**: Link `lib/ccap.lib` | |
| - **macOS**: Link `lib/libccap.a` | |
| - **Linux x86_64**: Link `lib/libccap.a` | |
| - **Linux ARM64**: Link `lib/libccap.a` | |
| 5. Refer to example code in the `examples` directory | |
| **For Shared Linking:** | |
| 1. Download the appropriate shared library package | |
| 2. Extract to your project directory | |
| 3. Add the `include` directory to your compiler's include path | |
| 4. Link and configure runtime libraries: | |
| - **Windows Debug**: Link `lib/ccapd_import.lib`, ensure `lib/ccapd.dll` is in PATH or app directory | |
| - **Windows Release**: Link `lib/ccap_import.lib`, ensure `lib/ccap.dll` is in PATH or app directory | |
| - **macOS**: Link `lib/libccap.dylib`, ensure dylib is in DYLD_LIBRARY_PATH or install location | |
| - **Linux x86_64**: Link `lib/libccap.so`, ensure so is in LD_LIBRARY_PATH or install location | |
| - **Linux ARM64**: Link `lib/libccap.so`, ensure so is in LD_LIBRARY_PATH or install location | |
| 5. Refer to example code in the `examples` directory | |
| **For Standalone CLI Tool:** | |
| 1. Download the appropriate standalone package for your platform | |
| 2. Extract the archive | |
| 3. Run the command-line tool: | |
| **macOS users:** | |
| ```bash | |
| # Option 1: Use the convenience script (recommended, handles security automatically) | |
| ./run_ccap.sh --list-devices | |
| ./run_ccap.sh --help | |
| # Option 2: Remove quarantine attribute manually | |
| xattr -d com.apple.quarantine ccap | |
| ./ccap --list-devices | |
| ``` | |
| **Windows/Linux users:** | |
| ```bash | |
| # List available cameras | |
| ./ccap --list-devices | |
| # Capture a frame | |
| ./ccap --output my-capture.bmp | |
| ``` | |
| 4. See `USAGE.md` in the package for complete usage guide and troubleshooting | |
| > **Note for macOS users:** macOS Gatekeeper will block unsigned executables. Use `run_ccap.sh` script (included) or see USAGE.md for solutions. | |
| ### 📋 System Requirements | |
| - **macOS**: 10.13 or higher | |
| - **Windows**: Windows 10 or higher (requires Visual C++ Redistributable) | |
| - **Linux**: Modern Linux distribution with kernel 2.6+ (supports V4L2) | |
| --- | |
| For complete changelog, see the auto-generated content below. | |
| EOF | |
| - name: Create GitHub Release | |
| # Only execute when triggered by tag or manual trigger with create_release enabled | |
| if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && github.event.inputs.create_release == 'true') | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: ${{ steps.release_type.outputs.tag_name }} | |
| name: ${{ steps.release_type.outputs.release_name }} | |
| body_path: release_notes.md | |
| files: | | |
| artifacts/*/ccap-*.zip | |
| artifacts/*/ccap-*.tar.gz | |
| draft: false | |
| prerelease: ${{ steps.release_type.outputs.prerelease }} | |
| generate_release_notes: true | |
| make_latest: ${{ steps.release_type.outputs.prerelease == 'false' }} | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Summary | |
| shell: bash | |
| run: | | |
| echo "## 🎉 Release Created Successfully!" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "**Release**: ${{ steps.release_type.outputs.release_name }}" >> $GITHUB_STEP_SUMMARY | |
| echo "**Tag**: ${{ steps.release_type.outputs.tag_name }}" >> $GITHUB_STEP_SUMMARY | |
| echo "**Prerelease**: ${{ steps.release_type.outputs.prerelease }}" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### ✅ Quality Assurance" >> $GITHUB_STEP_SUMMARY | |
| echo "- ✅ **Windows Tests**: All functional and performance tests passed" >> $GITHUB_STEP_SUMMARY | |
| echo "- ✅ **Linux x86_64 Tests**: All functional and performance tests passed" >> $GITHUB_STEP_SUMMARY | |
| echo "- ✅ **Linux ARM64 Tests**: Cross-compilation and functional tests passed" >> $GITHUB_STEP_SUMMARY | |
| echo "- ✅ **macOS Tests**: All functional and performance tests passed" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "> 🛡️ **This release was only created after ALL tests passed successfully!**" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### 📦 Included Files:" >> $GITHUB_STEP_SUMMARY | |
| echo "**Static Library Packages:**" >> $GITHUB_STEP_SUMMARY | |
| echo "- ccap-macos-universal-static.tar.gz" >> $GITHUB_STEP_SUMMARY | |
| echo "- ccap-msvc-x86_64-static.zip (includes Debug and Release versions)" >> $GITHUB_STEP_SUMMARY | |
| echo "- ccap-linux-x86_64-static.tar.gz" >> $GITHUB_STEP_SUMMARY | |
| echo "- ccap-linux-arm64-static.tar.gz" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "**Shared Library Packages:**" >> $GITHUB_STEP_SUMMARY | |
| echo "- ccap-macos-universal-shared.tar.gz" >> $GITHUB_STEP_SUMMARY | |
| echo "- ccap-msvc-x86_64-shared.zip (includes Debug and Release versions)" >> $GITHUB_STEP_SUMMARY | |
| echo "- ccap-linux-x86_64-shared.tar.gz" >> $GITHUB_STEP_SUMMARY | |
| echo "- ccap-linux-arm64-shared.tar.gz" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "**CLI Tool Packages:**" >> $GITHUB_STEP_SUMMARY | |
| echo "- ccap-cli-macos-universal.tar.gz" >> $GITHUB_STEP_SUMMARY | |
| echo "- ccap-cli-msvc-x86_64.zip" >> $GITHUB_STEP_SUMMARY | |
| echo "- ccap-cli-linux-x86_64.tar.gz" >> $GITHUB_STEP_SUMMARY | |
| echo "- ccap-cli-linux-arm64.tar.gz" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "Release page: ${{ github.server_url }}/${{ github.repository }}/releases/tag/${{ steps.release_type.outputs.tag_name }}" >> $GITHUB_STEP_SUMMARY |