toolframe-testkit 0.1.2__tar.gz → 0.2.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {toolframe_testkit-0.1.2 → toolframe_testkit-0.2.1}/.github/workflows/ci.yml +1 -1
- toolframe_testkit-0.2.1/.github/workflows/compatibility.yml +85 -0
- {toolframe_testkit-0.1.2 → toolframe_testkit-0.2.1}/.github/workflows/publish.yml +7 -11
- toolframe_testkit-0.2.1/.github/workflows/quality.yml +63 -0
- toolframe_testkit-0.2.1/.pre-commit-config.yaml +29 -0
- toolframe_testkit-0.2.1/CONTRIBUTING.md +49 -0
- {toolframe_testkit-0.1.2 → toolframe_testkit-0.2.1}/PKG-INFO +10 -5
- {toolframe_testkit-0.1.2 → toolframe_testkit-0.2.1}/README.md +8 -3
- toolframe_testkit-0.2.1/cchk.toml +10 -0
- {toolframe_testkit-0.1.2 → toolframe_testkit-0.2.1}/docs/development.md +18 -19
- {toolframe_testkit-0.1.2 → toolframe_testkit-0.2.1}/docs/harness.md +2 -2
- {toolframe_testkit-0.1.2 → toolframe_testkit-0.2.1}/docs/releasing.md +12 -11
- {toolframe_testkit-0.1.2 → toolframe_testkit-0.2.1}/examples/installed_application.py +2 -2
- {toolframe_testkit-0.1.2 → toolframe_testkit-0.2.1}/pyproject.toml +2 -2
- toolframe_testkit-0.2.1/scripts/check_branch_name.py +59 -0
- {toolframe_testkit-0.1.2 → toolframe_testkit-0.2.1}/scripts/smoke_wheel.py +3 -3
- {toolframe_testkit-0.1.2 → toolframe_testkit-0.2.1}/src/toolframe_testkit/__init__.py +1 -1
- {toolframe_testkit-0.1.2 → toolframe_testkit-0.2.1}/tests/assets/service-plugin/pyproject.toml +1 -1
- {toolframe_testkit-0.1.2 → toolframe_testkit-0.2.1}/tests/assets/service-plugin/src/fixture_records/definition.py +1 -1
- {toolframe_testkit-0.1.2 → toolframe_testkit-0.2.1}/tests/assets/service-plugin/src/fixture_records/service.py +3 -3
- {toolframe_testkit-0.1.2 → toolframe_testkit-0.2.1}/tests/test_installed_core.py +1 -1
- {toolframe_testkit-0.1.2 → toolframe_testkit-0.2.1}/uv.lock +2 -2
- {toolframe_testkit-0.1.2 → toolframe_testkit-0.2.1}/.gitignore +0 -0
- {toolframe_testkit-0.1.2 → toolframe_testkit-0.2.1}/LICENSE +0 -0
- {toolframe_testkit-0.1.2 → toolframe_testkit-0.2.1}/conftest.py +0 -0
- {toolframe_testkit-0.1.2 → toolframe_testkit-0.2.1}/docs/contracts.md +0 -0
- {toolframe_testkit-0.1.2 → toolframe_testkit-0.2.1}/examples/check_fixture.py +0 -0
- {toolframe_testkit-0.1.2 → toolframe_testkit-0.2.1}/scripts/validate_release.py +0 -0
- {toolframe_testkit-0.1.2 → toolframe_testkit-0.2.1}/src/toolframe_testkit/contracts.py +0 -0
- {toolframe_testkit-0.1.2 → toolframe_testkit-0.2.1}/src/toolframe_testkit/harness.py +0 -0
- {toolframe_testkit-0.1.2 → toolframe_testkit-0.2.1}/src/toolframe_testkit/py.typed +0 -0
- {toolframe_testkit-0.1.2 → toolframe_testkit-0.2.1}/src/toolframe_testkit/pytest_plugin.py +0 -0
- {toolframe_testkit-0.1.2 → toolframe_testkit-0.2.1}/tests/assets/service-plugin/LICENSE +0 -0
- {toolframe_testkit-0.1.2 → toolframe_testkit-0.2.1}/tests/assets/service-plugin/src/fixture_records/__init__.py +0 -0
- {toolframe_testkit-0.1.2 → toolframe_testkit-0.2.1}/tests/assets/service-plugin/src/fixture_records/docs/usage.md +0 -0
- {toolframe_testkit-0.1.2 → toolframe_testkit-0.2.1}/tests/assets/service-plugin/src/fixture_records/migrations/0001_init.sql +0 -0
- {toolframe_testkit-0.1.2 → toolframe_testkit-0.2.1}/tests/test_contracts.py +0 -0
- {toolframe_testkit-0.1.2 → toolframe_testkit-0.2.1}/tests/test_harness_http.py +0 -0
- {toolframe_testkit-0.1.2 → toolframe_testkit-0.2.1}/tests/test_harness_process.py +0 -0
- {toolframe_testkit-0.1.2 → toolframe_testkit-0.2.1}/tests/test_pytest_plugin.py +0 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
name: SDK compatibility
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
push:
|
|
6
|
+
branches: [main]
|
|
7
|
+
schedule:
|
|
8
|
+
- cron: '17 4 * * *'
|
|
9
|
+
workflow_dispatch:
|
|
10
|
+
workflow_call:
|
|
11
|
+
|
|
12
|
+
permissions:
|
|
13
|
+
contents: read
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
wheel:
|
|
17
|
+
name: Build the shared plugin wheel
|
|
18
|
+
runs-on: ubuntu-24.04
|
|
19
|
+
timeout-minutes: 10
|
|
20
|
+
steps:
|
|
21
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
22
|
+
with:
|
|
23
|
+
persist-credentials: false
|
|
24
|
+
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
|
25
|
+
with:
|
|
26
|
+
python-version: '3.14.7'
|
|
27
|
+
- uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
|
|
28
|
+
with:
|
|
29
|
+
version: '0.12.8'
|
|
30
|
+
enable-cache: false
|
|
31
|
+
- run: uv build --no-sources --wheel --out-dir build/compatibility-wheel
|
|
32
|
+
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
33
|
+
with:
|
|
34
|
+
name: compatibility-wheel
|
|
35
|
+
path: build/compatibility-wheel/*.whl
|
|
36
|
+
if-no-files-found: error
|
|
37
|
+
retention-days: 7
|
|
38
|
+
|
|
39
|
+
compatibility:
|
|
40
|
+
name: SDK ${{ matrix.sdk }}
|
|
41
|
+
needs: wheel
|
|
42
|
+
runs-on: ubuntu-24.04
|
|
43
|
+
timeout-minutes: 20
|
|
44
|
+
strategy:
|
|
45
|
+
fail-fast: false
|
|
46
|
+
matrix:
|
|
47
|
+
sdk: [minimum, latest]
|
|
48
|
+
steps:
|
|
49
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
50
|
+
with:
|
|
51
|
+
persist-credentials: false
|
|
52
|
+
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
|
53
|
+
with:
|
|
54
|
+
python-version: '3.14.7'
|
|
55
|
+
- uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
|
|
56
|
+
with:
|
|
57
|
+
version: '0.12.8'
|
|
58
|
+
enable-cache: false
|
|
59
|
+
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
60
|
+
with:
|
|
61
|
+
name: compatibility-wheel
|
|
62
|
+
path: build/compatibility-wheel
|
|
63
|
+
- name: Select the unchanged wheel
|
|
64
|
+
id: wheels
|
|
65
|
+
shell: bash
|
|
66
|
+
run: |
|
|
67
|
+
plugin=(build/compatibility-wheel/toolframe_testkit-*.whl)
|
|
68
|
+
test "${#plugin[@]}" = 1
|
|
69
|
+
echo "plugin=${plugin[0]}" >> "$GITHUB_OUTPUT"
|
|
70
|
+
- name: Verify installed wheel and package tests
|
|
71
|
+
uses: toolframe/toolframe-sdk/.github/actions/plugin-compatibility@2e2a3d79f35fbdb04249e8351fe9c75fc2164269
|
|
72
|
+
with:
|
|
73
|
+
mode: ${{ matrix.sdk }}
|
|
74
|
+
wheel: ${{ steps.wheels.outputs.plugin }}
|
|
75
|
+
output: build/compatibility-${{ matrix.sdk }}
|
|
76
|
+
- name: Retain compatibility evidence
|
|
77
|
+
if: always()
|
|
78
|
+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
79
|
+
with:
|
|
80
|
+
name: compatibility-${{ matrix.sdk }}
|
|
81
|
+
path: |
|
|
82
|
+
build/compatibility-${{ matrix.sdk }}/report.json
|
|
83
|
+
build/compatibility-${{ matrix.sdk }}/pytest.xml
|
|
84
|
+
if-no-files-found: warn
|
|
85
|
+
retention-days: 14
|
|
@@ -2,19 +2,12 @@ name: Publish Python distribution
|
|
|
2
2
|
|
|
3
3
|
on:
|
|
4
4
|
workflow_dispatch:
|
|
5
|
-
inputs:
|
|
6
|
-
index:
|
|
7
|
-
description: Package index; run against the matching vVERSION tag
|
|
8
|
-
type: choice
|
|
9
|
-
options: [testpypi, pypi]
|
|
10
|
-
default: testpypi
|
|
11
|
-
required: true
|
|
12
5
|
|
|
13
6
|
permissions:
|
|
14
7
|
contents: read
|
|
15
8
|
|
|
16
9
|
concurrency:
|
|
17
|
-
group: publish
|
|
10
|
+
group: publish-pypi
|
|
18
11
|
cancel-in-progress: false
|
|
19
12
|
|
|
20
13
|
jobs:
|
|
@@ -45,12 +38,15 @@ jobs:
|
|
|
45
38
|
uses: ./.github/workflows/ci.yml
|
|
46
39
|
secrets: inherit
|
|
47
40
|
|
|
41
|
+
compatibility:
|
|
42
|
+
uses: ./.github/workflows/compatibility.yml
|
|
43
|
+
|
|
48
44
|
publish:
|
|
49
|
-
needs: [release-tag, validate]
|
|
45
|
+
needs: [release-tag, validate, compatibility]
|
|
50
46
|
runs-on: ubuntu-24.04
|
|
51
47
|
timeout-minutes: 10
|
|
52
48
|
environment:
|
|
53
|
-
name:
|
|
49
|
+
name: pypi
|
|
54
50
|
permissions:
|
|
55
51
|
contents: read
|
|
56
52
|
id-token: write
|
|
@@ -62,4 +58,4 @@ jobs:
|
|
|
62
58
|
- uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
|
|
63
59
|
with:
|
|
64
60
|
packages-dir: dist/
|
|
65
|
-
repository-url:
|
|
61
|
+
repository-url: https://upload.pypi.org/legacy/
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
name: Code quality
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
types: [opened, synchronize, reopened, edited]
|
|
8
|
+
workflow_dispatch:
|
|
9
|
+
|
|
10
|
+
permissions:
|
|
11
|
+
contents: read
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
branch-policy:
|
|
15
|
+
runs-on: ubuntu-24.04
|
|
16
|
+
timeout-minutes: 5
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
19
|
+
with:
|
|
20
|
+
persist-credentials: false
|
|
21
|
+
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
|
22
|
+
with:
|
|
23
|
+
python-version: '3.14.7'
|
|
24
|
+
- name: Check the actual source branch
|
|
25
|
+
env:
|
|
26
|
+
BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
|
|
27
|
+
run: python scripts/check_branch_name.py "$BRANCH_NAME"
|
|
28
|
+
|
|
29
|
+
code-quality:
|
|
30
|
+
runs-on: ubuntu-24.04
|
|
31
|
+
timeout-minutes: 10
|
|
32
|
+
steps:
|
|
33
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
34
|
+
with:
|
|
35
|
+
persist-credentials: false
|
|
36
|
+
- uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
|
|
37
|
+
with:
|
|
38
|
+
version: '0.12.8'
|
|
39
|
+
enable-cache: false
|
|
40
|
+
- name: Check Python lint and formatting
|
|
41
|
+
run: uvx prek==0.5.5 run ruff-check ruff-format --all-files --show-diff-on-failure
|
|
42
|
+
|
|
43
|
+
commit-policy:
|
|
44
|
+
if: github.event_name == 'pull_request'
|
|
45
|
+
runs-on: ubuntu-24.04
|
|
46
|
+
timeout-minutes: 10
|
|
47
|
+
permissions:
|
|
48
|
+
contents: read
|
|
49
|
+
pull-requests: read
|
|
50
|
+
steps:
|
|
51
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
52
|
+
with:
|
|
53
|
+
fetch-depth: 0
|
|
54
|
+
persist-credentials: false
|
|
55
|
+
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
|
56
|
+
with:
|
|
57
|
+
python-version: '3.14.7'
|
|
58
|
+
- uses: commit-check/commit-check-action@a61779784a953fd9047c001c102cfeac5cf26964 # commit-check 2.18.3
|
|
59
|
+
with:
|
|
60
|
+
message: true
|
|
61
|
+
branch: false
|
|
62
|
+
pr-title: true
|
|
63
|
+
pr-comments: false
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
default_install_hook_types: [pre-commit, commit-msg]
|
|
2
|
+
default_stages: [pre-commit]
|
|
3
|
+
default_language_version:
|
|
4
|
+
python: python3.11
|
|
5
|
+
|
|
6
|
+
repos:
|
|
7
|
+
- repo: local
|
|
8
|
+
hooks:
|
|
9
|
+
- id: check-branch-name
|
|
10
|
+
name: Check branch naming policy
|
|
11
|
+
entry: python scripts/check_branch_name.py
|
|
12
|
+
language: python
|
|
13
|
+
always_run: true
|
|
14
|
+
pass_filenames: false
|
|
15
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
16
|
+
rev: v0.17.0
|
|
17
|
+
hooks:
|
|
18
|
+
- id: ruff-check
|
|
19
|
+
types_or: [python, pyi]
|
|
20
|
+
files: '^(src|tests|scripts)/'
|
|
21
|
+
- id: ruff-format
|
|
22
|
+
types_or: [python, pyi]
|
|
23
|
+
args: [--check]
|
|
24
|
+
files: '^(src|tests|scripts)/'
|
|
25
|
+
- repo: https://github.com/commit-check/commit-check
|
|
26
|
+
rev: v2.18.3
|
|
27
|
+
hooks:
|
|
28
|
+
- id: check-message
|
|
29
|
+
stages: [commit-msg]
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
## Commit hooks
|
|
4
|
+
|
|
5
|
+
Install [prek](https://prek.j178.dev/) and enable the repository's hooks:
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
uv tool install prek==0.5.5
|
|
9
|
+
prek install --allow-missing-config
|
|
10
|
+
prek run --all-files
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
The missing-config option allows older worktrees without this configuration to
|
|
14
|
+
keep working. Worktrees containing `.pre-commit-config.yaml` still run the checks.
|
|
15
|
+
The `pre-commit` hook checks the branch name, Python lint, and formatting. The
|
|
16
|
+
`commit-msg` hook checks Conventional Commits. Both Ruff hooks only check files;
|
|
17
|
+
they do not rewrite a commit's contents. Fix lint and formatting explicitly:
|
|
18
|
+
|
|
19
|
+
```sh
|
|
20
|
+
uvx ruff==0.17.0 check --fix src tests scripts
|
|
21
|
+
uvx ruff==0.17.0 format src tests scripts
|
|
22
|
+
prek run --all-files
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The Ruff version and checked paths are pinned in
|
|
26
|
+
[the hook configuration](.pre-commit-config.yaml); the Python rules remain in
|
|
27
|
+
[pyproject.toml](pyproject.toml). CI uses these same hooks on all tracked files
|
|
28
|
+
within their configured paths. Existing package, integration, and release checks
|
|
29
|
+
run independently.
|
|
30
|
+
|
|
31
|
+
## Branches and commits
|
|
32
|
+
|
|
33
|
+
[cchk.toml](cchk.toml) defines the allowed branch and commit types. Work branches
|
|
34
|
+
use `feat/`, `fix/`, `release/`, or `chore/`, followed by lowercase ASCII letters
|
|
35
|
+
and digits separated by single hyphens. Release versions may also contain dots,
|
|
36
|
+
as in `release/v1.2.0-rc.1`; every separator must have an alphanumeric character on
|
|
37
|
+
both sides. The default branch `main` is the standalone exception.
|
|
38
|
+
|
|
39
|
+
Commit types are independent of branch types: for example,
|
|
40
|
+
`docs: clarify setup` is valid on `chore/update-docs`. Commit-check uses Conventional
|
|
41
|
+
Commits with its default subject length of 5–80 characters. Pull requests check
|
|
42
|
+
their commits and title so the title can be used for a Conventional Commit when
|
|
43
|
+
squash merging.
|
|
44
|
+
|
|
45
|
+
Local hooks can be bypassed. CI reports `branch-policy`, `code-quality`, and
|
|
46
|
+
`commit-policy`; maintainers must configure required status checks separately to
|
|
47
|
+
make passing checks a merge requirement.
|
|
48
|
+
|
|
49
|
+
For package setup and broader verification, see [the development guide](docs/development.md).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: toolframe-testkit
|
|
3
|
-
Version: 0.1
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: Public contract checks and installed-application tests for Toolframe developers
|
|
5
5
|
Project-URL: Homepage, https://github.com/toolframe/toolframe-testkit
|
|
6
6
|
Project-URL: Repository, https://github.com/toolframe/toolframe-testkit
|
|
@@ -15,7 +15,7 @@ Requires-Dist: jsonschema<5,>=4.23
|
|
|
15
15
|
Requires-Dist: psutil<9,>=7
|
|
16
16
|
Requires-Dist: pydantic<3,>=2.12.5
|
|
17
17
|
Requires-Dist: tomlkit<1,>=0.13
|
|
18
|
-
Requires-Dist: toolframe-sdk
|
|
18
|
+
Requires-Dist: toolframe-sdk<1,>=0.4.0
|
|
19
19
|
Provides-Extra: pytest
|
|
20
20
|
Requires-Dist: pytest<10,>=8; extra == 'pytest'
|
|
21
21
|
Description-Content-Type: text/markdown
|
|
@@ -24,8 +24,8 @@ Description-Content-Type: text/markdown
|
|
|
24
24
|
|
|
25
25
|
Toolframe testkit is a Python library for checking a plugin's public SDK contract
|
|
26
26
|
and exercising an installed Toolframe Core application through its public CLI and
|
|
27
|
-
HTTP APIs. It is a developer test dependency, not a Core runtime plugin.
|
|
28
|
-
|
|
27
|
+
HTTP APIs. It is a developer test dependency, not a Core runtime plugin.
|
|
28
|
+
Use Python 3.14 or newer and a compatible SDK satisfying `toolframe-sdk>=0.4.0,<1`.
|
|
29
29
|
|
|
30
30
|
The package is Apache-2.0 licensed. It does not install Core, choose a global
|
|
31
31
|
application, or load a pytest plugin automatically.
|
|
@@ -35,7 +35,7 @@ application, or load a pytest plugin automatically.
|
|
|
35
35
|
Install the checker and opt-in pytest integration from PyPI:
|
|
36
36
|
|
|
37
37
|
```sh
|
|
38
|
-
python -m pip install 'toolframe-testkit[pytest]==0.1
|
|
38
|
+
python -m pip install 'toolframe-testkit[pytest]==0.2.1'
|
|
39
39
|
```
|
|
40
40
|
|
|
41
41
|
The SDK is installed as a dependency. See
|
|
@@ -106,3 +106,8 @@ remote deployments. It redacts its owned management token and common bearer/toke
|
|
|
106
106
|
query forms, but cannot guarantee redaction of arbitrary plugin output.
|
|
107
107
|
|
|
108
108
|
For release validation and Trusted Publisher setup, see [releasing](docs/releasing.md).
|
|
109
|
+
|
|
110
|
+
For development hooks and contribution rules, see [Contributing](CONTRIBUTING.md).
|
|
111
|
+
|
|
112
|
+
Plugin dependencies allow compatible SDK 0.x updates from the declared minimum.
|
|
113
|
+
See the [SDK compatibility policy](https://github.com/toolframe/toolframe-sdk/blob/main/docs/compatibility.md) for API guarantees and minimum/latest verification.
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Toolframe testkit is a Python library for checking a plugin's public SDK contract
|
|
4
4
|
and exercising an installed Toolframe Core application through its public CLI and
|
|
5
|
-
HTTP APIs. It is a developer test dependency, not a Core runtime plugin.
|
|
6
|
-
|
|
5
|
+
HTTP APIs. It is a developer test dependency, not a Core runtime plugin.
|
|
6
|
+
Use Python 3.14 or newer and a compatible SDK satisfying `toolframe-sdk>=0.4.0,<1`.
|
|
7
7
|
|
|
8
8
|
The package is Apache-2.0 licensed. It does not install Core, choose a global
|
|
9
9
|
application, or load a pytest plugin automatically.
|
|
@@ -13,7 +13,7 @@ application, or load a pytest plugin automatically.
|
|
|
13
13
|
Install the checker and opt-in pytest integration from PyPI:
|
|
14
14
|
|
|
15
15
|
```sh
|
|
16
|
-
python -m pip install 'toolframe-testkit[pytest]==0.1
|
|
16
|
+
python -m pip install 'toolframe-testkit[pytest]==0.2.1'
|
|
17
17
|
```
|
|
18
18
|
|
|
19
19
|
The SDK is installed as a dependency. See
|
|
@@ -84,3 +84,8 @@ remote deployments. It redacts its owned management token and common bearer/toke
|
|
|
84
84
|
query forms, but cannot guarantee redaction of arbitrary plugin output.
|
|
85
85
|
|
|
86
86
|
For release validation and Trusted Publisher setup, see [releasing](docs/releasing.md).
|
|
87
|
+
|
|
88
|
+
For development hooks and contribution rules, see [Contributing](CONTRIBUTING.md).
|
|
89
|
+
|
|
90
|
+
Plugin dependencies allow compatible SDK 0.x updates from the declared minimum.
|
|
91
|
+
See the [SDK compatibility policy](https://github.com/toolframe/toolframe-sdk/blob/main/docs/compatibility.md) for API guarantees and minimum/latest verification.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
[commit]
|
|
2
|
+
conventional_commits = true
|
|
3
|
+
allow_commit_types = ["feat", "fix", "docs", "style", "refactor", "test", "chore", "perf", "build", "ci"]
|
|
4
|
+
|
|
5
|
+
[branch]
|
|
6
|
+
conventional_branch = true
|
|
7
|
+
allow_branch_types = ["feat", "fix", "release", "chore"]
|
|
8
|
+
allow_branch_names = ["main"]
|
|
9
|
+
# scripts/check_branch_name.py enforces these names literally, without the
|
|
10
|
+
# additional standalone branch exceptions built into commit-check.
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
# Development and verification
|
|
2
2
|
|
|
3
3
|
The testkit repository is independently buildable. Runtime dependencies are
|
|
4
|
-
`toolframe-sdk
|
|
4
|
+
`toolframe-sdk>=0.4.0,<1`, `httpx`, `tomlkit`, `psutil`, `pydantic`, and `jsonschema`;
|
|
5
5
|
pytest is available
|
|
6
|
-
through the `[pytest]` extra. Python 3.14 or newer is required.
|
|
7
|
-
published yet, so consumer-style installation uses a local wheelhouse.
|
|
6
|
+
through the `[pytest]` extra. Python 3.14 or newer is required. Consumer-style installation can use PyPI or an explicitly built local wheelhouse.
|
|
8
7
|
|
|
9
8
|
## Local environment
|
|
10
9
|
|
|
@@ -13,7 +12,7 @@ install the package with the development tools. The install must use the
|
|
|
13
12
|
wheelhouse so uv does not select an unrelated source checkout:
|
|
14
13
|
|
|
15
14
|
```sh
|
|
16
|
-
mkdir -p
|
|
15
|
+
mkdir -p build/wheelhouse
|
|
17
16
|
```
|
|
18
17
|
|
|
19
18
|
When testing sibling development checkouts, build their wheels into a local,
|
|
@@ -21,15 +20,15 @@ ignored wheelhouse. These commands are for a sibling checkout layout and are
|
|
|
21
20
|
deliberately explicit about `--no-sources`:
|
|
22
21
|
|
|
23
22
|
```sh
|
|
24
|
-
uv build --no-sources --wheel --out-dir
|
|
23
|
+
uv build --no-sources --wheel --out-dir build/wheelhouse \
|
|
25
24
|
../toolframe-sdk/.worktrees/plugin-services
|
|
26
|
-
uv build --no-sources --wheel --out-dir
|
|
25
|
+
uv build --no-sources --wheel --out-dir build/wheelhouse \
|
|
27
26
|
../fastapi-cordis
|
|
28
|
-
uv build --no-sources --wheel --out-dir
|
|
29
|
-
../toolframe-core
|
|
30
|
-
uv build --no-sources --wheel --out-dir
|
|
31
|
-
../toolframe-
|
|
32
|
-
uv build --no-sources --wheel --out-dir
|
|
27
|
+
uv build --no-sources --wheel --out-dir build/wheelhouse \
|
|
28
|
+
../toolframe-core
|
|
29
|
+
uv build --no-sources --wheel --out-dir build/wheelhouse \
|
|
30
|
+
../toolframe-sql-sqlite
|
|
31
|
+
uv build --no-sources --wheel --out-dir build/wheelhouse \
|
|
33
32
|
tests/assets/service-plugin
|
|
34
33
|
```
|
|
35
34
|
|
|
@@ -39,7 +38,7 @@ SDK wheel, install the development environment:
|
|
|
39
38
|
|
|
40
39
|
```sh
|
|
41
40
|
uv venv .venv
|
|
42
|
-
uv pip install --python .venv/bin/python --find-links
|
|
41
|
+
uv pip install --python .venv/bin/python --find-links build/wheelhouse \
|
|
43
42
|
-e '.[pytest]' pytest ruff build hatchling
|
|
44
43
|
```
|
|
45
44
|
|
|
@@ -71,16 +70,16 @@ Install the matching Core command into the same environment after its Core,
|
|
|
71
70
|
Cordis, and SDK wheels are in the wheelhouse:
|
|
72
71
|
|
|
73
72
|
```sh
|
|
74
|
-
uv pip install --python .venv/bin/python --find-links
|
|
75
|
-
toolframe-core==0.
|
|
73
|
+
uv pip install --python .venv/bin/python --find-links build/wheelhouse \
|
|
74
|
+
toolframe-core==0.7.0
|
|
76
75
|
```
|
|
77
76
|
|
|
78
77
|
```sh
|
|
79
|
-
export TOOLFRAME_TESTKIT_STORAGE_WHEEL="$PWD
|
|
80
|
-
export TOOLFRAME_TESTKIT_FIXTURE_WHEEL="$PWD
|
|
78
|
+
export TOOLFRAME_TESTKIT_STORAGE_WHEEL="$PWD/build/wheelhouse/toolframe_sql_sqlite-0.4.0-py3-none-any.whl"
|
|
79
|
+
export TOOLFRAME_TESTKIT_FIXTURE_WHEEL="$PWD/build/wheelhouse/toolframe_testkit_fixture-0.1.0-py3-none-any.whl"
|
|
81
80
|
.venv/bin/python -m pytest tests/test_installed_core.py \
|
|
82
81
|
--toolframe-core-command "$PWD/.venv/bin/toolframe-core" \
|
|
83
|
-
--toolframe-find-links "$PWD
|
|
82
|
+
--toolframe-find-links "$PWD/build/wheelhouse" \
|
|
84
83
|
--toolframe-output-directory build/installed-core
|
|
85
84
|
```
|
|
86
85
|
|
|
@@ -104,8 +103,8 @@ find-links directory, and output directory:
|
|
|
104
103
|
```sh
|
|
105
104
|
.venv/bin/python scripts/smoke_wheel.py \
|
|
106
105
|
--wheel dist/toolframe_testkit-0.1.2-py3-none-any.whl \
|
|
107
|
-
--fixture-wheel
|
|
108
|
-
--find-links
|
|
106
|
+
--fixture-wheel build/wheelhouse/toolframe_testkit_fixture-0.1.0-py3-none-any.whl \
|
|
107
|
+
--find-links build/wheelhouse \
|
|
109
108
|
--output build/wheel-smoke
|
|
110
109
|
```
|
|
111
110
|
|
|
@@ -14,9 +14,9 @@ from toolframe_testkit import ApplicationHarness
|
|
|
14
14
|
with ApplicationHarness(
|
|
15
15
|
"/path/to/toolframe-core",
|
|
16
16
|
output_directory=Path("build/installed-core"),
|
|
17
|
-
find_links=[Path("
|
|
17
|
+
find_links=[Path("build/wheelhouse")],
|
|
18
18
|
) as app:
|
|
19
|
-
app.install_plugin("
|
|
19
|
+
app.install_plugin("build/wheelhouse/my_plugin-0.1.0-py3-none-any.whl")
|
|
20
20
|
app.wait_for_plugin("my-plugin")
|
|
21
21
|
response = app.service_call(
|
|
22
22
|
"my-plugin", "my.records", "list", {}, entrance="public"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Releasing toolframe-testkit
|
|
2
2
|
|
|
3
|
-
The distribution name is `toolframe-testkit
|
|
3
|
+
The distribution name is `toolframe-testkit`. The version is defined in `pyproject.toml`.
|
|
4
4
|
Release tags must exactly match `v<project.version>`. Ordinary CI never uploads
|
|
5
5
|
packages. The manual `publish.yml` workflow reruns validation and publishes only
|
|
6
6
|
artifacts produced by that successful run.
|
|
@@ -19,13 +19,14 @@ metadata and licenses, runs Ruff and package tests against an installed wheel,
|
|
|
19
19
|
and installs each distribution into a separate fresh consumer environment.
|
|
20
20
|
Optional database/service integrations require their documented fixtures.
|
|
21
21
|
|
|
22
|
-
Publish
|
|
22
|
+
Publish any newly required SDK version before the plugin. Compatible Core or SDK
|
|
23
|
+
updates alone do not require a plugin release. The pytest extra is installed with `toolframe-testkit[pytest]`. The coordinated order and native CLI channels are documented in
|
|
23
24
|
[Toolframe's release guide](https://github.com/toolframe/toolframe-core/blob/main/docs/releasing.md).
|
|
24
25
|
|
|
25
26
|
## Trusted Publishers
|
|
26
27
|
|
|
27
|
-
Create GitHub
|
|
28
|
-
|
|
28
|
+
Create a GitHub environment named `pypi`. On PyPI, configure a normal or
|
|
29
|
+
pending Trusted Publisher with these values:
|
|
29
30
|
|
|
30
31
|
| Field | Value |
|
|
31
32
|
| --- | --- |
|
|
@@ -33,7 +34,7 @@ package index, configure a normal or pending Trusted Publisher with these values
|
|
|
33
34
|
| GitHub owner | `toolframe` |
|
|
34
35
|
| GitHub repository | `toolframe-testkit` |
|
|
35
36
|
| Workflow filename | `publish.yml` |
|
|
36
|
-
| Environment | `
|
|
37
|
+
| Environment | `pypi` |
|
|
37
38
|
|
|
38
39
|
If publishing from another owner, use that actual owner in the publisher binding.
|
|
39
40
|
Follow [PyPI's Trusted Publishing guide](https://docs.pypi.org/trusted-publishers/using-a-publisher/).
|
|
@@ -49,7 +50,7 @@ sibling owner where the workflow exposes that variable.
|
|
|
49
50
|
## Publish and verify
|
|
50
51
|
|
|
51
52
|
After the release commit and matching tag are available remotely, manually run
|
|
52
|
-
`publish.yml` at `
|
|
53
|
+
`publish.yml` at the matching `v<project.version>` tag to publish to PyPI.
|
|
53
54
|
A branch or mismatched tag is rejected. Downloaded files are the artifacts from
|
|
54
55
|
that run; the publishing job does not rebuild them. Published files cannot be
|
|
55
56
|
replaced under the same version.
|
|
@@ -58,11 +59,11 @@ After PyPI publication, verify a clean installation:
|
|
|
58
59
|
|
|
59
60
|
```sh
|
|
60
61
|
uv venv --python 3.14.7 build/consumer
|
|
61
|
-
uv pip install --python build/consumer/bin/python 'toolframe-testkit==0.1
|
|
62
|
+
uv pip install --python build/consumer/bin/python 'toolframe-testkit==0.2.1'
|
|
62
63
|
uv pip check --python build/consumer/bin/python
|
|
63
64
|
```
|
|
64
65
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
66
|
+
Test the specific artifact with dependencies from PyPI. Local artifact checks
|
|
67
|
+
alone do not demonstrate that the published package resolves correctly from
|
|
68
|
+
PyPI. Keep logs, hashes and exact revisions in CI artifacts or ignored local
|
|
69
|
+
evidence directories.
|
|
@@ -5,7 +5,7 @@ Example::
|
|
|
5
5
|
python examples/installed_application.py \
|
|
6
6
|
--core-command /path/to/toolframe-core \
|
|
7
7
|
--wheelhouse .local/wheelhouse \
|
|
8
|
-
--storage-wheel /tmp/
|
|
8
|
+
--storage-wheel /tmp/toolframe_sql_sqlite.whl \
|
|
9
9
|
--fixture-wheel /tmp/toolframe_testkit_fixture.whl \
|
|
10
10
|
--output build/installed-application
|
|
11
11
|
|
|
@@ -21,7 +21,7 @@ from pathlib import Path
|
|
|
21
21
|
|
|
22
22
|
from toolframe_testkit import ApplicationHarness
|
|
23
23
|
|
|
24
|
-
_STORAGE_PLUGIN = "
|
|
24
|
+
_STORAGE_PLUGIN = "sql-sqlite"
|
|
25
25
|
_FIXTURE_PLUGIN = "fixture-records"
|
|
26
26
|
_SERVICE = "fixture-records.records"
|
|
27
27
|
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "toolframe-testkit"
|
|
7
|
-
version = "0.1
|
|
7
|
+
version = "0.2.1"
|
|
8
8
|
description = "Public contract checks and installed-application tests for Toolframe developers"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "Apache-2.0"
|
|
@@ -12,7 +12,7 @@ license-files = ["LICENSE"]
|
|
|
12
12
|
authors = [{ name = "byfb", email = "62798989+byfb@users.noreply.github.com" }]
|
|
13
13
|
requires-python = ">=3.14"
|
|
14
14
|
dependencies = [
|
|
15
|
-
"toolframe-sdk
|
|
15
|
+
"toolframe-sdk>=0.4.0,<1", "httpx>=0.28,<1", "tomlkit>=0.13,<1", "psutil>=7,<9",
|
|
16
16
|
"jsonschema>=4.23,<5", "pydantic>=2.12.5,<3",
|
|
17
17
|
]
|
|
18
18
|
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""Validate a working branch using the repository's cchk.toml policy."""
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import re
|
|
5
|
+
import subprocess
|
|
6
|
+
import sys
|
|
7
|
+
import tomllib
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def main() -> int:
|
|
12
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
13
|
+
parser.add_argument("branch", nargs="?", help="actual source branch in CI")
|
|
14
|
+
args = parser.parse_args()
|
|
15
|
+
root = Path(__file__).resolve().parent.parent
|
|
16
|
+
policy = tomllib.loads((root / "cchk.toml").read_text())["branch"]
|
|
17
|
+
branch = args.branch
|
|
18
|
+
if branch is None:
|
|
19
|
+
result = subprocess.run(
|
|
20
|
+
["git", "symbolic-ref", "--quiet", "--short", "HEAD"],
|
|
21
|
+
cwd=root,
|
|
22
|
+
capture_output=True,
|
|
23
|
+
text=True,
|
|
24
|
+
check=False,
|
|
25
|
+
)
|
|
26
|
+
if result.returncode:
|
|
27
|
+
print(
|
|
28
|
+
"Pass the actual source branch when HEAD is detached.", file=sys.stderr
|
|
29
|
+
)
|
|
30
|
+
return 1
|
|
31
|
+
branch = result.stdout.strip()
|
|
32
|
+
|
|
33
|
+
if branch in policy["allow_branch_names"]:
|
|
34
|
+
return 0
|
|
35
|
+
prefix, separator, description = branch.partition("/")
|
|
36
|
+
pattern = (
|
|
37
|
+
r"[a-z0-9]+(?:[.-][a-z0-9]+)*"
|
|
38
|
+
if prefix == "release"
|
|
39
|
+
else r"[a-z0-9]+(?:-[a-z0-9]+)*"
|
|
40
|
+
)
|
|
41
|
+
if (
|
|
42
|
+
separator
|
|
43
|
+
and prefix in policy["allow_branch_types"]
|
|
44
|
+
and re.fullmatch(pattern, description)
|
|
45
|
+
):
|
|
46
|
+
return 0
|
|
47
|
+
|
|
48
|
+
allowed = ", ".join(policy["allow_branch_types"])
|
|
49
|
+
print(
|
|
50
|
+
f"Invalid branch {branch!r}. Use <type>/<description> with type: {allowed}. "
|
|
51
|
+
"Use lowercase letters and digits separated by single hyphens; "
|
|
52
|
+
"release versions may also contain dots.",
|
|
53
|
+
file=sys.stderr,
|
|
54
|
+
)
|
|
55
|
+
return 1
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
if __name__ == "__main__":
|
|
59
|
+
raise SystemExit(main())
|
|
@@ -21,7 +21,7 @@ from toolframe_sdk import load_object
|
|
|
21
21
|
from toolframe_testkit import ResourceExample, ServiceExample, check_definition
|
|
22
22
|
|
|
23
23
|
distribution = metadata.distribution("toolframe-testkit")
|
|
24
|
-
assert distribution.version ==
|
|
24
|
+
assert distribution.version == sys.argv[1]
|
|
25
25
|
assert importlib.util.find_spec("toolframe_core") is None
|
|
26
26
|
assert importlib.util.find_spec("pytest") is None
|
|
27
27
|
assert "pytest" not in sys.modules
|
|
@@ -66,7 +66,7 @@ def main():
|
|
|
66
66
|
assert document["Requires-Python"] == ">=3.14"
|
|
67
67
|
assert document["License-Expression"] == "Apache-2.0"
|
|
68
68
|
requirements = document.get_all("Requires-Dist") or []
|
|
69
|
-
assert any(item.startswith("toolframe-sdk
|
|
69
|
+
assert any(item.startswith("toolframe-sdk<1,>=0.4.0") for item in requirements)
|
|
70
70
|
assert not any(" @ " in item or "file:" in item for item in requirements)
|
|
71
71
|
assert not any(
|
|
72
72
|
"fixture_records/" in name or name.startswith("tests/") for name in names
|
|
@@ -109,7 +109,7 @@ def main():
|
|
|
109
109
|
timeout=180,
|
|
110
110
|
)
|
|
111
111
|
result = subprocess.run(
|
|
112
|
-
[str(python), "-I", "-c", CHECK],
|
|
112
|
+
[str(python), "-I", "-c", CHECK, document["Version"]],
|
|
113
113
|
cwd=output,
|
|
114
114
|
env=environment,
|
|
115
115
|
capture_output=True,
|
{toolframe_testkit-0.1.2 → toolframe_testkit-0.2.1}/tests/assets/service-plugin/pyproject.toml
RENAMED
|
@@ -9,7 +9,7 @@ description = "Small independently packaged Toolframe records plugin for testkit
|
|
|
9
9
|
license = "Apache-2.0"
|
|
10
10
|
license-files = ["LICENSE"]
|
|
11
11
|
requires-python = ">=3.14"
|
|
12
|
-
dependencies = ["toolframe-sdk
|
|
12
|
+
dependencies = ["toolframe-sdk>=0.4.0,<1"]
|
|
13
13
|
|
|
14
14
|
[project.entry-points."toolframe.plugins"]
|
|
15
15
|
fixture-records = "fixture_records.definition:plugin"
|
|
@@ -87,7 +87,7 @@ plugin = PluginDefinition(
|
|
|
87
87
|
"fixture-records.write": ScopeSpec(title="Append fixture records"),
|
|
88
88
|
}
|
|
89
89
|
),
|
|
90
|
-
requires={"
|
|
90
|
+
requires={"sql": Requirement(service="toolframe.sql")},
|
|
91
91
|
services={
|
|
92
92
|
SERVICE: ServiceSpec(
|
|
93
93
|
methods={
|
|
@@ -32,7 +32,7 @@ class Records(Plugin):
|
|
|
32
32
|
|
|
33
33
|
async def append(self, body: object) -> dict:
|
|
34
34
|
body = _validate_body(body)
|
|
35
|
-
storage = self.context.
|
|
35
|
+
storage = self.context.sql()
|
|
36
36
|
async with storage.transaction() as transaction:
|
|
37
37
|
result = await transaction.execute_returning(
|
|
38
38
|
"INSERT INTO records(body) VALUES (?) RETURNING id",
|
|
@@ -62,7 +62,7 @@ class Records(Plugin):
|
|
|
62
62
|
|
|
63
63
|
async def list_records(self, limit: object = 20) -> dict:
|
|
64
64
|
limit = _validate_limit(limit)
|
|
65
|
-
records = await self.context.
|
|
65
|
+
records = await self.context.sql().query(
|
|
66
66
|
"SELECT id, body FROM records ORDER BY id DESC LIMIT ?",
|
|
67
67
|
(limit,),
|
|
68
68
|
)
|
|
@@ -90,5 +90,5 @@ class Records(Plugin):
|
|
|
90
90
|
raise PluginError("method_not_found", f"Unknown method {service}/{method}")
|
|
91
91
|
|
|
92
92
|
async def health(self) -> dict:
|
|
93
|
-
await self.context.
|
|
93
|
+
await self.context.sql().query("SELECT id FROM records LIMIT 1")
|
|
94
94
|
return {"runtime": "ready", "business": "ready"}
|
|
@@ -17,7 +17,7 @@ pytestmark = pytest.mark.installed_core
|
|
|
17
17
|
|
|
18
18
|
_STORAGE_WHEEL_ENV = "TOOLFRAME_TESTKIT_STORAGE_WHEEL"
|
|
19
19
|
_FIXTURE_WHEEL_ENV = "TOOLFRAME_TESTKIT_FIXTURE_WHEEL"
|
|
20
|
-
_STORAGE_PLUGIN = "
|
|
20
|
+
_STORAGE_PLUGIN = "sql-sqlite"
|
|
21
21
|
_FIXTURE_PLUGIN = "fixture-records"
|
|
22
22
|
_SERVICE = "fixture-records.records"
|
|
23
23
|
|
|
@@ -448,7 +448,7 @@ wheels = [
|
|
|
448
448
|
|
|
449
449
|
[[package]]
|
|
450
450
|
name = "toolframe-sdk"
|
|
451
|
-
version = "0.
|
|
451
|
+
version = "0.4.0"
|
|
452
452
|
source = { editable = "../toolframe-sdk" }
|
|
453
453
|
dependencies = [
|
|
454
454
|
{ name = "jsonschema" },
|
|
@@ -475,7 +475,7 @@ dev = [
|
|
|
475
475
|
|
|
476
476
|
[[package]]
|
|
477
477
|
name = "toolframe-testkit"
|
|
478
|
-
version = "0.1
|
|
478
|
+
version = "0.2.1"
|
|
479
479
|
source = { editable = "." }
|
|
480
480
|
dependencies = [
|
|
481
481
|
{ name = "httpx" },
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|