diff --git a/.github/workflows/pr-ci.yml b/.github/workflows/pr-ci.yml new file mode 100644 index 0000000..7dc96fa --- /dev/null +++ b/.github/workflows/pr-ci.yml @@ -0,0 +1,136 @@ +name: pr-ci + +# Consolidated PR-only CI gate. Runs the per-PR validation suite as a +# single workflow so the cancel-in-progress concurrency group, path +# filters, and uv cache apply uniformly. Post-merge validation lives +# in release-ci.yml. +# +# L1 concurrency — cancel superseded PR runs. +# L3 path filters — skip docs/skills/examples/markdown-only PRs. +# L4 conditional matrix — full ["3.11", "3.12", "3.13"] on PR (this trigger). +# L5a uv-managed Python (`uv python install` + `uv venv --python`) so the +# matrix leg is not bound to the runner's system Python. +# L5b .venv/bin on PATH (and explicit `.venv/bin/python` for clarity) so +# console scripts resolve without needing `uv run`. + +on: + pull_request: + branches: [main, develop] + paths-ignore: + - 'docs/**' + - '_skills/**' + - 'examples/**' + - '**.md' + - 'CHANGELOG.*' + - '.github/CODEOWNERS' + - '.github/ISSUE_TEMPLATE/**' + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +jobs: + import-smoke: + name: import-smoke-on-ubuntu-py3-12 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install uv + uses: astral-sh/setup-uv@v3 + with: + enable-cache: true + cache-dependency-glob: pyproject.toml + - name: Set up Python 3.12 (uv-managed) + run: uv python install 3.12 + - name: Install (no extras) in uv venv + run: | + uv venv --python 3.12 .venv + uv pip install --python .venv/bin/python -e . + - name: Import smoke + run: .venv/bin/python -c "import crossref_local; print(getattr(crossref_local, '__version__', 'unversioned'))" + - name: smoke crossref-local --help + run: .venv/bin/crossref-local --help + - name: smoke crossref-local-mcp --help + run: .venv/bin/crossref-local-mcp --help + dep-hygiene-smoke: + name: dep-hygiene-smoke + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install uv + uses: astral-sh/setup-uv@v3 + with: + enable-cache: true + cache-dependency-glob: pyproject.toml + - name: Set up Python 3.11 (uv-managed) + run: uv python install 3.11 + - name: Bare install (no extras) in uv venv + run: | + uv venv --python 3.11 .venv + uv pip install --python .venv/bin/python . + - name: Import package + run: .venv/bin/python -c "import crossref_local; print(crossref_local.__name__)" + + tests: + name: pytest-matrix-on-ubuntu-py${{ matrix.python-version }} + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: ${{ github.event_name == 'pull_request' && fromJson('["3.11", "3.12", "3.13"]') || fromJson('["3.12"]') }} + steps: + - uses: actions/checkout@v4 + - name: Install uv + uses: astral-sh/setup-uv@v3 + with: + enable-cache: true + cache-dependency-glob: pyproject.toml + - name: Set up Python ${{ matrix.python-version }} (uv-managed) + run: uv python install ${{ matrix.python-version }} + - name: Create uv venv on this matrix Python + run: uv venv --python ${{ matrix.python-version }} .venv + - name: Install dependencies + # Fallback chain: prefer [all,dev] so optional-dep code paths + # get exercised. Falls back to [dev] then plain editable so a + # packaging hiccup doesn't strand CI. + run: | + uv pip install --python .venv/bin/python -e ".[all,dev]" \ + || uv pip install --python .venv/bin/python -e ".[dev]" \ + || uv pip install --python .venv/bin/python -e . + - name: Run tests + # PATH prepend so any test that subprocess-calls a console script + # finds it in .venv/bin/. + run: | + export PATH="$PWD/.venv/bin:$PATH" + .venv/bin/pytest tests/ --cov=src/crossref_local --cov-report=xml --cov-report=term + - name: Upload coverage to Codecov + if: always() && matrix.python-version == '3.12' + uses: codecov/codecov-action@v5 + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: ./coverage.xml + fail_ci_if_error: false + + audit: + name: audit + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - uses: actions/checkout@v4 + - name: Install uv + uses: astral-sh/setup-uv@v3 + with: + enable-cache: true + cache-dependency-glob: pyproject.toml + - name: Set up Python 3.12 (uv-managed) + run: uv python install 3.12 + - name: Install package + audit tooling + run: | + uv venv --python 3.12 .venv + uv pip install --python .venv/bin/python -e ".[dev]" + uv pip install --python .venv/bin/python "scitex-dev[cli-audit]" + - name: Run scitex-dev ecosystem audit-all + run: | + export PATH="$PWD/.venv/bin:$PATH" + scitex-dev ecosystem audit-all crossref-local --no-version-check diff --git a/.github/workflows/release-ci.yml b/.github/workflows/release-ci.yml new file mode 100644 index 0000000..0c76489 --- /dev/null +++ b/.github/workflows/release-ci.yml @@ -0,0 +1,100 @@ +name: release-ci + +# Post-merge validation gate. Runs on push to protected branches +# (main, develop) and as the canonical scheduled run. Always uses the +# FULL pytest matrix — no conditional skipping — so the protected-branch +# history is exercised on every supported Python. +# +# L1 concurrency — coalesce overlapping pushes on the same branch. +# L5a uv-managed Python (`uv python install` + `uv venv --python`) so each +# matrix leg pins its interpreter independently of the runner. +# L5b .venv/bin on PATH so console scripts resolve without `uv run`. + +on: + push: + branches: [main, develop] + schedule: + - cron: "0 7 * * *" # nightly 07:00 UTC + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: false + +jobs: + tests: + name: pytest-matrix-on-ubuntu-py${{ matrix.python-version }} + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: ["3.11", "3.12", "3.13"] + steps: + - uses: actions/checkout@v4 + - name: Install uv + uses: astral-sh/setup-uv@v3 + with: + enable-cache: true + cache-dependency-glob: pyproject.toml + - name: Set up Python ${{ matrix.python-version }} (uv-managed) + run: uv python install ${{ matrix.python-version }} + - name: Create uv venv on this matrix Python + run: uv venv --python ${{ matrix.python-version }} .venv + - name: Install dependencies + run: | + uv pip install --python .venv/bin/python -e ".[all,dev]" \ + || uv pip install --python .venv/bin/python -e ".[dev]" \ + || uv pip install --python .venv/bin/python -e . + - name: Run tests + run: | + export PATH="$PWD/.venv/bin:$PATH" + .venv/bin/pytest tests/ --cov=src/crossref_local --cov-report=xml --cov-report=term + - name: Upload coverage to Codecov + if: always() && matrix.python-version == '3.12' + uses: codecov/codecov-action@v5 + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: ./coverage.xml + fail_ci_if_error: false + + import-smoke: + name: import-smoke-on-ubuntu-py3-12 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install uv + uses: astral-sh/setup-uv@v3 + with: + enable-cache: true + cache-dependency-glob: pyproject.toml + - name: Set up Python 3.12 (uv-managed) + run: uv python install 3.12 + - name: Install (no extras) in uv venv + run: | + uv venv --python 3.12 .venv + uv pip install --python .venv/bin/python -e . + - name: Import smoke + run: .venv/bin/python -c "import crossref_local; print(getattr(crossref_local, '__version__', 'unversioned'))" + + audit: + name: audit + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - uses: actions/checkout@v4 + - name: Install uv + uses: astral-sh/setup-uv@v3 + with: + enable-cache: true + cache-dependency-glob: pyproject.toml + - name: Set up Python 3.12 (uv-managed) + run: uv python install 3.12 + - name: Install package + audit tooling + run: | + uv venv --python 3.12 .venv + uv pip install --python .venv/bin/python -e ".[dev]" + uv pip install --python .venv/bin/python "scitex-dev[cli-audit]" + - name: Run scitex-dev ecosystem audit-all + run: | + export PATH="$PWD/.venv/bin:$PATH" + scitex-dev ecosystem audit-all crossref-local --no-version-check