Skip to content

feat: add initial ci.yml workflow #3

feat: add initial ci.yml workflow

feat: add initial ci.yml workflow #3

Workflow file for this run

name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
# Discover all projects in the repository
discover:
runs-on: ubuntu-latest
outputs:
node-projects: ${{ steps.find-projects.outputs.node-projects }}
poetry-projects: ${{ steps.find-projects.outputs.poetry-projects }}
uv-projects: ${{ steps.find-projects.outputs.uv-projects }}
pip-projects: ${{ steps.find-projects.outputs.pip-projects }}
steps:
- uses: actions/checkout@v4
- name: Find all projects
id: find-projects
run: |
# Find Node.js projects (have package.json but not in node_modules)
NODE_PROJECTS=$(find . -name "package.json" -not -path "*/node_modules/*" -exec dirname {} \; | sort -u | jq -R -s -c 'split("\n") | map(select(length > 0))')
echo "node-projects=$NODE_PROJECTS" >> $GITHUB_OUTPUT
# Find Poetry projects (have poetry.lock)
POETRY_PROJECTS=$(find . -name "poetry.lock" -exec dirname {} \; | sort -u | jq -R -s -c 'split("\n") | map(select(length > 0))')
echo "poetry-projects=$POETRY_PROJECTS" >> $GITHUB_OUTPUT
# Find uv projects (have uv.lock)
UV_PROJECTS=$(find . -name "uv.lock" -exec dirname {} \; | sort -u | jq -R -s -c 'split("\n") | map(select(length > 0))')
echo "uv-projects=$UV_PROJECTS" >> $GITHUB_OUTPUT
# Find pip projects (have requirements.txt but no poetry.lock or uv.lock)
PIP_PROJECTS=$(find . -name "requirements.txt" -exec dirname {} \; | while read dir; do
if [ ! -f "$dir/poetry.lock" ] && [ ! -f "$dir/uv.lock" ]; then
echo "$dir"
fi
done | sort -u | jq -R -s -c 'split("\n") | map(select(length > 0))')
echo "pip-projects=$PIP_PROJECTS" >> $GITHUB_OUTPUT
# Debug output
echo "Node.js projects: $NODE_PROJECTS"
echo "Poetry projects: $POETRY_PROJECTS"
echo "uv projects: $UV_PROJECTS"
echo "pip projects: $PIP_PROJECTS"
# Build and lint Node.js projects
node:
needs: discover
if: ${{ needs.discover.outputs.node-projects != '[]' && needs.discover.outputs.node-projects != '' }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
project: ${{ fromJson(needs.discover.outputs.node-projects) }}
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
- name: Get npm cache directory
id: npm-cache-dir
shell: bash
run: echo "dir=$(npm config get cache)" >> $GITHUB_OUTPUT
- name: Cache npm dependencies
uses: actions/cache@v4
with:
path: ${{ steps.npm-cache-dir.outputs.dir }}
key: ${{ runner.os }}-node-${{ hashFiles(format('{0}/package-lock.json', matrix.project), format('{0}/package.json', matrix.project)) }}
restore-keys: |
${{ runner.os }}-node-
- name: Install dependencies
working-directory: ${{ matrix.project }}
run: npm install
- name: Build
working-directory: ${{ matrix.project }}
run: |
if npm run build --if-present 2>/dev/null; then
echo "Build completed successfully"
else
echo "No build script found, skipping..."
fi
- name: Lint
working-directory: ${{ matrix.project }}
run: |
if npm run lint --if-present 2>/dev/null; then
echo "Lint completed successfully"
else
echo "No lint script found, skipping..."
fi
# Build and lint Poetry projects
poetry:
needs: discover
if: ${{ needs.discover.outputs.poetry-projects != '[]' && needs.discover.outputs.poetry-projects != '' }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
project: ${{ fromJson(needs.discover.outputs.poetry-projects) }}
steps:
- uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install Poetry
uses: snok/install-poetry@v1
with:
version: latest
virtualenvs-create: true
virtualenvs-in-project: true
- name: Cache Poetry dependencies
uses: actions/cache@v4
with:
path: ${{ matrix.project }}/.venv
key: ${{ runner.os }}-poetry-${{ hashFiles(format('{0}/poetry.lock', matrix.project)) }}
restore-keys: |
${{ runner.os }}-poetry-
- name: Install dependencies
working-directory: ${{ matrix.project }}
run: poetry install --no-interaction
- name: Lint with ruff (if available)
working-directory: ${{ matrix.project }}
run: |
if poetry run ruff check . 2>/dev/null; then
echo "Ruff lint completed"
elif poetry run flake8 . 2>/dev/null; then
echo "Flake8 lint completed"
else
echo "No Python linter found, skipping..."
fi
continue-on-error: true
- name: Type check with mypy (if available)
working-directory: ${{ matrix.project }}
run: |
if poetry run mypy . 2>/dev/null; then
echo "Type check completed"
else
echo "No type checker found, skipping..."
fi
continue-on-error: true
# Build and lint uv projects
uv:
needs: discover
if: ${{ needs.discover.outputs.uv-projects != '[]' && needs.discover.outputs.uv-projects != '' }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
project: ${{ fromJson(needs.discover.outputs.uv-projects) }}
steps:
- uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install uv
uses: astral-sh/setup-uv@v4
- name: Install dependencies
working-directory: ${{ matrix.project }}
run: uv sync
- name: Lint with ruff (if available)
working-directory: ${{ matrix.project }}
run: |
if uv run ruff check . 2>/dev/null; then
echo "Ruff lint completed"
elif uv run flake8 . 2>/dev/null; then
echo "Flake8 lint completed"
else
echo "No Python linter found, skipping..."
fi
continue-on-error: true
- name: Type check with mypy (if available)
working-directory: ${{ matrix.project }}
run: |
if uv run mypy . 2>/dev/null; then
echo "Type check completed"
else
echo "No type checker found, skipping..."
fi
continue-on-error: true
# Build and lint pip projects
pip:
needs: discover
if: ${{ needs.discover.outputs.pip-projects != '[]' && needs.discover.outputs.pip-projects != '' }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
project: ${{ fromJson(needs.discover.outputs.pip-projects) }}
steps:
- uses: actions/checkout@v4
- name: Detect Python version
id: python-version
working-directory: ${{ matrix.project }}
run: |
if [ -f ".python-version" ]; then
VERSION=$(cat .python-version | tr -d '[:space:]')
echo "version=$VERSION" >> $GITHUB_OUTPUT
else
echo "version=3.12" >> $GITHUB_OUTPUT
fi
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: ${{ steps.python-version.outputs.version }}
- name: Cache pip dependencies
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles(format('{0}/requirements.txt', matrix.project)) }}
restore-keys: |
${{ runner.os }}-pip-
- name: Install dependencies
working-directory: ${{ matrix.project }}
run: |
python -m pip install --upgrade pip
# Use project-level pip.conf if it exists
if [ -f "pip.conf" ]; then
export PIP_CONFIG_FILE="$(pwd)/pip.conf"
fi
pip install -r requirements.txt
- name: Install linters
run: pip install ruff flake8
continue-on-error: true
- name: Lint with ruff
working-directory: ${{ matrix.project }}
run: ruff check . || echo "Ruff lint skipped or failed"
continue-on-error: true
# Summary job to ensure all checks passed
ci-success:
needs: [node, poetry, uv, pip]
if: always()
runs-on: ubuntu-latest
steps:
- name: Check all jobs passed
run: |
if [ "${{ needs.node.result }}" == "failure" ] || \
[ "${{ needs.poetry.result }}" == "failure" ] || \
[ "${{ needs.uv.result }}" == "failure" ] || \
[ "${{ needs.pip.result }}" == "failure" ]; then
echo "One or more jobs failed"
exit 1
fi
echo "All CI checks passed!"