Skip to content

Skip test_key_file_permissions on Windows #49

Skip test_key_file_permissions on Windows

Skip test_key_file_permissions on Windows #49

Workflow file for this run

name: Terradev CI (Professional)
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main, develop ]
workflow_dispatch:
inputs:
run_integration:
description: 'Run live integration tests'
required: false
default: false
type: boolean
test_provider:
description: 'Specific provider to test'
required: false
default: 'all'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
COVERAGE_THRESHOLD: 60
PYTHONUNBUFFERED: 1
jobs:
# === Dependency Security Scanning ===
dependency-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install pip-audit
run: pip install pip-audit
- name: Run dependency vulnerability scan
run: pip-audit --desc --format json > dependency-report.json || true
- name: Upload dependency report
uses: actions/upload-artifact@v4
with:
name: dependency-report
path: dependency-report.json
# === Code Quality (Linting) ===
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install linting tools
run: |
pip install mypy ruff black isort
- name: Black formatting check
run: black --check --fast --target-version py311 terradev_cli/ tests/ || true
- name: isort import ordering check
run: isort --check-only terradev_cli/ tests/ || true
- name: MyPy strict type checking
run: mypy terradev_cli/ --strict --ignore-missing-imports || true
- name: Ruff linting (BLE, F401, E501 - auto-fixable)
run: ruff check terradev_cli/ tests/ --fix || true
continue-on-error: true
# === Rust Extension Build ===
build-rust:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Cache Rust build artifacts
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
rust/target
key: ${{ runner.os }}-cargo-${{ hashFiles('rust/Cargo.lock') }}
restore-keys: ${{ runner.os }}-cargo-
- name: Install maturin
run: pip install "maturin>=1.4,<2.0"
- name: Build Rust extensions
run: bash scripts/build_rust.sh --dev
- name: Verify imports
run: |
python -c "
import terradev_dag_executor

Check failure on line 110 in .github/workflows/ci-improved.yml

View workflow run for this annotation

GitHub Actions / .github/workflows/ci-improved.yml

Invalid workflow file

You have an error in your yaml syntax on line 110
import terradev_credential_vault
import terradev_gpu_topology
import terradev_semantic_router
print('All Rust extensions imported successfully')
"
# === Unit Tests (Linux) ===
test-linux:
runs-on: ubuntu-latest
needs: [build-rust]
strategy:
matrix:
python-version: ['3.10', '3.11', '3.12', '3.13']
env:
TERRADEV_SKIP_ONBOARDING: "1"
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/checkout@v4
with:
submodules: false
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'
- name: Clear pip cache
run: |
pip cache purge
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Cache Rust build artifacts
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
rust/target
key: ${{ runner.os }}-cargo-${{ matrix.python-version }}-${{ hashFiles('rust/Cargo.lock') }}
restore-keys: ${{ runner.os }}-cargo-
- name: Install package + test deps
run: |
pip install -e ".[dev]"
pip install pytest pytest-asyncio pytest-cov pytest-xdist
pip install "maturin>=1.4,<2.0"
- name: Build and install Rust extensions
run: bash scripts/build_rust.sh --dev
- name: CLI smoke test
run: |
terradev --version
terradev --help
- name: Check for legacy duplicate files
run: |
if find terradev_cli -type f \( -name "*_old.py" -o -name "*_backup.py" -o -name "*_fixed.py" -o -name "*_simple.py" -o -name "*_final.py" -o -name "*_clean.py" -o -name "*_tiered.py" \) | grep -q .; then
echo "ERROR: Legacy duplicate files found"
exit 1
fi
- name: Run unit tests with coverage
run: |
timeout 600 pytest tests/ -v --tb=short \
--cov=terradev_cli --cov-report=xml --cov-report=html \
--cov-fail-under=${{ env.COVERAGE_THRESHOLD }} \
--ignore=tests/test_mcp_handlers.py
- name: Provider conformance tests
run: |
timeout 300 pytest tests/test_provider_conformance.py -v --tb=short
- name: CLI-specific coverage report
run: |
echo "=== CLI Module Coverage ==="
timeout 300 pytest tests/ -v --tb=short --cov=terradev_cli.cli --cov-report=term-missing --ignore=tests/test_mcp_handlers.py || true
echo "=========================="
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v4
with:
file: ./coverage.xml
flags: unittests-linux-${{ matrix.python-version }}
name: codecov-linux-${{ matrix.python-version }}
fail_ci_if_error: false
continue-on-error: true
# === CLI Functionality Tests ===
cli-functionality:
runs-on: ubuntu-latest
needs: [lint]
env:
TERRADEV_SKIP_ONBOARDING: "1"
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install package
run: pip install -e ".[dev]"
- name: Test CLI help commands
run: |
terradev --help
terradev quote --help
terradev provision --help
terradev status --help
terradev configure --help
- name: Test quote command with invalid GPU
run: |
OUTPUT=$(terradev quote -g invalid_gpu_name 2>&1 || true)
echo "$OUTPUT"
if echo "$OUTPUT" | grep -q "ERROR\|error\|No quotes"; then
echo "✓ Error handling works for invalid GPU"
else
echo "✗ Error handling failed for invalid GPU"
exit 1
fi
- name: Test quote command without credentials
run: |
OUTPUT=$(terradev quote -g a100 -p runpod 2>&1 || true)
echo "$OUTPUT"
if echo "$OUTPUT" | grep -q "credentials\|configure\|No quotes"; then
echo "✓ Credential error prompt works"
else
echo "✗ Credential error prompt failed"
exit 1
fi
- name: Test configure command validation
run: |
# Test configure without provider
OUTPUT=$(terradev configure 2>&1 || true)
if echo "$OUTPUT" | grep -q "provider\|required"; then
echo "✓ Configure validation works"
else
echo "✗ Configure validation failed"
exit 1
fi
- name: Test status command without instances
run: |
OUTPUT=$(terradev status 2>&1 || true)
echo "$OUTPUT"
# Should handle gracefully even with no instances
echo "✓ Status command handles empty state"
# === Error Prompt Validation ===
error-prompt-tests:
runs-on: ubuntu-latest
needs: [lint]
env:
TERRADEV_SKIP_ONBOARDING: "1"
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install package
run: pip install -e ".[dev]"
- name: Test missing credentials error message
run: |
rm -rf ~/.terradev/credentials.json 2>/dev/null || true
OUTPUT=$(terradev quote -g a100 -p runpod 2>&1 || true)
echo "$OUTPUT"
# Check for helpful error message
if echo "$OUTPUT" | grep -qi "configure\|credentials\|setup"; then
echo "✓ Helpful credentials error message"
else
echo "✗ Unhelpful error message"
exit 1
fi
- name: Test invalid provider error message
run: |
OUTPUT=$(terradev quote -g a100 -p nonexistent_provider 2>&1 || true)
echo "$OUTPUT"
if echo "$OUTPUT" | grep -qi "provider\|available\|supported"; then
echo "✓ Helpful provider error message"
else
echo "✗ Unhelpful provider error message"
exit 1
fi
- name: Test malformed GPU type error
run: |
OUTPUT=$(terradev quote -g "" 2>&1 || true)
echo "$OUTPUT"
if echo "$OUTPUT" | grep -qi "required\|gpu\|type"; then
echo "✓ Helpful GPU type error message"
else
echo "✗ Unhelpful GPU type error message"
exit 1
fi
- name: Test provision without credentials error
run: |
OUTPUT=$(terradev provision -g a100 2>&1 || true)
echo "$OUTPUT"
if echo "$OUTPUT" | grep -qi "credentials\|configure\|quote"; then
echo "✓ Helpful provision error message"
else
echo "✗ Unhelpful provision error message"
exit 1
fi
# === Provider Mock Tests ===
provider-mock-tests:
runs-on: ubuntu-latest
needs: [lint]
env:
TERRADEV_SKIP_ONBOARDING: "1"
MOCK_MODE: "true"
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install package
run: pip install -e ".[dev]"
- name: Test provider imports
run: |
python -c "
from terradev_cli.providers import LambdaLabsProvider, RunPodProvider
from terradev_cli.providers.base_provider import BaseProvider
from terradev_cli.providers.types import QuoteRequest, ProvisionRequest
print('✓ All provider imports successful')
"
- name: Test provider initialization
run: |
python -c "
from terradev_cli.providers import LambdaLabsProvider, RunPodProvider
lambda_provider = LambdaLabsProvider({})
runpod_provider = RunPodProvider({})
print('✓ Provider initialization successful')
"
- name: Test GPU catalog normalization
run: |
python -c "
from terradev_cli.providers.gpu_catalog import normalize
result = normalize('A100')
assert result is not None, 'GPU normalization failed'
print('✓ GPU catalog normalization works')
"
- name: Test typed API contracts
run: |
python -c "
from terradev_cli.providers.types import QuoteRequest, ProvisionRequest, GPUDescriptor, GPUVendor
gpu = GPUDescriptor(name='A100', vendor=GPUVendor.NVIDIA, vram_gb=80)
quote_req = QuoteRequest(gpu=gpu, region='us-east-1')
print('✓ Typed API contracts work')
"
# === Package Installation Tests ===
package-installation:
runs-on: ubuntu-latest
needs: [build]
strategy:
matrix:
python-version: ['3.10', '3.11', '3.12', '3.13']
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Download built package
uses: actions/download-artifact@v4
with:
name: dist
path: dist/
- name: Install from wheel
run: |
pip install dist/*.whl
terradev --version
- name: Test CLI after installation
run: |
terradev --help
terradev quote --help
- name: Test module imports
run: |
python -c "import terradev_cli; print('✓ terradev_cli imports')"
python -c "from terradev_cli.providers import BaseProvider; print('✓ providers imports')"
python -c "from terradev_cli.providers.types import Quote; print('✓ types imports')"
- name: Uninstall and reinstall from sdist
run: |
pip uninstall -y terradev-cli
pip install dist/*.tar.gz
terradev --version
# === Unit Tests (Windows) ===
test-windows:
runs-on: windows-latest
strategy:
matrix:
python-version: ['3.10', '3.11', '3.12']
env:
TERRADEV_SKIP_ONBOARDING: "1"
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'
- name: Install package + test deps
run: |
pip install -e ".[dev]"
pip install pytest pytest-asyncio pytest-cov
- name: CLI smoke test
run: |
terradev --version
terradev --help
- name: Run unit tests (Windows)
run: pytest tests/ -v --tb=short --ignore=tests/test_mcp_handlers.py --ignore=tests/test_integration.py
# === Unit Tests (macOS) ===
test-macos:
runs-on: macos-latest
strategy:
matrix:
python-version: ['3.10', '3.11', '3.12']
env:
TERRADEV_SKIP_ONBOARDING: "1"
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'
- name: Install package + test deps
run: |
pip install -e ".[dev]"
pip install pytest pytest-asyncio pytest-cov
- name: CLI smoke test
run: |
terradev --version
terradev --help
- name: Run unit tests (macOS)
run: pytest tests/ -v --tb=short --ignore=tests/test_mcp_handlers.py --ignore=tests/test_integration.py
# === Build & Verify Package ===
build:
runs-on: ubuntu-latest
needs: [lint, test-linux]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Build wheel + sdist
run: |
pip install build
python -m build
- name: Verify package contents
run: |
python -c "
import zipfile
import glob
whl_file = glob.glob('dist/*.whl')[0]
z = zipfile.ZipFile(whl_file)
files = z.namelist()
required = [
'terradev_cli/providers/',
'terradev_cli/terraform/',
'terradev_cli/kubernetes/',
'terradev_cli/README.md',
'terradev_cli/core/semantic_signals/routing_policy.yaml',
'terradev_cli/integrations/',
]
missing = [r for r in required if not any(r in f for f in files)]
if missing:
print(f'Missing required files: {missing}')
print('Files in wheel:')
for f in sorted(files):
print(f' {f}')
exit(1)
print('✓ Package contents verified')
print(f'Total files in wheel: {len(files)}')
"
- name: Verify built package installs
run: |
pip install dist/*.whl
terradev --version
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: dist
path: dist/
retention-days: 7
# === Security Scanning ===
security:
runs-on: ubuntu-latest
needs: [build]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install package for scan
run: pip install -e ".[dev]"
- name: Install + scan (non-blocking)
run: |
pip install bandit safety
bandit -r terradev_cli/ -f json -o bandit-report.json -ll || true
safety scan --json > safety-report.json || true
continue-on-error: true
- name: Upload security reports
uses: actions/upload-artifact@v4
with:
name: security-reports
path: |
bandit-report.json
safety-report.json
# === Integration Tests (Mock) ===
integration-mock:
runs-on: ubuntu-latest
needs: [lint]
env:
MOCK: "true"
TERRADEV_SKIP_ONBOARDING: "1"
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install package + test deps
run: |
pip install -e ".[dev]"
pip install pytest pytest-asyncio
- name: Run integration tests (mock)
run: python tests/test_integration.py --mock --suite providers
# === Live Integration Tests (Manual) ===
integration-live:
runs-on: ubuntu-latest
needs: [lint, security]
if: github.event_name == 'workflow_dispatch' && inputs.run_integration == true
env:
TERRADEV_SKIP_ONBOARDING: "1"
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_TEST_KEY }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_TEST_SECRET }}
RUNPOD_API_KEY: ${{ secrets.RUNPOD_TEST_KEY }}
VAST_API_KEY: ${{ secrets.VAST_TEST_KEY }}
LAMBDA_API_KEY: ${{ secrets.LAMBDA_TEST_KEY }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install package + test deps
run: |
pip install -e ".[dev]"
pip install pytest pytest-asyncio
- name: Run live integration tests
run: |
if [ "${{ inputs.test_provider }}" = "all" ]; then
python tests/test_integration.py --suite providers
else
python tests/test_integration.py --suite providers --provider ${{ inputs.test_provider }}
fi
continue-on-error: true
# === Performance Benchmarking ===
benchmark:
runs-on: ubuntu-latest
needs: [test-linux]
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install package + test deps
run: |
pip install -e ".[dev]"
pip install pytest pytest-asyncio
- name: Run performance benchmarks
run: python tests/test_integration.py --performance --suite performance
# === Publish to TestPyPI (Staging) ===
publish-test:
if: github.event_name == 'push' && github.ref == 'refs/heads/develop'
needs: [test-linux, test-windows, test-macos, build, security, cli-functionality, error-prompt-tests, provider-mock-tests, package-installation]
runs-on: ubuntu-latest
permissions:
id-token: write
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Build
run: |
pip install build
python -m build
- name: Publish to TestPyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
password: ${{ secrets.TEST_PYPI_API_TOKEN }}
repository-url: https://test.pypi.org/legacy/
- name: Install from TestPyPI and verify
run: |
pip install --index-url https://test.pypi.org/simple/ terradev-cli
terradev --version
terradev --help
# === Publish to PyPI (Production) ===
publish:
if: startsWith(github.ref, 'refs/tags/v')
needs: [test-linux, test-windows, test-macos, build, security, cli-functionality, error-prompt-tests, provider-mock-tests, package-installation]
runs-on: ubuntu-latest
permissions:
id-token: write
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Build
run: |
pip install build
python -m build
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
password: ${{ secrets.PYPI_API_TOKEN }}
- name: Install from PyPI and verify
run: |
pip install terradev-cli
terradev --version
terradev --help