From 444b26d831792762a6b333a8647fdc46973118db Mon Sep 17 00:00:00 2001 From: Denis_Drobyshev Date: Wed, 5 Aug 2026 17:08:25 +0300 Subject: [PATCH 1/2] Bring CI up to the standard the other repositories already meet praxis had the thinnest pipeline in the organisation: one job, one Python version, one operating system, and actions two major versions behind. The package claims 3.11 through 3.13 and ships three console scripts, none of which anything verified. - test matrix across 3.11-3.13, plus macOS and Windows on 3.12 - a build job that installs the wheel into a clean environment and runs every console script, because an editable install hides a module missing from the wheel - an aggregate `CI` job, so branch protection has one check to require and adding a matrix entry cannot silently leave it unguarded - CodeQL weekly and on every push - Dependabot for pip and actions, with the ml extra left pinned - pre-commit, including ruff-format `ruff format --check` is deliberately absent from CI: nineteen files predate the formatter, so the gate would be red on day one. pre-commit formats what a commit touches until that is fixed in its own change. --- .github/dependabot.yml | 38 +++++++ .github/workflows/ci.yml | 113 ++++++++++++++++++-- .github/workflows/codeql.yml | 41 +++++++ .github/workflows/dependabot-auto-merge.yml | 49 +++++++++ .pre-commit-config.yaml | 44 ++++++++ 5 files changed, 278 insertions(+), 7 deletions(-) create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/codeql.yml create mode 100644 .github/workflows/dependabot-auto-merge.yml create mode 100644 .pre-commit-config.yaml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..bfb7768 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,38 @@ +version: 2 + +updates: + # Actions first: a stale third-party action is a supply-chain risk in every + # workflow that uses it, and the bump is almost always safe to take. + - package-ecosystem: github-actions + directory: "/" + schedule: + interval: weekly + day: monday + commit-message: + prefix: "ci" + groups: + actions: + patterns: ["*"] + + - package-ecosystem: pip + directory: "/" + schedule: + interval: weekly + day: monday + commit-message: + prefix: "deps" + # One PR for the dev toolchain, separate PRs for anything a user installs: + # a ruff bump needs a glance, a FastAPI or psycopg bump needs reading. + groups: + dev-tooling: + patterns: ["ruff", "pytest*", "mypy", "pre-commit", "build", "twine"] + dependency-type: development + ignore: + # The ml extra is deliberately heavy and deliberately pinned by whoever + # installs it. Retrieval quality here is measured against specific model + # revisions, so a silent bump moves the numbers the eval suite reports. + - dependency-name: torch + - dependency-name: transformers + - dependency-name: sentence-transformers + - dependency-name: FlagEmbedding + open-pull-requests-limit: 5 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e873930..19b58d3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,18 +4,117 @@ on: push: branches: [master] pull_request: + workflow_dispatch: + +concurrency: + group: ci-${{ github.ref }} + cancel-in-progress: true + +# Read-only by default. A job that needs more asks for it itself, so a +# compromised dependency in one step cannot push to the repository. +permissions: + contents: read + +env: + PYTHONUNBUFFERED: "1" + PIP_DISABLE_PIP_VERSION_CHECK: "1" + FORCE_COLOR: "1" jobs: - test: + lint: + name: Lint runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 + - uses: actions/checkout@v7 + - uses: actions/setup-python@v7 with: python-version: "3.12" - - name: Install - run: pip install -e ".[dev]" - - name: Lint (ruff) + cache: pip + cache-dependency-path: pyproject.toml + - run: pip install -e ".[dev]" + # No `ruff format --check` yet: nineteen files predate the formatter and + # reformatting them belongs in its own commit, not in a lint gate that + # would be red from the day it lands. pre-commit formats what a + # contributor touches in the meantime. + - name: ruff check run: ruff check src tests scripts clients - - name: Tests (offline, детерминированно) + + test: + name: Tests (${{ matrix.os }}, Python ${{ matrix.python }}) + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + include: + - os: ubuntu-latest + python: "3.11" + - os: ubuntu-latest + python: "3.12" + - os: ubuntu-latest + python: "3.13" + - os: macos-latest + python: "3.12" + - os: windows-latest + python: "3.12" + steps: + - uses: actions/checkout@v7 + - uses: actions/setup-python@v7 + with: + python-version: ${{ matrix.python }} + cache: pip + cache-dependency-path: pyproject.toml + - run: pip install -e ".[dev]" + # The v0 slice is stdlib-only and the suite is offline and deterministic, + # so the matrix proves exactly one thing: that the promise holds on every + # interpreter and operating system the package claims to support. + - name: Run the test suite run: pytest -q + + build: + name: Package builds and installs cleanly + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + - uses: actions/setup-python@v7 + with: + python-version: "3.12" + - run: pip install build twine + - name: Build the wheel and sdist + run: python -m build + - name: Check the metadata + run: twine check dist/* + # An editable install hides packaging mistakes: it puts the working tree + # on the path, so a module missing from the wheel still imports and a + # broken console script still resolves. + - name: Install the wheel into a clean environment + run: | + python -m venv /tmp/fresh + /tmp/fresh/bin/pip install dist/*.whl + /tmp/fresh/bin/python -c "import praxis; print(praxis.__name__)" + # Every console script the package advertises has to resolve. A typo + # in an entry point is invisible until a user types the command. + /tmp/fresh/bin/praxis-demo --help > /dev/null + /tmp/fresh/bin/praxis-ask --help > /dev/null + /tmp/fresh/bin/praxis-eval --help > /dev/null + - uses: actions/upload-artifact@v7 + with: + name: dist + path: dist/ + retention-days: 14 + + ci: + name: CI + runs-on: ubuntu-latest + if: always() + needs: [lint, test, build] + steps: + # One aggregate check to require in branch protection. Without it, adding + # a job to the matrix silently leaves it unrequired, and a red job stops + # blocking merges. + - name: Fail if any job did not succeed + if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') + run: | + echo "One or more jobs failed:" + echo '${{ toJSON(needs) }}' + exit 1 + - run: echo "All checks passed." diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..f74e6ab --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,41 @@ +name: CodeQL + +on: + push: + branches: [master] + pull_request: + branches: [master] + schedule: + # Weekly, because advisories land between pushes: a rule added after the + # last commit would otherwise never run against this code. + - cron: "17 4 * * 1" + workflow_dispatch: + +permissions: + contents: read + +jobs: + analyze: + name: Analyze Python + runs-on: ubuntu-latest + # Code scanning on a private repository needs GitHub Advanced Security. + # Skipped rather than left permanently red, and it starts running by itself + # if the repository ever goes private and comes back. + if: github.event.repository.visibility == 'public' + permissions: + security-events: write + actions: read + contents: read + steps: + - uses: actions/checkout@v7 + + - uses: github/codeql-action/init@v4 + with: + languages: python + # security-and-quality over the default: this service parses untrusted + # documents and serves HTTP, so the extra checks earn their noise. + queries: security-and-quality + + - uses: github/codeql-action/analyze@v4 + with: + category: "/language:python" diff --git a/.github/workflows/dependabot-auto-merge.yml b/.github/workflows/dependabot-auto-merge.yml new file mode 100644 index 0000000..d06aeda --- /dev/null +++ b/.github/workflows/dependabot-auto-merge.yml @@ -0,0 +1,49 @@ +name: Dependabot auto-merge + +# What this does and does not do: +# +# auto-merged - GitHub Actions bumps, and patch-level bumps of anything else +# left for you - every minor and major bump of a runtime dependency +# +# A patch release and an action bump are the updates that pile up unread until +# the queue is too long to review honestly. A minor bump can change behaviour, +# so it keeps a human. Auto-merge is queued, not immediate: GitHub still waits +# for the required checks to pass, and a red build leaves the PR open. + +on: pull_request_target + +permissions: + contents: read + +jobs: + auto-merge: + if: github.actor == 'dependabot[bot]' + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + steps: + # Reads the update type from the PR that Dependabot opened. Nothing from + # the branch is checked out or executed, which is what makes + # pull_request_target safe to use here. + - id: metadata + uses: dependabot/fetch-metadata@v2 + + - name: Approve and queue the merge + if: >- + steps.metadata.outputs.package-ecosystem == 'github_actions' || + steps.metadata.outputs.update-type == 'version-update:semver-patch' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PR: ${{ github.event.pull_request.html_url }} + run: | + gh pr review --approve "$PR" + gh pr merge --auto --squash "$PR" + + - name: Explain why this one was left alone + if: >- + steps.metadata.outputs.package-ecosystem != 'github_actions' && + steps.metadata.outputs.update-type != 'version-update:semver-patch' + env: + TYPE: ${{ steps.metadata.outputs.update-type }} + run: echo "$TYPE is not auto-merged; this pull request needs a human." diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..67056b9 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,44 @@ +# Install with: pre-commit install +# +# ruff-format runs here and not in CI on purpose. Nineteen files predate the +# formatter; a CI gate would be red on day one, while this hook only touches +# what a commit already changes. +repos: + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.15.20 + hooks: + - id: ruff + args: [--fix] + - id: ruff-format + + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v5.0.0 + hooks: + - id: check-added-large-files + args: [--maxkb=512] + - id: check-merge-conflict + - id: check-toml + - id: check-yaml + - id: end-of-file-fixer + - id: mixed-line-ending + args: [--fix=lf] + - id: trailing-whitespace + + - repo: local + hooks: + # A built index or a downloaded model checkpoint is almost always an + # accident, and both are painful to remove from history afterwards. + # `language: fail` refuses any staged path that matches, which is the + # check that actually applies to a filename rather than to file contents. + - id: reject-index-and-checkpoints + name: reject built indexes and model checkpoints + entry: this file looks like a built artefact and should not be committed + language: fail + files: \.(sqlite|db|faiss|pt|pth|safetensors|bin)$ + + - id: pytest + name: pytest + entry: pytest -q + language: system + pass_filenames: false + stages: [pre-push] From a1724b7e462f36e55a3cea9740b3474dfa4dd053 Mon Sep 17 00:00:00 2001 From: Denis_Drobyshev Date: Wed, 5 Aug 2026 22:04:07 +0300 Subject: [PATCH 2/2] Leave dependabot.yml to the pull request that already adds it PR #1 has been open since 3 August with that file, alongside the docs site and the English README. Duplicating it here would have made whichever merged second a conflict. --- .github/dependabot.yml | 38 -------------------------------------- 1 file changed, 38 deletions(-) delete mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index bfb7768..0000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,38 +0,0 @@ -version: 2 - -updates: - # Actions first: a stale third-party action is a supply-chain risk in every - # workflow that uses it, and the bump is almost always safe to take. - - package-ecosystem: github-actions - directory: "/" - schedule: - interval: weekly - day: monday - commit-message: - prefix: "ci" - groups: - actions: - patterns: ["*"] - - - package-ecosystem: pip - directory: "/" - schedule: - interval: weekly - day: monday - commit-message: - prefix: "deps" - # One PR for the dev toolchain, separate PRs for anything a user installs: - # a ruff bump needs a glance, a FastAPI or psycopg bump needs reading. - groups: - dev-tooling: - patterns: ["ruff", "pytest*", "mypy", "pre-commit", "build", "twine"] - dependency-type: development - ignore: - # The ml extra is deliberately heavy and deliberately pinned by whoever - # installs it. Retrieval quality here is measured against specific model - # revisions, so a silent bump moves the numbers the eval suite reports. - - dependency-name: torch - - dependency-name: transformers - - dependency-name: sentence-transformers - - dependency-name: FlagEmbedding - open-pull-requests-limit: 5