From af16b54f99c907536a5f32de1265fc40259b1d74 Mon Sep 17 00:00:00 2001 From: CoderDeltaLan Date: Wed, 17 Sep 2025 12:53:23 +0100 Subject: [PATCH 1/8] ci: solidify reusable Python/TS workflows; remove backup --- .github/workflows/py-ci.yml | 84 ++++++++++++++++++--------- .github/workflows/ts-ci.yml | 113 +++++++++++++++++++++++++----------- 2 files changed, 138 insertions(+), 59 deletions(-) diff --git a/.github/workflows/py-ci.yml b/.github/workflows/py-ci.yml index 7a63784..34750d9 100644 --- a/.github/workflows/py-ci.yml +++ b/.github/workflows/py-ci.yml @@ -1,35 +1,67 @@ -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=$? + # 5 = "no tests collected" → se considera éxito en plantilla + if [[ "${rc:-0}" -eq 5 ]]; then + echo "No tests collected; treating as success." + exit 0 + fi + exit "${rc:-0}" diff --git a/.github/workflows/ts-ci.yml b/.github/workflows/ts-ci.yml index 04d2185..6b9a36a 100644 --- a/.github/workflows/ts-ci.yml +++ b/.github/workflows/ts-ci.yml @@ -1,54 +1,101 @@ -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: + 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: Enable Corepack (pnpm/yarn) + shell: bash + run: | + corepack enable || true + corepack prepare pnpm@latest --activate || true - - name: Install deps + - name: Install dependencies (lockfile-aware) + shell: bash run: | - if [ -f pnpm-lock.yaml ]; then - pnpm install --frozen-lockfile + 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 i --package-lock-only + npm ci 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 [[ -n "${{ inputs.test-command }}" ]]; then + ${{ + inputs.test-command + }} else - echo "No JS/TS files to check." + echo "No test command provided; skipping." 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 From f2760ce2c79db1a4a2d0aa274c71c092c4816fe3 Mon Sep 17 00:00:00 2001 From: CoderDeltaLan Date: Wed, 17 Sep 2025 13:02:25 +0100 Subject: [PATCH 2/8] ci(ts): skip job when no package.json; harden install step --- .github/workflows/ts-ci.yml | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ts-ci.yml b/.github/workflows/ts-ci.yml index 6b9a36a..15019e3 100644 --- a/.github/workflows/ts-ci.yml +++ b/.github/workflows/ts-ci.yml @@ -29,6 +29,8 @@ concurrency: jobs: build: + # Solo corre si existe al menos un package.json en el repo + if: ${{ hashFiles('**/package.json') != '' }} name: node ${{ matrix.node }} • ${{ matrix.os }} runs-on: ${{ matrix.os }} strategy: @@ -54,6 +56,9 @@ jobs: - 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 @@ -61,8 +66,7 @@ jobs: elif [[ -f yarn.lock ]]; then yarn install --frozen-lockfile else - npm i --package-lock-only - npm ci + npm ci || npm i fi - name: Prettier (check if config/files present) @@ -83,12 +87,10 @@ jobs: - name: Tests shell: bash run: | - if [[ -n "${{ inputs.test-command }}" ]]; then - ${{ - inputs.test-command - }} + if [[ -f package.json ]]; then + ${{ inputs.test-command }} else - echo "No test command provided; skipping." + echo "No package.json → skip tests" fi - name: Build (only if script exists) From e9813bd6c6e6871a2cf665ad3f4c788c482138b0 Mon Sep 17 00:00:00 2001 From: CoderDeltaLan Date: Wed, 17 Sep 2025 13:03:46 +0100 Subject: [PATCH 3/8] ci: gate TS job at top-level CI when no package.json --- .github/workflows/build.yml | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 22896e8..f602d07 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,11 +1,34 @@ name: CI + on: + pull_request: push: branches: [main] - pull_request: workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: py: + name: py (reusable) uses: ./.github/workflows/py-ci.yml + with: + python-versions: '["3.11","3.12"]' + os: '["ubuntu-latest"]' + run-tests: true + ts: + # Solo crea el job si hay al menos un package.json en el repo + if: ${{ hashFiles('**/package.json') != '' }} + name: ts (reusable) uses: ./.github/workflows/ts-ci.yml + with: + node-versions: '["22.x"]' + os: '["ubuntu-latest"]' + package-manager: "pnpm" + test-command: "npm test --if-present" From 6997471d940c7dc1ddbe8d027aef377ae07809f0 Mon Sep 17 00:00:00 2001 From: CoderDeltaLan Date: Wed, 17 Sep 2025 13:12:33 +0100 Subject: [PATCH 4/8] ci: rename reusable inputs to snake_case and align build --- .github/workflows/build.yml | 11 +++++------ .github/workflows/py-ci.yml | 11 +++++------ .github/workflows/ts-ci.yml | 13 ++++++------- 3 files changed, 16 insertions(+), 19 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f602d07..028cbaa 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,17 +18,16 @@ jobs: name: py (reusable) uses: ./.github/workflows/py-ci.yml with: - python-versions: '["3.11","3.12"]' + python_versions: '["3.11","3.12"]' os: '["ubuntu-latest"]' - run-tests: true + run_tests: true ts: - # Solo crea el job si hay al menos un package.json en el repo if: ${{ hashFiles('**/package.json') != '' }} name: ts (reusable) uses: ./.github/workflows/ts-ci.yml with: - node-versions: '["22.x"]' + node_versions: '["22.x"]' os: '["ubuntu-latest"]' - package-manager: "pnpm" - test-command: "npm test --if-present" + package_manager: "pnpm" + test_command: "npm test --if-present" diff --git a/.github/workflows/py-ci.yml b/.github/workflows/py-ci.yml index 34750d9..c5e1471 100644 --- a/.github/workflows/py-ci.yml +++ b/.github/workflows/py-ci.yml @@ -3,7 +3,7 @@ name: "Python CI (reusable)" on: workflow_call: inputs: - python-versions: + python_versions: type: string required: false default: '["3.11","3.12"]' @@ -11,7 +11,7 @@ on: type: string required: false default: '["ubuntu-latest"]' - run-tests: + run_tests: type: boolean required: false default: true @@ -30,7 +30,7 @@ jobs: strategy: fail-fast: false matrix: - python: ${{ fromJson(inputs.python-versions) }} + python: ${{ fromJson(inputs.python_versions) }} os: ${{ fromJson(inputs.os) }} steps: - uses: actions/checkout@v4 @@ -53,15 +53,14 @@ jobs: black . --check mypy || true - name: Tests - if: inputs.run-tests == true + if: inputs.run_tests == true env: PYTHONPATH: src shell: bash run: | pytest -q || rc=$? - # 5 = "no tests collected" → se considera éxito en plantilla if [[ "${rc:-0}" -eq 5 ]]; then echo "No tests collected; treating as success." - exit 0 + rc=0 fi exit "${rc:-0}" diff --git a/.github/workflows/ts-ci.yml b/.github/workflows/ts-ci.yml index 15019e3..bb4a046 100644 --- a/.github/workflows/ts-ci.yml +++ b/.github/workflows/ts-ci.yml @@ -3,7 +3,7 @@ name: "TypeScript/Node CI (reusable)" on: workflow_call: inputs: - node-versions: + node_versions: type: string required: false default: '["22.x"]' @@ -11,11 +11,11 @@ on: type: string required: false default: '["ubuntu-latest"]' - package-manager: + package_manager: type: string required: false default: "pnpm" - test-command: + test_command: type: string required: false default: "npm test --if-present" @@ -29,14 +29,13 @@ concurrency: jobs: build: - # Solo corre si existe al menos un package.json en el repo if: ${{ hashFiles('**/package.json') != '' }} name: node ${{ matrix.node }} • ${{ matrix.os }} runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: - node: ${{ fromJson(inputs.node-versions) }} + node: ${{ fromJson(inputs.node_versions) }} os: ${{ fromJson(inputs.os) }} steps: - uses: actions/checkout@v4 @@ -45,7 +44,7 @@ jobs: uses: actions/setup-node@v4 with: node-version: ${{ matrix.node }} - cache: ${{ inputs.package-manager }} + cache: ${{ inputs.package_manager }} - name: Enable Corepack (pnpm/yarn) shell: bash @@ -88,7 +87,7 @@ jobs: shell: bash run: | if [[ -f package.json ]]; then - ${{ inputs.test-command }} + ${{ inputs.test_command }} else echo "No package.json → skip tests" fi From 942500f25e0db624afa92c0842a28adca97dab59 Mon Sep 17 00:00:00 2001 From: CoderDeltaLan Date: Wed, 17 Sep 2025 13:19:53 +0100 Subject: [PATCH 5/8] ci: call reusables via owner/repo@branch and inherit secrets --- .github/workflows/build.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 028cbaa..c372c95 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,18 +16,20 @@ concurrency: jobs: py: name: py (reusable) - uses: ./.github/workflows/py-ci.yml + uses: CoderDeltaLAN/ci-matrix-starter/.github/workflows/py-ci.yml@feat/ci-reusables-solid with: python_versions: '["3.11","3.12"]' os: '["ubuntu-latest"]' run_tests: true + secrets: inherit ts: if: ${{ hashFiles('**/package.json') != '' }} name: ts (reusable) - uses: ./.github/workflows/ts-ci.yml + uses: CoderDeltaLAN/ci-matrix-starter/.github/workflows/ts-ci.yml@feat/ci-reusables-solid with: node_versions: '["22.x"]' os: '["ubuntu-latest"]' package_manager: "pnpm" test_command: "npm test --if-present" + secrets: inherit From 72ae71e5eb9b2e873aa90949a837f1da9c41f11c Mon Sep 17 00:00:00 2001 From: CoderDeltaLan Date: Wed, 17 Sep 2025 13:38:55 +0100 Subject: [PATCH 6/8] docs: normalize EoF and whitespace in CI base note --- .github/workflows/build.yml | 12 ++++++++---- docs/research/20250917-ci-base.md | 16 ++++++++++++++++ 2 files changed, 24 insertions(+), 4 deletions(-) create mode 100644 docs/research/20250917-ci-base.md diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c372c95..6d8e515 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,8 +2,14 @@ name: CI on: pull_request: + paths-ignore: + - "docs/**" + - "**/*.md" push: branches: [main] + paths-ignore: + - "docs/**" + - "**/*.md" workflow_dispatch: permissions: @@ -16,20 +22,18 @@ concurrency: jobs: py: name: py (reusable) - uses: CoderDeltaLAN/ci-matrix-starter/.github/workflows/py-ci.yml@feat/ci-reusables-solid + uses: ./.github/workflows/py-ci.yml with: python_versions: '["3.11","3.12"]' os: '["ubuntu-latest"]' run_tests: true - secrets: inherit ts: if: ${{ hashFiles('**/package.json') != '' }} name: ts (reusable) - uses: CoderDeltaLAN/ci-matrix-starter/.github/workflows/ts-ci.yml@feat/ci-reusables-solid + uses: ./.github/workflows/ts-ci.yml with: node_versions: '["22.x"]' os: '["ubuntu-latest"]' package_manager: "pnpm" test_command: "npm test --if-present" - secrets: inherit diff --git a/docs/research/20250917-ci-base.md b/docs/research/20250917-ci-base.md new file mode 100644 index 0000000..7ffe9a9 --- /dev/null +++ b/docs/research/20250917-ci-base.md @@ -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`. From d4b4ac44b51d453ea2160c513d8eb20e24f3947e Mon Sep 17 00:00:00 2001 From: CoderDeltaLan Date: Wed, 17 Sep 2025 13:38:56 +0100 Subject: [PATCH 7/8] ci: call reusables via owner/repo@branch; keep paths-ignore --- .github/workflows/build.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6d8e515..8bad00a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -22,18 +22,20 @@ concurrency: jobs: py: name: py (reusable) - uses: ./.github/workflows/py-ci.yml + uses: CoderDeltaLAN/ci-matrix-starter/.github/workflows/py-ci.yml@feat/ci-reusables-solid with: python_versions: '["3.11","3.12"]' os: '["ubuntu-latest"]' run_tests: true + secrets: inherit ts: if: ${{ hashFiles('**/package.json') != '' }} name: ts (reusable) - uses: ./.github/workflows/ts-ci.yml + uses: CoderDeltaLAN/ci-matrix-starter/.github/workflows/ts-ci.yml@feat/ci-reusables-solid with: node_versions: '["22.x"]' os: '["ubuntu-latest"]' package_manager: "pnpm" test_command: "npm test --if-present" + secrets: inherit From 2ddaf02df881505da5bec4b16f4b55df74df896e Mon Sep 17 00:00:00 2001 From: CoderDeltaLan Date: Wed, 17 Sep 2025 13:53:24 +0100 Subject: [PATCH 8/8] ci: pin reusable workflows by commit SHA and grant actions:read --- .github/workflows/build.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8bad00a..4e79569 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,6 +14,7 @@ on: permissions: contents: read + actions: read concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -22,7 +23,7 @@ concurrency: jobs: py: name: py (reusable) - uses: CoderDeltaLAN/ci-matrix-starter/.github/workflows/py-ci.yml@feat/ci-reusables-solid + uses: CoderDeltaLAN/ci-matrix-starter/.github/workflows/py-ci.yml@d4b4ac44b51d453ea2160c513d8eb20e24f3947e with: python_versions: '["3.11","3.12"]' os: '["ubuntu-latest"]' @@ -32,7 +33,7 @@ jobs: ts: if: ${{ hashFiles('**/package.json') != '' }} name: ts (reusable) - uses: CoderDeltaLAN/ci-matrix-starter/.github/workflows/ts-ci.yml@feat/ci-reusables-solid + uses: CoderDeltaLAN/ci-matrix-starter/.github/workflows/ts-ci.yml@d4b4ac44b51d453ea2160c513d8eb20e24f3947e with: node_versions: '["22.x"]' os: '["ubuntu-latest"]'