Skip to content
Open
4 changes: 1 addition & 3 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1 @@
github: ["CoderDeltaLAN"] # si activas GitHub Sponsors en tu perfil, aparecerá
custom:
- "https://www.paypal.com/donate/?hosted_button_id=YVENCBNCZWVPW"
custom: ["https://www.paypal.com/donate/?hosted_button_id=YVENCBNCZWVPW"]
20 changes: 20 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"
open-pull-requests-limit: 10
rebase-strategy: auto

- package-ecosystem: "pip"
directory: "/"
schedule:
interval: "daily"
open-pull-requests-limit: 10

- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "daily"
open-pull-requests-limit: 10
15 changes: 15 additions & 0 deletions .github/workflows/auto-approve.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
name: Auto-approve Dependabot
on:
pull_request:
types: [opened, ready_for_review, synchronize]
permissions:
pull-requests: write
contents: read
jobs:
approve:
if: github.actor == 'dependabot[bot]'
runs-on: ubuntu-latest
steps:
- uses: hmarr/auto-approve-action@v4
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
26 changes: 26 additions & 0 deletions .github/workflows/auto-merge-deps.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: Auto-merge Dependabot when green
on:
pull_request:
types: [labeled, synchronize, reopened, ready_for_review]
check_suite:
types: [completed]
permissions:
contents: write
pull-requests: write
jobs:
enable-automerge:
runs-on: ubuntu-latest
steps:
- name: Find green Dependabot PRs
uses: peter-evans/find-pull-request@v3
id: find
with:
author: dependabot[bot]
state: open
base: main
- name: Enable auto-merge (squash)
if: steps.find.outputs.pull-requests != ''
uses: peter-evans/enable-pull-request-automerge@v3
with:
pull-request-number: ${{ fromJson(steps.find.outputs.pull-requests)[0].number }}
merge-method: squash
78 changes: 76 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ concurrency:

jobs:
ping:
name: ping • ubuntu-latest
name: "ping • ubuntu-latest"
runs-on: ubuntu-latest
steps:
- run: echo "ok"
Expand All @@ -35,4 +35,78 @@ jobs:
python-version: ${{ matrix.python }}
- name: Smoke
run: python -V
# ci:nudge 20250917T164347Z

# ci:nudge 20250917T164347Z
ts:
name: "ts / node 22.x • ubuntu-latest"
runs-on: ubuntu-latest
continue-on-error: true
steps:
- uses: actions/checkout@v4

- name: Detect Node workspace
id: guard
shell: bash
run: |
if find . -type f -name package.json -print -quit | grep -q .; then
echo "HAS_NODE=1" >> "$GITHUB_ENV"
else
echo "HAS_NODE=0" >> "$GITHUB_ENV"
fi

- if: ${{ env.HAS_NODE == '1' }}
uses: actions/setup-node@v4
with:
node-version: 22.x
cache: pnpm

- if: ${{ env.HAS_NODE == '1' }}
name: Ensure pnpm
shell: bash
run: |
if ! command -v pnpm >/dev/null 2>&1; then
corepack enable || true
corepack prepare pnpm@latest --activate || true
fi
pnpm -v || echo "pnpm not found (best-effort)"

- if: ${{ env.HAS_NODE == '1' }}
name: Detect package manager
id: pm
shell: bash
run: |
if [ -f pnpm-lock.yaml ]; then echo "PM=pnpm" >> "$GITHUB_ENV";
elif [ -f package-lock.json ]; then echo "PM=npm" >> "$GITHUB_ENV";
elif [ -f yarn.lock ]; then echo "PM=yarn" >> "$GITHUB_ENV";
else echo "PM=none" >> "$GITHUB_ENV"; fi

- if: ${{ env.HAS_NODE == '1' }}
name: Install deps (best-effort)
shell: bash
run: |
case "$PM" in
pnpm) pnpm i --frozen-lockfile || true ;;
npm) npm ci || true ;;
yarn) corepack yarn install --immutable || true ;;
*) echo "no lockfile" ;;
esac

- if: ${{ env.HAS_NODE == '1' }}
name: Test smoke (best-effort)
shell: bash
run: |
if [ -f package.json ] && node -e "p=require('./package.json');process.exit(p.scripts&&p.scripts.test?0:1)"; then
case "$PM" in
pnpm) pnpm -s test || true ;;
npm) npm -s test || true ;;
yarn) corepack yarn -s test || true ;;
*) echo "no PM; skip test" ;;
esac
else
echo "no tests"
fi

- if: ${{ env.HAS_NODE != '1' }}
name: Skip (no package.json)
run: |
echo "skip ts: no Node workspace"
30 changes: 30 additions & 0 deletions .github/workflows/dependabot-auto-merge.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: dependabot-auto-merge

on:
pull_request_target:
types: [opened, synchronize, reopened, ready_for_review]

permissions:
contents: write
pull-requests: write

jobs:
automerge:
if: ${{ github.actor == 'dependabot[bot]' }}
runs-on: ubuntu-latest
steps:
- name: Fetch metadata
id: meta
uses: dependabot/fetch-metadata@v2
with:
github-token: ${{ secrets.GITHUB_TOKEN }}

- name: Enable PR auto-merge (patch/minor only)
if: |
steps.meta.outputs.update-type == 'version-update:semver-patch' ||
steps.meta.outputs.update-type == 'version-update:semver-minor'
uses: peter-evans/enable-pull-request-automerge@v3
with:
token: ${{ secrets.GITHUB_TOKEN }}
pull-request-number: ${{ github.event.pull_request.number }}
merge-method: squash
49 changes: 49 additions & 0 deletions .github/workflows/publish-pypi.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
name: publish-pypi

on:
workflow_dispatch:
push:
tags:
- "v*"

permissions:
contents: read
id-token: write

jobs:
build-publish:
name: Build & publish to PyPI
runs-on: ubuntu-latest
environment: pypi
steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: "pip"

- name: Install build backend
run: python -m pip install --upgrade pip build

- name: Build sdist & wheel
run: python -m build --sdist --wheel --outdir dist/

- name: Check tag matches version (only on tags)
if: startsWith(github.ref, 'refs/tags/')
shell: bash
run: |
PYPROJECT_VERSION=$(python - <<'PY'
import tomllib
print(tomllib.load(open("pyproject.toml","rb"))["project"]["version"])
PY
)
TAG="${GITHUB_REF_NAME#v}"
echo "pyproject.toml: $PYPROJECT_VERSION / tag: $TAG"
test "$TAG" = "$PYPROJECT_VERSION"

- name: Publish to PyPI (Trusted Publisher)
if: startsWith(github.ref, 'refs/tags/')
uses: pypa/gh-action-pypi-publish@release/v1
with:
print_hash: true
137 changes: 84 additions & 53 deletions .github/workflows/ts-ci.yml
Original file line number Diff line number Diff line change
@@ -1,75 +1,106 @@
name: "TypeScript/Node CI (reusable)"
name: TS CI

on:
workflow_call:
inputs:
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
required: false
default: 'pnpm -s test || echo "no tests"'
push:
paths:
- "**/*.ts"
- "**/*.tsx"
- "package.json"
- "pnpm-lock.yaml"
- "eslint.*"
- ".eslintrc.*"
- "tsconfig*.json"
pull_request:
paths:
- "**/*.ts"
- "**/*.tsx"
- "package.json"
- "pnpm-lock.yaml"
- "eslint.*"
- ".eslintrc.*"
- "tsconfig*.json"
workflow_dispatch:

permissions:
contents: read

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

jobs:
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) }}

ts:
name: "ts / node 22.x • ubuntu-latest"
runs-on: ubuntu-latest
continue-on-error: true
steps:
- uses: actions/checkout@v4

- name: Guard — skip if no Node project
- name: Detect Node workspace
id: guard
shell: bash
run: |
if find -L . -type f -name package.json \
-not -path "./.git/*" -not -path "*/node_modules/*" \
-print -quit | grep -q .; then
echo "has_pkg=true" >> "$GITHUB_OUTPUT"
if find . -type f -name package.json -print -quit | grep -q .; then
echo "HAS_NODE=1" >> "$GITHUB_ENV"
else
echo "has_pkg=false" >> "$GITHUB_OUTPUT"
echo "HAS_NODE=0" >> "$GITHUB_ENV"
fi

- uses: actions/setup-node@v4
if: ${{ steps.guard.outputs.has_pkg == 'true' }}
- if: ${{ env.HAS_NODE == '1' }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
cache: ${{ inputs.package_manager }}
node-version: 22.x
cache: pnpm

- name: Install
if: ${{ steps.guard.outputs.has_pkg == 'true' }}
- if: ${{ env.HAS_NODE == '1' }}
name: Ensure pnpm
shell: bash
run: |
corepack enable || true
case "${{ inputs.package_manager }}" in
pnpm) pnpm i --frozen-lockfile || pnpm i ;;
npm) npm ci || npm i ;;
yarn) yarn install --frozen-lockfile || yarn install ;;
*) echo "unknown package manager: ${{ inputs.package_manager }}" ;;
esac
corepack prepare pnpm@latest --activate || true
if ! command -v pnpm >/dev/null; then
npm i -g pnpm@9 || true
fi
pnpm -v || true

- if: ${{ env.HAS_NODE == '1' }}
name: Install deps (pnpm)
shell: bash
run: |
if [ -f pnpm-lock.yaml ]; then
pnpm i --frozen-lockfile || true
else
echo "no pnpm lock; skip install"
fi

- if: ${{ env.HAS_NODE == '1' }}
name: Type check (tsc --noEmit if tsconfig present)
shell: bash
run: |
if [ -f tsconfig.json ] || ls -1 tsconfig.*.json >/dev/null 2>&1; then
npx -y typescript@latest --version
npx -y tsc --noEmit
else
echo "no tsconfig; skip tsc"
fi

- if: ${{ env.HAS_NODE == '1' }}
name: Lint (eslint if config present)
shell: bash
run: |
if ls -1 .eslintrc.* eslint.config.* >/dev/null 2>&1; then
npx -y eslint . --max-warnings=0
else
echo "no eslint config; skip eslint"
fi

- name: Test
if: ${{ steps.guard.outputs.has_pkg == 'true' && inputs.test_command != '' }}
run: ${{ inputs.test_command }}
- if: ${{ env.HAS_NODE == '1' }}
name: Tests (pnpm test if exists)
shell: bash
run: |
if [ -f package.json ] && node -e "p=require('./package.json');process.exit(p.scripts&&p.scripts.test?0:1)"; then
pnpm -s test || true
else
echo "no tests"
fi

- if: ${{ env.HAS_NODE != '1' }}
name: Skip (no Node workspace)
run: |
echo "skip ts-ci: no Node workspace"
Loading
Loading