diff --git a/.github/actions/check_cache/action.yaml b/.github/actions/check_cache/action.yaml deleted file mode 100644 index 4a4b6c30..00000000 --- a/.github/actions/check_cache/action.yaml +++ /dev/null @@ -1,35 +0,0 @@ -name: Check cache -description: Check is cache exists -inputs: - profile: - description: conan-profile - required: true - default: default - -outputs: - exists: - value: ${{steps.check-cache.outputs.cache-hit}} - -runs: - using: composite - steps: - - name: Set conan storage path - if: runner.os != 'Windows' - shell: bash - run: | - echo "CONAN_STORAGE=$HOME/.conan2/p" >> $GITHUB_ENV - - - name: Set conan storage path - if: runner.os == 'Windows' - shell: pwsh - run: | - echo "CONAN_STORAGE=$HOME\.conan2/p" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append - - - name: Cache conan packages - id: check-cache - uses: actions/cache@v4 - with: - path: ${{env.CONAN_STORAGE}} - key: ${{inputs.profile}} - lookup-only: true - diff --git a/.github/actions/conan_cache_create/action.yaml b/.github/actions/conan_cache_create/action.yaml new file mode 100644 index 00000000..94f3e24c --- /dev/null +++ b/.github/actions/conan_cache_create/action.yaml @@ -0,0 +1,48 @@ +name: Conan Cache create +description: Create conan cache +inputs: + profile: + description: conan-profile + required: true + default: default + args: + description: conan build apponly option + required: false + default: "" + +runs: + using: composite + steps: + - name: Conan install + shell: bash + id: conan-install + run: conan install . --profile:build=${{inputs.profile}} --profile:host=${{inputs.profile}} --lockfile=conan.lock --build=missing --output-folder=. ${{inputs.args}} + + - name: Conan cache clear + shell: bash + run: conan cache clean "*" --source --build --temp --download + + - name: Conan graph + shell: bash + run: conan graph info . --profile:build=${{inputs.profile}} --profile:host=${{inputs.profile}} --lockfile=conan.lock --format=json > graph.json + + - name: Conan pkg list + shell: bash + run: conan list --graph graph.json --format=json > pkglist.json + + - name: Conan cache create + shell: bash + run: conan cache save -l pkglist.json --no-source --file conan-cache-${{inputs.profile}}.tzst -c core.gzip:compresslevel=1 + + - name: Copy conan.lock to cache path + shell: bash + run: cp -f conan.lock conan-${{inputs.profile}}.lock + + - name: Conan cache save + id: cache-save + uses: actions/cache/save@v6 + with: + path: | + conan-cache-${{inputs.profile}}.tzst + conan-${{inputs.profile}}.lock + key: ${{inputs.profile}} \ No newline at end of file diff --git a/.github/actions/deploy/action.yaml b/.github/actions/deploy/action.yaml index bff8ec03..fc2bd4ee 100644 --- a/.github/actions/deploy/action.yaml +++ b/.github/actions/deploy/action.yaml @@ -25,7 +25,7 @@ runs: - name: Attest if: ${{inputs.attestation == 'true'}} - uses: actions/attest-build-provenance@v1 + uses: actions/attest-build-provenance@v4 with: subject-path: | ${{inputs.builddir}}/*.zip diff --git a/.github/actions/deploy_with_sign/action.yaml b/.github/actions/deploy_with_sign/action.yaml index f55ad59b..96ca847f 100644 --- a/.github/actions/deploy_with_sign/action.yaml +++ b/.github/actions/deploy_with_sign/action.yaml @@ -43,13 +43,13 @@ runs: slug: ${{inputs.sign-slug}} - name: Attest - uses: actions/attest-build-provenance@v1 + uses: actions/attest-build-provenance@v4 with: subject-path: "${{inputs.signeddir}}/**" - name: Upload Signed artifacts id: upload - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: "${{inputs.artifact}}-signed" path: ${{inputs.signeddir}} diff --git a/.github/actions/enable_conan/action.yaml b/.github/actions/enable_conan/action.yaml index b427ce41..08301edf 100644 --- a/.github/actions/enable_conan/action.yaml +++ b/.github/actions/enable_conan/action.yaml @@ -13,29 +13,58 @@ inputs: runs: using: composite steps: - - name: Set storage path + - name: Conan defaiult profile detect shell: bash - run: echo "CONAN_STORAGE=$(conan config home)/p" >> $GITHUB_ENV + run: conan profile detect --force - - name: Cache conan packages + - name: Conan profiles install + shell: bash + run: conan config install CI/conan + + - name: Conan lock create + shell: bash + run: conan lock create . --profile:build=${{inputs.profile}} --profile:host=${{inputs.profile}} --lockfile-out=conan.lock + + - name: Conan cache download id: cache - uses: actions/cache@v4 + uses: actions/cache/restore@v6 with: - path: ${{env.CONAN_STORAGE}} + path: | + conan-cache-${{inputs.profile}}.tzst + conan-${{inputs.profile}}.lock key: ${{inputs.profile}} - - if: ${{steps.cache.outputs.cache-hit != 'true'}} - name: Conan install + - name: Check cache hit + id: cache-hit + shell: bash + run: | + if [ -f "conan-cache-${{inputs.profile}}.tzst" ]; then + echo "cache_exists=true" >> $GITHUB_OUTPUT + else + echo "cache_exists=false" >> $GITHUB_OUTPUT + fi + + - name: Conan cache restore + if: steps.cache-hit.outputs.cache_exists == 'true' shell: bash - run: | - conan config install CI/conan - conan install . --build=missing --profile:build=${{inputs.profile}} --profile:host=${{inputs.profile}} --output-folder=. ${{inputs.args}} - conan cache clean "*" --source --build --temp --download + run: conan cache restore conan-cache-${{inputs.profile}}.tzst - - name: Always Save Cache - id: cache-save - if: always() && steps.cache.outputs.cache-hit != 'true' - uses: actions/cache/save@v4 + - name: Check cache is out of date + if: steps.cache-hit.outputs.cache_exists == 'true' + id: cache-out-of-date + shell: bash + run: | + if [ -f "conan-${{inputs.profile}}.lock" ] && cmp -s conan.lock conan-${{inputs.profile}}.lock; then + echo "cache_out_of_date=false" >> $GITHUB_OUTPUT + else + echo "cache_out_of_date=true" >> $GITHUB_OUTPUT + fi + + - name: Conan cache create + if: steps.cache-hit.outputs.cache_exists == 'false' || steps.cache-out-of-date.outputs.cache_out_of_date == 'true' + uses: ./.github/actions/conan_cache_create with: - path: ${{env.CONAN_STORAGE}} - key: ${{inputs.profile}} + profile: ${{inputs.profile}} + args: ${{inputs.args}} + + diff --git a/.github/actions/enable_python/action.yaml b/.github/actions/enable_python/action.yaml index 531eb51c..0902fc53 100644 --- a/.github/actions/enable_python/action.yaml +++ b/.github/actions/enable_python/action.yaml @@ -4,14 +4,14 @@ description: Install python with requirenments and cache it runs: using: composite steps: - - name: Set up Python 3.13 - uses: actions/setup-python@v5 + - name: Set up Python 3.14 + uses: actions/setup-python@v6 with: - python-version: 3.13 + python-version: 3.14 - name: Cache dependencies id: cache - uses: actions/cache@v4 + uses: actions/cache@v6 with: path: ${{ env.pythonLocation }} key: ${{ env.pythonLocation }}-${{ hashFiles('requirements.txt') }} diff --git a/.github/actions/enable_python_macos/action.yaml b/.github/actions/enable_python_macos/action.yaml index 01b6270e..bfafe656 100644 --- a/.github/actions/enable_python_macos/action.yaml +++ b/.github/actions/enable_python_macos/action.yaml @@ -3,10 +3,10 @@ description: Install python deps for MacOS runs: using: composite steps: - - name: Set up Python 3.13 - uses: actions/setup-python@v5 + - name: Set up Python 3.14 + uses: actions/setup-python@v6 with: - python-version: '3.13' + python-version: '3.14' cache: pip - run: pip install -r requirements.txt diff --git a/.github/actions/pack/action.yaml b/.github/actions/pack/action.yaml index 07f13626..5234f2de 100644 --- a/.github/actions/pack/action.yaml +++ b/.github/actions/pack/action.yaml @@ -29,7 +29,7 @@ runs: - name: Archive production artifacts id: upload - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: ${{inputs.artifact}} path: | diff --git a/.github/actions/setup_ubuntu_env/action.yaml b/.github/actions/setup_ubuntu_env/action.yaml index c412e78a..974378ee 100644 --- a/.github/actions/setup_ubuntu_env/action.yaml +++ b/.github/actions/setup_ubuntu_env/action.yaml @@ -7,52 +7,48 @@ runs: shell: bash run: | sudo apt-get update - sudo apt-get install -y libgl-dev \ - libgl-dev \ - libgl1-mesa-dev \ - libx11-dev \ - libx11-xcb-dev \ - libfontenc-dev \ - libice-dev \ - libsm-dev \ - libxau-dev \ - libxaw7-dev \ - libx11-xcb-dev \ - libfontenc-dev \ - libxcomposite-dev \ - libxcursor-dev \ - libxdamage-dev \ - libxfixes-dev \ - libxi-dev \ - libxinerama-dev \ - libxmu-dev \ - libxmuu-dev \ - libxpm-dev \ - libxrandr-dev \ - libxres-dev \ - libxss-dev \ - libxtst-dev \ - libxv-dev \ - libxxf86vm-dev \ - libxcb-glx0-dev \ - libxcb-render-util0-dev \ - libxcb-xkb-dev \ - libxcb-icccm4-dev \ - libxcb-image0-dev \ - libxcb-keysyms1-dev \ - libxcb-randr0-dev \ - libxcb-shape0-dev \ - libxcb-sync-dev \ - libxcb-xfixes0-dev \ - libxcb-xinerama0-dev \ - libxcb-dri3-dev \ - libxcb-cursor-dev \ - libxcb-dri2-0-dev \ - libxcb-dri3-dev \ - libxcb-present-dev \ - libxcb-composite0-dev \ - libxcb-ewmh-dev \ - libxcb-res0-dev \ - libxv-dev \ - libxcb-util-dev \ - libxcb-util0-dev \ No newline at end of file + sudo apt-get update && sudo apt-get install -y \ + libfontenc-dev \ + libgl-dev \ + libgl1-mesa-dev \ + libice-dev \ + libsm-dev \ + libx11-dev \ + libx11-xcb-dev \ + libxau-dev \ + libxaw7-dev \ + libxcb-composite0-dev \ + libxcb-cursor-dev \ + libxcb-dri2-0-dev \ + libxcb-dri3-dev \ + libxcb-ewmh-dev \ + libxcb-glx0-dev \ + libxcb-icccm4-dev \ + libxcb-image0-dev \ + libxcb-keysyms1-dev \ + libxcb-present-dev \ + libxcb-randr0-dev \ + libxcb-render-util0-dev \ + libxcb-res0-dev \ + libxcb-shape0-dev \ + libxcb-sync-dev \ + libxcb-util-dev \ + libxcb-util0-dev \ + libxcb-xfixes0-dev \ + libxcb-xinerama0-dev \ + libxcb-xkb-dev \ + libxcomposite-dev \ + libxcursor-dev \ + libxdamage-dev \ + libxfixes-dev \ + libxi-dev \ + libxinerama-dev \ + libxmu-dev \ + libxmuu-dev \ + libxpm-dev \ + libxrandr-dev \ + libxres-dev \ + libxss-dev \ + libxtst-dev \ + libxv-dev \ + libxxf86vm-dev diff --git a/.github/actions/sign/action.yaml b/.github/actions/sign/action.yaml index d037c3a6..5d96013b 100644 --- a/.github/actions/sign/action.yaml +++ b/.github/actions/sign/action.yaml @@ -25,7 +25,7 @@ inputs: runs: using: composite steps: - - uses: signpath/github-action-submit-signing-request@v1.2 + - uses: signpath/github-action-submit-signing-request@v2 with: api-token: '${{ inputs.token }}' organization-id: '458e74d2-1c0a-4e3a-b1e7-9bc24caf673e' diff --git a/.github/actions/tests/action.yaml b/.github/actions/tests/action.yaml index 1c4434bf..8f447598 100644 --- a/.github/actions/tests/action.yaml +++ b/.github/actions/tests/action.yaml @@ -23,7 +23,7 @@ runs: shell: bash run: bin/daggy --version - - uses: mikepenz/action-junit-report@v5 + - uses: mikepenz/action-junit-report@v6 with: report_paths: ${{inputs.testdir}}/tests/local_tests.xml detailed_summary: true diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 65ed2b3d..45e7b8c3 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -12,19 +12,15 @@ on: type: boolean jobs: - cache: - uses: ./.github/workflows/make_cache.yaml - linux-x64-build: - runs-on: ubuntu-22.04 - needs: [cache] + runs-on: ubuntu-26.04 permissions: id-token: write contents: read checks: write attestations: write steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 with: fetch-depth: 0 @@ -32,106 +28,74 @@ jobs: - uses: ./.github/actions/enable_python - uses: ./.github/actions/conan_build with: - profile: linux-gcc12_x64 + profile: linux-gcc15_x64 - uses: ./.github/actions/tests with: testdir: build/Release - name: linux-gcc12_x64 tests + name: linux-gcc15_x64 tests - uses: ./.github/actions/deploy if: inputs.deploy with: builddir: build/Release - artifact: linux-gcc12_x64 + artifact: linux-gcc15_x64 attestation: ${{inputs.attestation}} macos-arm-build: - runs-on: macos-14 - needs: [cache] + runs-on: macos-26 permissions: id-token: write contents: read checks: write attestations: write steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 with: fetch-depth: 0 - uses: ./.github/actions/enable_python_macos - uses: ./.github/actions/conan_build with: - profile: macos-clang15_arm + profile: macos-clang17_arm - uses: ./.github/actions/tests with: testdir: build/Release - name: macos-clang15_arm tests - - - uses: ./.github/actions/deploy - if: inputs.deploy - with: - builddir: build/Release - artifact: macos-clang15_arm - attestation: ${{inputs.attestation}} + name: macos-clang17_arm tests - macos-x64-build: - runs-on: macos-13 - needs: [cache] - permissions: - id-token: write - contents: read - checks: write - attestations: write - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - uses: ./.github/actions/enable_python_macos - - uses: ./.github/actions/conan_build - with: - profile: macos-clang15_x64 - - - uses: ./.github/actions/tests - with: - testdir: build/Release - name: macos-clang15_x64 tests - - uses: ./.github/actions/deploy if: inputs.deploy with: builddir: build/Release - artifact: macos-clang15_x64 + artifact: macos-clang17_arm attestation: ${{inputs.attestation}} win-x64-build: - runs-on: windows-2022 - needs: [cache] + runs-on: windows-2025-vs2026 permissions: id-token: write contents: read checks: write attestations: write steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 with: fetch-depth: 0 - uses: ./.github/actions/enable_python - uses: ./.github/actions/conan_build with: - profile: win-msvc194_x64 + profile: win-msvc195_x64 - uses: ./.github/actions/tests with: testdir: build - name: win-msvc194_x64 tests + name: win-msvc195_x64 tests - uses: ./.github/actions/deploy if: inputs.deploy with: builddir: build - artifact: win-msvc194_x64 + artifact: win-msvc195_x64 attestation: ${{inputs.attestation}} diff --git a/.github/workflows/check_cache.yaml b/.github/workflows/check_cache.yaml deleted file mode 100644 index f0acbe6f..00000000 --- a/.github/workflows/check_cache.yaml +++ /dev/null @@ -1,57 +0,0 @@ -name: Check cache deps -on: - workflow_call: - outputs: - linux_x64_hit: - value: ${{jobs.linux-x64-check-cache.outputs.exists}} - macos_arm_hit: - value: ${{jobs.macos-arm-check-cache.outputs.exists}} - macos_x64_hit: - value: ${{jobs.macos-x64-check-cache.outputs.exists}} - win_x64_hit: - value: ${{jobs.win-x64-check-cache.outputs.exists}} - -jobs: - linux-x64-check-cache: - runs-on: ubuntu-22.04 - outputs: - exists: ${{steps.check-cache.outputs.exists}} - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/check_cache - id: check-cache - with: - profile: linux-gcc12_x64 - - macos-arm-check-cache: - runs-on: macos-14 - outputs: - exists: ${{steps.check-cache.outputs.exists}} - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/check_cache - id: check-cache - with: - profile: macos-clang15_arm - - macos-x64-check-cache: - runs-on: macos-13 - outputs: - exists: ${{steps.check-cache.outputs.exists}} - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/check_cache - id: check-cache - with: - profile: macos-clang15_x64 - - win-x64-check-cache: - runs-on: windows-2022 - outputs: - exists: ${{steps.check-cache.outputs.exists}} - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/check_cache - id: check-cache - with: - profile: win-msvc194_x64 diff --git a/.github/workflows/make_cache.yaml b/.github/workflows/make_cache.yaml deleted file mode 100644 index 831d3241..00000000 --- a/.github/workflows/make_cache.yaml +++ /dev/null @@ -1,52 +0,0 @@ -name: Make cache deps -on: - workflow_call: - -jobs: - check-cache: - uses: ./.github/workflows/check_cache.yaml - - linux-x64-make-cache: - runs-on: ubuntu-22.04 - needs: [check-cache] - if: ${{ needs.check-cache.outputs.linux_x64_hit == false }} - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/setup_ubuntu_env - - uses: ./.github/actions/enable_python - - uses: ./.github/actions/enable_conan - with: - profile: linux-gcc12_x64 - - macos-arm-make-cache: - runs-on: macos-14 - needs: [check-cache] - if: ${{ needs.check-cache.outputs.macos_arm_hit == false }} - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/enable_python_macos - - uses: ./.github/actions/enable_conan - with: - profile: macos-clang15_arm - - macos-x64-make-cache: - runs-on: macos-13 - needs: [check-cache] - if: ${{ needs.check-cache.outputs.macos_x64_hit == false }} - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/enable_python_macos - - uses: ./.github/actions/enable_conan - with: - profile: macos-clang15_x64 - - win-x64-make-cache: - runs-on: windows-2022 - needs: [check-cache] - if: ${{ needs.check-cache.outputs.win_x64_hit == false }} - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/enable_python - - uses: ./.github/actions/enable_conan - with: - profile: win-msvc194_x64 diff --git a/.github/workflows/sign.yaml b/.github/workflows/sign.yaml index e74e8951..4f644216 100644 --- a/.github/workflows/sign.yaml +++ b/.github/workflows/sign.yaml @@ -1,19 +1,12 @@ name: Sign build on: workflow_dispatch: - inputs: - sign-policy: - type: choice - required: true - default: test-signing - options: - - test-signing - - release-signing + jobs: build: uses: ./.github/workflows/sign_build.yaml with: - sign: ${{inputs.sign-policy}} + sign: release-signing secrets: inherit \ No newline at end of file diff --git a/.github/workflows/sign_build.yaml b/.github/workflows/sign_build.yaml index 7ea664bb..6208a95b 100644 --- a/.github/workflows/sign_build.yaml +++ b/.github/workflows/sign_build.yaml @@ -9,22 +9,25 @@ on: jobs: win-x64-apponly-build: - runs-on: windows-2022 + runs-on: windows-2025-vs2026 permissions: id-token: write contents: read checks: write attestations: write steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 with: fetch-depth: 0 - name: Add msbuild to PATH - uses: microsoft/setup-msbuild@v2 + uses: microsoft/setup-msbuild@v3 - name: Install WiX run: dotnet tool install --global wix + + - name: Accept eula for WiX + run: wix eula accept wix7 - name: Install WiX UI extension run: wix extension add --global WixToolset.UI.wixext @@ -32,18 +35,18 @@ jobs: - uses: ./.github/actions/enable_python - uses: ./.github/actions/conan_build with: - profile: win-msvc194_x64_static + profile: win-msvc195_x64_static args: -o daggy/*:apponly=True - uses: ./.github/actions/tests with: testdir: build - name: win-msvc194_x64_static tests + name: win-msvc195_x64_static tests - uses: ./.github/actions/deploy_with_sign with: builddir: build - artifact: win-msvc194_x64_static + artifact: win-msvc195_x64_static sign: ${{inputs.sign}} signeddir: build/signed token: ${{secrets.SIGNPATH_API_TOKEN}} \ No newline at end of file diff --git a/.github/workflows/test_sign.yaml b/.github/workflows/test_sign.yaml new file mode 100644 index 00000000..52b5c4a7 --- /dev/null +++ b/.github/workflows/test_sign.yaml @@ -0,0 +1,11 @@ +name: Test sign build +on: pull_request + + +jobs: + build: + uses: ./.github/workflows/sign_build.yaml + with: + sign: test-signing + secrets: inherit + \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000..7de61bbd --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,269 @@ +--- +description: AI Agent Context for Daggy Project +--- + +# AGENTS.md + +**Daggy** - Data Aggregation Utility. Server-less cross-platform tool for running multiple local/remote processes simultaneously and streaming aggregated output. + +## Project Structure + +``` +src/ + Daggy/ # Console application (CLI) + main.cpp # Entry point + CConsoleDaggy.cpp # Main console logic + DaggyCore/ # Core library (C++23) + Core.cpp/hpp # Main API + Sources.cpp/hpp # Configuration types + Types.h/hpp # Enums (DaggyProviderStates, DaggyCommandStates, DaggyStates) + Errors.cpp/hpp # Error handling + providers/ # Data source agents (Local, SSH, SSH2) + aggregators/ # Data consumer agents (Console, File, Callback) +build/ # CMake build outputs +docs/ # Documentation (markdown) +CI/conan/ # Conan package configs +test_package/ # Integration tests +``` + +## Tech Stack + +- **Language**: C++23 with Qt6 (QObject, signals/slots) +- **Build**: CMake 3.20+ +- **Package**: Conan 2.0+ +- **SSH**: libssh2, OpenSSL +- **Config**: YAML (libyaml) and JSON (nlohmann/json) +- **Tests**: CTest (CMake) + +## Core Architecture + +### Providers (Data Sources) + +| Type | Class | Purpose | +|------|-------|---------| +| `local` | `CLocal` | Execute local processes | +| `ssh` | `CSsh` | SSH v1/2 connections (pure SSH) | +| `ssh2` | `CSsh2` | SSH via libssh2 | + +All providers implement `IProvider` interface with signals: `stateChanged`, `commandStateChanged`, `commandStream`, `commandError`, `error`. + +### Aggregators (Data Consumers) + +| Type | Class | Purpose | +|------|-------|---------| +| Console | `CConsole` | Stream to stdout | +| File | `CFile` | Write to output files | +| Callback | `CCallback` | Custom C++ callbacks | + +All aggregators implement `IAggregator` interface with slots: `onDataProviderStateChanged`, `onCommandStateChanged`, `onCommandStream`, `onCommandError`, `onDaggyStateChanged`. + +### State Enums (Types.h) + +```cpp +enum class DaggyProviderStates { Created, Running, Stopped, Completed }; +enum class DaggyCommandStates { Queued, Running, Stopped, Completed, Error }; +enum class DaggyStates { Created, Aggregating, Stopped, Completed }; +enum class DaggyStreamTypes { Stdout, Stderr }; +``` + +### Configuration Types (Sources.hpp) + +```cpp +struct commands::Properties { + QString extension; // File extension + QString exec; // Command to execute + QVariantMap parameters; // Optional params + bool restart; // Auto-restart on exit +}; + +struct sources::Properties { + QString type; // "local", "ssh", "ssh2" + QString host; // For ssh/ssh2 + Commands commands; // Map + bool reconnect; // Auto-reconnect on disconnect + QVariantMap parameters; // ssh connection params +}; + +using Sources = QMap; +``` + +## Key Classes & Interfaces + +### IProvider (providers/IProvider.hpp) +- `start()`, `stop()` - lifecycle +- `type()` - returns provider type string +- `commands()` - returns configured commands +- `state()` - current DaggyProviderStates +- Emits signals on state/stream changes + +### IAggregator (aggregators/IAggregator.hpp) +- `isReady()` - check readiness before use +- Slots for all provider events +- Processes incoming streams based on type + +### Core API (Core.hpp) +- Main entry point for library users +- Manages providers and aggregators lifecycle +- Emits DaggyStates changes + +## Common Implementation Patterns + +### Add New Provider Type + +1. Create `CSomething.cpp/hpp` in `src/DaggyCore/providers/` +2. Inherit from `IProvider` +3. Implement `start()`, `stop()`, `type()` +4. Emit signals: `setState()`, `commandStream()`, `commandStateChanged()` +5. Create factory class `CSomethingFabric` inheriting `IFabric` +6. Register in provider factory logic + +### Add New Aggregator Type + +1. Create `CSomething.cpp/hpp` in `src/DaggyCore/aggregators/` +2. Inherit from `IAggregator` +3. Implement all virtual slots +4. Connect to provider signals +5. Process streams in `onCommandStream()` + +### Configuration Example + +```yaml +sources: + local: + type: local + commands: + - name: cmd1 + command: ping ya.ru + extension: log + remote: + type: ssh + host: 192.168.1.100 + connection: + login: user + key: ~/.ssh/id_rsa + port: 22 + commands: + - name: cmd2 + command: df -h + extension: log +``` + +## Build & Test + +### Build +```bash +# With Conan +conan install . --build=missing +cmake --preset conan-release +cmake --build --preset conan-release + +# Without Conan (development) +mkdir build && cd build +cmake .. +cmake --build . -j4 +``` + +### Run +```bash +# Console app +./build/bin/daggy config.yaml -o output_dir + +# From stdin +cat config.yaml | ./build/bin/daggy -f yaml -i + +# With output folder +./build/bin/daggy config.yaml -o ./results +``` + +### Test +```bash +ctest --preset conan-release +# or +cmake --build build --target test +``` + +## Error Handling + +- All functions return `std::error_code` +- Errors also emitted as signals from providers +- Aggregators receive errors via `onCommandError()` and `onDataProviderError()` +- Check `Errors.h` for error categories and codes + +## File I/O + +### Input +- Configuration: YAML/JSON parsed via `sources::convertors::yaml()` or `json()` +- Parsed into `Sources` map (type QMap) + +### Output (File Aggregator) +- Creates output directory: `{date}-{config_basename}/` +- Files: `{hostname}_{command_name}.{extension}` +- Streamed writes (minimal memory) + +## Qt Integration + +- Uses Qt6 `QObject` for signal/slot mechanism +- `QMap`, `QString`, `QByteArray` for data types +- No GUI - purely C++ core library +- Console app uses minimal Qt (mainly for event loop) + +## Key Signals/Slots + +**Provider → Aggregator:** +``` +commandStream(QString id, Stream data) +commandStateChanged(QString id, DaggyCommandStates state, int exit_code) +commandError(QString id, std::error_code error_code) +stateChanged(DaggyProviderStates state) +error(std::error_code error_code) +``` + +**Core → Aggregator:** +``` +onDaggyStateChanged(DaggyStates state) +``` + +## Environment & Versioning + +- Version in: `src/cmake/version.cmake` +- C++ standard: C++23 +- Qt version: 6.0+ +- CMake: 3.20+ + +## Dependencies + +Core (required): +- Qt (Core module minimum) +- libssh2 (for SSH providers) +- libyaml (for YAML parsing) +- nlohmann/json (for JSON parsing) + +Optional: +- OpenSSL (for SSH/TLS) +- zlib (compression) + +## Common Issues & Solutions + +| Issue | Cause | Solution | +|-------|-------|----------| +| SSH connection timeout | Firewall/network | Check host, port, credentials | +| Command not found | Remote path differs | Use full path in command | +| Permission denied | SSH key/sudoers | Check SSH key permissions (600) | +| Output file empty | Command failed silently | Check command state signal | +| Reconnect loop | Server unreachable | Set reconnect=false | + +## Performance Tips + +- Use `local` type for same-machine commands (no overhead) +- SSH connection pooling handled per-provider +- Streams are chunked (check `CFile` buffer size) +- Qt event loop scales with command count +- Use `extension: log` for text, appropriate types for binary + +## Documentation Files + +- `docs/README.md` - Overview and getting started +- `docs/how-it-works.md` - Workflow and CLI usage +- `docs/daggy-core/c++23-interface.md` - Library API +- `docs/data-aggregation-config.md` - Configuration format +- `docs/daggy_*.plantuml` - Architecture diagrams (PlantUML) diff --git a/CI/conan/profiles/common b/CI/conan/profiles/common new file mode 100644 index 00000000..981ae045 --- /dev/null +++ b/CI/conan/profiles/common @@ -0,0 +1,2 @@ +[tool_requires] +cmake/4.3.2 \ No newline at end of file diff --git a/CI/conan/profiles/linux-gcc12_x64 b/CI/conan/profiles/linux-gcc15_x64 similarity index 60% rename from CI/conan/profiles/linux-gcc12_x64 rename to CI/conan/profiles/linux-gcc15_x64 index 11a8edaf..52322825 100644 --- a/CI/conan/profiles/linux-gcc12_x64 +++ b/CI/conan/profiles/linux-gcc15_x64 @@ -1,8 +1,10 @@ +include(common) + [settings] os=Linux arch=x86_64 compiler=gcc -compiler.version=12 -compiler.cppstd=17 +compiler.version=15 compiler.libcxx=libstdc++ +compiler.cppstd=gnu23 build_type=Release diff --git a/CI/conan/profiles/macos-clang15_x64 b/CI/conan/profiles/macos-clang15_x64 deleted file mode 100644 index fbace614..00000000 --- a/CI/conan/profiles/macos-clang15_x64 +++ /dev/null @@ -1,8 +0,0 @@ -[settings] -arch=x86_64 -build_type=Release -compiler=apple-clang -compiler.cppstd=17 -compiler.libcxx=libc++ -compiler.version=15 -os=Macos diff --git a/CI/conan/profiles/macos-clang15_arm b/CI/conan/profiles/macos-clang17_arm similarity index 62% rename from CI/conan/profiles/macos-clang15_arm rename to CI/conan/profiles/macos-clang17_arm index ff3eccbc..45dbbe5b 100644 --- a/CI/conan/profiles/macos-clang15_arm +++ b/CI/conan/profiles/macos-clang17_arm @@ -1,8 +1,10 @@ +include(common) + [settings] arch=armv8 -build_type=Release compiler=apple-clang -compiler.cppstd=17 compiler.libcxx=libc++ -compiler.version=15 +compiler.version=17 +compiler.cppstd=23 +build_type=Release os=Macos diff --git a/CI/conan/profiles/win-msvc194_x64_static b/CI/conan/profiles/win-msvc194_x64_static deleted file mode 100644 index 209057c1..00000000 --- a/CI/conan/profiles/win-msvc194_x64_static +++ /dev/null @@ -1,10 +0,0 @@ -include(win-msvc194_x64) - -[settings] -compiler.runtime=static - -[tool_requires] -!cmake/*: cmake/[>=3 <4] - -[options] -*:shared=False \ No newline at end of file diff --git a/CI/conan/profiles/win-msvc194_x64 b/CI/conan/profiles/win-msvc195_x64 similarity index 61% rename from CI/conan/profiles/win-msvc194_x64 rename to CI/conan/profiles/win-msvc195_x64 index d7945ac1..64023428 100644 --- a/CI/conan/profiles/win-msvc194_x64 +++ b/CI/conan/profiles/win-msvc195_x64 @@ -1,8 +1,10 @@ +include(common) + [settings] arch=x86_64 -build_type=Release compiler=msvc -compiler.cppstd=17 compiler.runtime=dynamic -compiler.version=194 +compiler.version=195 +compiler.cppstd=23 +build_type=Release os=Windows diff --git a/CI/conan/profiles/win-msvc195_x64_static b/CI/conan/profiles/win-msvc195_x64_static new file mode 100644 index 00000000..a766c1f7 --- /dev/null +++ b/CI/conan/profiles/win-msvc195_x64_static @@ -0,0 +1,7 @@ +include(win-msvc195_x64) + +[settings] +compiler.runtime=static + +[options] +*:shared=False \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 23f05ffa..c0ca040b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,14 +1,6 @@ -# This cmake only for developing daggy -# Use ${PROJECT_SOURCE_DIR}/cmake/conan_provider.cmake for working from IDE (see https://github.com/conan-io/cmake-conan for details) - -cmake_minimum_required(VERSION 3.24.0) +cmake_minimum_required(VERSION 4.0.0) message(WARNING "This CMakeLists.txt only for developing") - -if (NOT CMAKE_PROJECT_TOP_LEVEL_INCLUDES) - message(FATAL "Set CMAKE_PROJECT_TOP_LEVEL_INCLUDES with conan_provider") -endif() - project(daggy_dev) set(PORTABLE_BUILD ON) diff --git a/conanfile.py b/conanfile.py index 4575c534..d63c9c13 100644 --- a/conanfile.py +++ b/conanfile.py @@ -47,10 +47,12 @@ class DaggyConan(ConanFile): "fPIC": True } generators = "CMakeDeps" - exports = ["git_version.py", "src/*"] + exports = ["src/*"] + _gv = GitVersion() + def set_version(self): - self.version = GitVersion().version + self.version = self._gv.full def validate(self): check_min_cppstd(self, "17") @@ -61,27 +63,43 @@ def config_options(self): if self.options.apponly: self.options.shared = False - - self.options["qt/*"].shared = self.options.shared - self.options["libssh2/*"].shared = self.options.shared - self.options["yaml-cpp/*"].shared = self.options.shared def configure(self): if self.options.shared: self.options.rm_safe("fPIC") if self.options.apponly: - self.options.rm_safe("shared") + self.options.rm_safe("shared") + + if not self.options.apponly: + self.options["qt/*"].shared = self.options.shared + self.options["libssh2/*"].shared = self.options.shared + self.options["yaml-cpp/*"].shared = self.options.shared + + self.options["qt/*"].gui = False + self.options["qt/*"].widgets = False + self.options["qt/*"].with_libpng = False + self.options["qt/*"].with_libjpeg = False + self.options["qt/*"].with_freetype = False + self.options["qt/*"].with_harfbuzz = False + self.options["qt/*"].with_fontconfig = False + self.options["qt/*"].with_icu = False + + # Completely disable SQL drivers + self.options["qt/*"].with_sqlite3 = False + self.options["qt/*"].with_mysql = False + self.options["qt/*"].with_pq = False + self.options["qt/*"].with_odbc = False def build_requirements(self): - self.tool_requires("cmake/3.30.1") - self.tool_requires("gtest/1.15.0") + self.tool_requires("cmake/4.3.2") + self.tool_requires("gtest/1.17.0") def requirements(self): - self.requires("qt/6.7.3") + self.requires("qt/6.11.1") self.requires("kainjow-mustache/4.1") - self.requires("yaml-cpp/0.8.0") + self.requires("yaml-cpp/0.9.0") self.requires("libssh2/1.11.1") @@ -118,8 +136,8 @@ def generate(self): tc.cache_variables["PORTABLE_BUILD"] = not self.options.apponly tc.cache_variables["BUILD_TESTING"] = True tc.cache_variables["CONAN_BUILD"] = True - tc.cache_variables["VERSION"] = GitVersion().version - tc.cache_variables["VERSION_EXTENDED"] = GitVersion().extended + tc.cache_variables["VERSION"] = self._gv.version + tc.cache_variables["VERSION_EXTENDED"] = self._gv.extended tc.generate() diff --git a/requirements.txt b/requirements.txt index d4b0d507..3ccd8a72 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,2 @@ -conan>=2.0 \ No newline at end of file +conan>=2.29 +git-version-utils>=0.2.1.0 \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 878e1a1f..85a8bc94 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.24) +cmake_minimum_required(VERSION 4.0) set(CMAKE_PROJECT_MODULES ${CMAKE_CURRENT_SOURCE_DIR}/cmake) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_PROJECT_MODULES}) diff --git a/src/Daggy/CMakeLists.txt b/src/Daggy/CMakeLists.txt index efa9d086..8e9378d6 100644 --- a/src/Daggy/CMakeLists.txt +++ b/src/Daggy/CMakeLists.txt @@ -2,9 +2,11 @@ set(TARGET daggy) option(PCAPNG_SUPPORT "pcap to pcapng convertor" OFF) -find_package(Qt6 COMPONENTS Core Network CONFIG REQUIRED) +find_package(Qt6 REQUIRED COMPONENTS Core Network CONFIG REQUIRED) -add_executable(${TARGET}) +qt_standard_project_setup() + +qt_add_executable(${TARGET}) include(rpath_bin) set_target_properties(${TARGET} PROPERTIES AUTOMOC ON) include(versionmeta) @@ -37,5 +39,10 @@ add_definitions(-DAPP_ICON="${CMAKE_CURRENT_LIST_DIR}/../icons/daggy.ico") target_link_libraries(${TARGET} PRIVATE ${LINK_LIBRARIES}) target_precompile_headers(${TARGET} PRIVATE Precompiled.hpp) +if(WIN32) + target_sources(${TARGET} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../cmake/app.manifest) +endif() + install(TARGETS ${TARGET} RUNTIME) + include(package_deps) diff --git a/src/DaggyCore/CMakeLists.txt b/src/DaggyCore/CMakeLists.txt index 72eb3d2b..a39ef27b 100644 --- a/src/DaggyCore/CMakeLists.txt +++ b/src/DaggyCore/CMakeLists.txt @@ -126,6 +126,6 @@ if (NOT APPONLY_BUILD) install(TARGETS ${TARGET} PUBLIC_HEADER FILE_SET HEADERS LIBRARY) + include(package_deps) endif() - include(package_deps) endif() diff --git a/src/DaggyCore/tests/local/CMakeLists.txt b/src/DaggyCore/tests/local/CMakeLists.txt index 6a744aca..1ee6b9e3 100644 --- a/src/DaggyCore/tests/local/CMakeLists.txt +++ b/src/DaggyCore/tests/local/CMakeLists.txt @@ -12,7 +12,7 @@ set(SOURCES main.cpp ) -qt6_add_executable(${TARGET} ${SOURCES}) +add_executable(${TARGET} ${SOURCES}) include(rpath_bin) add_test(NAME ${TARGET} COMMAND ${TARGET} WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_BINDIR}) set_tests_properties(${TARGET} PROPERTIES ENVIRONMENT "PATH=$ENV{PATH}:${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_BINDIR}") diff --git a/src/DaggyCore/tests/pingpong/CMakeLists.txt b/src/DaggyCore/tests/pingpong/CMakeLists.txt index e476276c..98973a7a 100644 --- a/src/DaggyCore/tests/pingpong/CMakeLists.txt +++ b/src/DaggyCore/tests/pingpong/CMakeLists.txt @@ -11,7 +11,7 @@ set(SOURCES main.cpp ) -qt6_add_executable(${TARGET} ${SOURCES}) +add_executable(${TARGET} ${SOURCES}) include(rpath_bin) target_link_libraries(${TARGET} PRIVATE DaggyCore) target_precompile_headers(${TARGET} PRIVATE Precompiled.h) diff --git a/src/cmake/app.exe.manifest.in b/src/cmake/app.exe.manifest.in new file mode 100644 index 00000000..752ae8f7 --- /dev/null +++ b/src/cmake/app.exe.manifest.in @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/cmake/app.manifest b/src/cmake/app.manifest new file mode 100644 index 00000000..4f06f205 --- /dev/null +++ b/src/cmake/app.manifest @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/cmake/compiler.cmake b/src/cmake/compiler.cmake index 7e3f0abd..c1f49209 100644 --- a/src/cmake/compiler.cmake +++ b/src/cmake/compiler.cmake @@ -1,7 +1,7 @@ include(GNUInstallDirs) if (NOT CMAKE_CXX_STANDARD) - set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD 23) endif() set(CMAKE_CXX_STANDARD_REQUIRED ON)