From 48cf362c399a77e05ed3d45c8dcc33189805a01c Mon Sep 17 00:00:00 2001 From: Zeth Date: Mon, 6 Apr 2026 17:32:11 +0100 Subject: [PATCH 01/11] Support comparisons. --- README.md | 24 +++++++++++++++++++++ src/caste.hpp | 37 +++++++++++++++++++++++++++++++++ tests/test_classify.cpp | 46 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 107 insertions(+) diff --git a/README.md b/README.md index 635e3c8..e05b353 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,30 @@ auto result = detect_caste(); // result.reason is a short string (optional) ``` +If you want to use the detected caste in policy code, +`caste` supports direct comparisons: + +```cpp +Caste caste = detect_caste().caste; + +if (caste >= Caste::Developer) { + // developer, workstation, or rig +} +``` + +You can also define reusable ranges: + +```cpp +constexpr CasteRange user_or_below{ + Caste::Mini, + Caste::User +}; + +if (user_or_below.contains(caste)) { + // mini or user +} +``` + If you only want the single-word label: ```cpp diff --git a/src/caste.hpp b/src/caste.hpp index cd3889e..8d0d9b7 100644 --- a/src/caste.hpp +++ b/src/caste.hpp @@ -11,6 +11,43 @@ enum class Caste { Rig }; +constexpr int caste_rank(Caste caste) { + return static_cast(caste); +} + +constexpr bool operator>=(Caste a, Caste b) { + return caste_rank(a) >= caste_rank(b); +} + +constexpr bool operator<=(Caste a, Caste b) { + return caste_rank(a) <= caste_rank(b); +} + +constexpr bool operator>(Caste a, Caste b) { + return caste_rank(a) > caste_rank(b); +} + +constexpr bool operator<(Caste a, Caste b) { + return caste_rank(a) < caste_rank(b); +} + +constexpr bool caste_at_least(Caste actual, Caste minimum) { + return actual >= minimum; +} + +constexpr bool caste_at_most(Caste actual, Caste maximum) { + return actual <= maximum; +} + +struct CasteRange { + Caste min; + Caste max; + + constexpr bool contains(Caste caste) const { + return caste >= min && caste <= max; + } +}; + enum class GpuKind { None, Integrated, // Intel UHD/Iris Xe, AMD iGPU, etc. (shared memory) diff --git a/tests/test_classify.cpp b/tests/test_classify.cpp index 3ecbed7..3e770bc 100644 --- a/tests/test_classify.cpp +++ b/tests/test_classify.cpp @@ -80,6 +80,52 @@ TEST_CASE("Caste names are stable") { REQUIRE(std::string(caste_name(Caste::Rig)) == "Rig"); } +TEST_CASE("Castes support direct ordered comparison") { + REQUIRE(Caste::Mini < Caste::User); + REQUIRE(Caste::User < Caste::Developer); + REQUIRE(Caste::Developer < Caste::Workstation); + REQUIRE(Caste::Workstation < Caste::Rig); + + REQUIRE(Caste::Rig > Caste::Workstation); + REQUIRE(Caste::Workstation >= Caste::Developer); + REQUIRE(Caste::Developer >= Caste::Developer); + REQUIRE(Caste::User <= Caste::Developer); + REQUIRE(Caste::Mini <= Caste::Mini); +} + +TEST_CASE("Named caste comparison helpers match operator semantics") { + REQUIRE(caste_at_least(Caste::Developer, Caste::Developer)); + REQUIRE(caste_at_least(Caste::Workstation, Caste::Developer)); + REQUIRE(caste_at_least(Caste::Rig, Caste::Developer)); + REQUIRE_FALSE(caste_at_least(Caste::User, Caste::Developer)); + + REQUIRE(caste_at_most(Caste::Mini, Caste::User)); + REQUIRE(caste_at_most(Caste::User, Caste::User)); + REQUIRE_FALSE(caste_at_most(Caste::Developer, Caste::User)); +} + +TEST_CASE("Caste ranges support reusable policy buckets") { + constexpr CasteRange user_or_below{Caste::Mini, Caste::User}; + constexpr CasteRange dev_or_above{Caste::Developer, Caste::Rig}; + constexpr CasteRange dev_to_workstation{ + Caste::Developer, + Caste::Workstation + }; + + REQUIRE(user_or_below.contains(Caste::Mini)); + REQUIRE(user_or_below.contains(Caste::User)); + REQUIRE_FALSE(user_or_below.contains(Caste::Developer)); + + REQUIRE(dev_or_above.contains(Caste::Developer)); + REQUIRE(dev_or_above.contains(Caste::Workstation)); + REQUIRE(dev_or_above.contains(Caste::Rig)); + REQUIRE_FALSE(dev_or_above.contains(Caste::User)); + + REQUIRE(dev_to_workstation.contains(Caste::Developer)); + REQUIRE(dev_to_workstation.contains(Caste::Workstation)); + REQUIRE_FALSE(dev_to_workstation.contains(Caste::Rig)); +} + namespace { bool is_valid_caste(Caste c) { switch (c) { From c815183a644d621fe48ef0be2e5ad16a4f1ab811 Mon Sep 17 00:00:00 2001 From: Zeth Date: Mon, 6 Apr 2026 21:22:04 +0100 Subject: [PATCH 02/11] Handle ranges backwards. --- src/caste.hpp | 10 +++++++++- tests/test_classify.cpp | 12 ++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/caste.hpp b/src/caste.hpp index 8d0d9b7..2987f3f 100644 --- a/src/caste.hpp +++ b/src/caste.hpp @@ -43,8 +43,16 @@ struct CasteRange { Caste min; Caste max; + constexpr Caste lower() const { + return min <= max ? min : max; + } + + constexpr Caste upper() const { + return min <= max ? max : min; + } + constexpr bool contains(Caste caste) const { - return caste >= min && caste <= max; + return caste >= lower() && caste <= upper(); } }; diff --git a/tests/test_classify.cpp b/tests/test_classify.cpp index 3e770bc..9fe43e4 100644 --- a/tests/test_classify.cpp +++ b/tests/test_classify.cpp @@ -126,6 +126,18 @@ TEST_CASE("Caste ranges support reusable policy buckets") { REQUIRE_FALSE(dev_to_workstation.contains(Caste::Rig)); } +TEST_CASE("Caste ranges normalize reversed endpoints") { + constexpr CasteRange rigs_down_to_user{Caste::Rig, Caste::User}; + + REQUIRE(rigs_down_to_user.lower() == Caste::User); + REQUIRE(rigs_down_to_user.upper() == Caste::Rig); + REQUIRE(rigs_down_to_user.contains(Caste::User)); + REQUIRE(rigs_down_to_user.contains(Caste::Developer)); + REQUIRE(rigs_down_to_user.contains(Caste::Workstation)); + REQUIRE(rigs_down_to_user.contains(Caste::Rig)); + REQUIRE_FALSE(rigs_down_to_user.contains(Caste::Mini)); +} + namespace { bool is_valid_caste(Caste c) { switch (c) { From 405d8fc0d974d6aeb14c875c6ce31d53aec34d35 Mon Sep 17 00:00:00 2001 From: Zeth Date: Mon, 6 Apr 2026 21:33:24 +0100 Subject: [PATCH 03/11] Add some common comparisons. --- README.md | 8 ++++++++ src/caste.hpp | 5 +++++ tests/test_classify.cpp | 18 ++++++++++++++++++ 3 files changed, 31 insertions(+) diff --git a/README.md b/README.md index e05b353..096ec83 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,14 @@ if (user_or_below.contains(caste)) { } ``` +Common policy bands are also provided directly: + +```cpp +if (dev_or_above.contains(caste)) { + // developer, workstation, or rig +} +``` + If you only want the single-word label: ```cpp diff --git a/src/caste.hpp b/src/caste.hpp index 2987f3f..8eb851e 100644 --- a/src/caste.hpp +++ b/src/caste.hpp @@ -56,6 +56,11 @@ struct CasteRange { } }; +constexpr CasteRange user_or_below{Caste::Mini, Caste::User}; +constexpr CasteRange user_or_above{Caste::User, Caste::Rig}; +constexpr CasteRange dev_or_below{Caste::Mini, Caste::Developer}; +constexpr CasteRange dev_or_above{Caste::Developer, Caste::Rig}; + enum class GpuKind { None, Integrated, // Intel UHD/Iris Xe, AMD iGPU, etc. (shared memory) diff --git a/tests/test_classify.cpp b/tests/test_classify.cpp index 9fe43e4..7272936 100644 --- a/tests/test_classify.cpp +++ b/tests/test_classify.cpp @@ -138,6 +138,24 @@ TEST_CASE("Caste ranges normalize reversed endpoints") { REQUIRE_FALSE(rigs_down_to_user.contains(Caste::Mini)); } +TEST_CASE("Predefined caste ranges cover common policy bands") { + REQUIRE(user_or_below.contains(Caste::Mini)); + REQUIRE(user_or_below.contains(Caste::User)); + REQUIRE_FALSE(user_or_below.contains(Caste::Developer)); + + REQUIRE_FALSE(user_or_above.contains(Caste::Mini)); + REQUIRE(user_or_above.contains(Caste::User)); + REQUIRE(user_or_above.contains(Caste::Rig)); + + REQUIRE(dev_or_below.contains(Caste::Mini)); + REQUIRE(dev_or_below.contains(Caste::Developer)); + REQUIRE_FALSE(dev_or_below.contains(Caste::Workstation)); + + REQUIRE_FALSE(dev_or_above.contains(Caste::User)); + REQUIRE(dev_or_above.contains(Caste::Developer)); + REQUIRE(dev_or_above.contains(Caste::Rig)); +} + namespace { bool is_valid_caste(Caste c) { switch (c) { From ea6c7b219946c90b7909646d493f1307d9c7fcfb Mon Sep 17 00:00:00 2001 From: Zeth Date: Tue, 7 Apr 2026 22:06:17 +0100 Subject: [PATCH 04/11] Make workflows more specific. --- .github/workflows/ci.yml | 30 ++++++++++++++------- .github/workflows/release.yml | 48 +++++++++++++++++++++++++--------- .github/workflows/testpypi.yml | 38 ++++++++++++++++++++------- CMakeLists.txt | 3 ++- 4 files changed, 87 insertions(+), 32 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 14da2a9..5c890be 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,6 +4,9 @@ on: push: pull_request: +permissions: + contents: read + jobs: cpp: runs-on: ${{ matrix.os }} @@ -12,7 +15,9 @@ jobs: matrix: os: [ubuntu-latest, macos-latest, windows-latest] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false - name: Configure run: cmake -S . -B build -DCASTE_BUILD_TESTS=ON - name: Build @@ -28,22 +33,29 @@ jobs: os: [ubuntu-latest, macos-latest, windows-latest] python-version: ["3.9", "3.10", "3.11", "3.12"] steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false + - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: python-version: ${{ matrix.python-version }} - name: Install and test run: | - python -m pip install -U pip cd python - python -m pip install -e ".[test]" + python -m pip install \ + "scikit-build-core==0.10.0" \ + "pybind11==2.11.0" \ + "pytest==7.4.4" + python -m pip install --no-build-isolation -e . python -m pytest -q coverage: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false + - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: python-version: "3.12" - name: Configure (coverage) @@ -57,11 +69,11 @@ jobs: -DCMAKE_SHARED_LINKER_FLAGS="--coverage" - name: Build and test (coverage) run: | + python -m pip install "gcovr==7.0" cmake --build build-cov --config Debug ctest --test-dir build-cov -C Debug --output-on-failure - name: Generate coverage report run: | - python -m pip install -U pip gcovr gcovr \ --root . \ build-cov \ @@ -81,7 +93,7 @@ jobs: --output build-cov/coverage.xml \ --html-details build-cov/coverage.html - name: Upload coverage artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: coverage-report path: | diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1194af4..7360b14 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,6 +5,9 @@ on: tags: - "v*" +permissions: + contents: read + jobs: cpp-artifacts: runs-on: ${{ matrix.os }} @@ -13,7 +16,9 @@ jobs: matrix: os: [ubuntu-latest, macos-latest, windows-latest] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false - name: Configure run: cmake -S . -B build -DCASTE_BUILD_TESTS=OFF - name: Build @@ -27,7 +32,7 @@ jobs: cd stage tar -czf "../${name}.tar.gz" . - name: Upload workflow artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: cpp-artifact-${{ runner.os }} path: caste-*.tar.gz @@ -39,29 +44,28 @@ jobs: contents: write steps: - name: Download cpp artifacts - uses: actions/download-artifact@v4 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 with: pattern: cpp-artifact-* merge-multiple: true path: release-assets - name: Upload release assets - uses: softprops/action-gh-release@v2 + uses: softprops/action-gh-release@6da8fa9354ddfdc4aeace5fc48d7f679b5214090 # v2.4.1 with: files: release-assets/caste-*.tar.gz - pypi: + pypi-build: runs-on: ubuntu-latest - permissions: - id-token: write - contents: read steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false + - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: python-version: "3.12" - name: Build sdist and wheels run: | - python -m pip install -U pip build cibuildwheel + python -m pip install "build==1.4.2" "cibuildwheel==3.4.1" CASTE_VERSION="$(python - <<'PY' import tomllib with open("python/pyproject.toml", "rb") as f: @@ -83,7 +87,7 @@ jobs: ./ .pypi-stage/ cat > .pypi-stage/pyproject.toml << EOF [build-system] - requires = ["scikit-build-core>=0.10.0", "pybind11>=2.11.0"] + requires = ["scikit-build-core==0.10.0", "pybind11==2.11.0"] build-backend = "scikit_build_core.build" [project] @@ -113,7 +117,25 @@ jobs: EOF python -m build --sdist --outdir dist .pypi-stage python -m cibuildwheel --output-dir dist .pypi-stage + - name: Upload PyPI workflow artifact + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: pypi-dist + path: dist/* + + pypi-publish: + runs-on: ubuntu-latest + needs: pypi-build + permissions: + id-token: write + contents: read + steps: + - name: Download PyPI workflow artifact + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: pypi-dist + path: dist - name: Upload to PyPI - uses: pypa/gh-action-pypi-publish@release/v1 + uses: pypa/gh-action-pypi-publish@106e0b0b7c337fa67ed433972f777c6357f78598 # v1.13.0 with: packages-dir: dist diff --git a/.github/workflows/testpypi.yml b/.github/workflows/testpypi.yml index 8635b89..019b6fe 100644 --- a/.github/workflows/testpypi.yml +++ b/.github/workflows/testpypi.yml @@ -3,20 +3,22 @@ name: TestPyPI on: workflow_dispatch: +permissions: + contents: read + jobs: - pypi-test: + pypi-test-build: runs-on: ubuntu-latest - permissions: - id-token: write - contents: read steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false + - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: python-version: "3.12" - name: Build sdist and wheels run: | - python -m pip install -U pip build cibuildwheel + python -m pip install "build==1.4.2" "cibuildwheel==3.4.1" CASTE_VERSION="$(python - <<'PY' import tomllib with open("python/pyproject.toml", "rb") as f: @@ -38,7 +40,7 @@ jobs: ./ .pypi-stage/ cat > .pypi-stage/pyproject.toml << EOF [build-system] - requires = ["scikit-build-core>=0.10.0", "pybind11>=2.11.0"] + requires = ["scikit-build-core==0.10.0", "pybind11==2.11.0"] build-backend = "scikit_build_core.build" [project] @@ -68,8 +70,26 @@ jobs: EOF python -m build --sdist --outdir dist .pypi-stage python -m cibuildwheel --output-dir dist .pypi-stage + - name: Upload TestPyPI workflow artifact + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: testpypi-dist + path: dist/* + + pypi-test-publish: + runs-on: ubuntu-latest + needs: pypi-test-build + permissions: + id-token: write + contents: read + steps: + - name: Download TestPyPI workflow artifact + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: testpypi-dist + path: dist - name: Upload to TestPyPI - uses: pypa/gh-action-pypi-publish@release/v1 + uses: pypa/gh-action-pypi-publish@106e0b0b7c337fa67ed433972f777c6357f78598 # v1.13.0 with: repository-url: https://test.pypi.org/legacy/ packages-dir: dist diff --git a/CMakeLists.txt b/CMakeLists.txt index 1fab856..ffa7a27 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -61,7 +61,8 @@ if (CASTE_BUILD_TESTS) FetchContent_Declare( Catch2 GIT_REPOSITORY https://github.com/catchorg/Catch2.git - GIT_TAG v3.5.4 + GIT_TAG abb467ecd60fae9a727afca033c1eb5d20af2c12 # v3.5.4 + GIT_SHALLOW TRUE ) FetchContent_MakeAvailable(Catch2) endif() From 37e1f5c3fe8cbd87ae7cb2a88d3e9309cf94713d Mon Sep 17 00:00:00 2001 From: Zeth Date: Tue, 7 Apr 2026 22:09:46 +0100 Subject: [PATCH 05/11] Rewrite tests to be unittest. --- python/tests/test_caste.py | 48 +++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/python/tests/test_caste.py b/python/tests/test_caste.py index 37ed742..b6406fb 100644 --- a/python/tests/test_caste.py +++ b/python/tests/test_caste.py @@ -1,30 +1,34 @@ -import caste +import unittest +import caste -def test_detect_caste_word(): - word = caste.detect_caste_word() - assert word in {"Mini", "User", "Developer", "Workstation", "Rig"} +class CasteTests(unittest.TestCase): + def test_detect_caste_word(self) -> None: + word = caste.detect_caste_word() + self.assertIn(word, {"Mini", "User", "Developer", "Workstation", "Rig"}) -def test_detect_caste_tuple(): - word, reason = caste.detect_caste() - assert word in {"Mini", "User", "Developer", "Workstation", "Rig"} - assert isinstance(reason, str) + def test_detect_caste_tuple(self) -> None: + word, reason = caste.detect_caste() + self.assertIn(word, {"Mini", "User", "Developer", "Workstation", "Rig"}) + self.assertIsInstance(reason, str) + def test_detect_hw_facts_shape(self) -> None: + facts = caste.detect_hw_facts() + self.assertIsInstance(facts, dict) + self.assertGreaterEqual(facts["ram_bytes"], 0) + self.assertIsInstance(facts["physical_cores"], int) + self.assertIsInstance(facts["logical_threads"], int) + self.assertIn(facts["gpu_kind"], {0, 1, 2, 3}) + self.assertGreaterEqual(facts["vram_bytes"], 0) + self.assertIsInstance(facts["has_discrete_gpu"], bool) + self.assertIsInstance(facts["is_apple_silicon"], bool) + self.assertIsInstance(facts["is_intel_arc"], bool) -def test_detect_hw_facts_shape(): - facts = caste.detect_hw_facts() - assert isinstance(facts, dict) - assert facts["ram_bytes"] >= 0 - assert isinstance(facts["physical_cores"], int) - assert isinstance(facts["logical_threads"], int) - assert facts["gpu_kind"] in {0, 1, 2, 3} - assert facts["vram_bytes"] >= 0 - assert isinstance(facts["has_discrete_gpu"], bool) - assert isinstance(facts["is_apple_silicon"], bool) - assert isinstance(facts["is_intel_arc"], bool) + def test_version_string(self) -> None: + self.assertIsInstance(caste.__version__, str) + self.assertTrue(caste.__version__) -def test_version_string(): - assert isinstance(caste.__version__, str) - assert caste.__version__ +if __name__ == "__main__": + unittest.main() From 5ec4172ecd1e6fcb88319ddfedd0b84f61771491 Mon Sep 17 00:00:00 2001 From: Zeth Date: Tue, 7 Apr 2026 22:14:21 +0100 Subject: [PATCH 06/11] Removed pytest dependency. --- .github/workflows/ci.yml | 5 ++--- .github/workflows/release.yml | 3 --- .github/workflows/testpypi.yml | 3 --- python/pyproject.toml | 3 --- 4 files changed, 2 insertions(+), 12 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5c890be..de56d56 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,10 +44,9 @@ jobs: cd python python -m pip install \ "scikit-build-core==0.10.0" \ - "pybind11==2.11.0" \ - "pytest==7.4.4" + "pybind11==2.11.0" python -m pip install --no-build-isolation -e . - python -m pytest -q + python -m unittest discover -s tests -p 'test_*.py' coverage: runs-on: ubuntu-latest diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7360b14..f58f1eb 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -104,9 +104,6 @@ jobs: Repository = "https://github.com/zeth/caste" Issues = "https://github.com/zeth/caste/issues" - [project.optional-dependencies] - test = ["pytest"] - [tool.scikit-build] cmake.version = ">=3.20" cmake.source-dir = "python" diff --git a/.github/workflows/testpypi.yml b/.github/workflows/testpypi.yml index 019b6fe..1b6d556 100644 --- a/.github/workflows/testpypi.yml +++ b/.github/workflows/testpypi.yml @@ -57,9 +57,6 @@ jobs: Repository = "https://github.com/zeth/caste" Issues = "https://github.com/zeth/caste/issues" - [project.optional-dependencies] - test = ["pytest"] - [tool.scikit-build] cmake.version = ">=3.20" cmake.source-dir = "python" diff --git a/python/pyproject.toml b/python/pyproject.toml index 2de485b..6925126 100644 --- a/python/pyproject.toml +++ b/python/pyproject.toml @@ -16,9 +16,6 @@ keywords = ["hardware", "classification", "system-info", "gpu", "cpu"] Repository = "https://github.com/zeth/caste" Issues = "https://github.com/zeth/caste/issues" -[project.optional-dependencies] -test = ["pytest"] - [tool.scikit-build] cmake.version = ">=3.20" cmake.source-dir = "." From 6fa7247735e33e1f2f766fb9f4ad15667f29b710 Mon Sep 17 00:00:00 2001 From: Zeth Date: Tue, 7 Apr 2026 22:45:44 +0100 Subject: [PATCH 07/11] Pin all the CI deps to hashed versions. --- .github/workflows/ci.yml | 4 +-- .github/workflows/release.yml | 6 ++++- .github/workflows/testpypi.yml | 6 ++++- python/constraints-ci.txt | 48 ++++++++++++++++++++++++++++++++++ 4 files changed, 59 insertions(+), 5 deletions(-) create mode 100644 python/constraints-ci.txt diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index de56d56..1481092 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -42,9 +42,7 @@ jobs: - name: Install and test run: | cd python - python -m pip install \ - "scikit-build-core==0.10.0" \ - "pybind11==2.11.0" + python -m pip install --require-hashes -r constraints-ci.txt python -m pip install --no-build-isolation -e . python -m unittest discover -s tests -p 'test_*.py' diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f58f1eb..a8e1760 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -56,6 +56,10 @@ jobs: pypi-build: runs-on: ubuntu-latest + env: + PIP_CONSTRAINT: ${{ github.workspace }}/python/constraints-ci.txt + PIP_REQUIRE_HASHES: "1" + CIBW_ENVIRONMENT: PIP_CONSTRAINT=/project/python/constraints-ci.txt PIP_REQUIRE_HASHES=1 steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: @@ -65,7 +69,7 @@ jobs: python-version: "3.12" - name: Build sdist and wheels run: | - python -m pip install "build==1.4.2" "cibuildwheel==3.4.1" + python -m pip install --require-hashes -r python/constraints-ci.txt CASTE_VERSION="$(python - <<'PY' import tomllib with open("python/pyproject.toml", "rb") as f: diff --git a/.github/workflows/testpypi.yml b/.github/workflows/testpypi.yml index 1b6d556..189974f 100644 --- a/.github/workflows/testpypi.yml +++ b/.github/workflows/testpypi.yml @@ -9,6 +9,10 @@ permissions: jobs: pypi-test-build: runs-on: ubuntu-latest + env: + PIP_CONSTRAINT: ${{ github.workspace }}/python/constraints-ci.txt + PIP_REQUIRE_HASHES: "1" + CIBW_ENVIRONMENT: PIP_CONSTRAINT=/project/python/constraints-ci.txt PIP_REQUIRE_HASHES=1 steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: @@ -18,7 +22,7 @@ jobs: python-version: "3.12" - name: Build sdist and wheels run: | - python -m pip install "build==1.4.2" "cibuildwheel==3.4.1" + python -m pip install --require-hashes -r python/constraints-ci.txt CASTE_VERSION="$(python - <<'PY' import tomllib with open("python/pyproject.toml", "rb") as f: diff --git a/python/constraints-ci.txt b/python/constraints-ci.txt new file mode 100644 index 0000000..ba29b59 --- /dev/null +++ b/python/constraints-ci.txt @@ -0,0 +1,48 @@ +# Pinned Python build and release toolchain for CI/publish workflows. +# Install with: +# python -m pip install --require-hashes -r python/constraints-ci.txt + +scikit-build-core==0.10.0 \ + --hash=sha256:d6ba451a84da6515b696d1aaecc935ce4e7a28ed18a8093d6effa2726d50e3fe +pybind11==2.11.0 \ + --hash=sha256:307443ea89b73ce88f68fa48687d160c036622a54bc2a25aae9d5ea792bef268 +packaging==26.0 \ + --hash=sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529 +pathspec==1.0.4 \ + --hash=sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723 + +build==1.4.2 \ + --hash=sha256:7a4d8651ea877cb2a89458b1b198f2e69f536c95e89129dbf5d448045d60db88 +pyproject-hooks==1.2.0 \ + --hash=sha256:9e5c6bfa8dcc30091c74b0cf803c81fdd29d94f01992a7707bc97babb1141913 +wheel==0.46.3 \ + --hash=sha256:4b399d56c9d9338230118d705d9737a2a468ccca63d5e813e2a4fc7815d8bc4d +tomli==2.3.0 ; python_version < "3.11" \ + --hash=sha256:64be704a875d2a59753d80ee8a533c3fe183e3f06807ff7dc2232938ccb01549 +importlib-metadata==8.7.0 ; python_full_version < "3.10.2" \ + --hash=sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd +zipp==3.23.0 ; python_full_version < "3.10.2" \ + --hash=sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e +colorama==0.4.6 ; os_name == "nt" \ + --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6 + +cibuildwheel==3.4.1 \ + --hash=sha256:d495eb8780473029382976e86363562923401ac575c18bc52e6a9102512853ad +bashlex==0.18 \ + --hash=sha256:91d73a23a3e51711919c1c899083890cdecffc91d8c088942725ac13e9dcfffa +bracex==2.6 \ + --hash=sha256:0b0049264e7340b3ec782b5cb99beb325f36c3782a32e36e876452fd49a09952 +certifi==2026.2.25 \ + --hash=sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa +dependency-groups==1.3.1 \ + --hash=sha256:51aeaa0dfad72430fcfb7bcdbefbd75f3792e5919563077f30bc0d73f4493030 +filelock==3.25.2 \ + --hash=sha256:ca8afb0da15f229774c9ad1b455ed96e85a81373065fb10446672f64444ddf70 +humanize==4.15.0 \ + --hash=sha256:b1186eb9f5a9749cd9cb8565aee77919dd7c8d076161cf44d70e59e3301e1769 +patchelf==0.17.2.4 ; (sys_platform == "linux" or sys_platform == "darwin") and (platform_machine == "x86_64" or platform_machine == "arm64" or platform_machine == "aarch64") \ + --hash=sha256:d9b35ebfada70c02679ad036407d9724ffe1255122ba4ac5e4be5868618a5689 +platformdirs==4.9.4 \ + --hash=sha256:68a9a4619a666ea6439f2ff250c12a853cd1cbd5158d258bd824a7df6be2f868 +pyelftools==0.32 \ + --hash=sha256:013df952a006db5e138b1edf6d8a68ecc50630adbd0d83a2d41e7f846163d738 From 7b74129059277d977f46cb56e8155a91d48cfb4e Mon Sep 17 00:00:00 2001 From: Zeth Date: Wed, 8 Apr 2026 23:08:00 +0100 Subject: [PATCH 08/11] Update CI. --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1481092..1a70109 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -42,7 +42,7 @@ jobs: - name: Install and test run: | cd python - python -m pip install --require-hashes -r constraints-ci.txt + python -m pip install --require-hashes -r constraints-test.txt python -m pip install --no-build-isolation -e . python -m unittest discover -s tests -p 'test_*.py' From 7f737e5af9c3c65e59aea8e3d078706ef5aef031 Mon Sep 17 00:00:00 2001 From: Zeth Date: Wed, 8 Apr 2026 23:09:13 +0100 Subject: [PATCH 09/11] Add test constraints. --- python/constraints-test.txt | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 python/constraints-test.txt diff --git a/python/constraints-test.txt b/python/constraints-test.txt new file mode 100644 index 0000000..ca1ca51 --- /dev/null +++ b/python/constraints-test.txt @@ -0,0 +1,14 @@ +# Pinned Python test/build toolchain for the CI matrix. +# Install with: +# python -m pip install --require-hashes -r python/constraints-test.txt + +scikit-build-core==0.10.0 \ + --hash=sha256:d6ba451a84da6515b696d1aaecc935ce4e7a28ed18a8093d6effa2726d50e3fe +pybind11==2.11.0 \ + --hash=sha256:307443ea89b73ce88f68fa48687d160c036622a54bc2a25aae9d5ea792bef268 +packaging==26.0 \ + --hash=sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529 +pathspec==1.0.4 \ + --hash=sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723 +tomli==2.3.0 ; python_version < "3.11" \ + --hash=sha256:64be704a875d2a59753d80ee8a533c3fe183e3f06807ff7dc2232938ccb01549 From 5eebc70dea51ec7642617e279ac7a1d9c2caf139 Mon Sep 17 00:00:00 2001 From: Zeth Date: Wed, 8 Apr 2026 23:11:44 +0100 Subject: [PATCH 10/11] Update contraints. --- python/constraints-test.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/python/constraints-test.txt b/python/constraints-test.txt index ca1ca51..5820f0e 100644 --- a/python/constraints-test.txt +++ b/python/constraints-test.txt @@ -10,5 +10,9 @@ packaging==26.0 \ --hash=sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529 pathspec==1.0.4 \ --hash=sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723 +exceptiongroup==1.3.1 ; python_version < "3.11" \ + --hash=sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598 +typing-extensions==4.15.0 ; python_version < "3.11" \ + --hash=sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548 tomli==2.3.0 ; python_version < "3.11" \ --hash=sha256:64be704a875d2a59753d80ee8a533c3fe183e3f06807ff7dc2232938ccb01549 From c23fc9fbbc489eef8c3c20d6a7a86b9c4e76ebc7 Mon Sep 17 00:00:00 2001 From: Zeth Date: Wed, 8 Apr 2026 23:14:06 +0100 Subject: [PATCH 11/11] Update licence string. --- .github/workflows/release.yml | 2 +- .github/workflows/testpypi.yml | 2 +- python/pyproject.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a8e1760..af92bde 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -100,7 +100,7 @@ jobs: description = "Opinionated hardware classification library." readme = "python/README.md" requires-python = ">=3.8" - license = "MIT" + license = { text = "MIT" } authors = [{ name = "Zeth", email = "holdercardteam+caste@gmail.com" }] keywords = ["hardware", "classification", "system-info", "gpu", "cpu"] diff --git a/.github/workflows/testpypi.yml b/.github/workflows/testpypi.yml index 189974f..e6023d8 100644 --- a/.github/workflows/testpypi.yml +++ b/.github/workflows/testpypi.yml @@ -53,7 +53,7 @@ jobs: description = "Opinionated hardware classification library." readme = "python/README.md" requires-python = ">=3.8" - license = "MIT" + license = { text = "MIT" } authors = [{ name = "Zeth", email = "holdercardteam+caste@gmail.com" }] keywords = ["hardware", "classification", "system-info", "gpu", "cpu"] diff --git a/python/pyproject.toml b/python/pyproject.toml index 6925126..d7b472d 100644 --- a/python/pyproject.toml +++ b/python/pyproject.toml @@ -8,7 +8,7 @@ version = "0.1.4" description = "Opinionated hardware classification library." readme = "README.md" requires-python = ">=3.8" -license = "MIT" +license = { text = "MIT" } authors = [{ name = "Zeth", email = "holdercardteam+caste@gmail.com" }] keywords = ["hardware", "classification", "system-info", "gpu", "cpu"]