dexpot 0.1.0__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.
- dexpot-0.1.0/.github/dependabot.yml +39 -0
- dexpot-0.1.0/.github/labeler.yml +33 -0
- dexpot-0.1.0/.github/workflows/changelog.yml +41 -0
- dexpot-0.1.0/.github/workflows/ci.yml +125 -0
- dexpot-0.1.0/.github/workflows/labeler.yml +26 -0
- dexpot-0.1.0/.github/workflows/release.yml +182 -0
- dexpot-0.1.0/.github/workflows/stale.yml +40 -0
- dexpot-0.1.0/.gitignore +15 -0
- dexpot-0.1.0/.pre-commit-config.yaml +14 -0
- dexpot-0.1.0/CHANGELOG.md +22 -0
- dexpot-0.1.0/CONTRIBUTING.md +95 -0
- dexpot-0.1.0/LICENSE +21 -0
- dexpot-0.1.0/Makefile +34 -0
- dexpot-0.1.0/PKG-INFO +378 -0
- dexpot-0.1.0/README.md +337 -0
- dexpot-0.1.0/ROADMAP.md +148 -0
- dexpot-0.1.0/changelog.d/README.md +21 -0
- dexpot-0.1.0/docs/releasing.md +79 -0
- dexpot-0.1.0/docs/reviewing.md +107 -0
- dexpot-0.1.0/examples/minimal.py +22 -0
- dexpot-0.1.0/pyproject.toml +122 -0
- dexpot-0.1.0/src/dexpot/__init__.py +13 -0
- dexpot-0.1.0/src/dexpot/app.py +693 -0
- dexpot-0.1.0/src/dexpot/cli.py +95 -0
- dexpot-0.1.0/src/dexpot/commands/__init__.py +1 -0
- dexpot-0.1.0/src/dexpot/commands/add_skills.py +170 -0
- dexpot-0.1.0/src/dexpot/requests.py +31 -0
- dexpot-0.1.0/src/dexpot/skills/__init__.py +1 -0
- dexpot-0.1.0/src/dexpot/skills/content.py +50 -0
- dexpot-0.1.0/src/dexpot/templates/skills/dexpot.md +153 -0
- dexpot-0.1.0/src/dexpot/typing_ext.py +26 -0
- dexpot-0.1.0/tests/test_add_skills.py +150 -0
- dexpot-0.1.0/tests/test_cli.py +46 -0
- dexpot-0.1.0/tests/test_docs.py +84 -0
- dexpot-0.1.0/tests/test_e2e.py +202 -0
- dexpot-0.1.0/tests/test_multiprocess.py +124 -0
- dexpot-0.1.0/tests/test_skills_content.py +68 -0
- dexpot-0.1.0/tests/worker_app.py +19 -0
- dexpot-0.1.0/uv.lock +686 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
updates:
|
|
3
|
+
- package-ecosystem: "github-actions"
|
|
4
|
+
directory: "/"
|
|
5
|
+
schedule:
|
|
6
|
+
interval: "weekly"
|
|
7
|
+
day: "monday"
|
|
8
|
+
commit-message:
|
|
9
|
+
prefix: "chore"
|
|
10
|
+
include: "scope"
|
|
11
|
+
labels:
|
|
12
|
+
- "dependencies"
|
|
13
|
+
- "skip-changelog"
|
|
14
|
+
|
|
15
|
+
- package-ecosystem: "uv"
|
|
16
|
+
directory: "/"
|
|
17
|
+
schedule:
|
|
18
|
+
interval: "weekly"
|
|
19
|
+
day: "monday"
|
|
20
|
+
commit-message:
|
|
21
|
+
prefix: "chore"
|
|
22
|
+
include: "scope"
|
|
23
|
+
labels:
|
|
24
|
+
- "dependencies"
|
|
25
|
+
- "skip-changelog"
|
|
26
|
+
groups:
|
|
27
|
+
dev-dependencies:
|
|
28
|
+
patterns:
|
|
29
|
+
- "pytest*"
|
|
30
|
+
- "ruff"
|
|
31
|
+
- "pyright"
|
|
32
|
+
- "pre-commit"
|
|
33
|
+
- "towncrier"
|
|
34
|
+
- "pyyaml"
|
|
35
|
+
runtime-dependencies:
|
|
36
|
+
patterns:
|
|
37
|
+
- "msgspec"
|
|
38
|
+
- "typer"
|
|
39
|
+
- "httpx"
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
ci:
|
|
2
|
+
- changed-files:
|
|
3
|
+
- any-glob-to-any-file:
|
|
4
|
+
- ".github/**/*"
|
|
5
|
+
- "Makefile"
|
|
6
|
+
- ".pre-commit-config.yaml"
|
|
7
|
+
|
|
8
|
+
documentation:
|
|
9
|
+
- changed-files:
|
|
10
|
+
- any-glob-to-any-file:
|
|
11
|
+
- "README.md"
|
|
12
|
+
- "CONTRIBUTING.md"
|
|
13
|
+
- "ROADMAP.md"
|
|
14
|
+
- "docs/**/*"
|
|
15
|
+
|
|
16
|
+
source:
|
|
17
|
+
- changed-files:
|
|
18
|
+
- any-glob-to-any-file: "src/**/*"
|
|
19
|
+
|
|
20
|
+
tests:
|
|
21
|
+
- changed-files:
|
|
22
|
+
- any-glob-to-any-file: "tests/**/*"
|
|
23
|
+
|
|
24
|
+
dependencies:
|
|
25
|
+
- changed-files:
|
|
26
|
+
- any-glob-to-any-file:
|
|
27
|
+
- "pyproject.toml"
|
|
28
|
+
- "uv.lock"
|
|
29
|
+
- ".github/dependabot.yml"
|
|
30
|
+
|
|
31
|
+
examples:
|
|
32
|
+
- changed-files:
|
|
33
|
+
- any-glob-to-any-file: "examples/**/*"
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
name: Changelog
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
branches: [main]
|
|
6
|
+
types: [opened, synchronize, reopened, labeled, unlabeled]
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
pull-requests: read # Required to inspect the pull request's changed files.
|
|
11
|
+
|
|
12
|
+
concurrency:
|
|
13
|
+
group: changelog-${{ github.event.pull_request.number }}
|
|
14
|
+
cancel-in-progress: true
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
changelog:
|
|
18
|
+
name: changelog
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
timeout-minutes: 5
|
|
21
|
+
env:
|
|
22
|
+
GH_TOKEN: ${{ github.token }}
|
|
23
|
+
PR_NUMBER: ${{ github.event.pull_request.number }}
|
|
24
|
+
REPOSITORY: ${{ github.repository }}
|
|
25
|
+
steps:
|
|
26
|
+
- name: Skip non-user-facing changes
|
|
27
|
+
if: contains(github.event.pull_request.labels.*.name, 'skip-changelog')
|
|
28
|
+
run: echo "skip-changelog label present"
|
|
29
|
+
- name: Require this pull request's changelog fragment
|
|
30
|
+
if: ${{ !contains(github.event.pull_request.labels.*.name, 'skip-changelog') }}
|
|
31
|
+
shell: bash
|
|
32
|
+
run: |
|
|
33
|
+
set -euo pipefail
|
|
34
|
+
pattern="^changelog\.d/${PR_NUMBER}\.(added|changed|deprecated|removed|fixed)\.md$"
|
|
35
|
+
if gh api "repos/${REPOSITORY}/pulls/${PR_NUMBER}/files" \
|
|
36
|
+
--paginate --jq '.[].filename' | grep -Eq "$pattern"; then
|
|
37
|
+
echo "changelog fragment found for PR #${PR_NUMBER}"
|
|
38
|
+
exit 0
|
|
39
|
+
fi
|
|
40
|
+
echo "::error::no changelog fragment for PR #${PR_NUMBER} — add changelog.d/${PR_NUMBER}.<type>.md or apply the skip-changelog label"
|
|
41
|
+
exit 1
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
workflow_call:
|
|
9
|
+
|
|
10
|
+
permissions:
|
|
11
|
+
contents: read
|
|
12
|
+
|
|
13
|
+
concurrency:
|
|
14
|
+
group: ci-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
|
15
|
+
cancel-in-progress: true
|
|
16
|
+
|
|
17
|
+
jobs:
|
|
18
|
+
lint:
|
|
19
|
+
name: lint
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
timeout-minutes: 10
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
24
|
+
with:
|
|
25
|
+
persist-credentials: false
|
|
26
|
+
- uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
|
|
27
|
+
- run: uv sync --all-extras --locked
|
|
28
|
+
- run: uv run ruff check src/ tests/
|
|
29
|
+
- run: uv run ruff format --check src/ tests/
|
|
30
|
+
|
|
31
|
+
typecheck:
|
|
32
|
+
name: typecheck
|
|
33
|
+
runs-on: ubuntu-latest
|
|
34
|
+
timeout-minutes: 10
|
|
35
|
+
steps:
|
|
36
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
37
|
+
with:
|
|
38
|
+
persist-credentials: false
|
|
39
|
+
- uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
|
|
40
|
+
- run: uv sync --all-extras --locked
|
|
41
|
+
- run: uv run pyright src/ tests/
|
|
42
|
+
|
|
43
|
+
test:
|
|
44
|
+
name: test (${{ matrix.python-version }})
|
|
45
|
+
runs-on: ubuntu-latest
|
|
46
|
+
timeout-minutes: 15
|
|
47
|
+
strategy:
|
|
48
|
+
fail-fast: false
|
|
49
|
+
matrix:
|
|
50
|
+
python-version: ["3.12", "3.13", "3.14", "3.14t"]
|
|
51
|
+
steps:
|
|
52
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
53
|
+
with:
|
|
54
|
+
persist-credentials: false
|
|
55
|
+
- uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
|
|
56
|
+
with:
|
|
57
|
+
python-version: ${{ matrix.python-version }}
|
|
58
|
+
- run: uv sync --all-extras --locked
|
|
59
|
+
- name: Verify interpreter mode
|
|
60
|
+
env:
|
|
61
|
+
EXPECTED_PYTHON: ${{ matrix.python-version }}
|
|
62
|
+
run: |
|
|
63
|
+
uv run python - <<'PY'
|
|
64
|
+
import os
|
|
65
|
+
import sys
|
|
66
|
+
|
|
67
|
+
expected = os.environ["EXPECTED_PYTHON"]
|
|
68
|
+
is_gil_enabled = getattr(sys, "_is_gil_enabled", lambda: True)()
|
|
69
|
+
print(sys.version)
|
|
70
|
+
print(f"gil_enabled={is_gil_enabled}")
|
|
71
|
+
if expected.endswith("t") and is_gil_enabled:
|
|
72
|
+
raise SystemExit("expected a free-threaded interpreter")
|
|
73
|
+
if not expected.endswith("t") and not is_gil_enabled:
|
|
74
|
+
raise SystemExit("expected a GIL-enabled interpreter")
|
|
75
|
+
PY
|
|
76
|
+
- run: >-
|
|
77
|
+
uv run pytest tests/ -v
|
|
78
|
+
--cov=src/dexpot
|
|
79
|
+
--cov-report=term-missing
|
|
80
|
+
--cov-fail-under=65
|
|
81
|
+
|
|
82
|
+
build:
|
|
83
|
+
name: build
|
|
84
|
+
runs-on: ubuntu-latest
|
|
85
|
+
timeout-minutes: 10
|
|
86
|
+
steps:
|
|
87
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
88
|
+
with:
|
|
89
|
+
persist-credentials: false
|
|
90
|
+
- uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
|
|
91
|
+
- run: uv build
|
|
92
|
+
- name: Verify distribution artifacts
|
|
93
|
+
run: |
|
|
94
|
+
python3 - <<'PY'
|
|
95
|
+
import glob
|
|
96
|
+
import tarfile
|
|
97
|
+
import zipfile
|
|
98
|
+
|
|
99
|
+
wheels = glob.glob("dist/*.whl")
|
|
100
|
+
sdists = glob.glob("dist/*.tar.gz")
|
|
101
|
+
assert len(wheels) == 1, f"expected one wheel, found {wheels}"
|
|
102
|
+
assert len(sdists) == 1, f"expected one sdist, found {sdists}"
|
|
103
|
+
|
|
104
|
+
with zipfile.ZipFile(wheels[0]) as archive:
|
|
105
|
+
names = archive.namelist()
|
|
106
|
+
for required in (
|
|
107
|
+
"dexpot/__init__.py",
|
|
108
|
+
"dexpot/templates/skills/dexpot.md",
|
|
109
|
+
):
|
|
110
|
+
assert required in names, f"{required} missing from wheel"
|
|
111
|
+
|
|
112
|
+
with tarfile.open(sdists[0]) as archive:
|
|
113
|
+
names = archive.getnames()
|
|
114
|
+
for suffix in (
|
|
115
|
+
"/src/dexpot/__init__.py",
|
|
116
|
+
"/README.md",
|
|
117
|
+
"/ROADMAP.md",
|
|
118
|
+
"/CONTRIBUTING.md",
|
|
119
|
+
):
|
|
120
|
+
assert any(name.endswith(suffix) for name in names), (
|
|
121
|
+
f"{suffix} missing from sdist"
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
print(f"verified {wheels[0]} and {sdists[0]}")
|
|
125
|
+
PY
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
name: Auto-label pull requests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request_target:
|
|
5
|
+
types: [opened, synchronize, reopened, ready_for_review]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: read
|
|
9
|
+
|
|
10
|
+
concurrency:
|
|
11
|
+
group: labeler-${{ github.event.pull_request.number }}
|
|
12
|
+
cancel-in-progress: true
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
label:
|
|
16
|
+
name: label
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
timeout-minutes: 5
|
|
19
|
+
permissions:
|
|
20
|
+
contents: read
|
|
21
|
+
pull-requests: write # Required to apply labels to pull requests.
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/labeler@bf12e9b00b37c5c0ca2b87b79b2daf7891dbda13 # v7.0.0
|
|
24
|
+
with:
|
|
25
|
+
repo-token: ${{ github.token }}
|
|
26
|
+
sync-labels: true
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags: ["v*"]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: read
|
|
9
|
+
|
|
10
|
+
concurrency:
|
|
11
|
+
group: release-${{ github.ref }}
|
|
12
|
+
cancel-in-progress: false
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
ci:
|
|
16
|
+
name: CI
|
|
17
|
+
uses: ./.github/workflows/ci.yml
|
|
18
|
+
|
|
19
|
+
verify-version:
|
|
20
|
+
name: verify version and changelog
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
timeout-minutes: 5
|
|
23
|
+
steps:
|
|
24
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
25
|
+
with:
|
|
26
|
+
persist-credentials: false
|
|
27
|
+
fetch-depth: 0
|
|
28
|
+
- name: Verify release commit is on main
|
|
29
|
+
run: |
|
|
30
|
+
if ! git merge-base --is-ancestor "$GITHUB_SHA" origin/main; then
|
|
31
|
+
echo "::error::release tags must point to a commit reachable from main"
|
|
32
|
+
exit 1
|
|
33
|
+
fi
|
|
34
|
+
- name: Verify tag, package version, and changelog
|
|
35
|
+
env:
|
|
36
|
+
RELEASE_TAG: ${{ github.ref_name }}
|
|
37
|
+
run: |
|
|
38
|
+
python3 - <<'PY'
|
|
39
|
+
import os
|
|
40
|
+
import pathlib
|
|
41
|
+
import re
|
|
42
|
+
import sys
|
|
43
|
+
import tomllib
|
|
44
|
+
|
|
45
|
+
tag = os.environ["RELEASE_TAG"]
|
|
46
|
+
if not tag.startswith("v") or len(tag) == 1:
|
|
47
|
+
print(f"::error::release tag must be v<version>, got {tag!r}")
|
|
48
|
+
sys.exit(1)
|
|
49
|
+
|
|
50
|
+
version = tag[1:]
|
|
51
|
+
package_version = tomllib.loads(
|
|
52
|
+
pathlib.Path("pyproject.toml").read_text()
|
|
53
|
+
)["project"]["version"]
|
|
54
|
+
if version != package_version:
|
|
55
|
+
print(
|
|
56
|
+
f"::error::tag {tag} does not match pyproject.toml version "
|
|
57
|
+
f"{package_version} — run 'uv version {version}' on main first"
|
|
58
|
+
)
|
|
59
|
+
sys.exit(1)
|
|
60
|
+
|
|
61
|
+
changelog = pathlib.Path("CHANGELOG.md").read_text()
|
|
62
|
+
heading = re.compile(rf"^## \[{re.escape(version)}\](?:\s|$)", re.MULTILINE)
|
|
63
|
+
if heading.search(changelog) is None:
|
|
64
|
+
print(
|
|
65
|
+
f"::error::CHANGELOG.md has no exact '## [{version}]' section — "
|
|
66
|
+
"run 'make changelog' and commit it before tagging"
|
|
67
|
+
)
|
|
68
|
+
sys.exit(1)
|
|
69
|
+
|
|
70
|
+
print(f"{tag} matches pyproject.toml and CHANGELOG.md")
|
|
71
|
+
PY
|
|
72
|
+
|
|
73
|
+
build:
|
|
74
|
+
name: build release artifacts
|
|
75
|
+
needs: [ci, verify-version]
|
|
76
|
+
runs-on: ubuntu-latest
|
|
77
|
+
timeout-minutes: 10
|
|
78
|
+
steps:
|
|
79
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
80
|
+
with:
|
|
81
|
+
persist-credentials: false
|
|
82
|
+
- uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
|
|
83
|
+
with:
|
|
84
|
+
enable-cache: false
|
|
85
|
+
- run: uv build
|
|
86
|
+
- name: Verify release artifacts
|
|
87
|
+
run: |
|
|
88
|
+
python3 - <<'PY'
|
|
89
|
+
import glob
|
|
90
|
+
import tarfile
|
|
91
|
+
import zipfile
|
|
92
|
+
|
|
93
|
+
wheels = glob.glob("dist/*.whl")
|
|
94
|
+
sdists = glob.glob("dist/*.tar.gz")
|
|
95
|
+
assert len(wheels) == 1, f"expected one wheel, found {wheels}"
|
|
96
|
+
assert len(sdists) == 1, f"expected one sdist, found {sdists}"
|
|
97
|
+
with zipfile.ZipFile(wheels[0]) as archive:
|
|
98
|
+
names = archive.namelist()
|
|
99
|
+
assert "dexpot/__init__.py" in names
|
|
100
|
+
assert "dexpot/templates/skills/dexpot.md" in names
|
|
101
|
+
with tarfile.open(sdists[0]) as archive:
|
|
102
|
+
names = archive.getnames()
|
|
103
|
+
assert any(name.endswith("/src/dexpot/__init__.py") for name in names)
|
|
104
|
+
assert any(name.endswith("/ROADMAP.md") for name in names)
|
|
105
|
+
print(f"verified {wheels[0]} and {sdists[0]}")
|
|
106
|
+
PY
|
|
107
|
+
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
108
|
+
with:
|
|
109
|
+
name: dist
|
|
110
|
+
path: dist/
|
|
111
|
+
if-no-files-found: error
|
|
112
|
+
|
|
113
|
+
publish:
|
|
114
|
+
name: publish to PyPI
|
|
115
|
+
needs: build
|
|
116
|
+
runs-on: ubuntu-latest
|
|
117
|
+
timeout-minutes: 10
|
|
118
|
+
environment: pypi
|
|
119
|
+
permissions:
|
|
120
|
+
contents: read
|
|
121
|
+
id-token: write # Required for PyPI trusted publishing through OIDC.
|
|
122
|
+
steps:
|
|
123
|
+
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
124
|
+
with:
|
|
125
|
+
name: dist
|
|
126
|
+
path: dist/
|
|
127
|
+
- uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
|
|
128
|
+
with:
|
|
129
|
+
attestations: true
|
|
130
|
+
|
|
131
|
+
github-release:
|
|
132
|
+
name: create GitHub Release
|
|
133
|
+
needs: publish
|
|
134
|
+
runs-on: ubuntu-latest
|
|
135
|
+
timeout-minutes: 10
|
|
136
|
+
permissions:
|
|
137
|
+
contents: write # Required to create the GitHub Release and upload assets.
|
|
138
|
+
steps:
|
|
139
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
140
|
+
with:
|
|
141
|
+
persist-credentials: false
|
|
142
|
+
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
143
|
+
with:
|
|
144
|
+
name: dist
|
|
145
|
+
path: dist/
|
|
146
|
+
- name: Extract release notes from changelog
|
|
147
|
+
env:
|
|
148
|
+
RELEASE_VERSION: ${{ github.ref_name }}
|
|
149
|
+
run: |
|
|
150
|
+
python3 - <<'PY' > release-notes.md
|
|
151
|
+
import os
|
|
152
|
+
import pathlib
|
|
153
|
+
import re
|
|
154
|
+
import sys
|
|
155
|
+
|
|
156
|
+
version = os.environ["RELEASE_VERSION"].removeprefix("v")
|
|
157
|
+
text = pathlib.Path("CHANGELOG.md").read_text()
|
|
158
|
+
match = re.search(
|
|
159
|
+
rf"^## \[{re.escape(version)}\][^\n]*\n(.*?)(?=^## \[|\Z)",
|
|
160
|
+
text,
|
|
161
|
+
re.MULTILINE | re.DOTALL,
|
|
162
|
+
)
|
|
163
|
+
if match is None:
|
|
164
|
+
sys.exit(f"no exact '## [{version}]' section in CHANGELOG.md")
|
|
165
|
+
print(match.group(1).strip())
|
|
166
|
+
PY
|
|
167
|
+
cat release-notes.md
|
|
168
|
+
- name: Create GitHub Release
|
|
169
|
+
env:
|
|
170
|
+
GH_TOKEN: ${{ github.token }}
|
|
171
|
+
RELEASE_TAG: ${{ github.ref_name }}
|
|
172
|
+
run: |
|
|
173
|
+
set -euo pipefail
|
|
174
|
+
version="${RELEASE_TAG#v}"
|
|
175
|
+
args=()
|
|
176
|
+
case "$version" in *-*) args+=(--prerelease) ;; esac
|
|
177
|
+
gh release create "$RELEASE_TAG" \
|
|
178
|
+
--title "$RELEASE_TAG" \
|
|
179
|
+
--notes-file release-notes.md \
|
|
180
|
+
--verify-tag \
|
|
181
|
+
"${args[@]}" \
|
|
182
|
+
dist/*
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
name: Stale issue and pull request management
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
schedule:
|
|
5
|
+
- cron: "30 1 * * *"
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
|
|
8
|
+
permissions: {}
|
|
9
|
+
|
|
10
|
+
concurrency:
|
|
11
|
+
group: stale-management
|
|
12
|
+
cancel-in-progress: false
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
stale:
|
|
16
|
+
name: stale
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
timeout-minutes: 10
|
|
19
|
+
permissions:
|
|
20
|
+
issues: write # Required to label, comment on, and close stale issues.
|
|
21
|
+
pull-requests: write # Required to manage stale pull requests.
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/stale@4391f3da665fdf50b6810c1a66712fb9ba21aa93 # v11.0.0
|
|
24
|
+
with:
|
|
25
|
+
stale-issue-message: >
|
|
26
|
+
This issue has been marked stale because it has not had recent activity.
|
|
27
|
+
It will close in 14 days unless new information is added.
|
|
28
|
+
stale-pr-message: >
|
|
29
|
+
This pull request has been marked stale because it has not had recent activity.
|
|
30
|
+
It will close in 14 days unless work resumes.
|
|
31
|
+
close-issue-message: This issue was closed due to inactivity.
|
|
32
|
+
close-pr-message: This pull request was closed due to inactivity.
|
|
33
|
+
stale-issue-label: stale
|
|
34
|
+
stale-pr-label: stale
|
|
35
|
+
days-before-stale: 60
|
|
36
|
+
days-before-close: 14
|
|
37
|
+
days-before-pr-stale: 30
|
|
38
|
+
days-before-pr-close: 14
|
|
39
|
+
exempt-issue-labels: enhancement,pinned,security
|
|
40
|
+
exempt-pr-labels: pinned,security,dependencies
|
dexpot-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
3
|
+
rev: v0.16.4
|
|
4
|
+
hooks:
|
|
5
|
+
- id: ruff-check
|
|
6
|
+
args: [--fix]
|
|
7
|
+
- id: ruff-format
|
|
8
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
9
|
+
rev: v6.0.0
|
|
10
|
+
hooks:
|
|
11
|
+
- id: check-toml
|
|
12
|
+
- id: check-yaml
|
|
13
|
+
- id: end-of-file-fixer
|
|
14
|
+
- id: trailing-whitespace
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to dexpot will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and versions
|
|
6
|
+
follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
Unreleased changes live as files in [`changelog.d/`](changelog.d/) until release
|
|
9
|
+
preparation assembles them here. Run `make changelog-draft` to preview the next release.
|
|
10
|
+
|
|
11
|
+
<!-- towncrier release notes start -->
|
|
12
|
+
|
|
13
|
+
## [0.1.0] - 2026-08-25
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- Add the initial dexpot serving core, typed routing API, adaptive scheduler, CLI, tests, and CI. ([#1](https://github.com/tugrulguner/dexpot/pull/1))
|
|
18
|
+
- Add `dexpot add skills` for six coding agents, expanded framework documentation and roadmap, and release-ready repository automation. ([#3](https://github.com/tugrulguner/dexpot/pull/3))
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
|
|
22
|
+
- Add compiled endpoint plans, duplicate-route detection, and DEXPOT_WORKERS multiprocess serving on GIL builds. ([#2](https://github.com/tugrulguner/dexpot/pull/2))
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# Contributing to dexpot
|
|
2
|
+
|
|
3
|
+
Thanks for helping build dexpot as a synchronous API framework for GIL and free-threaded
|
|
4
|
+
CPython.
|
|
5
|
+
|
|
6
|
+
## Development setup
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
git clone https://github.com/tugrulguner/dexpot.git
|
|
10
|
+
cd dexpot
|
|
11
|
+
uv sync --all-extras
|
|
12
|
+
uv run pre-commit install
|
|
13
|
+
make check
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Use `make format` before committing.
|
|
17
|
+
|
|
18
|
+
## What a change must preserve
|
|
19
|
+
|
|
20
|
+
- Plain synchronous handlers; no coroutine bridge in the request path.
|
|
21
|
+
- Route and argument errors fail during registration when they can be known then.
|
|
22
|
+
- A worker owns a keep-alive connection until close.
|
|
23
|
+
- GIL admission remains bounded and returns 503 instead of accumulating unlimited work.
|
|
24
|
+
- Free-threaded and GIL behavior are tested deliberately rather than assumed equivalent.
|
|
25
|
+
- Public performance claims come from reproducible, correctness-matched benchmarks.
|
|
26
|
+
|
|
27
|
+
The rules above come from the route-binding, keep-alive, signal, and multiprocess bugs
|
|
28
|
+
already exercised by the end-to-end suite. Treat them as architecture contracts rather
|
|
29
|
+
than style preferences.
|
|
30
|
+
|
|
31
|
+
## Tests
|
|
32
|
+
|
|
33
|
+
Run the complete gate:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
make check
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The gate includes lint, formatting, type checking, real HTTP tests, and coverage. Useful
|
|
40
|
+
narrower commands are:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
uv run pytest tests/test_e2e.py -v
|
|
44
|
+
uv run pytest tests/test_multiprocess.py -v
|
|
45
|
+
uv run pytest tests/test_add_skills.py tests/test_skills_content.py -v
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
A behavior change needs an end-to-end test against the real socket server. A unit test of a
|
|
49
|
+
private helper is not enough by itself. Multiprocess, signal, restart, and drain behavior
|
|
50
|
+
must run in a subprocess because Python only permits signal handlers in the main thread.
|
|
51
|
+
|
|
52
|
+
## Pull requests
|
|
53
|
+
|
|
54
|
+
1. Branch from the latest `main`.
|
|
55
|
+
2. Keep the change focused on one concern.
|
|
56
|
+
3. Add or update real-user tests and executable examples when behavior changes.
|
|
57
|
+
4. Update README, roadmap, agent skill, and contributor guidance in the same change when
|
|
58
|
+
their contract changes.
|
|
59
|
+
5. Add a changelog fragment for a user-facing change.
|
|
60
|
+
6. Run `make check`, `make build`, and the workflow checks in
|
|
61
|
+
[`docs/reviewing.md`](docs/reviewing.md).
|
|
62
|
+
7. Open a pull request with the current behavior, the change, and verification evidence.
|
|
63
|
+
|
|
64
|
+
## Changelog fragments
|
|
65
|
+
|
|
66
|
+
User-facing changes require one file named for the pull request:
|
|
67
|
+
|
|
68
|
+
```text
|
|
69
|
+
changelog.d/<pr-number>.<type>.md
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Types are `added`, `changed`, `deprecated`, `removed`, and `fixed`. Write one sentence about
|
|
73
|
+
what changed for a user. Do not edit `CHANGELOG.md` directly; Towncrier assembles it during
|
|
74
|
+
release preparation. For maintenance with no user-visible effect, a maintainer may apply
|
|
75
|
+
the `skip-changelog` label.
|
|
76
|
+
|
|
77
|
+
## Releasing
|
|
78
|
+
|
|
79
|
+
Maintainers follow [`docs/releasing.md`](docs/releasing.md). Do not bump the version or tag a
|
|
80
|
+
feature pull request. `pyproject.toml` is the one editable version source; `uv version`
|
|
81
|
+
updates it and the lockfile together.
|
|
82
|
+
|
|
83
|
+
## Reporting issues
|
|
84
|
+
|
|
85
|
+
Search existing issues first. A useful bug report includes:
|
|
86
|
+
|
|
87
|
+
- Python version and whether `sys._is_gil_enabled()` is true or false;
|
|
88
|
+
- operating system;
|
|
89
|
+
- a minimal runnable application;
|
|
90
|
+
- the exact request and observed response;
|
|
91
|
+
- concurrency settings such as `DEXPOT_POOL`, `DEXPOT_MAX_QUEUE`, and `DEXPOT_WORKERS`;
|
|
92
|
+
- whether the failure reproduces with a single connection and a single process.
|
|
93
|
+
|
|
94
|
+
For substantial API or scheduler changes, open an issue and align on the contract before
|
|
95
|
+
implementation.
|
dexpot-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Tugrul Guner
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|