name: Build and Test
# Top-level permissions set to read-only for OSSF Scorecard Token-Permissions check
# See: https://github.com/ossf/scorecard/blob/main/docs/checks.md#token-permissions
permissions:
contents: read
packages: read
on:
push:
branches: [main, develop]
tags:
- 'v*'
paths:
- 'src/**/*.py'
- 'tests/**/*.py'
- 'pyproject.toml'
- 'requirements.txt'
- 'Dockerfile'
- '.dockerignore'
- 'config/**/*.yml'
- '.github/workflows/*.yml'
pull_request:
branches: [main]
# Paths filter removed - workflows run on all PRs but skip jobs when only docs change
jobs:
check-changes:
name: Check for code changes
runs-on: ubuntu-latest
permissions:
contents: read
outputs:
has-code-changes: ${{ steps.is-tag.outputs.is-tag == 'true' || steps.filter.outputs.src == 'true' || steps.filter.outputs.tests == 'true' || steps.filter.outputs.config == 'true' || steps.filter.outputs.docker == 'true' }}
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- name: Check if tag push
id: is-tag
run: |
if [[ "${{ github.ref_type }}" == "tag" ]]; then
echo "is-tag=true" >> $GITHUB_OUTPUT
else
echo "is-tag=false" >> $GITHUB_OUTPUT
fi
- name: Check for code changes
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3
id: filter
with:
filters: |
src:
- 'src/**/*.py'
tests:
- 'tests/**/*.py'
config:
- 'pyproject.toml'
- 'requirements.txt'
- 'config/**/*.yml'
- '.github/workflows/*.yml'
docker:
- 'Dockerfile'
- '.dockerignore'
lint:
name: Lint
needs: check-changes
if: needs.check-changes.outputs.has-code-changes == 'true'
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- name: Set up Python
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: "3.11"
- name: Install dependencies
run: |
python -m pip install pip==24.0
pip install -e ".[dev]"
- name: Run ruff
run: ruff check src/ tests/
- name: Run black
run: black --check src/ tests/
- name: Run mypy
run: mypy src/ --ignore-missing-imports
continue-on-error: true
test:
needs: [check-changes, lint]
if: needs.check-changes.outputs.has-code-changes == 'true' && (needs.lint.result == 'success' || needs.lint.result == 'skipped')
name: Test Python ${{ matrix.python-version }}
runs-on: ubuntu-latest
permissions:
contents: read
strategy:
matrix:
python-version: ["3.11", "3.12", "3.13"]
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install pip==24.0
pip install -e ".[test]"
- name: Run tests
run: |
pytest --cov=mcp_ssh --cov-report=xml --cov-report=term
- name: Upload coverage
uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5
if: matrix.python-version == '3.11'
with:
files: ./coverage.xml
flags: unittests
continue-on-error: true
docker:
needs: [check-changes, lint]
if: needs.check-changes.outputs.has-code-changes == 'true' && (needs.lint.result == 'success' || needs.lint.result == 'skipped')
name: Docker Build
runs-on: ubuntu-latest
permissions:
contents: read
packages: read
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3
- name: Build Docker image
uses: docker/build-push-action@ca052bb54ab0790a636c9b5f226502c73d547a25 # v5
with:
context: .
push: false
load: true
tags: mcp-ssh-orchestrator:test
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Test Docker image
run: |
echo "Testing Docker image..."
docker images | grep mcp-ssh-orchestrator
docker run --rm mcp-ssh-orchestrator:test python -c "import mcp_ssh; print('MCP SSH module imported successfully')"
echo "Docker image test passed!"
skip-docs-only:
name: Skip (docs only)
needs: check-changes
if: needs.check-changes.outputs.has-code-changes != 'true'
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Skip workflow
run: echo "Documentation-only PR - build and test checks skipped"