diff --git a/.github/workflows/test-bash-framework.yml b/.github/workflows/test-bash-framework.yml index bd70815..6745a10 100644 --- a/.github/workflows/test-bash-framework.yml +++ b/.github/workflows/test-bash-framework.yml @@ -3,9 +3,9 @@ name: Test Bash Framework on: push: branches: [main] - paths-ignore: [ 'docs/**', '**/*.md' ] + paths: [ 'scripts-demo/**', 'tests/shell/bash/**' ] pull_request: - paths-ignore: [ 'docs/**', '**/*.md' ] + paths: [ 'scripts-demo/**', 'tests/shell/bash/**' ] workflow_dispatch: jobs: @@ -21,14 +21,8 @@ jobs: - os: ubuntu-latest shell-name: bash shell-cmd: bash - - os: ubuntu-latest - shell-name: zsh - shell-cmd: zsh - # macOS - bash and zsh - - os: macos-latest - shell-name: bash - shell-cmd: bash + # macOS - zsh - os: macos-latest shell-name: zsh shell-cmd: zsh @@ -42,13 +36,6 @@ jobs: - name: Checkout code uses: actions/checkout@v4 - - name: Install zsh (Ubuntu only) - if: matrix.os == 'ubuntu-latest' && matrix.shell-name == 'zsh' - shell: bash - run: | - sudo apt-get update - sudo apt-get install -y zsh - - name: Run demo tests (excluding intentional failures) shell: bash working-directory: tests/shell/bash diff --git a/.github/workflows/test-bash-scripts.yml b/.github/workflows/test-bash-scripts.yml index 7cd42be..c5c3b4f 100644 --- a/.github/workflows/test-bash-scripts.yml +++ b/.github/workflows/test-bash-scripts.yml @@ -3,9 +3,9 @@ name: Test Bash Scripts on: push: branches: [main] - paths-ignore: [ 'docs/**', '**/*.md' ] + paths: [ 'scripts/**', 'tests/shell/bash/**' ] pull_request: - paths-ignore: [ 'docs/**', '**/*.md' ] + paths: [ 'scripts/**', 'tests/shell/bash/**' ] workflow_dispatch: jobs: @@ -34,7 +34,8 @@ jobs: working-directory: tests/shell/bash run: | ./test-runner.sh \ - --fail-fast true + --fail-fast true \ + --verbose - name: Test results summary if: always() diff --git a/.github/workflows/test-framework-compose-filename-manual.yml b/.github/workflows/test-framework-compose-filename-manual.yml new file mode 100644 index 0000000..a4c0ed1 --- /dev/null +++ b/.github/workflows/test-framework-compose-filename-manual.yml @@ -0,0 +1,57 @@ +name: "Call action manually: framework compose filename" + +on: + workflow_dispatch: + inputs: + runner: + description: "Runner to use (e.g. ubuntu-latest, ubuntu-22.04, windows-latest, etc)" + type: string + required: false + default: ubuntu-latest + version: + description: 'OpenDAQ version (if not set, resolves to latest from openDAQ/openDAQ)' + required: false + platform: + description: 'Target platform (if not set, auto-detected)' + required: false + packaging: + description: 'Packaging format for cpack (if not set, uses runner OS name)' + required: false + +jobs: + test-compose-filename-manually: + runs-on: ${{ inputs.runner }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Compose openDAQ framework filename + id: call-framework-compose-filename + uses: ./framework-compose-filename + with: + version: ${{ inputs.version }} + platform: ${{ inputs.platform }} + packaging: ${{ inputs.packaging }} + + - name: Display Results + shell: bash + run: | + echo "╔══════════════════════════════════════════════════════════════════════════════╗" + echo "║ Package Filename Composition Results ║" + echo "╠══════════════════════════════════════════════════════════════════════════════╣" + echo "║ 🏷️ Version (full): ${{ steps.call-framework-compose-filename.outputs.version }}" + echo "║ ├─ Major: ${{ steps.call-framework-compose-filename.outputs.version-major }}" + echo "║ ├─ Minor: ${{ steps.call-framework-compose-filename.outputs.version-minor }}" + echo "║ ├─ Patch: ${{ steps.call-framework-compose-filename.outputs.version-patch }}" + echo "║ ├─ Suffix: ${{ steps.call-framework-compose-filename.outputs.version-suffix || '*** NOT SET ***' }}" + echo "║ └─ Hash: ${{ steps.call-framework-compose-filename.outputs.version-hash || '*** NOT SET ***' }}" + echo "╠══════════════════════════════════════════════════════════════════════════════╣" + echo "║ 🖥️ Platform (full): ${{ steps.call-framework-compose-filename.outputs.platform }}" + echo "║ ├─ OS Name: ${{ steps.call-framework-compose-filename.outputs.platform-os-name }}" + echo "║ ├─ OS Version: ${{ steps.call-framework-compose-filename.outputs.platform-os-version }}" + echo "║ └─ Architecture: ${{ steps.call-framework-compose-filename.outputs.platform-os-arch }}" + echo "╠══════════════════════════════════════════════════════════════════════════════╣" + echo "║ 📦 Packaging: ${{ steps.call-framework-compose-filename.outputs.packaging }}" + echo "╠══════════════════════════════════════════════════════════════════════════════╣" + echo "║ 📄 Filename: ${{ steps.call-framework-compose-filename.outputs.filename }}" + echo "╚══════════════════════════════════════════════════════════════════════════════╝" diff --git a/.github/workflows/test-framework-download-release-manual.yml b/.github/workflows/test-framework-download-release-manual.yml new file mode 100644 index 0000000..af74537 --- /dev/null +++ b/.github/workflows/test-framework-download-release-manual.yml @@ -0,0 +1,82 @@ +name: Test Download Release Asset + +on: + workflow_dispatch: + inputs: + runner-os: + description: 'Runner OS to use' + required: true + type: string + default: 'ubuntu-latest' + + version: + description: 'Release version (e.g., v3.20.4) or "latest"' + required: false + type: string + default: 'latest' + + platform: + description: 'Target platform (e.g., ubuntu22.04-x86_64, win64) or leave empty for auto-detect' + required: false + type: string + default: '' + + packaging: + description: 'Package format override (e.g., deb, exe, tar.gz, zip) or leave empty for auto-detect' + required: false + type: string + default: '' + + asset-pattern: + description: 'Custom glob pattern to filter assets (overrides auto-detection)' + required: false + type: string + default: '' + + output-dir: + description: 'Output directory for downloaded assets (leave empty for runner.temp)' + required: false + type: string + default: '' + + verbose: + description: 'Enable verbose output' + required: false + type: boolean + default: true + +jobs: + download-release: + name: Download on ${{ inputs.runner-os }} + runs-on: ${{ inputs.runner-os }} + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Download OpenDAQ release asset + id: download + uses: ./framework-download-release + with: + version: ${{ inputs.version }} + platform: ${{ inputs.platform }} + packaging: ${{ inputs.packaging }} + asset-pattern: ${{ inputs.asset-pattern }} + output-dir: ${{ inputs.output-dir }} + verbose: ${{ inputs.verbose }} + + - name: Display results + shell: bash + run: | + echo "=== Download Results ===" + echo "" + echo "📦 Asset: ${{ steps.download.outputs.asset-filename }}" + echo "📍 Path: ${{ steps.download.outputs.asset }}" + echo "📂 Directory: ${{ steps.download.outputs.asset-dir }}" + echo "" + echo "🏷️ Version: ${{ steps.download.outputs.version }}" + echo "🖥️ Platform: ${{ steps.download.outputs.platform }}" + echo "📦 Packaging: ${{ steps.download.outputs.packaging }}" + echo "" + echo "📊 Size: ${{ steps.download.outputs.asset-filesize }} bytes" + echo "🔐 SHA256: ${{ steps.download.outputs.asset-checksum }}" diff --git a/docs/scripts/shell/bash/CONVENTIONS.md b/docs/scripts/shell/bash/CONVENTIONS.md index a55e582..68fff3c 100644 --- a/docs/scripts/shell/bash/CONVENTIONS.md +++ b/docs/scripts/shell/bash/CONVENTIONS.md @@ -184,7 +184,70 @@ Each script module uses consistent prefix: | `version-format.sh` | `daq_version_` | `daq_version_compose` | | `platform-format.sh` | `daq_platform_` | `daq_platform_detect` | | `packaging-format.sh` | `daq_packaging_` | `daq_packaging_detect_from_cpack` | -| `api-github-gh.sh` | `daq_gh_` | `daq_gh_release_list` | +| `api-github-gh.sh` | `daq_api_gh_` | `daq_api_gh_version_latest` | + +## Special Module Naming Cases + +### API Wrapper Modules + +Modules that wrap external APIs or services may use extended prefixes for clarity. + +**Pattern**: `api--.sh` with prefix `daq_api__` + +**When to use**: +- Module wraps external API or CLI tool +- Need to distinguish from domain modules +- Additional context improves API clarity + +**Examples**: + +```bash +# GitHub API wrapper +api-github-gh.sh # Prefix: daq_api_gh_ + daq_api_gh_version_latest() + daq_api_gh_version_list() + +# Hypothetical examples +api-gitlab-cli.sh # Prefix: daq_api_gitlab_ +api-docker-sdk.sh # Prefix: daq_api_docker_ +``` + +**Comparison with domain modules**: + +| Type | File Pattern | Prefix Pattern | Use Case | +|------|--------------|----------------|----------| +| Domain module | `-format.sh` | `daq__` | Data format parsing/composition | +| API wrapper | `api--.sh` | `daq_api__` | External service integration | + +**Example distinction**: + +```bash +# Domain module - formats and parsing +version-format.sh → daq_version_compose() # Create version string +version-format.sh → daq_version_parse() # Parse version string + +# API wrapper - external service calls +api-github-gh.sh → daq_api_gh_version_latest() # Fetch from GitHub API +api-github-gh.sh → daq_api_gh_version_list() # List versions from GitHub +``` + +**Rationale**: +- Prefix `daq_api_gh_` clearly indicates GitHub API wrapper +- Distinguishes from potential `daq_github_` (format-related functions) +- Prevents confusion between API calls and format operations +- Allows both modules to coexist: `github-format.sh` (formats) and `api-github-gh.sh` (API) + +**Namespace protection**: + +```bash +# Safe to source together +source github-format.sh # Hypothetical: daq_github_parse() +source api-github-gh.sh # Actual: daq_api_gh_version_latest() + +# No naming conflicts +daq_github_parse "v1.0.0" # Format parsing +daq_api_gh_version_latest # API call +``` ## Namespace Protection @@ -228,10 +291,10 @@ daq_package_compose --version "$version" --platform "$platform" Each module should have one clear purpose: -- ✅ `version-format.sh` - handles version strings -- ✅ `platform-format.sh` - handles platform identifiers -- ✅ `packaging-format.sh` - handles package extensions -- ⌠`utils.sh` - too generic, unclear purpose +- `version-format.sh` - handles version strings +- `platform-format.sh` - handles platform identifiers +- `packaging-format.sh` - handles package extensions +- `utils.sh` - too generic, unclear purpose ### Focused API @@ -299,8 +362,8 @@ daq_packaging_validate_generator() # New: validate generator name Use this checklist when creating or reviewing scripts: -- [ ] All public functions start with `daq__` -- [ ] All private functions start with `__daq__` +- [ ] All public functions start with `daq__` or `daq_api__` (for API wrappers) +- [ ] All private functions start with `__daq__` or `__daq_api__` (for API wrappers) - [ ] All public constants start with `OPENDAQ__` - [ ] All private variables start with `__DAQ__` - [ ] Match variables start with `__MATCH_` @@ -323,6 +386,7 @@ Following these conventions provides: ## See Also -- [packaginh-format.sh](packaging-format/README.md) - Example implementation -- [platform-format.sh](platform-format/README.md) - Example implementation -- [version-format.sh](version-format/README.md) - Example implementation +- [version-format.sh](version-format/README.md) - Domain module example +- [platform-format.sh](platform-format/README.md) - Domain module example +- [packaging-format.sh](packaging-format/README.md) - Domain module example +- [api-github-gh.sh](api-github-gh/README.md) - API wrapper module example diff --git a/docs/scripts/shell/bash/api-github-gh/README.md b/docs/scripts/shell/bash/api-github-gh/README.md new file mode 100644 index 0000000..8f2156c --- /dev/null +++ b/docs/scripts/shell/bash/api-github-gh/README.md @@ -0,0 +1,124 @@ +# GitHub API Wrapper (api-github-gh.sh) + +Bash/zsh wrapper for GitHub CLI (`gh`) providing convenient functions for working with release versions. + +## Overview + +`api-github-gh.sh` provides a simplified interface for GitHub release version operations: +- 🔍 Discovering and verifying release versions +- 📋 Listing available versions + +The script wraps GitHub CLI (`gh`) with retry logic, error handling, and consistent API patterns. + +## Features + +- ✅ **Bash 3.2+ and zsh compatible** +- ✅ **Version resolution** (latest, specific tags) +- ✅ **Automatic authentication** via GitHub CLI +- ✅ **Rate limit handling** +- ✅ **Verbose and debug modes** + +## Prerequisites + +Required tools: +- `gh` - GitHub CLI ([installation](https://cli.github.com)) +- `jq` - JSON processor ([installation](https://jqlang.github.io/jq/)) + +The script automatically checks for dependencies and provides installation instructions. + +## Usage + +### Basic Examples + +```bash +# Get latest version +./api-github-gh.sh openDAQ/openDAQ + +# Verify specific version exists +./api-github-gh.sh openDAQ/openDAQ --version v3.0.0 + +# List all versions +./api-github-gh.sh openDAQ/openDAQ --list-versions + +# List last 5 versions +./api-github-gh.sh openDAQ/openDAQ --list-versions --limit 5 +``` + +### CLI Options + +| Option | Argument | Description | +|--------|----------|-------------| +| `--version` | VERSION | Check specific version (default: latest) | +| `--list-versions` | - | List all available versions | +| `--limit` | N | Limit number of versions (default: 30, use 'all' for all) | +| `--verbose` | - | Enable verbose output | +| `--help` | - | Show help message | + +## Sourcing as Library + +```bash +#!/usr/bin/env bash +source ./api-github-gh.sh + +# Use API functions +latest=$(daq_api_gh_version_latest "openDAQ/openDAQ") +echo "Latest version: $latest" + +# List versions +daq_api_gh_version_list "openDAQ/openDAQ" 10 + +# Verify version +if daq_api_gh_version_verify "openDAQ/openDAQ" "v3.0.0"; then + echo "Version exists" +fi +``` + +## Public API Functions + +### Version Functions + +- `daq_api_gh_version_latest(repo)` - Get latest version tag +- `daq_api_gh_version_verify(repo, version)` - Verify version exists +- `daq_api_gh_version_resolve(repo, version)` - Resolve version tag +- `daq_api_gh_version_list(repo, [limit])` - List version tags + +## Environment Variables + +- `OPENDAQ_GH_API_DEBUG=1` - Enable debug output +- `OPENDAQ_GH_API_GITHUB_REPO` - Set default GitHub repo +- `OPENDAQ_GH_API_CACHE_DIR=/tmp` - Temp dir for cache responses + +## Error Handling + +The script uses `set -u` for undefined variable detection and returns non-zero exit codes on errors. + +```bash +if ./api-github-gh.sh openDAQ/openDAQ --version v999.0.0; then + echo "Version found" +else + echo "Version not found" >&2 + exit 1 +fi +``` + +## Shell Compatibility + +- ✅ bash 3.2+ (macOS default) +- ✅ bash 4.0+ +- ✅ bash 5.0+ +- ✅ zsh 5.0+ + +Tested on: +- macOS (bash 3.2, zsh 5.9) +- Ubuntu Linux (bash 5.1+, zsh 5.8+) + +## Limitations + +- Requires authenticated `gh` CLI +- Rate limited by GitHub API (5000 requests/hour for authenticated users) + +## See Also + +- [GitHub CLI Documentation](https://cli.github.com/manual/) +- [GitHub REST API](https://docs.github.com/en/rest) +- [Naming Conventions](../CONVENTIONS.md) diff --git a/framework-compose-filename/README.md b/framework-compose-filename/README.md new file mode 100644 index 0000000..dbb87fd --- /dev/null +++ b/framework-compose-filename/README.md @@ -0,0 +1,188 @@ +# Compose OpenDAQ Package Filename + +Composes OpenDAQ installation package filename from version, platform, and packaging format. + +## Usage + +```yaml +- uses: openDAQ/openDAQ/.github/actions/framework-compose-filename@main + with: + # OpenDAQ version (if not set, resolves to latest from openDAQ/openDAQ) + # Optional + version: '' + + # Target platform (if not set, auto-detected) + # Optional + platform: '' + + # Packaging format for cpack (if not set, uses runner OS name) + # Optional + packaging: '' +``` + +## Outputs + +```yaml +outputs: + filename: # Composed package filename + version: # Resolved version (full) + version-major: # Version major component + version-minor: # Version minor component + version-patch: # Version patch component + version-suffix: # Version suffix (rc or empty) + version-hash: # Version hash (or empty) + platform: # Resolved platform (full) + platform-os-name: # Platform OS name + platform-os-version: # Platform OS version (empty for Windows) + platform-os-arch: # Platform OS architecture + packaging: # Resolved packaging format +``` + +## Format Specifications + +### Version Format + +Supports semantic versioning with optional prefix, suffix, and git hash: + +| Format | Example | Type | Use Case | +|--------|---------|------|----------| +| `X.YY.Z` | `1.2.3` | Release | Production releases (no prefix) | +| `vX.YY.Z` | `v1.2.3` | Release | Production releases (with prefix) | +| `X.YY.Z-rc` | `1.2.3-rc` | RC | Release candidates | +| `vX.YY.Z-rc` | `v1.2.3-rc` | RC | Release candidates (with prefix) | +| `X.YY.Z-HASH` | `1.2.3-a1b2c3d` | Dev | Development builds | +| `vX.YY.Z-HASH` | `v1.2.3-a1b2c3d` | Dev | Development builds (with prefix) | + +**Components**: +- **Major** (`X`): 0-999+ +- **Minor** (`YY`): 0-999 +- **Patch** (`Z`): 0-999+ +- **Suffix**: `rc` (release candidate) or git hash (7-40 lowercase hex chars) +- **Prefix**: `v` (optional) + +### Platform Format + +Platform identifiers follow these patterns: + +**Linux/macOS**: `{os}{version}-{arch}` +- **OS**: `ubuntu`, `debian`, `macos` +- **Version**: `20.04`, `22.04`, `24.04` (Ubuntu/Debian) or `13`, `14`, `15` (macOS) +- **Architecture**: `arm64`, `x86_64` +- Examples: `ubuntu22.04-x86_64`, `macos14-arm64`, `debian12-arm64` + +**Windows**: `win{arch}` +- **Architecture**: `32`, `64` (bits, not x86/x64) +- Examples: `win64`, `win32` + +**Supported Platforms**: +- Ubuntu: 20.04, 22.04, 24.04 +- Debian: 8, 9, 10, 11, 12 +- macOS: 13-18, 26 (Ventura to Sequoia + future) +- Windows: 32-bit, 64-bit + +### Packaging Format + +File extensions for installation packages: + +| OS | Format | Extension | CPack Generator | +|----|--------|-----------|-----------------| +| **Windows** | Installer | `.exe` | `NSIS`, `NSIS64`, `WIX` | +| **Ubuntu/Debian** | Package | `.deb` | `DEB` | +| **macOS** | Archive | `.tar.gz` | `TGZ` | +| **macOS** | Archive | `.zip` | `ZIP` | + +The action automatically detects the appropriate packaging format based on the runner OS or CPack generator. + +## Examples + +### Default (auto-detect everything) + +```yaml +- uses: openDAQ/openDAQ/.github/actions/framework-compose-filename@main + id: compose + +# Result: opendaq-3.30.0-ubuntu22.04-x86_64.deb +``` + +### Specify version + +```yaml +- uses: openDAQ/openDAQ/.github/actions/framework-compose-filename@main + id: compose + with: + version: 'v3.29.0-rc' + +# Result: opendaq-3.29.0-rc-ubuntu22.04-x86_64.deb +``` + +### Specify version without prefix + +```yaml +- uses: openDAQ/openDAQ/.github/actions/framework-compose-filename@main + id: compose + with: + version: '3.29.0-rc' + +# Result: opendaq-3.29.0-rc-ubuntu22.04-x86_64.deb +``` + +### Specify platform + +```yaml +- uses: openDAQ/openDAQ/.github/actions/framework-compose-filename@main + id: compose + with: + platform: 'win64' + +# Result: opendaq-3.30.0-win64.exe +``` + +### Release candidate + +```yaml +- uses: openDAQ/openDAQ/.github/actions/framework-compose-filename@main + id: compose + with: + version: 'v3.29.0-rc' + platform: 'macos14-arm64' + +# Result: opendaq-3.29.0-rc-macos14-arm64.tar.gz +``` + +### Development build with hash + +```yaml +- uses: openDAQ/openDAQ/.github/actions/framework-compose-filename@main + id: compose + with: + version: 'v3.30.0-a1b2c3d' + platform: 'debian12-x86_64' + +# Result: opendaq-3.30.0-a1b2c3d-debian12-x86_64.deb +``` + +### Full specification + +```yaml +- uses: openDAQ/openDAQ/.github/actions/framework-compose-filename@main + id: compose + with: + version: 'v3.29.0-rc' + platform: 'ubuntu22.04-x86_64' + packaging: 'DEB' + +# Result: opendaq-3.29.0-rc-ubuntu22.04-x86_64.deb +``` + +### Using outputs + +```yaml +- uses: openDAQ/openDAQ/.github/actions/framework-compose-filename@main + id: compose + +- name: Download package + run: | + echo "Filename: ${{ steps.compose.outputs.filename }}" + echo "Version: ${{ steps.compose.outputs.version }}" + echo "Platform: ${{ steps.compose.outputs.platform }}" +``` diff --git a/framework-compose-filename/action.yml b/framework-compose-filename/action.yml new file mode 100644 index 0000000..cee07b3 --- /dev/null +++ b/framework-compose-filename/action.yml @@ -0,0 +1,297 @@ +name: 'Compose OpenDAQ Package Filename' +description: 'Compose OpenDAQ installation package filename from version, platform, and packaging format' + +inputs: + version: + description: 'OpenDAQ version (if not set, resolves to latest from openDAQ/openDAQ)' + required: false + platform: + description: 'Target platform (if not set, auto-detected)' + required: false + packaging: + description: 'Packaging format for cpack (if not set, uses runner OS name)' + required: false + +outputs: + filename: + description: 'Composed package filename' + value: ${{ steps.compose.outputs.filename }} + version: + description: 'Resolved version (full)' + value: ${{ steps.resolve-version.outputs.version }} + version-major: + description: 'Version major component' + value: ${{ steps.parse-components.outputs.version-major }} + version-minor: + description: 'Version minor component' + value: ${{ steps.parse-components.outputs.version-minor }} + version-patch: + description: 'Version patch component' + value: ${{ steps.parse-components.outputs.version-patch }} + version-suffix: + description: 'Version suffix (rc or empty)' + value: ${{ steps.parse-components.outputs.version-suffix }} + version-hash: + description: 'Version hash (or empty)' + value: ${{ steps.parse-components.outputs.version-hash }} + platform: + description: 'Resolved platform (full)' + value: ${{ steps.resolve-platform.outputs.platform }} + platform-os-name: + description: 'Platform OS name' + value: ${{ steps.parse-components.outputs.platform-os-name }} + platform-os-version: + description: 'Platform OS version (empty for Windows)' + value: ${{ steps.parse-components.outputs.platform-os-version }} + platform-os-arch: + description: 'Platform OS architecture' + value: ${{ steps.parse-components.outputs.platform-os-arch }} + packaging: + description: 'Resolved packaging format' + value: ${{ steps.resolve-packaging.outputs.packaging }} + +runs: + using: composite + steps: + - name: Init shell scripts + id: shell-scripts + shell: bash + env: + OPENDAQ_ACTIONS_SCRIPS_DIR: "${{ github.action_path }}/../scripts/shell/bash" + OPENDAQ_GH_API_CACHE_DIR: "${{ runner.temp }}" + run: | + dirs=( + "OPENDAQ_ACTIONS_SCRIPS_DIR" + "OPENDAQ_GH_API_CACHE_DIR" + ) + for dir_name in "${dirs[@]}"; do + # Get current value via indirect expansion + dir_value="${!dir_name}" + + # Normalize path for Windows (convert to Unix-style) + if command -v cygpath >/dev/null 2>&1; then + dir_value="$(cygpath "$dir_value")" + fi + + # Check if directory exists + if [ ! -d "$dir_value" ]; then + echo "❌ Error: directory not found at '$dir_value'" >&2 + exit 1 + fi + + # Resolve absolute path + dir_value="$(cd "$dir_value" >/dev/null 2>&1 && pwd)" + echo "🧭 Normalized dir $dir_name=$dir_value" + + # Assign back via indirect expansion + export "$dir_name"="$dir_value" + + done + + # Normalize path for Windows (convert to Unix-style) + if command -v cygpath >/dev/null 2>&1; then + OPENDAQ_ACTIONS_SCRIPS_DIR="$(cygpath "$OPENDAQ_ACTIONS_SCRIPS_DIR")" + fi + + # Check if directory exists + if [ ! -d "$OPENDAQ_ACTIONS_SCRIPS_DIR" ]; then + echo "❌ Error: scripts directory not found at '$OPENDAQ_ACTIONS_SCRIPS_DIR'" >&2 + exit 1 + fi + + # Resolve absolute path (remove ../ segments) + OPENDAQ_ACTIONS_SCRIPS_DIR="$(cd "$OPENDAQ_ACTIONS_SCRIPS_DIR" >/dev/null 2>&1 && pwd)" + echo "🧭 Normalized scripts dir: $OPENDAQ_ACTIONS_SCRIPS_DIR" + + # List of scripts that must be executable + scripts=( + "version-format.sh" + "platform-format.sh" + "packaging-format.sh" + "api-github-gh.sh" + ) + + # Iterate and apply +x with error handling + for rel_path in "${scripts[@]}"; do + script_path="${OPENDAQ_ACTIONS_SCRIPS_DIR}/${rel_path}" + + if [ ! -f "$script_path" ]; then + echo "❌ Error: missing script '$script_path'" >&2 + exit 1 + fi + + if ! chmod +x "$script_path"; then + echo "❌ Error: failed to chmod +x '$script_path'" >&2 + exit 1 + fi + + echo "✅ Marked as executable: $rel_path" + done + + # Export normalized directory path to step output + echo "dir=$OPENDAQ_ACTIONS_SCRIPS_DIR" >> "$GITHUB_OUTPUT" + echo "tmp=$OPENDAQ_GH_API_CACHE_DIR" >> "$GITHUB_OUTPUT" + + - name: Resolve Version + id: resolve-version + shell: bash + env: + GITHUB_TOKEN: ${{ github.token }} + OPENDAQ_GH_API_GITHUB_REPO: openDAQ/openDAQ + OPENDAQ_GH_API_CACHE_DIR: ${{ steps.shell-scripts.outputs.tmp }} + OPENDAQ_ACTIONS_SCRIPS_DIR: ${{ steps.shell-scripts.outputs.dir }} + OPENDAQ_FRAMEWORK_VERSION: ${{ inputs.version }} + run: | + if [ -z "$OPENDAQ_FRAMEWORK_VERSION" ] || [ "$OPENDAQ_FRAMEWORK_VERSION" = "latest" ]; then + echo "🔍 No version provided, resolving latest from openDAQ/openDAQ" + + # Get latest version using GitHub API CLI + version=$("${OPENDAQ_ACTIONS_SCRIPS_DIR}/api-github-gh.sh" --version latest) + + if [ -z "$version" ]; then + echo "❌ Error: Failed to resolve latest version" >&2 + exit 1 + fi + + echo "✅ Resolved latest version: $version" + else + echo "🧭 Validating provided version: $OPENDAQ_FRAMEWORK_VERSION" + + # Validate version using CLI + if "${OPENDAQ_ACTIONS_SCRIPS_DIR}/version-format.sh" validate "$OPENDAQ_FRAMEWORK_VERSION"; then + version="$OPENDAQ_FRAMEWORK_VERSION" + echo "✅ Version validated: $version" + else + echo "❌ Error: Invalid version format: $OPENDAQ_FRAMEWORK_VERSION" >&2 + exit 1 + fi + fi + + # Remove 'v' prefix if present for filename + version="${version#v}" + + echo "version=$version" >> $GITHUB_OUTPUT + + - name: Resolve Platform + id: resolve-platform + shell: bash + env: + OPENDAQ_ACTIONS_SCRIPS_DIR: ${{ steps.shell-scripts.outputs.dir }} + OPENDAQ_FRAMEWORK_PLATFORM: ${{ inputs.platform }} + run: | + if [ -n "$OPENDAQ_FRAMEWORK_PLATFORM" ]; then + echo "🧭 Validating provided platform: $OPENDAQ_FRAMEWORK_PLATFORM" + + # Validate platform using CLI + if "${OPENDAQ_ACTIONS_SCRIPS_DIR}/platform-format.sh" validate "$OPENDAQ_FRAMEWORK_PLATFORM"; then + platform="$OPENDAQ_FRAMEWORK_PLATFORM" + echo "✅ Platform validated: $platform" + else + echo "❌ Error: Invalid platform format: $OPENDAQ_FRAMEWORK_PLATFORM" >&2 + exit 1 + fi + else + echo "🔍 No platform provided, detecting from runner" + + # Detect platform using CLI + platform=$("${OPENDAQ_ACTIONS_SCRIPS_DIR}/platform-format.sh" detect) + + if [ -z "$platform" ]; then + echo "❌ Error: Failed to detect platform" >&2 + exit 1 + fi + + echo "✅ Platform detected: $platform" + fi + + echo "platform=$platform" >> $GITHUB_OUTPUT + + - name: Resolve Packaging Format + id: resolve-packaging + shell: bash + env: + OPENDAQ_ACTIONS_SCRIPS_DIR: ${{ steps.shell-scripts.outputs.dir }} + OPENDAQ_FRAMEWORK_PACKAGING: ${{ inputs.packaging }} + OPENDAQ_RUNNER_OS: ${{ steps.resolve-platform.outputs.platform }} + run: | + if [ -n "$OPENDAQ_FRAMEWORK_PACKAGING" ]; then + echo "🧭 Using provided packaging format (cpack generator): $OPENDAQ_FRAMEWORK_PACKAGING" + + # Detect packaging from CPack generator using CLI + packaging=$("${OPENDAQ_ACTIONS_SCRIPS_DIR}/packaging-format.sh" detect --cpack-generator "$OPENDAQ_FRAMEWORK_PACKAGING") + + if [ -z "$packaging" ]; then + echo "❌ Error: Failed to determine packaging format for CPack generator: $OPENDAQ_FRAMEWORK_PACKAGING" >&2 + exit 1 + fi + + echo "✅ Resolved packaging from cpack: $packaging" + else + echo "🔍 No packaging format provided, deriving from runner OS: $OPENDAQ_RUNNER_OS" + + # Detect packaging from OS name using CLI + packaging=$("${OPENDAQ_ACTIONS_SCRIPS_DIR}/packaging-format.sh" detect --os-name "$OPENDAQ_RUNNER_OS") + + if [ -z "$packaging" ]; then + echo "❌ Error: Failed to determine packaging format for OS: $OPENDAQ_RUNNER_OS" >&2 + exit 1 + fi + + echo "✅ Derived packaging from OS: $packaging" + fi + + echo "packaging=$packaging" >> $GITHUB_OUTPUT + + - name: Compose Filename + id: compose + shell: bash + env: + OPENDAQ_FRAMEWORK_VERSION: ${{ steps.resolve-version.outputs.version }} + OPENDAQ_FRAMEWORK_PLATFORM: ${{ steps.resolve-platform.outputs.platform }} + OPENDAQ_FRAMEWORK_PACKAGING: ${{ steps.resolve-packaging.outputs.packaging }} + run: | + filename="opendaq-${OPENDAQ_FRAMEWORK_VERSION}-${OPENDAQ_FRAMEWORK_PLATFORM}.${OPENDAQ_FRAMEWORK_PACKAGING}" + echo "✅ Composed filename: $filename" + echo "filename=$filename" >> $GITHUB_OUTPUT + + - name: Parse Components + id: parse-components + shell: bash + env: + OPENDAQ_ACTIONS_SCRIPS_DIR: ${{ steps.shell-scripts.outputs.dir }} + OPENDAQ_FRAMEWORK_VERSION: ${{ steps.resolve-version.outputs.version }} + OPENDAQ_FRAMEWORK_PLATFORM: ${{ steps.resolve-platform.outputs.platform }} + run: | + echo "🔍 Parsing version components from: $OPENDAQ_FRAMEWORK_VERSION" + + # Parse version components + version_major=$("${OPENDAQ_ACTIONS_SCRIPS_DIR}/version-format.sh" parse "$OPENDAQ_FRAMEWORK_VERSION" --major) + version_minor=$("${OPENDAQ_ACTIONS_SCRIPS_DIR}/version-format.sh" parse "$OPENDAQ_FRAMEWORK_VERSION" --minor) + version_patch=$("${OPENDAQ_ACTIONS_SCRIPS_DIR}/version-format.sh" parse "$OPENDAQ_FRAMEWORK_VERSION" --patch) + version_suffix=$("${OPENDAQ_ACTIONS_SCRIPS_DIR}/version-format.sh" parse "$OPENDAQ_FRAMEWORK_VERSION" --suffix) + version_hash=$("${OPENDAQ_ACTIONS_SCRIPS_DIR}/version-format.sh" parse "$OPENDAQ_FRAMEWORK_VERSION" --hash) + + echo "version-major=$version_major" >> $GITHUB_OUTPUT + echo "version-minor=$version_minor" >> $GITHUB_OUTPUT + echo "version-patch=$version_patch" >> $GITHUB_OUTPUT + echo "version-suffix=$version_suffix" >> $GITHUB_OUTPUT + echo "version-hash=$version_hash" >> $GITHUB_OUTPUT + + echo "✅ Version: $version_major.$version_minor.$version_patch${version_suffix:+-}${version_suffix}${version_hash:+-}${version_hash}" + + echo "🔍 Parsing platform components from: $OPENDAQ_FRAMEWORK_PLATFORM" + + # Parse platform components + platform_os_name=$("${OPENDAQ_ACTIONS_SCRIPS_DIR}/platform-format.sh" parse "$OPENDAQ_FRAMEWORK_PLATFORM" --os-name) + platform_os_version=$("${OPENDAQ_ACTIONS_SCRIPS_DIR}/platform-format.sh" parse "$OPENDAQ_FRAMEWORK_PLATFORM" --os-version) + platform_os_arch=$("${OPENDAQ_ACTIONS_SCRIPS_DIR}/platform-format.sh" parse "$OPENDAQ_FRAMEWORK_PLATFORM" --os-arch) + + echo "platform-os-name=$platform_os_name" >> $GITHUB_OUTPUT + echo "platform-os-version=$platform_os_version" >> $GITHUB_OUTPUT + echo "platform-os-arch=$platform_os_arch" >> $GITHUB_OUTPUT + + if [ -n "$platform_os_version" ]; then + echo "✅ Platform: $platform_os_name $platform_os_version ($platform_os_arch)" + else + echo "✅ Platform: $platform_os_name ($platform_os_arch)" + fi diff --git a/framework-install/README.md b/framework-install/README.md new file mode 100644 index 0000000..73882a3 --- /dev/null +++ b/framework-install/README.md @@ -0,0 +1,122 @@ +# Install openDAQ Framework Package + +This action installs the openDAQ framework package on Windows and Linux runners. + +## Usage + +```yaml +- uses: openDAQ/install-opendaq-action@v1 + with: + # Full path to the openDAQ framework package file + # Required + framework-filename: '' +``` + +## Inputs + +### `framework-filename` + +**Required** Full path to the openDAQ framework package file. + +- **Windows**: Path to `.exe` installer (e.g., `opendaq-v3.20.4-win64.exe`) +- **Linux**: Path to `.deb` package (e.g., `opendaq-v3.20.4-ubuntu20.04-x86_64.deb`) + +## Supported Platforms + +- ✅ Windows (via `.exe` installer) +- ✅ Linux (via `.deb` package) +- ❌ macOS (not yet supported) + +## Examples + +### Install from downloaded artifact + +```yaml +steps: + - name: Download openDAQ package + uses: actions/download-artifact@v4 + with: + name: opendaq-package + path: ${{ runner.temp }}/packages + + - name: Install openDAQ + uses: openDAQ/install-opendaq-action@v1 + with: + framework-filename: ${{ runner.temp }}/packages/opendaq-v3.20.4-win64.exe +``` + +### Install from release + +```yaml +steps: + - name: Download openDAQ release + run: | + curl -L -o ${{ runner.temp }}/opendaq.deb \ + https://github.com/openDAQ/openDAQ/releases/download/v3.20.4/opendaq-v3.20.4-ubuntu22.04-x86_64.deb + + - name: Install openDAQ + uses: openDAQ/install-opendaq-action@v1 + with: + framework-filename: ${{ runner.temp }}/opendaq.deb +``` + +### Matrix build with multiple platforms + +```yaml +jobs: + test: + strategy: + matrix: + include: + - os: ubuntu-22.04 + package: opendaq-v3.20.4-ubuntu22.04-x86_64.deb + - os: ubuntu-20.04 + package: opendaq-v3.20.4-ubuntu20.04-x86_64.deb + - os: windows-2022 + package: opendaq-v3.20.4-win64.exe + + runs-on: ${{ matrix.os }} + + steps: + - name: Download package + run: | + # Download logic here + # Save to ${{ runner.temp }}/${{ matrix.package }} + + - name: Install openDAQ + uses: openDAQ/install-opendaq-action@v1 + with: + framework-filename: ${{ runner.temp }}/${{ matrix.package }} + + - name: Verify installation + run: | + # Your verification commands +``` + +## How It Works + +### Windows +1. Runs the `.exe` installer with `/S` (silent) flag +2. Waits for installation to complete +3. Adds `C:\Program Files\openDAQ\bin` to `PATH` +4. Verifies exit code + +### Linux +1. Uses `dpkg -i` with `sudo` to install the `.deb` package +2. Package dependencies are automatically resolved + +## Post-Installation + +After successful installation: + +- **Windows**: The openDAQ binaries are added to `PATH` +- **Linux**: The openDAQ libraries are installed system-wide + +You can immediately use openDAQ commands and libraries in subsequent steps. + +## Error Handling + +The action will fail if: +- The package file doesn't exist +- Installation returns a non-zero exit code +- The runner OS is not supported (macOS) diff --git a/framework-install/action.yml b/framework-install/action.yml new file mode 100644 index 0000000..bcddf9d --- /dev/null +++ b/framework-install/action.yml @@ -0,0 +1,50 @@ +name: Install openDAQ framework package + +inputs: + framework-filename: + required: true + description: Full path to the openDAQ framework package file + +runs: + using: composite + + steps: + - name: Convert path for Windows + if: runner.os == 'Windows' + id: windows-path + shell: bash + run: | + # Convert Unix-style path to Windows-style + windowsPath=$(cygpath -w "${{ inputs.framework-filename }}") + echo "path=$windowsPath" >> $GITHUB_OUTPUT + + - name: Install openDAQ framework package (Windows) + if: runner.os == 'Windows' + shell: pwsh + run: | + $packagePath = "${{ steps.windows-path.outputs.path }}" + Write-Host "Installing from: $packagePath" + + $process = Start-Process -FilePath $packagePath -ArgumentList "/S" -Wait -NoNewWindow -PassThru + if ($process.ExitCode -eq 0) { + Write-Host "OpenDAQ installed successfully, updating PATH..." + $openDAQBin = "C:\Program Files\openDAQ\bin" + Add-Content -Path $env:GITHUB_ENV -Value "PATH=$openDAQBin`;$env:PATH" + Write-Host $env:PATH + } + else { + Write-Host "OpenDAQ installation failed with exit code $($process.ExitCode)" + exit $process.ExitCode + } + + - name: Install openDAQ framework package (Linux) + if: runner.os == 'Linux' + shell: bash + run: sudo dpkg -i "${{ inputs.framework-filename }}" + + - name: Unsupported runner OS + if: runner.os != 'Windows' && runner.os != 'Linux' + shell: bash + run: | + echo "Install openDAQ is not supported for ${{ runner.os }}" + exit 1 diff --git a/scripts/shell/bash/api-github-gh.sh b/scripts/shell/bash/api-github-gh.sh new file mode 100755 index 0000000..f1e6dc2 --- /dev/null +++ b/scripts/shell/bash/api-github-gh.sh @@ -0,0 +1,414 @@ +#!/usr/bin/env bash +# api-github-gh.sh - GitHub API wrapper for working with releases +# Supports listing and resolving release versions +# Compatible with bash 3.2+ and zsh + +# Exit on undefined variables +set -u + +__DAQ_GH_API_SHELL="unknown" +if [[ -n "${BASH_VERSION:-}" ]]; then + __DAQ_GH_API_SHELL="bash" + __DAQ_GH_API_SHELL_VERSION="${BASH_VERSION}" + # Check if sourced (BASH_SOURCE available since Bash 3.0) + if [[ "${BASH_SOURCE[0]}" != "${0}" ]]; then + __DAQ_GH_API_SOURCED=1 + else + __DAQ_GH_API_SOURCED=0 + fi + # Enable pipefail for Bash 4+ (not available in Bash 3.2) + if [[ "${BASH_VERSINFO:-0}" -ge 4 ]]; then + set -o pipefail + fi +elif [[ -n "${ZSH_VERSION:-}" ]]; then + __DAQ_GH_API_SHELL="zsh" + __DAQ_GH_API_SHELL_VERSION="${ZSH_VERSION}" + # Check if sourced in Zsh + if [[ "${ZSH_EVAL_CONTEXT:-}" == *:file ]]; then + __DAQ_GH_API_SOURCED=1 + else + __DAQ_GH_API_SOURCED=0 + fi + + setopt PIPE_FAIL 2>/dev/null || true # Zsh equivalent of pipefail +else + # Unknown shell, assume not sourced + __DAQ_GH_API_SOURCED=0 +fi + +# Public variables +OPENDAQ_GH_API_DEBUG="${OPENDAQ_GH_API_DEBUG:-0}" +OPENDAQ_GH_API_INITIALIZED=0 + +# Private variables +__DAQ_GH_API_VERBOSE=0 +__DAQ_GH_API_REPO="" +__DAQ_GH_API_OWNER="" +__DAQ_GH_API_VERSION="" + +__DAQ_GH_API_GITHUB_REPO="${OPENDAQ_GH_API_GITHUB_REPO:-}" +__DAQ_GH_API_CACHE_DIR="${OPENDAQ_GH_API_CACHE_DIR:-${TMPDIR:-${TEMP:-${TMP:-/tmp}}}}" +__DAQ_GH_API_CACHE_DIR_RESPONSE="${__DAQ_GH_API_CACHE_DIR/response}" +__DAQ_GH_API_CACHE_DIR_ERROR="${__DAQ_GH_API_CACHE_DIR/error}" + +# Safe comparison for older bash versions +__daq_api_gh_regex_match() { + local string="$1" + local pattern="$2" + + # The bash 3.2 (on macOS) - simply use grep + echo "$string" | grep -qE "$pattern" +} + +__DAQ_GH_API_HELP_EXAMPLE_REPO="openDAQ/openDAQ" +__DAQ_GH_API_HELP_EXAMPLE_VERSION="v3.20.4" + +__daq_api_gh_help() { + cat <&2 +} + +__daq_api_gh_info() { + if [[ "${__DAQ_GH_API_VERBOSE}" -eq 1 ]]; then + echo "[INFO] $*" >&2 + fi +} + +__daq_api_gh_debug() { + if [[ "${OPENDAQ_GH_API_DEBUG}" -eq 1 ]]; then + echo "[DEBUG] [$__DAQ_GH_API_SHELL $__DAQ_GH_API_SHELL_VERSION] $*" >&2 + fi +} + +__daq_api_gh_check_deps() { + local has_error=0 + local missing_deps="" + + if ! command -v gh >/dev/null 2>&1; then + missing_deps="${missing_deps} - gh (GitHub CLI)\n" + has_error=1 + fi + + if ! command -v jq >/dev/null 2>&1; then + missing_deps="${missing_deps} - jq (JSON processor)\n" + has_error=1 + fi + + if [[ $has_error -eq 1 ]]; then + __daq_api_gh_error "Missing required dependencies:" + printf "%b" "$missing_deps" >&2 + __daq_api_gh_error "" + __daq_api_gh_error "Installation:" + __daq_api_gh_error " gh: https://cli.github.com" + __daq_api_gh_error " jq: brew install jq (macOS) or https://jqlang.github.io/jq/" + return 1 + fi + + return 0 +} + +# Generic API request wrapper +daq_api_gh_request() { + local endpoint="$1" + local temp_error="${__DAQ_GH_API_CACHE_DIR_ERROR}/gh_error_$$" + + __daq_api_gh_debug "API request: gh api $endpoint" + + # Make API request and capture both stdout and stderr + if ! gh api "$endpoint" 2>"$temp_error"; then + local error_msg="" + if [[ -f "$temp_error" ]]; then + error_msg=$(cat "$temp_error") + rm -f "$temp_error" + fi + + # Parse error type + if echo "$error_msg" | grep -q "404"; then + __daq_api_gh_debug "Resource not found (404)" + return 1 + elif echo "$error_msg" | grep -q "rate limit"; then + __daq_api_gh_error "GitHub API rate limit exceeded" + __daq_api_gh_error "Try again later or authenticate with: gh auth login" + return 1 + elif echo "$error_msg" | grep -q "401"; then + __daq_api_gh_error "Authentication required" + __daq_api_gh_error "Run: gh auth login" + return 1 + else + __daq_api_gh_debug "API request failed: $error_msg" + return 1 + fi + fi + + rm -f "$temp_error" + return 0 +} + +daq_api_gh_init() { + if [[ "${OPENDAQ_GH_API_INITIALIZED}" -eq 1 ]]; then + __daq_api_gh_debug "Already initialized" + return 0 + fi + + # Check dependencies + __daq_api_gh_check_deps || return 1 + + # Check gh authentication + if ! gh auth status >/dev/null 2>&1; then + __daq_api_gh_error "GitHub CLI not authenticated" + __daq_api_gh_error "Run: gh auth login" + return 1 + fi + + OPENDAQ_GH_API_INITIALIZED=1 + __daq_api_gh_debug "Initialization complete" + return 0 +} + +daq_api_gh_repo_parse() { + local repo="${1:-}" + + if [[ -z "$repo" ]]; then + __daq_api_gh_error "Repository not specified" + return 1 + fi + + # Use grep instead of =~ for compatibility + if ! __daq_api_gh_regex_match "$repo" "^[^/]+/[^/]+$"; then + __daq_api_gh_error "Invalid repository format. Expected: owner/repo" + return 1 + fi + + # Safe strings separation (works for both bash 3.2 and zsh) + __DAQ_GH_API_OWNER="${repo%%/*}" + __DAQ_GH_API_REPO="${repo#*/}" + + __daq_api_gh_debug "Parsed: owner=$__DAQ_GH_API_OWNER, repo=$__DAQ_GH_API_REPO" + return 0 +} + +# Get latest release version +daq_api_gh_version_latest() { + local endpoint="repos/${__DAQ_GH_API_OWNER}/${__DAQ_GH_API_REPO}/releases/latest" + local temp_file="${__DAQ_GH_API_CACHE_DIR_RESPONSE}/gh_response_$$" + + __daq_api_gh_info "Getting latest version for ${__DAQ_GH_API_OWNER}/${__DAQ_GH_API_REPO}" + + # Get release data + if ! daq_api_gh_request "$endpoint" > "$temp_file"; then + rm -f "$temp_file" + __daq_api_gh_debug "No releases found or repository doesn't exist" + return 1 + fi + + # Extract tag_name using jq + local tag_name + tag_name=$(jq -r '.tag_name // empty' < "$temp_file" 2>/dev/null) + rm -f "$temp_file" + + if [[ -z "$tag_name" ]]; then + __daq_api_gh_error "Could not extract tag_name from response" + return 1 + fi + + __daq_api_gh_info "Latest version: $tag_name" + echo "$tag_name" + return 0 +} + +# Verify if specific version exists +daq_api_gh_version_verify() { + local version="${1:-}" + + if [[ -z "$version" ]]; then + __daq_api_gh_error "Version not specified" + return 1 + fi + + local endpoint="repos/${__DAQ_GH_API_OWNER}/${__DAQ_GH_API_REPO}/releases/tags/${version}" + + __daq_api_gh_info "Verifying version $version for ${__DAQ_GH_API_OWNER}/${__DAQ_GH_API_REPO}" + + if daq_api_gh_request "$endpoint" >/dev/null 2>&1; then + __daq_api_gh_info "Version $version exists" + return 0 + else + __daq_api_gh_info "Version $version not found" + return 1 + fi +} + +# Resolve version (latest or verify specific) +daq_api_gh_version_resolve() { + local version="${1:-latest}" + + __daq_api_gh_debug "Resolving version: $version" + + if [[ "$version" == "latest" ]]; then + if ! daq_api_gh_version_latest; then + __daq_api_gh_error "Failed to get latest version" + return 1 + fi + else + if daq_api_gh_version_verify "$version"; then + echo "$version" + return 0 + else + __daq_api_gh_error "Version $version not found" + return 1 + fi + fi +} + +# List all versions (limit supported) +daq_api_gh_version_list() { + local limit="${1:-30}" + local endpoint="repos/${__DAQ_GH_API_OWNER}/${__DAQ_GH_API_REPO}/releases" + local temp_file="${__DAQ_GH_API_CACHE_DIR_RESPONSE}/gh_response_$$" + + __daq_api_gh_info "Listing versions for ${__DAQ_GH_API_OWNER}/${__DAQ_GH_API_REPO}" + + # Adjust endpoint based on limit + if [[ "$limit" != "all" ]]; then + endpoint="${endpoint}?per_page=${limit}" + fi + + # Get releases + if ! daq_api_gh_request "$endpoint" > "$temp_file"; then + rm -f "$temp_file" + __daq_api_gh_error "Failed to get releases" + return 1 + fi + + # Extract tag names + jq -r '.[] | .tag_name // empty' < "$temp_file" 2>/dev/null + local exit_code=$? + rm -f "$temp_file" + + return $exit_code +} + +__daq_api_gh_main() { + local repo=${__DAQ_GH_API_GITHUB_REPO} + local action="" + local limit="30" + local version="" + + # Parse arguments (POSIX-style for compatibility) + while [[ $# -gt 0 ]]; do + case "$1" in + --version) + if [[ $# -lt 2 ]]; then + __daq_api_gh_error "Option --version requires an argument" + return 1 + fi + __DAQ_GH_API_VERSION="$2" + shift 2 + ;; + --list-versions) + action="list-versions" + shift + ;; + --limit) + if [[ $# -lt 2 ]]; then + __daq_api_gh_error "Option --limit requires an argument" + return 1 + fi + limit="$2" + shift 2 + ;; + --verbose) + __DAQ_GH_API_VERBOSE=1 + shift + ;; + --help|-h) + __daq_api_gh_help + return 0 + ;; + --*) + __daq_api_gh_error "Unknown option: $1" + return 1 + ;; + *) + if [[ -z "$repo" ]]; then + repo="$1" + else + __daq_api_gh_error "Unexpected argument: $1" + return 1 + fi + shift + ;; + esac + done + + if [[ -z "$repo" ]]; then + __daq_api_gh_error "Repository not specified" + __daq_api_gh_help + return 1 + fi + + # Initialize and parse + daq_api_gh_init || return 1 + daq_api_gh_repo_parse "$repo" || return 1 + + __DAQ_GH_API_VERSION="${__DAQ_GH_API_VERSION:-latest}" + if [[ "$__DAQ_GH_API_VERSION" == "latest" ]]; then + __DAQ_GH_API_VERSION=$(daq_api_gh_version_latest) + fi + + if [[ -z "$action" ]]; then + action="version" + fi + + # Execute action + case "$action" in + version) + __DAQ_GH_API_VERSION="${__DAQ_GH_API_VERSION:-latest}" + daq_api_gh_version_resolve "$__DAQ_GH_API_VERSION" + ;; + list-versions) + daq_api_gh_version_list "$limit" + ;; + *) + __daq_api_gh_error "Action not specified" + __daq_api_gh_help + ;; + esac +} + +if [[ "${__DAQ_GH_API_SOURCED}" -eq 0 ]]; then + __daq_api_gh_main "$@" + exit $? +fi diff --git a/scripts/shell/bash/packaging-format.sh b/scripts/shell/bash/packaging-format.sh old mode 100644 new mode 100755 diff --git a/scripts/shell/bash/platform-format.sh b/scripts/shell/bash/platform-format.sh old mode 100644 new mode 100755 index ec83992..8f67f25 --- a/scripts/shell/bash/platform-format.sh +++ b/scripts/shell/bash/platform-format.sh @@ -257,7 +257,7 @@ __daq_platform_parse() { if ! __daq_platform_is_valid "$platform"; then __daq_platform_error "Invalid platform alias: $platform" - exit 1 + return 1 fi local os_name="" @@ -298,7 +298,7 @@ __daq_platform_parse() { ;; *) __daq_platform_error "Cannot parse platform: $platform" - exit 1 + return 1 ;; esac @@ -327,13 +327,13 @@ daq_platform_validate() { if ! __daq_platform_is_valid "$platform"; then __daq_platform_verbose "Platform validation failed: $platform" - exit 1 + return 1 fi # If no flags, just validate and exit if [ $# -eq 0 ]; then __daq_platform_verbose "Platform is valid: $platform" - exit 0 + return 0 fi # Determine OS name from platform using case @@ -363,41 +363,41 @@ daq_platform_validate() { [ "$os_name" = "ubuntu" ] || [ "$os_name" = "debian" ] || [ "$os_name" = "macos" ] result=$? __daq_platform_verbose "Check --is-unix for $platform: $([ $result -eq 0 ] && echo 'true' || echo 'false')" - exit $result + return $result ;; --is-linux) [ "$os_name" = "ubuntu" ] || [ "$os_name" = "debian" ] result=$? __daq_platform_verbose "Check --is-linux for $platform: $([ $result -eq 0 ] && echo 'true' || echo 'false')" - exit $result + return $result ;; --is-ubuntu) [ "$os_name" = "ubuntu" ] result=$? __daq_platform_verbose "Check --is-ubuntu for $platform: $([ $result -eq 0 ] && echo 'true' || echo 'false')" - exit $result + return $result ;; --is-debian) [ "$os_name" = "debian" ] result=$? __daq_platform_verbose "Check --is-debian for $platform: $([ $result -eq 0 ] && echo 'true' || echo 'false')" - exit $result + return $result ;; --is-macos) [ "$os_name" = "macos" ] result=$? __daq_platform_verbose "Check --is-macos for $platform: $([ $result -eq 0 ] && echo 'true' || echo 'false')" - exit $result + return $result ;; --is-win) [ "$os_name" = "win" ] result=$? __daq_platform_verbose "Check --is-win for $platform: $([ $result -eq 0 ] && echo 'true' || echo 'false')" - exit $result + return $result ;; *) __daq_platform_error "Unknown flag: $flag" - exit 1 + return 1 ;; esac } @@ -426,6 +426,12 @@ daq_platform_parse() { local parsed_output parsed_output=$(__daq_platform_parse "$platform") + local parse_exit_code=$? + + # Check if parsing failed + if [ $parse_exit_code -ne 0 ]; then + return $parse_exit_code + fi # Determine if this is Windows (2 components) or Linux/macOS (3 components) local os_name os_version os_arch @@ -447,7 +453,7 @@ daq_platform_parse() { if [ $# -eq 0 ]; then __daq_platform_verbose "Outputting all components" echo "$parsed_output" - exit 0 + return 0 fi # Output specific components @@ -470,7 +476,7 @@ daq_platform_parse() { ;; *) __daq_platform_error "Unknown flag: $1" - exit 1 + return 1 ;; esac shift @@ -510,7 +516,7 @@ daq_platform_compose() { --os-name) if [ $# -lt 2 ] || [ -z "${2:-}" ]; then __daq_platform_error "--os-name requires a value" - exit 1 + return 1 fi os_name="$2" __daq_platform_debug "Set os-name: $os_name" @@ -519,7 +525,7 @@ daq_platform_compose() { --os-version) if [ $# -lt 2 ] || [ -z "${2:-}" ]; then __daq_platform_error "--os-version requires a value" - exit 1 + return 1 fi os_version="$2" __daq_platform_debug "Set os-version: $os_version" @@ -528,7 +534,7 @@ daq_platform_compose() { --os-arch) if [ $# -lt 2 ] || [ -z "${2:-}" ]; then __daq_platform_error "--os-arch requires a value" - exit 1 + return 1 fi os_arch="$2" __daq_platform_debug "Set os-arch: $os_arch" @@ -536,7 +542,7 @@ daq_platform_compose() { ;; *) __daq_platform_error "Unknown argument: $1" - exit 1 + return 1 ;; esac done @@ -544,12 +550,12 @@ daq_platform_compose() { # Validate required fields if [ -z "$os_name" ]; then __daq_platform_error "--os-name is required" - exit 1 + return 1 fi if [ -z "$os_arch" ]; then __daq_platform_error "--os-arch is required" - exit 1 + return 1 fi # Compose platform alias @@ -560,7 +566,7 @@ daq_platform_compose() { else if [ -z "$os_version" ]; then __daq_platform_error "--os-version is required for non-Windows platforms" - exit 1 + return 1 fi platform="${os_name}${os_version}-${os_arch}" __daq_platform_verbose "Composing Linux/macOS platform: $platform" @@ -569,7 +575,7 @@ daq_platform_compose() { # Validate composed platform if ! __daq_platform_is_valid "$platform"; then __daq_platform_error "Invalid platform composition: $platform" - exit 1 + return 1 fi __daq_platform_verbose "Successfully composed platform: $platform" @@ -595,7 +601,7 @@ daq_platform_detect() { # Detect OS info local os_info if ! os_info=$(__daq_platform_detect_os_info); then - exit 1 + return 1 fi local os_name os_version @@ -609,7 +615,7 @@ daq_platform_detect() { # Detect architecture local os_arch if ! os_arch=$(__daq_platform_detect_arch); then - exit 1 + return 1 fi __daq_platform_verbose "Detected architecture: $os_arch" @@ -621,7 +627,7 @@ daq_platform_detect() { else if [ -z "$os_version" ]; then __daq_platform_error "Could not detect OS version for $os_name" - exit 1 + return 1 fi platform="${os_name}${os_version}-${os_arch}" fi @@ -632,7 +638,7 @@ daq_platform_detect() { if ! __daq_platform_is_valid "$platform"; then __daq_platform_error "Detected platform $platform is not supported" \ "Supported platforms can be listed with: --list-platforms" - exit 1 + return 1 fi __daq_platform_verbose "Platform is supported: $platform" @@ -640,37 +646,6 @@ daq_platform_detect() { } # Main CLI entry point -# Processes command-line arguments in two passes: -# 1. Extract and process global flags (--verbose, --debug, --quiet) -# 2. Route to appropriate command handler with remaining arguments -# -# Arguments: -# Global flags (can appear anywhere): -# --verbose, -v Enable verbose output -# --debug, -d Enable debug output -# --quiet, -q Suppress error messages -# -# Commands: -# detect -# validate [--is-*] -# parse [--os-name] [--os-version] [--os-arch] -# extract [--os-name] [--os-version] [--os-arch] -# compose --os-name [--os-version ] --os-arch -# --list-platforms -# -# Output: -# Usage information if no arguments provided -# Otherwise delegates to appropriate command function -# -# Exit code: -# 0 - Success -# 1 - Error (invalid command, missing arguments, etc.) -# -# Examples: -# __daq_platform_main detect -# __daq_platform_main validate ubuntu20.04-arm64 -# __daq_platform_main --verbose parse macos14-arm64 -# __daq_platform_main --debug compose --os-name debian --os-version 11 --os-arch arm64 __daq_platform_main() { # FIRST PASS: Extract global flags local remaining_args=() @@ -700,7 +675,7 @@ __daq_platform_main() { __daq_platform_debug "Global flags parsed: verbose=$__DAQ_PLATFORM_VERBOSE debug=$__DAQ_PLATFORM_DEBUG quiet=$__DAQ_PLATFORM_QUIET" # SECOND PASS: Process commands with remaining arguments - set -- "${remaining_args[@]}" + set -- "${remaining_args[@]+"${remaining_args[@]}"}" if [ $# -eq 0 ]; then if [ "$__DAQ_PLATFORM_QUIET" -eq 0 ]; then @@ -721,7 +696,7 @@ __daq_platform_main() { echo "Options:" echo " --list-platforms List all supported platforms" fi - exit 1 + return 1 fi __daq_platform_debug "Processing command: $1" @@ -730,46 +705,48 @@ __daq_platform_main() { --list-platforms) __daq_platform_verbose "Listing all supported platforms" daq_platform_list + return $? ;; detect) shift daq_platform_detect "$@" + return $? ;; validate) shift daq_platform_validate "$@" + return $? ;; parse) shift daq_platform_parse "$@" + return $? ;; extract) shift daq_platform_extract "$@" + return $? ;; compose) shift daq_platform_compose "$@" + return $? ;; *) __daq_platform_error "Unknown command: $1" - exit 1 + return 1 ;; esac } -# Flag to track if script was sourced (0=executed, 1=sourced) +# Flag to track if script was sourced __DAQ_PLATFORM_SOURCED=0 if [ -n "${BASH_VERSION:-}" ]; then - # Bash: Compare script path with invocation path - # BASH_SOURCE[0] = script path, $0 = invocation path if [ "${BASH_SOURCE[0]}" != "${0}" ]; then __DAQ_PLATFORM_SOURCED=1 fi elif [ -n "${ZSH_VERSION:-}" ]; then - # Zsh: Use prompt expansion to get script name - # %N expands to script/function name __DAQ_PLATFORM_SCRIPT_PATH="${(%):-%N}" if [ "$__DAQ_PLATFORM_SCRIPT_PATH" != "${0}" ]; then __DAQ_PLATFORM_SOURCED=1 @@ -779,4 +756,5 @@ fi # Run main only if not sourced if [ "$__DAQ_PLATFORM_SOURCED" -eq 0 ]; then __daq_platform_main "$@" + exit $? fi diff --git a/scripts/shell/bash/version-format.sh b/scripts/shell/bash/version-format.sh old mode 100644 new mode 100755 diff --git a/tests/shell/bash/core/filter.sh b/tests/shell/bash/core/filter.sh index b0afb72..89d5816 100644 --- a/tests/shell/bash/core/filter.sh +++ b/tests/shell/bash/core/filter.sh @@ -62,16 +62,28 @@ __daq_tests_filter_matches_any() { local test_name="$2" local patterns_array_name="$3" - eval " - for __pattern in \"\${${patterns_array_name}[@]}\"; do - __daq_tests_filter_parse_pattern \"\${__pattern}\" + # Get array size first to handle empty arrays + local array_size + array_size=$(eval "echo \${#${patterns_array_name}[@]}") + + if [[ "${array_size}" -eq 0 ]]; then + return 1 + fi + + # Copy array to local variable using eval - single eval, safe approach + local -a patterns + eval "patterns=(\"\${${patterns_array_name}[@]}\")" + + # Clean iteration without eval + local pattern + for pattern in "${patterns[@]}"; do + __daq_tests_filter_parse_pattern "${pattern}" - if __daq_tests_match_pattern \"\${suite_name}\" \"\${__DAQ_TESTS_PATTERN_SUITE}\" && \ - __daq_tests_match_pattern \"\${test_name}\" \"\${__DAQ_TESTS_PATTERN_TEST}\"; then + if __daq_tests_match_pattern "${suite_name}" "${__DAQ_TESTS_PATTERN_SUITE}" && \ + __daq_tests_match_pattern "${test_name}" "${__DAQ_TESTS_PATTERN_TEST}"; then return 0 fi done - " return 1 } diff --git a/tests/shell/bash/suites/.gitkeep b/tests/shell/bash/suites/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/tests/shell/bash/suites/test-platform-format-api.sh b/tests/shell/bash/suites/test-platform-format-api.sh new file mode 100644 index 0000000..91fe13c --- /dev/null +++ b/tests/shell/bash/suites/test-platform-format-api.sh @@ -0,0 +1,389 @@ +#!/usr/bin/env bash +# test-platform-format-api.sh - API tests for platform-format.sh public functions +# +# Tests all public API functions (daq_platform_*): +# - daq_platform_validate +# - daq_platform_parse +# - daq_platform_extract +# - daq_platform_compose +# - daq_platform_list +# - daq_platform_detect + +# Source the script under test +source "${__DAQ_TESTS_SCRIPTS_DIR}/platform-format.sh" + +test-validate-ubuntu-valid() { + daq_platform_validate "ubuntu20.04-x86_64" >/dev/null 2>&1 + local exit_code=$? + + daq_assert_success $exit_code "validate should succeed for valid Ubuntu platform" || return 1 + return 0 +} + +test-validate-debian-valid() { + daq_platform_validate "debian11-arm64" >/dev/null 2>&1 + local exit_code=$? + + daq_assert_success $exit_code "validate should succeed for valid Debian platform" || return 1 + return 0 +} + +test-validate-macos-valid() { + daq_platform_validate "macos14-arm64" >/dev/null 2>&1 + local exit_code=$? + + daq_assert_success $exit_code "validate should succeed for valid macOS platform" || return 1 + return 0 +} + +test-validate-windows-valid() { + daq_platform_validate "win64" >/dev/null 2>&1 + local exit_code=$? + + daq_assert_success $exit_code "validate should succeed for valid Windows platform" || return 1 + return 0 +} + +test-validate-invalid-platform() { + daq_platform_validate "invalid-platform" >/dev/null 2>&1 + local exit_code=$? + + daq_assert_failure $exit_code "validate should fail for invalid platform" || return 1 + return 0 +} + +test-validate-is-unix-true() { + daq_platform_validate "ubuntu20.04-x86_64" --is-unix >/dev/null 2>&1 + local exit_code=$? + + daq_assert_success $exit_code "Ubuntu should be Unix" || return 1 + return 0 +} + +test-validate-is-unix-false() { + daq_platform_validate "win64" --is-unix >/dev/null 2>&1 + local exit_code=$? + + daq_assert_failure $exit_code "Windows should not be Unix" || return 1 + return 0 +} + +test-validate-is-linux-true() { + daq_platform_validate "ubuntu20.04-x86_64" --is-linux >/dev/null 2>&1 + local exit_code=$? + + daq_assert_success $exit_code "Ubuntu should be Linux" || return 1 + return 0 +} + +test-validate-is-linux-false() { + daq_platform_validate "macos14-arm64" --is-linux >/dev/null 2>&1 + local exit_code=$? + + daq_assert_failure $exit_code "macOS should not be Linux" || return 1 + return 0 +} + +test-validate-is-ubuntu-true() { + daq_platform_validate "ubuntu20.04-x86_64" --is-ubuntu >/dev/null 2>&1 + local exit_code=$? + + daq_assert_success $exit_code "Should identify Ubuntu" || return 1 + return 0 +} + +test-validate-is-debian-true() { + daq_platform_validate "debian11-arm64" --is-debian >/dev/null 2>&1 + local exit_code=$? + + daq_assert_success $exit_code "Should identify Debian" || return 1 + return 0 +} + +test-validate-is-macos-true() { + daq_platform_validate "macos14-arm64" --is-macos >/dev/null 2>&1 + local exit_code=$? + + daq_assert_success $exit_code "Should identify macOS" || return 1 + return 0 +} + +test-validate-is-win-true() { + daq_platform_validate "win64" --is-win >/dev/null 2>&1 + local exit_code=$? + + daq_assert_success $exit_code "Should identify Windows" || return 1 + return 0 +} + +test-parse-ubuntu-all-components() { + local result + result=$(daq_platform_parse "ubuntu20.04-x86_64") + local exit_code=$? + + daq_assert_success $exit_code "parse should succeed" || return 1 + daq_assert_equals "ubuntu 20.04 x86_64" "$result" "Ubuntu parse result mismatch" || return 1 + return 0 +} + +test-parse-debian-all-components() { + local result + result=$(daq_platform_parse "debian11-arm64") + local exit_code=$? + + daq_assert_success $exit_code "parse should succeed" || return 1 + daq_assert_equals "debian 11 arm64" "$result" "Debian parse result mismatch" || return 1 + return 0 +} + +test-parse-macos-all-components() { + local result + result=$(daq_platform_parse "macos14-arm64") + local exit_code=$? + + daq_assert_success $exit_code "parse should succeed" || return 1 + daq_assert_equals "macos 14 arm64" "$result" "macOS parse result mismatch" || return 1 + return 0 +} + +test-parse-windows-all-components() { + local result + result=$(daq_platform_parse "win64") + local exit_code=$? + + daq_assert_success $exit_code "parse should succeed" || return 1 + daq_assert_equals "win 64" "$result" "Windows parse result mismatch" || return 1 + return 0 +} + +test-parse-extract-os-name() { + local result + result=$(daq_platform_parse "ubuntu20.04-x86_64" --os-name) + local exit_code=$? + + daq_assert_success $exit_code "parse should succeed" || return 1 + daq_assert_equals "ubuntu" "$result" "OS name extraction mismatch" || return 1 + return 0 +} + +test-parse-extract-os-version() { + local result + result=$(daq_platform_parse "ubuntu20.04-x86_64" --os-version) + local exit_code=$? + + daq_assert_success $exit_code "parse should succeed" || return 1 + daq_assert_equals "20.04" "$result" "OS version extraction mismatch" || return 1 + return 0 +} + +test-parse-extract-os-arch() { + local result + result=$(daq_platform_parse "ubuntu20.04-x86_64" --os-arch) + local exit_code=$? + + daq_assert_success $exit_code "parse should succeed" || return 1 + daq_assert_equals "x86_64" "$result" "OS arch extraction mismatch" || return 1 + return 0 +} + +test-parse-windows-no-version() { + local result + result=$(daq_platform_parse "win64" --os-version) + local exit_code=$? + + daq_assert_success $exit_code "parse should succeed" || return 1 + daq_assert_empty "$result" "Windows should have no version" || return 1 + return 0 +} + +test-parse-invalid-platform() { + local result + result=$(daq_platform_parse "invalid-platform" 2>&1) + local exit_code=$? + + daq_assert_failure $exit_code "parse should fail for invalid platform" || return 1 + return 0 +} + +test-extract-ubuntu() { + local result + result=$(daq_platform_extract "ubuntu20.04-x86_64" --os-name) + local exit_code=$? + + daq_assert_success $exit_code "extract should succeed" || return 1 + daq_assert_equals "ubuntu" "$result" "Extract should work like parse" || return 1 + return 0 +} + +test-extract-multiple-components() { + local result + result=$(daq_platform_extract "debian11-arm64" --os-name --os-arch) + local exit_code=$? + + daq_assert_success $exit_code "extract should succeed" || return 1 + daq_assert_contains "debian" "$result" "Should contain OS name" || return 1 + daq_assert_contains "arm64" "$result" "Should contain architecture" || return 1 + return 0 +} + +test-compose-ubuntu() { + local result + result=$(daq_platform_compose --os-name ubuntu --os-version 20.04 --os-arch x86_64) + local exit_code=$? + + daq_assert_success $exit_code "compose should succeed" || return 1 + daq_assert_equals "ubuntu20.04-x86_64" "$result" "Ubuntu compose result mismatch" || return 1 + return 0 +} + +test-compose-debian() { + local result + result=$(daq_platform_compose --os-name debian --os-version 11 --os-arch arm64) + local exit_code=$? + + daq_assert_success $exit_code "compose should succeed" || return 1 + daq_assert_equals "debian11-arm64" "$result" "Debian compose result mismatch" || return 1 + return 0 +} + +test-compose-macos() { + local result + result=$(daq_platform_compose --os-name macos --os-version 14 --os-arch arm64) + local exit_code=$? + + daq_assert_success $exit_code "compose should succeed" || return 1 + daq_assert_equals "macos14-arm64" "$result" "macOS compose result mismatch" || return 1 + return 0 +} + +test-compose-windows() { + local result + result=$(daq_platform_compose --os-name win --os-arch 64) + local exit_code=$? + + daq_assert_success $exit_code "compose should succeed" || return 1 + daq_assert_equals "win64" "$result" "Windows compose result mismatch" || return 1 + return 0 +} + +test-compose-missing-os-name() { + local result + result=$(daq_platform_compose --os-version 20.04 --os-arch x86_64 2>&1) + local exit_code=$? + + daq_assert_failure $exit_code "compose should fail without os-name" || return 1 + return 0 +} + +test-compose-missing-os-arch() { + local result + result=$(daq_platform_compose --os-name ubuntu --os-version 20.04 2>&1) + local exit_code=$? + + daq_assert_failure $exit_code "compose should fail without os-arch" || return 1 + return 0 +} + +test-compose-linux-missing-version() { + local result + result=$(daq_platform_compose --os-name ubuntu --os-arch x86_64 2>&1) + local exit_code=$? + + daq_assert_failure $exit_code "compose should fail without version for Linux" || return 1 + return 0 +} + +test-compose-invalid-combination() { + local result + result=$(daq_platform_compose --os-name ubuntu --os-version 99.99 --os-arch x86_64 2>&1) + local exit_code=$? + + daq_assert_failure $exit_code "compose should fail for invalid version" || return 1 + return 0 +} + +test-list-returns-platforms() { + local result + result=$(daq_platform_list) + local exit_code=$? + + daq_assert_success $exit_code "list should succeed" || return 1 + daq_assert_not_empty "$result" "list should return platforms" || return 1 + return 0 +} + +test-list-contains-ubuntu() { + local result + result=$(daq_platform_list) + + daq_assert_contains "ubuntu20.04-x86_64" "$result" "Should contain Ubuntu 20.04 x86_64" || return 1 + return 0 +} + +test-list-contains-debian() { + local result + result=$(daq_platform_list) + + daq_assert_contains "debian11-arm64" "$result" "Should contain Debian 11 ARM64" || return 1 + return 0 +} + +test-list-contains-macos() { + local result + result=$(daq_platform_list) + + daq_assert_contains "macos14-arm64" "$result" "Should contain macOS 14 ARM64" || return 1 + return 0 +} + +test-list-contains-windows() { + local result + result=$(daq_platform_list) + + daq_assert_contains "win64" "$result" "Should contain Windows 64-bit" || return 1 + daq_assert_contains "win32" "$result" "Should contain Windows 32-bit" || return 1 + return 0 +} + +test-list-platform-count() { + local count + count=$(daq_platform_list | wc -l) + + # Should have multiple platforms (exact count may vary) + daq_assert_greater_than 30 "$count" "Should have more than 50 platforms" || return 1 + return 0 +} + +test-detect-returns-valid-platform() { + local result + result=$(daq_platform_detect 2>/dev/null) + local exit_code=$? + + # Detection may fail on some systems, so we check if it succeeds + if [[ $exit_code -eq 0 ]]; then + daq_assert_not_empty "$result" "detect should return platform" || return 1 + + # Validate that detected platform is valid + daq_platform_validate "$result" >/dev/null 2>&1 + daq_assert_success $? "detected platform should be valid" || return 1 + fi + + return 0 +} + +test-detect-platform-components() { + local result + result=$(daq_platform_detect 2>/dev/null) + local exit_code=$? + + # Detection may fail on some systems + if [[ $exit_code -eq 0 ]]; then + # Should be able to parse detected platform + local os_name + os_name=$(daq_platform_parse "$result" --os-name 2>/dev/null) + + daq_assert_not_empty "$os_name" "detected platform should have OS name" || return 1 + fi + + return 0 +} diff --git a/tests/shell/bash/suites/test-platform-format-cli.sh b/tests/shell/bash/suites/test-platform-format-cli.sh new file mode 100644 index 0000000..0e97cf1 --- /dev/null +++ b/tests/shell/bash/suites/test-platform-format-cli.sh @@ -0,0 +1,315 @@ +#!/usr/bin/env bash +# test-platform-format-cli.sh - CLI tests for platform-format.sh +# +# Tests the command-line interface of platform-format.sh: +# - Help and usage +# - detect command +# - validate command +# - parse/extract command +# - compose command +# - --list-platforms flag + +# CLI script path +PLATFORM_FORMAT_CLI="${__DAQ_TESTS_SCRIPTS_DIR}/platform-format.sh" + +test-cli-help-global() { + local result + result=$("${PLATFORM_FORMAT_CLI}" 2>&1) # No command + local exit_code=$? + + daq_assert_failure $exit_code "Should exit with error" + daq_assert_contains "Usage:" "$result" "Should show usage" + daq_assert_contains "Commands:" "$result" "Should show commands" +} + +test-cli-detect-current-platform() { + local result + result=$("${PLATFORM_FORMAT_CLI}" detect 2>/dev/null) + local exit_code=$? + + # Detection may fail on some systems + if [[ $exit_code -eq 0 ]]; then + daq_assert_not_empty "$result" "detect should return platform" || return 1 + fi + + return 0 +} + +test-cli-validate-ubuntu-valid() { + "${PLATFORM_FORMAT_CLI}" validate "ubuntu20.04-x86_64" >/dev/null 2>&1 + local exit_code=$? + + daq_assert_success $exit_code "validate should succeed for valid Ubuntu" || return 1 + return 0 +} + +test-cli-validate-debian-valid() { + "${PLATFORM_FORMAT_CLI}" validate "debian11-arm64" >/dev/null 2>&1 + local exit_code=$? + + daq_assert_success $exit_code "validate should succeed for valid Debian" || return 1 + return 0 +} + +test-cli-validate-macos-valid() { + "${PLATFORM_FORMAT_CLI}" validate "macos14-arm64" >/dev/null 2>&1 + local exit_code=$? + + daq_assert_success $exit_code "validate should succeed for valid macOS" || return 1 + return 0 +} + +test-cli-validate-windows-valid() { + "${PLATFORM_FORMAT_CLI}" validate "win64" >/dev/null 2>&1 + local exit_code=$? + + daq_assert_success $exit_code "validate should succeed for valid Windows" || return 1 + return 0 +} + +test-cli-validate-invalid() { + "${PLATFORM_FORMAT_CLI}" validate "invalid-platform" >/dev/null 2>&1 + local exit_code=$? + + daq_assert_failure $exit_code "validate should fail for invalid platform" || return 1 + return 0 +} + +test-cli-validate-is-unix() { + "${PLATFORM_FORMAT_CLI}" validate "ubuntu20.04-x86_64" --is-unix >/dev/null 2>&1 + local exit_code=$? + + daq_assert_success $exit_code "Ubuntu should be Unix" || return 1 + return 0 +} + +test-cli-validate-is-linux() { + "${PLATFORM_FORMAT_CLI}" validate "debian11-arm64" --is-linux >/dev/null 2>&1 + local exit_code=$? + + daq_assert_success $exit_code "Debian should be Linux" || return 1 + return 0 +} + +test-cli-validate-is-macos() { + "${PLATFORM_FORMAT_CLI}" validate "macos14-arm64" --is-macos >/dev/null 2>&1 + local exit_code=$? + + daq_assert_success $exit_code "Should identify macOS" || return 1 + return 0 +} + +test-cli-validate-is-win() { + "${PLATFORM_FORMAT_CLI}" validate "win64" --is-win >/dev/null 2>&1 + local exit_code=$? + + daq_assert_success $exit_code "Should identify Windows" || return 1 + return 0 +} + +test-cli-parse-ubuntu() { + local result + result=$("${PLATFORM_FORMAT_CLI}" parse "ubuntu20.04-x86_64") + local exit_code=$? + + daq_assert_success $exit_code "parse should succeed" || return 1 + daq_assert_equals "ubuntu 20.04 x86_64" "$result" "Ubuntu parse result mismatch" || return 1 + return 0 +} + +test-cli-parse-debian() { + local result + result=$("${PLATFORM_FORMAT_CLI}" parse "debian11-arm64") + local exit_code=$? + + daq_assert_success $exit_code "parse should succeed" || return 1 + daq_assert_equals "debian 11 arm64" "$result" "Debian parse result mismatch" || return 1 + return 0 +} + +test-cli-parse-macos() { + local result + result=$("${PLATFORM_FORMAT_CLI}" parse "macos14-arm64") + local exit_code=$? + + daq_assert_success $exit_code "parse should succeed" || return 1 + daq_assert_equals "macos 14 arm64" "$result" "macOS parse result mismatch" || return 1 + return 0 +} + +test-cli-parse-windows() { + local result + result=$("${PLATFORM_FORMAT_CLI}" parse "win64") + local exit_code=$? + + daq_assert_success $exit_code "parse should succeed" || return 1 + daq_assert_equals "win 64" "$result" "Windows parse result mismatch" || return 1 + return 0 +} + +test-cli-parse-extract-os-name() { + local result + result=$("${PLATFORM_FORMAT_CLI}" parse "ubuntu20.04-x86_64" --os-name) + local exit_code=$? + + daq_assert_success $exit_code "parse should succeed" || return 1 + daq_assert_equals "ubuntu" "$result" "OS name extraction mismatch" || return 1 + return 0 +} + +test-cli-parse-extract-os-version() { + local result + result=$("${PLATFORM_FORMAT_CLI}" parse "ubuntu20.04-x86_64" --os-version) + local exit_code=$? + + daq_assert_success $exit_code "parse should succeed" || return 1 + daq_assert_equals "20.04" "$result" "OS version extraction mismatch" || return 1 + return 0 +} + +test-cli-parse-extract-os-arch() { + local result + result=$("${PLATFORM_FORMAT_CLI}" parse "ubuntu20.04-x86_64" --os-arch) + local exit_code=$? + + daq_assert_success $exit_code "parse should succeed" || return 1 + daq_assert_equals "x86_64" "$result" "OS arch extraction mismatch" || return 1 + return 0 +} + +test-cli-parse-invalid() { + local result + result=$("${PLATFORM_FORMAT_CLI}" parse "invalid-platform" 2>&1) + local exit_code=$? + + daq_assert_failure $exit_code "parse should fail for invalid platform" || return 1 + return 0 +} + +test-cli-extract-os-name() { + local result + result=$("${PLATFORM_FORMAT_CLI}" extract "debian11-arm64" --os-name) + local exit_code=$? + + daq_assert_success $exit_code "extract should succeed" || return 1 + daq_assert_equals "debian" "$result" "Extract OS name mismatch" || return 1 + return 0 +} + +test-cli-extract-multiple-components() { + local result + result=$("${PLATFORM_FORMAT_CLI}" extract "macos14-arm64" --os-name --os-arch) + local exit_code=$? + + daq_assert_success $exit_code "extract should succeed" || return 1 + daq_assert_contains "macos" "$result" "Should contain OS name" || return 1 + daq_assert_contains "arm64" "$result" "Should contain architecture" || return 1 + return 0 +} + +test-cli-compose-ubuntu() { + local result + result=$("${PLATFORM_FORMAT_CLI}" compose --os-name ubuntu --os-version 20.04 --os-arch x86_64) + local exit_code=$? + + daq_assert_success $exit_code "compose should succeed" || return 1 + daq_assert_equals "ubuntu20.04-x86_64" "$result" "Ubuntu compose result mismatch" || return 1 + return 0 +} + +test-cli-compose-debian() { + local result + result=$("${PLATFORM_FORMAT_CLI}" compose --os-name debian --os-version 11 --os-arch arm64) + local exit_code=$? + + daq_assert_success $exit_code "compose should succeed" || return 1 + daq_assert_equals "debian11-arm64" "$result" "Debian compose result mismatch" || return 1 + return 0 +} + +test-cli-compose-macos() { + local result + result=$("${PLATFORM_FORMAT_CLI}" compose --os-name macos --os-version 14 --os-arch arm64) + local exit_code=$? + + daq_assert_success $exit_code "compose should succeed" || return 1 + daq_assert_equals "macos14-arm64" "$result" "macOS compose result mismatch" || return 1 + return 0 +} + +test-cli-compose-windows() { + local result + result=$("${PLATFORM_FORMAT_CLI}" compose --os-name win --os-arch 64) + local exit_code=$? + + daq_assert_success $exit_code "compose should succeed" || return 1 + daq_assert_equals "win64" "$result" "Windows compose result mismatch" || return 1 + return 0 +} + +test-cli-compose-missing-os-name() { + local result + result=$("${PLATFORM_FORMAT_CLI}" compose --os-version 20.04 --os-arch x86_64 2>&1) + local exit_code=$? + + daq_assert_failure $exit_code "compose should fail without os-name" || return 1 + return 0 +} + +test-cli-compose-missing-os-arch() { + local result + result=$("${PLATFORM_FORMAT_CLI}" compose --os-name ubuntu --os-version 20.04 2>&1) + local exit_code=$? + + daq_assert_failure $exit_code "compose should fail without os-arch" || return 1 + return 0 +} + +test-cli-compose-invalid-combination() { + local result + result=$("${PLATFORM_FORMAT_CLI}" compose --os-name ubuntu --os-version 99.99 --os-arch x86_64 2>&1) + local exit_code=$? + + daq_assert_failure $exit_code "compose should fail for invalid version" || return 1 + return 0 +} + +test-cli-list-platforms() { + local result + result=$("${PLATFORM_FORMAT_CLI}" --list-platforms) + local exit_code=$? + + daq_assert_success $exit_code "list-platforms should succeed" || return 1 + daq_assert_not_empty "$result" "Should return platforms" || return 1 + return 0 +} + +test-cli-list-platforms-contains-ubuntu() { + local result + result=$("${PLATFORM_FORMAT_CLI}" --list-platforms) + + daq_assert_contains "ubuntu20.04-x86_64" "$result" "Should contain Ubuntu" || return 1 + return 0 +} + +test-cli-list-platforms-contains-windows() { + local result + result=$("${PLATFORM_FORMAT_CLI}" --list-platforms) + + daq_assert_contains "win64" "$result" "Should contain Windows 64-bit" || return 1 + return 0 +} + +test-cli-list-platforms-count() { + local count + count=$("${PLATFORM_FORMAT_CLI}" --list-platforms | wc -l) + + # Expected: 32 platforms + # Ubuntu: 3 versions × 2 archs = 6 + # Debian: 5 versions × 2 archs = 10 + # macOS: 7 versions × 2 archs = 14 + # Windows: 2 archs = 2 + # Total: 32 + daq_assert_num_equals 32 "$count" "Should have exactly 32 platforms" || return 1 + return 0 +} diff --git a/tests/shell/bash/suites/test-version-format-api.sh b/tests/shell/bash/suites/test-version-format-api.sh new file mode 100644 index 0000000..1559b16 --- /dev/null +++ b/tests/shell/bash/suites/test-version-format-api.sh @@ -0,0 +1,364 @@ +#!/usr/bin/env bash +# test-version-format-api.sh - API tests for version-format.sh public functions +# +# Tests all public API functions (daq_version_*): +# - daq_version_compose +# - daq_version_parse +# - daq_version_validate +# - daq_version_extract + +# Source the script under test +source "${__DAQ_TESTS_SCRIPTS_DIR}/version-format.sh" + +test-compose-basic-release() { + local result + result=$(daq_version_compose --major 1 --minor 2 --patch 3) + local exit_code=$? + + daq_assert_success $exit_code "compose should succeed" || return 1 + daq_assert_equals "v1.2.3" "$result" "Basic release version mismatch" || return 1 + return 0 +} + +test-compose-release-without-prefix() { + local result + result=$(daq_version_compose --major 1 --minor 2 --patch 3 --exclude-prefix) + local exit_code=$? + + daq_assert_success $exit_code "compose should succeed" || return 1 + daq_assert_equals "1.2.3" "$result" "Release without prefix mismatch" || return 1 + return 0 +} + +test-compose-rc-with-prefix() { + local result + result=$(daq_version_compose --major 1 --minor 2 --patch 3 --suffix rc) + local exit_code=$? + + daq_assert_success $exit_code "compose should succeed" || return 1 + daq_assert_equals "v1.2.3-rc" "$result" "RC with prefix mismatch" || return 1 + return 0 +} + +test-compose-rc-without-prefix() { + local result + result=$(daq_version_compose --major 1 --minor 2 --patch 3 --suffix rc --exclude-prefix) + local exit_code=$? + + daq_assert_success $exit_code "compose should succeed" || return 1 + daq_assert_equals "1.2.3-rc" "$result" "RC without prefix mismatch" || return 1 + return 0 +} + +test-compose-hash-with-prefix() { + local result + result=$(daq_version_compose --major 1 --minor 2 --patch 3 --hash a1b2c3d) + local exit_code=$? + + daq_assert_success $exit_code "compose should succeed" || return 1 + daq_assert_equals "v1.2.3-a1b2c3d" "$result" "Hash with prefix mismatch" || return 1 + return 0 +} + +test-compose-hash-without-prefix() { + local result + result=$(daq_version_compose --major 1 --minor 2 --patch 3 --hash a1b2c3d --exclude-prefix) + local exit_code=$? + + daq_assert_success $exit_code "compose should succeed" || return 1 + daq_assert_equals "1.2.3-a1b2c3d" "$result" "Hash without prefix mismatch" || return 1 + return 0 +} + +test-compose-with-format-release() { + local result + result=$(daq_version_compose --major 1 --minor 2 --patch 3 --format "vX.YY.Z") + local exit_code=$? + + daq_assert_success $exit_code "compose should succeed" || return 1 + daq_assert_equals "v1.2.3" "$result" "Format release mismatch" || return 1 + return 0 +} + +test-compose-with-format-rc() { + local result + result=$(daq_version_compose --major 1 --minor 2 --patch 3 --suffix rc --format "X.YY.Z-rc") + local exit_code=$? + + daq_assert_success $exit_code "compose should succeed" || return 1 + daq_assert_equals "1.2.3-rc" "$result" "Format RC mismatch" || return 1 + return 0 +} + +test-compose-missing-major() { + local result + result=$(daq_version_compose --minor 2 --patch 3 2>&1) + local exit_code=$? + + daq_assert_failure $exit_code "compose should fail with missing major" || return 1 + return 0 +} + +test-parse-basic-release() { + local result + result=$(daq_version_parse "v1.2.3") + local exit_code=$? + + daq_assert_success $exit_code "parse should succeed" || return 1 + daq_assert_equals "1 2 3 v" "$result" "Parsed components mismatch" || return 1 + return 0 +} + +test-parse-release-without-prefix() { + local result + result=$(daq_version_parse "1.2.3") + local exit_code=$? + + daq_assert_success $exit_code "parse should succeed" || return 1 + daq_assert_equals "1 2 3 " "$result" "Parsed components without prefix mismatch" || return 1 + return 0 +} + +test-parse-rc-version() { + local result + result=$(daq_version_parse "v1.2.3-rc") + local exit_code=$? + + daq_assert_success $exit_code "parse should succeed" || return 1 + daq_assert_equals "1 2 3 rc v" "$result" "Parsed RC components mismatch" || return 1 + return 0 +} + +test-parse-hash-version() { + local result + result=$(daq_version_parse "v1.2.3-a1b2c3d") + local exit_code=$? + + daq_assert_success $exit_code "parse should succeed" || return 1 + daq_assert_equals "1 2 3 a1b2c3d v" "$result" "Parsed hash components mismatch" || return 1 + return 0 +} + +test-parse-extract-major() { + local result + result=$(daq_version_parse "v1.2.3" --major) + local exit_code=$? + + daq_assert_success $exit_code "parse should succeed" || return 1 + daq_assert_equals "1" "$result" "Major version mismatch" || return 1 + return 0 +} + +test-parse-extract-minor() { + local result + result=$(daq_version_parse "v1.2.3" --minor) + local exit_code=$? + + daq_assert_success $exit_code "parse should succeed" || return 1 + daq_assert_equals "2" "$result" "Minor version mismatch" || return 1 + return 0 +} + +test-parse-extract-patch() { + local result + result=$(daq_version_parse "v1.2.3" --patch) + local exit_code=$? + + daq_assert_success $exit_code "parse should succeed" || return 1 + daq_assert_equals "3" "$result" "Patch version mismatch" || return 1 + return 0 +} + +test-parse-extract-suffix() { + local result + result=$(daq_version_parse "v1.2.3-rc" --suffix) + local exit_code=$? + + daq_assert_success $exit_code "parse should succeed" || return 1 + daq_assert_equals "rc" "$result" "Suffix mismatch" || return 1 + return 0 +} + +test-parse-extract-hash() { + local result + result=$(daq_version_parse "v1.2.3-a1b2c3d" --hash) + local exit_code=$? + + daq_assert_success $exit_code "parse should succeed" || return 1 + daq_assert_equals "a1b2c3d" "$result" "Hash mismatch" || return 1 + return 0 +} + +test-parse-extract-prefix() { + local result + result=$(daq_version_parse "v1.2.3" --prefix) + local exit_code=$? + + daq_assert_success $exit_code "parse should succeed" || return 1 + daq_assert_equals "v" "$result" "Prefix mismatch" || return 1 + return 0 +} + +test-parse-invalid-version() { + local result + result=$(daq_version_parse "invalid-version" 2>&1) + local exit_code=$? + + daq_assert_failure $exit_code "parse should fail with invalid version" || return 1 + return 0 +} + +test-validate-basic-release() { + daq_version_validate "v1.2.3" >/dev/null 2>&1 + local exit_code=$? + + daq_assert_success $exit_code "validate should succeed for basic release" || return 1 + return 0 +} + +test-validate-release-without-prefix() { + daq_version_validate "1.2.3" >/dev/null 2>&1 + local exit_code=$? + + daq_assert_success $exit_code "validate should succeed for release without prefix" || return 1 + return 0 +} + +test-validate-rc() { + daq_version_validate "v1.2.3-rc" >/dev/null 2>&1 + local exit_code=$? + + daq_assert_success $exit_code "validate should succeed for RC" || return 1 + return 0 +} + +test-validate-hash() { + daq_version_validate "v1.2.3-a1b2c3d" >/dev/null 2>&1 + local exit_code=$? + + daq_assert_success $exit_code "validate should succeed for hash version" || return 1 + return 0 +} + +test-validate-with-format-match() { + daq_version_validate "v1.2.3" --format "vX.YY.Z" >/dev/null 2>&1 + local exit_code=$? + + daq_assert_success $exit_code "validate should succeed for matching format" || return 1 + return 0 +} + +test-validate-with-format-mismatch() { + daq_version_validate "v1.2.3" --format "X.YY.Z" >/dev/null 2>&1 + local exit_code=$? + + daq_assert_failure $exit_code "validate should fail for mismatching format" || return 1 + return 0 +} + +test-validate-is-release-true() { + daq_version_validate "v1.2.3" --is-release >/dev/null 2>&1 + local exit_code=$? + + daq_assert_success $exit_code "validate should succeed for release check" || return 1 + return 0 +} + +test-validate-is-release-false() { + daq_version_validate "v1.2.3-rc" --is-release >/dev/null 2>&1 + local exit_code=$? + + daq_assert_failure $exit_code "validate should fail for non-release" || return 1 + return 0 +} + +test-validate-is-rc-true() { + daq_version_validate "v1.2.3-rc" --is-rc >/dev/null 2>&1 + local exit_code=$? + + daq_assert_success $exit_code "validate should succeed for RC check" || return 1 + return 0 +} + +test-validate-is-rc-false() { + daq_version_validate "v1.2.3" --is-rc >/dev/null 2>&1 + local exit_code=$? + + daq_assert_failure $exit_code "validate should fail for non-RC" || return 1 + return 0 +} + +test-validate-is-dev-true() { + daq_version_validate "v1.2.3-a1b2c3d" --is-dev >/dev/null 2>&1 + local exit_code=$? + + daq_assert_success $exit_code "validate should succeed for dev check" || return 1 + return 0 +} + +test-validate-is-dev-false() { + daq_version_validate "v1.2.3" --is-dev >/dev/null 2>&1 + local exit_code=$? + + daq_assert_failure $exit_code "validate should fail for non-dev" || return 1 + return 0 +} + +test-extract-from-filename() { + local result + result=$(daq_version_extract "opendaq-v1.2.3-ubuntu20.04-x86_64.deb") + local exit_code=$? + + daq_assert_success $exit_code "extract should succeed" || return 1 + daq_assert_equals "v1.2.3" "$result" "Extracted version mismatch" || return 1 + return 0 +} + +test-extract-without-prefix() { + local result + result=$(daq_version_extract "package-1.2.3.zip") + local exit_code=$? + + daq_assert_success $exit_code "extract should succeed" || return 1 + daq_assert_equals "1.2.3" "$result" "Extracted version without prefix mismatch" || return 1 + return 0 +} + +test-extract-rc-version() { + local result + result=$(daq_version_extract "build-v1.2.3-rc-artifact.tar") + local exit_code=$? + + daq_assert_success $exit_code "extract should succeed" || return 1 + daq_assert_equals "v1.2.3-rc" "$result" "Extracted RC version mismatch" || return 1 + return 0 +} + +test-extract-hash-version() { + local result + result=$(daq_version_extract "commit-v1.2.3-a1b2c3d.log") + local exit_code=$? + + daq_assert_success $exit_code "extract should succeed" || return 1 + daq_assert_equals "v1.2.3-a1b2c3d" "$result" "Extracted hash version mismatch" || return 1 + return 0 +} + +test-extract-multiple-versions() { + local result + result=$(daq_version_extract "v1.0.0-to-v2.0.0-migration") + local exit_code=$? + + daq_assert_success $exit_code "extract should succeed" || return 1 + daq_assert_equals "v1.0.0" "$result" "Should extract first version" || return 1 + return 0 +} + +test-extract-no-version() { + local result + result=$(daq_version_extract "no-version-here.txt" 2>&1) + local exit_code=$? + + daq_assert_failure $exit_code "extract should fail when no version found" || return 1 + return 0 +} diff --git a/tests/shell/bash/suites/test-version-format-cli.sh b/tests/shell/bash/suites/test-version-format-cli.sh new file mode 100644 index 0000000..2f1602d --- /dev/null +++ b/tests/shell/bash/suites/test-version-format-cli.sh @@ -0,0 +1,274 @@ +#!/usr/bin/env bash +# test-version-format-cli.sh - CLI tests for version-format.sh +# +# Tests the command-line interface of version-format.sh: +# - Help and usage +# - compose command +# - parse command +# - validate command +# - extract command + +# CLI script path +VERSION_FORMAT_CLI="${__DAQ_TESTS_SCRIPTS_DIR}/version-format.sh" + +test-cli-help-flag() { + local result + result=$("${VERSION_FORMAT_CLI}" --help 2>&1) + local exit_code=$? + + daq_assert_success $exit_code "help should succeed" || return 1 + daq_assert_contains "Usage:" "$result" "Help should contain usage" || return 1 + return 0 +} + +test-cli-no-arguments() { + local result + result=$("${VERSION_FORMAT_CLI}" 2>&1) + local exit_code=$? + + daq_assert_failure $exit_code "no arguments should fail" || return 1 + daq_assert_contains "Usage:" "$result" "Should show usage on error" || return 1 + return 0 +} + +test-cli-compose-basic() { + local result + result=$("${VERSION_FORMAT_CLI}" compose --major 1 --minor 2 --patch 3) + local exit_code=$? + + daq_assert_success $exit_code "compose should succeed" || return 1 + daq_assert_equals "v1.2.3" "$result" "Basic compose result mismatch" || return 1 + return 0 +} + +test-cli-compose-with-rc() { + local result + result=$("${VERSION_FORMAT_CLI}" compose --major 1 --minor 2 --patch 3 --suffix rc) + local exit_code=$? + + daq_assert_success $exit_code "compose should succeed" || return 1 + daq_assert_equals "v1.2.3-rc" "$result" "RC compose result mismatch" || return 1 + return 0 +} + +test-cli-compose-with-hash() { + local result + result=$("${VERSION_FORMAT_CLI}" compose --major 1 --minor 2 --patch 3 --hash a1b2c3d) + local exit_code=$? + + daq_assert_success $exit_code "compose should succeed" || return 1 + daq_assert_equals "v1.2.3-a1b2c3d" "$result" "Hash compose result mismatch" || return 1 + return 0 +} + +test-cli-compose-exclude-prefix() { + local result + result=$("${VERSION_FORMAT_CLI}" compose --major 1 --minor 2 --patch 3 --exclude-prefix) + local exit_code=$? + + daq_assert_success $exit_code "compose should succeed" || return 1 + daq_assert_equals "1.2.3" "$result" "Compose without prefix result mismatch" || return 1 + return 0 +} + +test-cli-compose-missing-required() { + local result + result=$("${VERSION_FORMAT_CLI}" compose --major 1 2>&1) + local exit_code=$? + + daq_assert_failure $exit_code "compose should fail with missing arguments" || return 1 + return 0 +} + +test-cli-compose-invalid-suffix() { + local result + result=$("${VERSION_FORMAT_CLI}" compose --major 1 --minor 2 --patch 3 --suffix invalid 2>&1) + local exit_code=$? + + daq_assert_failure $exit_code "compose should fail with invalid suffix" || return 1 + return 0 +} + +test-cli-compose-with-format() { + local result + result=$("${VERSION_FORMAT_CLI}" compose --major 1 --minor 2 --patch 3 --format "X.YY.Z") + local exit_code=$? + + daq_assert_success $exit_code "compose should succeed" || return 1 + daq_assert_equals "1.2.3" "$result" "Format compose result mismatch" || return 1 + return 0 +} + +test-cli-parse-basic() { + local result + result=$("${VERSION_FORMAT_CLI}" parse v1.2.3) + local exit_code=$? + + daq_assert_success $exit_code "parse should succeed" || return 1 + daq_assert_equals "1 2 3 v" "$result" "Parse result mismatch" || return 1 + return 0 +} + +test-cli-parse-extract-major() { + local result + result=$("${VERSION_FORMAT_CLI}" parse v1.2.3 --major) + local exit_code=$? + + daq_assert_success $exit_code "parse should succeed" || return 1 + daq_assert_equals "1" "$result" "Major parse result mismatch" || return 1 + return 0 +} + +test-cli-parse-extract-minor() { + local result + result=$("${VERSION_FORMAT_CLI}" parse v1.2.3 --minor) + local exit_code=$? + + daq_assert_success $exit_code "parse should succeed" || return 1 + daq_assert_equals "2" "$result" "Minor parse result mismatch" || return 1 + return 0 +} + +test-cli-parse-extract-patch() { + local result + result=$("${VERSION_FORMAT_CLI}" parse v1.2.3 --patch) + local exit_code=$? + + daq_assert_success $exit_code "parse should succeed" || return 1 + daq_assert_equals "3" "$result" "Patch parse result mismatch" || return 1 + return 0 +} + +test-cli-parse-rc-version() { + local result + result=$("${VERSION_FORMAT_CLI}" parse v1.2.3-rc --suffix) + local exit_code=$? + + daq_assert_success $exit_code "parse should succeed" || return 1 + daq_assert_equals "rc" "$result" "RC suffix parse result mismatch" || return 1 + return 0 +} + +test-cli-parse-hash-version() { + local result + result=$("${VERSION_FORMAT_CLI}" parse v1.2.3-a1b2c3d --hash) + local exit_code=$? + + daq_assert_success $exit_code "parse should succeed" || return 1 + daq_assert_equals "a1b2c3d" "$result" "Hash parse result mismatch" || return 1 + return 0 +} + +test-cli-parse-invalid() { + local result + result=$("${VERSION_FORMAT_CLI}" parse invalid 2>&1) + local exit_code=$? + + daq_assert_failure $exit_code "parse should fail with invalid version" || return 1 + return 0 +} + +test-cli-validate-valid() { + "${VERSION_FORMAT_CLI}" validate v1.2.3 >/dev/null 2>&1 + local exit_code=$? + + daq_assert_success $exit_code "validate should succeed for valid version" || return 1 + return 0 +} + +test-cli-validate-invalid() { + "${VERSION_FORMAT_CLI}" validate not-valid >/dev/null 2>&1 + local exit_code=$? + + daq_assert_failure $exit_code "validate should fail for invalid version" || return 1 + return 0 +} + +test-cli-validate-with-format-match() { + "${VERSION_FORMAT_CLI}" validate v1.2.3 --format "vX.YY.Z" >/dev/null 2>&1 + local exit_code=$? + + daq_assert_success $exit_code "validate should succeed for matching format" || return 1 + return 0 +} + +test-cli-validate-with-format-mismatch() { + "${VERSION_FORMAT_CLI}" validate v1.2.3 --format "X.YY.Z" >/dev/null 2>&1 + local exit_code=$? + + daq_assert_failure $exit_code "validate should fail for mismatching format" || return 1 + return 0 +} + +test-cli-validate-is-release() { + "${VERSION_FORMAT_CLI}" validate v1.2.3 --is-release >/dev/null 2>&1 + local exit_code=$? + + daq_assert_success $exit_code "validate should succeed for release" || return 1 + return 0 +} + +test-cli-validate-is-rc-true() { + "${VERSION_FORMAT_CLI}" validate v1.2.3-rc --is-rc >/dev/null 2>&1 + local exit_code=$? + + daq_assert_success $exit_code "validate should succeed for RC" || return 1 + return 0 +} + +test-cli-validate-is-dev-true() { + "${VERSION_FORMAT_CLI}" validate v1.2.3-a1b2c3d --is-dev >/dev/null 2>&1 + local exit_code=$? + + daq_assert_success $exit_code "validate should succeed for dev version" || return 1 + return 0 +} + +test-cli-extract-from-text() { + local result + result=$("${VERSION_FORMAT_CLI}" extract "opendaq-v1.2.3-linux.tar.gz") + local exit_code=$? + + daq_assert_success $exit_code "extract should succeed" || return 1 + daq_assert_equals "v1.2.3" "$result" "Extract result mismatch" || return 1 + return 0 +} + +test-cli-extract-no-version() { + local result + result=$("${VERSION_FORMAT_CLI}" extract "no-version.txt" 2>&1) + local exit_code=$? + + daq_assert_failure $exit_code "extract should fail when no version" || return 1 + return 0 +} + +test-cli-extract-rc() { + local result + result=$("${VERSION_FORMAT_CLI}" extract "opendaq-v1.2.3-rc-ubuntu20.04-x86_64.deb") + local exit_code=$? + + daq_assert_success $exit_code "extract should succeed" || return 1 + daq_assert_equals "v1.2.3-rc" "$result" "RC extract result mismatch" || return 1 + return 0 +} + +test-cli-extract-hash() { + local result + result=$("${VERSION_FORMAT_CLI}" extract "opendaq-v1.2.3-a1b2c3d-ubuntu20.04-x86_64.deb") + local exit_code=$? + + daq_assert_success $exit_code "extract should succeed" || return 1 + daq_assert_equals "v1.2.3-a1b2c3d" "$result" "Hash extract result mismatch" || return 1 + return 0 +} + +test-cli-extract-multiple-versions() { + local result + result=$("${VERSION_FORMAT_CLI}" extract "v1.2.3-to-v5.6.7") + local exit_code=$? + + daq_assert_success $exit_code "extract should succeed" || return 1 + daq_assert_equals "v1.2.3" "$result" "Should extract first version" || return 1 + return 0 +}