diff --git a/.github/workflows/pr-gate.yml b/.github/workflows/pr-gate.yml new file mode 100644 index 0000000..221a20c --- /dev/null +++ b/.github/workflows/pr-gate.yml @@ -0,0 +1,41 @@ +name: pr-gate + +# Every pull request runs this repo's own check suite (scripts/check.sh) plus the shared em-dash and +# workflow-pinning guards. Those guards live in lib_bespok3d, mounted here as a git submodule, so the +# checkout must fetch it. +# +# The repo's own checkout uses the auto GITHUB_TOKEN, which is always valid for this repo. The submodule +# lives in a SEPARATE private repo that GITHUB_TOKEN cannot read, so it is fetched in its own step with a +# read token (MAIN_INDEX_TOKEN). The main checkout runs with persist-credentials off so it does not leave +# GITHUB_TOKEN in git config, where it would otherwise be reused for the submodule fetch and 403 on the +# private repo. The submodule URL in .gitmodules is HTTPS so the token applies. +# +# This is a check-only run: it never builds, packs, signs or publishes, so no signing key is in scope. +# The single secret it holds is the submodule read token. It is deliberately NOT named pr-build.yml: that +# name is reserved for a b3-builder preview build, which the pinning guard forbids from holding any secret +# at all. See the go-public finding: swap this token for a read-only one, or make lib_bespok3d public, +# before external forks open PRs (a fork PR receives no secret and its checkout of the private submodule +# would fail). +on: + pull_request: + +jobs: + gate: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: false + persist-credentials: false + + - name: Fetch the shared-tooling submodule + run: | + git config --global url."https://x-access-token:${{ secrets.MAIN_INDEX_TOKEN }}@github.com/".insteadOf "https://github.com/" + git submodule update --init --recursive + + - uses: actions/setup-python@v5 + with: + python-version: '3.11' + + - name: Repo gate (shared detectors + check suite) + run: bash scripts/check.sh diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..dd7e77d --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "lib_bespok3d"] + path = lib_bespok3d + url = https://github.com/Bespok3d/lib_bespok3d.git diff --git a/lib_bespok3d b/lib_bespok3d new file mode 160000 index 0000000..8ce6759 --- /dev/null +++ b/lib_bespok3d @@ -0,0 +1 @@ +Subproject commit 8ce67593b469619306db0c9f389ac21cedaf1a31 diff --git a/scripts/check.sh b/scripts/check.sh new file mode 100755 index 0000000..564f8a9 --- /dev/null +++ b/scripts/check.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash +# This repo's own gate: it must pass from this repo's root, with no sibling repo cloned except +# lib_bespok3d. Exits non-zero on any failure. +set -uo pipefail + +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +# The shared gate helpers and the detectors that enforce a workspace-wide rule live in one place. +# See lib_bespok3d/tooling/README.md. This is the only line that knows where they are. +B3D_TOOLING="${B3D_TOOLING:-$REPO_ROOT/lib_bespok3d/tooling}" +# shellcheck source=/dev/null +. "$B3D_TOOLING/gate-lib.sh" + +cd "$REPO_ROOT" || exit 1 + +echo "" +echo "reference-python-plugins gate" + +b3d_python_tools + +# print-time-human vendors humanize under files/site-packages, so its own tree is the import path +# its tests and its type checks resolve against. +export PYTHONPATH="$REPO_ROOT/print-time-human/files/site-packages" + +# That vendored tree is gitignored (baked into the package at publish), so a clean checkout, which +# is what CI gets, has no humanize: the status-feed test would importorskip it and never run, +# leaving the gate green on an untested plugin. Provision the plugin's own declared dependency into +# the tree PYTHONPATH resolves against so the test actually exercises the code. B3D_PY is the gate's +# tool interpreter, provisioned by b3d_python_tools above. +"$B3D_PY" -m pip install --quiet --target "$REPO_ROOT/print-time-human/files/site-packages" -r "$REPO_ROOT/status-feed/requirements.txt" + +run_check "pytest (status-feed)" pytest_in_dir "$REPO_ROOT/status-feed" tests +run_check "ruff (status-feed)" ruff_in_dir "$REPO_ROOT/status-feed" files tests +run_check "ruff (print-time-human)" ruff_in_dir "$REPO_ROOT/print-time-human" files/klipper + +unset PYTHONPATH + +workflow_pinning_check "$REPO_ROOT" +em_dash_check "$REPO_ROOT" +shellcheck_repo "$REPO_ROOT" + +gate_summary || exit 1