Skip to content
37 changes: 34 additions & 3 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,42 @@
name: CI

on:
pull_request:
paths-ignore:
- "docs/**"
- "**/*.md"
push:
branches: [main]
pull_request:
paths-ignore:
- "docs/**"
- "**/*.md"
workflow_dispatch:

permissions:
contents: read
actions: read

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
py:
uses: ./.github/workflows/py-ci.yml
name: py (reusable)
uses: CoderDeltaLAN/ci-matrix-starter/.github/workflows/py-ci.yml@d4b4ac44b51d453ea2160c513d8eb20e24f3947e
with:
python_versions: '["3.11","3.12"]'
os: '["ubuntu-latest"]'
run_tests: true
secrets: inherit

ts:
uses: ./.github/workflows/ts-ci.yml
if: ${{ hashFiles('**/package.json') != '' }}
name: ts (reusable)
uses: CoderDeltaLAN/ci-matrix-starter/.github/workflows/ts-ci.yml@d4b4ac44b51d453ea2160c513d8eb20e24f3947e
with:
node_versions: '["22.x"]'
os: '["ubuntu-latest"]'
package_manager: "pnpm"
test_command: "npm test --if-present"
secrets: inherit
83 changes: 57 additions & 26 deletions .github/workflows/py-ci.yml
Original file line number Diff line number Diff line change
@@ -1,35 +1,66 @@
name: py-ci (reusable)
name: "Python CI (reusable)"

on:
workflow_call: {}
workflow_call:
inputs:
python_versions:
type: string
required: false
default: '["3.11","3.12"]'
os:
type: string
required: false
default: '["ubuntu-latest"]'
run_tests:
type: boolean
required: false
default: true

permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
py:
name: Python (${{ matrix.python-version }})
runs-on: ubuntu-latest
lint-test:
name: py${{ matrix.python }} • ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
python-version: ["3.11", "3.12"]
python: ${{ fromJson(inputs.python_versions) }}
os: ${{ fromJson(inputs.os) }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Upgrade pip
run: python -m pip install -U pip
- name: Install tools
run: pip install pytest mypy ruff black
- name: Install project (editable)
run: pip install -e .
- name: Ruff
run: ruff check .
- name: Black (check)
run: black --check .
- name: Pytest
python-version: ${{ matrix.python }}
cache: "pip"
- name: Install dependencies
shell: bash
run: |
python -m pip install --upgrade pip
if [[ -f pyproject.toml ]]; then
pip install -e ".[dev]" || true
fi
pip install ruff black pytest mypy
- name: Lint
shell: bash
run: |
ruff check .
black . --check
mypy || true
- name: Tests
if: inputs.run_tests == true
env:
PYTHONPATH: src
run: pytest -q
- name: mypy (solo 3.12)
if: ${{ matrix.python-version == '3.12' }}
run: mypy .
shell: bash
run: |
pytest -q || rc=$?
if [[ "${rc:-0}" -eq 5 ]]; then
echo "No tests collected; treating as success."
rc=0
fi
exit "${rc:-0}"
114 changes: 81 additions & 33 deletions .github/workflows/ts-ci.yml
Original file line number Diff line number Diff line change
@@ -1,54 +1,102 @@
name: ts-ci
name: "TypeScript/Node CI (reusable)"

on:
workflow_call:
inputs:
node-version:
node_versions:
type: string
required: false
default: '["22.x"]'
os:
type: string
required: false
default: '["ubuntu-latest"]'
package_manager:
type: string
required: false
default: "pnpm"
test_command:
type: string
default: "20"
workflow_dispatch:
required: false
default: "npm test --if-present"

permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
ts:
runs-on: ubuntu-latest
build:
if: ${{ hashFiles('**/package.json') != '' }}
name: node ${{ matrix.node }} • ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
node: ${{ fromJson(inputs.node_versions) }}
os: ${{ fromJson(inputs.os) }}
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
# version: 9 # commented to defer to package.json packageManager
run_install: false
- uses: actions/checkout@v4

- name: Setup Node
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}
cache: pnpm
cache-dependency-path: pnpm-lock.yaml
node-version: ${{ matrix.node }}
cache: ${{ inputs.package_manager }}

- name: Install deps
- name: Enable Corepack (pnpm/yarn)
shell: bash
run: |
if [ -f pnpm-lock.yaml ]; then
pnpm install --frozen-lockfile
corepack enable || true
corepack prepare pnpm@latest --activate || true

- name: Install dependencies (lockfile-aware)
shell: bash
run: |
if [[ ! -f package.json ]]; then
echo "No package.json → skip install"; exit 0
fi
if [[ -f pnpm-lock.yaml ]]; then
pnpm i --frozen-lockfile
elif [[ -f package-lock.json ]]; then
npm ci
elif [[ -f yarn.lock ]]; then
yarn install --frozen-lockfile
else
pnpm install --no-frozen-lockfile
npm ci || npm i
fi

- name: ESLint
if: ${{ hashFiles('**/eslint.config.*', '**/.eslintrc*') != '' && hashFiles('**/*.{js,jsx,ts,tsx,mjs,cjs}') != '' }}
run: npx -y eslint . --ext .ts,.tsx,.js,.jsx,.mjs,.cjs || true
- name: Prettier (check if config/files present)
if: ${{ hashFiles('**/.prettierrc*','**/prettier.config.*','**/*.js','**/*.ts','**/*.tsx') != '' }}
shell: bash
run: npx -y prettier -c .

- name: ESLint (only if config exists)
if: ${{ hashFiles('**/.eslintrc*') != '' }}
shell: bash
run: npx -y eslint . --max-warnings=0

- name: Type check (only if tsconfig exists)
if: ${{ hashFiles('**/tsconfig.json') != '' }}
shell: bash
run: npx -y tsc --noEmit

- name: Prettier (check)
if: ${{ hashFiles('**/*.{js,jsx,ts,tsx,mjs,cjs}') != '' }}
- name: Tests
shell: bash
run: |
FILES="$(git ls-files '*.ts' '*.tsx' '*.js' '*.jsx' '*.mjs' '*.cjs' 2>/dev/null || true)"
if [ -n "$FILES" ]; then
npx -y prettier -c $FILES --ignore-path .prettierignore
if [[ -f package.json ]]; then
${{ inputs.test_command }}
else
echo "No JS/TS files to check."
echo "No package.json → skip tests"
fi

- name: Tests
run: pnpm test -r || npm test
- name: Build (only if script exists)
shell: bash
run: |
if [[ -f package.json ]] && node -e "process.exit(+(!!!(require('./package.json').scripts||{}).build))"; then
npm run build
else
echo "No build script; skipping."
fi
16 changes: 16 additions & 0 deletions docs/research/20250917-ci-base.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# CI base — 2025-09-17

Fuentes primarias y versiones actuales relevantes para este repo.

- Reusables (`on.workflow_call`, `inputs`, `secrets`). Ejemplos oficiales.
- `uses`: por ruta local o `{owner}/{repo}@ref`.
- `hashFiles()` para gates/caché.
- `ubuntu-latest` (Ubuntu 24.04).
- Node LTS 22, Python 3.13.x, pnpm 10.x, ESLint 9.x.
- Convención interna: `inputs` en snake_case.

Decisiones:

- Jobs pesados **no** se ejecutan si el cambio es solo en `docs/**` o `*.md`.
- TS job se salta si no hay `package.json`.
- Concurrency por `workflow+ref`.