aiogram-idle 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.
- aiogram_idle-0.1.0/.gitattributes +8 -0
- aiogram_idle-0.1.0/.github/ISSUE_TEMPLATE/bug_report.yml +22 -0
- aiogram_idle-0.1.0/.github/ISSUE_TEMPLATE/config.yml +5 -0
- aiogram_idle-0.1.0/.github/ISSUE_TEMPLATE/feature_request.yml +17 -0
- aiogram_idle-0.1.0/.github/PULL_REQUEST_TEMPLATE.md +15 -0
- aiogram_idle-0.1.0/.github/workflows/ci.yml +219 -0
- aiogram_idle-0.1.0/.github/workflows/nav-integration.yml +54 -0
- aiogram_idle-0.1.0/.github/workflows/publish.yml +91 -0
- aiogram_idle-0.1.0/.gitignore +20 -0
- aiogram_idle-0.1.0/CHANGELOG.md +53 -0
- aiogram_idle-0.1.0/CHANGES/README.md +17 -0
- aiogram_idle-0.1.0/CODE_OF_CONDUCT.md +14 -0
- aiogram_idle-0.1.0/CONTRIBUTING.md +41 -0
- aiogram_idle-0.1.0/LICENSE +21 -0
- aiogram_idle-0.1.0/PKG-INFO +123 -0
- aiogram_idle-0.1.0/README.md +96 -0
- aiogram_idle-0.1.0/SECURITY.md +33 -0
- aiogram_idle-0.1.0/coverage-nav.toml +6 -0
- aiogram_idle-0.1.0/docs/api.md +31 -0
- aiogram_idle-0.1.0/docs/architecture.md +32 -0
- aiogram_idle-0.1.0/docs/audit.md +52 -0
- aiogram_idle-0.1.0/docs/index.md +21 -0
- aiogram_idle-0.1.0/docs/installation.md +37 -0
- aiogram_idle-0.1.0/docs/navigation.md +199 -0
- aiogram_idle-0.1.0/docs/quickstart.md +36 -0
- aiogram_idle-0.1.0/docs/recovery.md +41 -0
- aiogram_idle-0.1.0/docs/redis-audit.md +79 -0
- aiogram_idle-0.1.0/docs/redis.md +156 -0
- aiogram_idle-0.1.0/docs/releasing.md +45 -0
- aiogram_idle-0.1.0/docs/standalone.md +70 -0
- aiogram_idle-0.1.0/docs/testing.md +65 -0
- aiogram_idle-0.1.0/docs/verification.md +154 -0
- aiogram_idle-0.1.0/examples/README.md +40 -0
- aiogram_idle-0.1.0/examples/demo.py +232 -0
- aiogram_idle-0.1.0/examples/navigation.py +193 -0
- aiogram_idle-0.1.0/mkdocs.yml +26 -0
- aiogram_idle-0.1.0/pyproject.toml +99 -0
- aiogram_idle-0.1.0/pyright-nav.json +8 -0
- aiogram_idle-0.1.0/reports/2026-11-10-repository-alignment.md +99 -0
- aiogram_idle-0.1.0/src/aiogram_idle/__init__.py +146 -0
- aiogram_idle-0.1.0/src/aiogram_idle/activity.py +122 -0
- aiogram_idle-0.1.0/src/aiogram_idle/clock.py +69 -0
- aiogram_idle-0.1.0/src/aiogram_idle/compat.py +72 -0
- aiogram_idle-0.1.0/src/aiogram_idle/config.py +71 -0
- aiogram_idle-0.1.0/src/aiogram_idle/controls.py +167 -0
- aiogram_idle-0.1.0/src/aiogram_idle/coordinator.py +146 -0
- aiogram_idle-0.1.0/src/aiogram_idle/effect_scope.py +27 -0
- aiogram_idle-0.1.0/src/aiogram_idle/effects.py +160 -0
- aiogram_idle-0.1.0/src/aiogram_idle/errors.py +61 -0
- aiogram_idle-0.1.0/src/aiogram_idle/events.py +21 -0
- aiogram_idle-0.1.0/src/aiogram_idle/failures.py +67 -0
- aiogram_idle-0.1.0/src/aiogram_idle/integration.py +275 -0
- aiogram_idle-0.1.0/src/aiogram_idle/integrations/nav/__init__.py +20 -0
- aiogram_idle-0.1.0/src/aiogram_idle/integrations/nav/adapters.py +187 -0
- aiogram_idle-0.1.0/src/aiogram_idle/integrations/nav/codec.py +78 -0
- aiogram_idle-0.1.0/src/aiogram_idle/integrations/nav/effects.py +98 -0
- aiogram_idle-0.1.0/src/aiogram_idle/integrations/nav/engine.py +245 -0
- aiogram_idle-0.1.0/src/aiogram_idle/integrations/nav/errors.py +25 -0
- aiogram_idle-0.1.0/src/aiogram_idle/integrations/nav/installation.py +295 -0
- aiogram_idle-0.1.0/src/aiogram_idle/integrations/nav/integration.py +245 -0
- aiogram_idle-0.1.0/src/aiogram_idle/integrations/nav/lifecycle.py +143 -0
- aiogram_idle-0.1.0/src/aiogram_idle/integrations/nav/mapping.py +106 -0
- aiogram_idle-0.1.0/src/aiogram_idle/integrations/nav/overlay.py +71 -0
- aiogram_idle-0.1.0/src/aiogram_idle/integrations/nav/policies.py +73 -0
- aiogram_idle-0.1.0/src/aiogram_idle/integrations/nav/presentation.py +105 -0
- aiogram_idle-0.1.0/src/aiogram_idle/integrations/nav/receipts.py +49 -0
- aiogram_idle-0.1.0/src/aiogram_idle/integrations/nav/recovery.py +119 -0
- aiogram_idle-0.1.0/src/aiogram_idle/integrations/nav/repository.py +375 -0
- aiogram_idle-0.1.0/src/aiogram_idle/integrations/nav/staging.py +195 -0
- aiogram_idle-0.1.0/src/aiogram_idle/machine.py +66 -0
- aiogram_idle-0.1.0/src/aiogram_idle/models.py +383 -0
- aiogram_idle-0.1.0/src/aiogram_idle/policies.py +87 -0
- aiogram_idle-0.1.0/src/aiogram_idle/presentation.py +299 -0
- aiogram_idle-0.1.0/src/aiogram_idle/protocols.py +68 -0
- aiogram_idle-0.1.0/src/aiogram_idle/py.typed +0 -0
- aiogram_idle-0.1.0/src/aiogram_idle/questions.py +184 -0
- aiogram_idle-0.1.0/src/aiogram_idle/receipts.py +129 -0
- aiogram_idle-0.1.0/src/aiogram_idle/runtime.py +385 -0
- aiogram_idle-0.1.0/src/aiogram_idle/storage/__init__.py +7 -0
- aiogram_idle-0.1.0/src/aiogram_idle/storage/base.py +118 -0
- aiogram_idle-0.1.0/src/aiogram_idle/storage/codec.py +292 -0
- aiogram_idle-0.1.0/src/aiogram_idle/storage/component_transaction.py +244 -0
- aiogram_idle-0.1.0/src/aiogram_idle/storage/control_tokens.py +61 -0
- aiogram_idle-0.1.0/src/aiogram_idle/storage/coordination.py +21 -0
- aiogram_idle-0.1.0/src/aiogram_idle/storage/memory.py +499 -0
- aiogram_idle-0.1.0/src/aiogram_idle/storage/queue.py +114 -0
- aiogram_idle-0.1.0/src/aiogram_idle/storage/receipts.py +99 -0
- aiogram_idle-0.1.0/src/aiogram_idle/storage/redis.py +352 -0
- aiogram_idle-0.1.0/src/aiogram_idle/storage/redis_admin.py +78 -0
- aiogram_idle-0.1.0/src/aiogram_idle/storage/redis_coordinator.py +311 -0
- aiogram_idle-0.1.0/src/aiogram_idle/storage/redis_engine.py +239 -0
- aiogram_idle-0.1.0/src/aiogram_idle/storage/redis_jobs.py +226 -0
- aiogram_idle-0.1.0/src/aiogram_idle/storage/redis_quarantine.py +142 -0
- aiogram_idle-0.1.0/src/aiogram_idle/storage/redis_scripts.py +162 -0
- aiogram_idle-0.1.0/src/aiogram_idle/storage/redis_store.py +464 -0
- aiogram_idle-0.1.0/src/aiogram_idle/storage/redis_transport.py +121 -0
- aiogram_idle-0.1.0/src/aiogram_idle/storage/retention.py +47 -0
- aiogram_idle-0.1.0/src/aiogram_idle/storage/state.py +51 -0
- aiogram_idle-0.1.0/src/aiogram_idle/worker.py +442 -0
- aiogram_idle-0.1.0/tests/__init__.py +1 -0
- aiogram_idle-0.1.0/tests/conftest.py +28 -0
- aiogram_idle-0.1.0/tests/integration/test_examples.py +52 -0
- aiogram_idle-0.1.0/tests/runtime_helpers.py +193 -0
- aiogram_idle-0.1.0/tests/test_audit_engine.py +345 -0
- aiogram_idle-0.1.0/tests/test_audit_fsm.py +260 -0
- aiogram_idle-0.1.0/tests/test_audit_recovery.py +180 -0
- aiogram_idle-0.1.0/tests/test_audit_scale.py +49 -0
- aiogram_idle-0.1.0/tests/test_audit_storage.py +336 -0
- aiogram_idle-0.1.0/tests/test_component_ownership.py +45 -0
- aiogram_idle-0.1.0/tests/test_component_review.py +61 -0
- aiogram_idle-0.1.0/tests/test_component_transactions.py +239 -0
- aiogram_idle-0.1.0/tests/test_effect_scope_review.py +33 -0
- aiogram_idle-0.1.0/tests/test_memory.py +377 -0
- aiogram_idle-0.1.0/tests/test_memory_edges.py +166 -0
- aiogram_idle-0.1.0/tests/test_nav_atomic_recovery.py +274 -0
- aiogram_idle-0.1.0/tests/test_nav_boundary.py +72 -0
- aiogram_idle-0.1.0/tests/test_nav_example.py +69 -0
- aiogram_idle-0.1.0/tests/test_nav_matrix.py +194 -0
- aiogram_idle-0.1.0/tests/test_nav_projection.py +403 -0
- aiogram_idle-0.1.0/tests/test_nav_reply_restore.py +169 -0
- aiogram_idle-0.1.0/tests/test_nav_review_fsm.py +238 -0
- aiogram_idle-0.1.0/tests/test_nav_review_policies.py +23 -0
- aiogram_idle-0.1.0/tests/test_nav_review_recovery.py +228 -0
- aiogram_idle-0.1.0/tests/test_nav_runtime.py +457 -0
- aiogram_idle-0.1.0/tests/test_nav_schema_recovery.py +53 -0
- aiogram_idle-0.1.0/tests/test_redis_admin.py +46 -0
- aiogram_idle-0.1.0/tests/test_redis_atomicity.py +407 -0
- aiogram_idle-0.1.0/tests/test_redis_backend.py +305 -0
- aiogram_idle-0.1.0/tests/test_redis_codec.py +241 -0
- aiogram_idle-0.1.0/tests/test_redis_commit_audit.py +337 -0
- aiogram_idle-0.1.0/tests/test_redis_coord.py +162 -0
- aiogram_idle-0.1.0/tests/test_redis_corruption.py +184 -0
- aiogram_idle-0.1.0/tests/test_redis_dispatcher.py +120 -0
- aiogram_idle-0.1.0/tests/test_redis_effect_audit.py +186 -0
- aiogram_idle-0.1.0/tests/test_redis_fsm_audit.py +295 -0
- aiogram_idle-0.1.0/tests/test_redis_presentation.py +84 -0
- aiogram_idle-0.1.0/tests/test_redis_queue_audit.py +62 -0
- aiogram_idle-0.1.0/tests/test_redis_recovery.py +354 -0
- aiogram_idle-0.1.0/tests/test_telegram.py +312 -0
- aiogram_idle-0.1.0/tests/test_telegram_failures.py +347 -0
- aiogram_idle-0.1.0/tests/test_timeout_audit.py +137 -0
- aiogram_idle-0.1.0/tests/unit/__init__.py +1 -0
- aiogram_idle-0.1.0/tests/unit/test_contracts.py +251 -0
- aiogram_idle-0.1.0/tests/unit/test_lookup_audit.py +13 -0
- aiogram_idle-0.1.0/tests/unit/test_package_metadata.py +21 -0
- aiogram_idle-0.1.0/tests/unit/test_recovery_regressions.py +197 -0
- aiogram_idle-0.1.0/tests/unit/test_runtime.py +354 -0
- aiogram_idle-0.1.0/tests/unit/test_runtime_edges.py +223 -0
- aiogram_idle-0.1.0/tools/README.md +15 -0
- aiogram_idle-0.1.0/tools/check_artifacts.py +50 -0
- aiogram_idle-0.1.0/tools/check_version_tag.py +32 -0
- aiogram_idle-0.1.0/uv.lock +3446 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
name: Bug report
|
|
2
|
+
description: Report reproducible behavior in aiogram-idle.
|
|
3
|
+
body:
|
|
4
|
+
- type: textarea
|
|
5
|
+
id: versions
|
|
6
|
+
attributes:
|
|
7
|
+
label: Environment
|
|
8
|
+
description: Python, aiogram-idle, aiogram and optional Redis/server versions.
|
|
9
|
+
validations:
|
|
10
|
+
required: true
|
|
11
|
+
- type: textarea
|
|
12
|
+
id: reproduction
|
|
13
|
+
attributes:
|
|
14
|
+
label: Reproduction
|
|
15
|
+
description: Minimal code, expected behavior and actual behavior. Remove credentials and message content.
|
|
16
|
+
validations:
|
|
17
|
+
required: true
|
|
18
|
+
- type: textarea
|
|
19
|
+
id: logs
|
|
20
|
+
attributes:
|
|
21
|
+
label: Sanitized diagnostics
|
|
22
|
+
description: Include storage backend, FSM isolation and worker configuration where relevant.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
name: Feature request
|
|
2
|
+
description: Propose an aiogram-idle improvement.
|
|
3
|
+
body:
|
|
4
|
+
- type: textarea
|
|
5
|
+
id: problem
|
|
6
|
+
attributes:
|
|
7
|
+
label: Problem
|
|
8
|
+
description: Explain the use case and current limitation.
|
|
9
|
+
validations:
|
|
10
|
+
required: true
|
|
11
|
+
- type: textarea
|
|
12
|
+
id: proposal
|
|
13
|
+
attributes:
|
|
14
|
+
label: Proposal
|
|
15
|
+
description: Describe the public behavior and alternatives considered.
|
|
16
|
+
validations:
|
|
17
|
+
required: true
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
## Change
|
|
2
|
+
|
|
3
|
+
Describe the problem and resulting behavior. Link related issues.
|
|
4
|
+
|
|
5
|
+
## Verification
|
|
6
|
+
|
|
7
|
+
List the checks run and any unverified behavior. Include recovery, ownership and
|
|
8
|
+
real-Redis tests when changing storage or coordination.
|
|
9
|
+
|
|
10
|
+
## Checklist
|
|
11
|
+
|
|
12
|
+
- [ ] Tests cover changed behavior.
|
|
13
|
+
- [ ] Public API and compatibility changes are documented.
|
|
14
|
+
- [ ] A `CHANGES/` fragment is included where appropriate.
|
|
15
|
+
- [ ] No credentials, generated audit reports or local environment files are included.
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
pull_request:
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: read
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
quality:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
timeout-minutes: 30
|
|
14
|
+
services:
|
|
15
|
+
redis:
|
|
16
|
+
image: redis:7-alpine
|
|
17
|
+
ports: ["6379:6379"]
|
|
18
|
+
options: --health-cmd "redis-cli ping" --health-interval 5s --health-timeout 3s --health-retries 10
|
|
19
|
+
env:
|
|
20
|
+
AIOGRAM_IDLE_REDIS_URL: redis://localhost:6379/0
|
|
21
|
+
AIOGRAM_IDLE_REQUIRE_REDIS: "1"
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@v4
|
|
24
|
+
- uses: astral-sh/setup-uv@v6
|
|
25
|
+
with:
|
|
26
|
+
enable-cache: true
|
|
27
|
+
- run: uv sync --frozen --extra redis --python 3.12
|
|
28
|
+
- run: uv run --no-sync ruff check .
|
|
29
|
+
- run: uv run --no-sync ruff format --check .
|
|
30
|
+
- run: uv run --no-sync pyright
|
|
31
|
+
- name: Verify standalone dependency boundary
|
|
32
|
+
run: uv run --no-sync python -c 'import importlib.util; assert importlib.util.find_spec("aiogram_nav") is None'
|
|
33
|
+
- run: uv run --no-sync pytest --cov=aiogram_idle --cov-report=term-missing
|
|
34
|
+
- run: uv run --no-sync python tools/check_version_tag.py
|
|
35
|
+
- run: uv run --no-sync towncrier build --draft --version 0.1.0
|
|
36
|
+
- run: uv build
|
|
37
|
+
- run: uv run --no-sync twine check dist/*
|
|
38
|
+
- run: uv run --no-sync python tools/check_artifacts.py dist/*.whl dist/*.tar.gz
|
|
39
|
+
- name: Compile examples without sending messages
|
|
40
|
+
run: uv run --no-sync python -m compileall -q examples
|
|
41
|
+
- name: Smoke test Redis wheel and sdist independently
|
|
42
|
+
shell: bash
|
|
43
|
+
run: |
|
|
44
|
+
for artifact in dist/*.whl dist/*.tar.gz; do
|
|
45
|
+
smoke_dir=$(mktemp -d)
|
|
46
|
+
uv venv --python 3.12 "$smoke_dir/env"
|
|
47
|
+
uv pip install --python "$smoke_dir/env/bin/python" "${artifact}[redis]"
|
|
48
|
+
cd "$smoke_dir"
|
|
49
|
+
"$smoke_dir/env/bin/python" - <<'PYREDIS'
|
|
50
|
+
import asyncio, importlib.util, os, uuid
|
|
51
|
+
from aiogram_idle.storage.redis import RedisIdleBackend
|
|
52
|
+
assert importlib.util.find_spec("aiogram_nav") is None
|
|
53
|
+
async def check():
|
|
54
|
+
backend = RedisIdleBackend.from_url(os.environ["AIOGRAM_IDLE_REDIS_URL"], prefix="artifact:" + uuid.uuid4().hex)
|
|
55
|
+
try:
|
|
56
|
+
await backend.initialize()
|
|
57
|
+
finally:
|
|
58
|
+
await backend.close()
|
|
59
|
+
asyncio.run(check())
|
|
60
|
+
PYREDIS
|
|
61
|
+
cd "$GITHUB_WORKSPACE"
|
|
62
|
+
done
|
|
63
|
+
|
|
64
|
+
documentation:
|
|
65
|
+
runs-on: ubuntu-latest
|
|
66
|
+
timeout-minutes: 10
|
|
67
|
+
steps:
|
|
68
|
+
- uses: actions/checkout@v4
|
|
69
|
+
- uses: astral-sh/setup-uv@v6
|
|
70
|
+
with:
|
|
71
|
+
enable-cache: true
|
|
72
|
+
- run: uv sync --frozen --python 3.12
|
|
73
|
+
- run: uv run --no-sync mkdocs build --strict
|
|
74
|
+
|
|
75
|
+
security:
|
|
76
|
+
runs-on: ubuntu-latest
|
|
77
|
+
timeout-minutes: 15
|
|
78
|
+
steps:
|
|
79
|
+
- uses: actions/checkout@v4
|
|
80
|
+
- uses: astral-sh/setup-uv@v6
|
|
81
|
+
with:
|
|
82
|
+
enable-cache: true
|
|
83
|
+
- run: uv sync --frozen --extra redis --python 3.12
|
|
84
|
+
- name: Audit source security
|
|
85
|
+
if: ${{ !cancelled() }}
|
|
86
|
+
run: uv run --no-sync bandit -r src -f json -o bandit-report.json
|
|
87
|
+
- name: Audit installed dependencies
|
|
88
|
+
if: ${{ !cancelled() }}
|
|
89
|
+
run: uv run --no-sync pip-audit --skip-editable --format json --output pip-audit-report.json
|
|
90
|
+
- name: Retain security reports
|
|
91
|
+
if: always()
|
|
92
|
+
uses: actions/upload-artifact@v4
|
|
93
|
+
with:
|
|
94
|
+
name: security-reports
|
|
95
|
+
path: |
|
|
96
|
+
bandit-report.json
|
|
97
|
+
pip-audit-report.json
|
|
98
|
+
if-no-files-found: error
|
|
99
|
+
|
|
100
|
+
compatibility:
|
|
101
|
+
runs-on: ubuntu-latest
|
|
102
|
+
timeout-minutes: 30
|
|
103
|
+
services:
|
|
104
|
+
redis:
|
|
105
|
+
image: redis:7-alpine
|
|
106
|
+
ports: ['6379:6379']
|
|
107
|
+
options: --health-cmd "redis-cli ping" --health-interval 5s --health-timeout 3s --health-retries 10
|
|
108
|
+
env:
|
|
109
|
+
AIOGRAM_IDLE_REDIS_URL: redis://localhost:6379/0
|
|
110
|
+
AIOGRAM_IDLE_REQUIRE_REDIS: '1'
|
|
111
|
+
strategy:
|
|
112
|
+
fail-fast: false
|
|
113
|
+
matrix:
|
|
114
|
+
python: ['3.10', '3.11', '3.12', '3.13', '3.14']
|
|
115
|
+
aiogram: [minimum, latest]
|
|
116
|
+
exclude:
|
|
117
|
+
# aiogram 3.22 pins pydantic before Python 3.14 wheel support.
|
|
118
|
+
- python: '3.14'
|
|
119
|
+
aiogram: minimum
|
|
120
|
+
steps:
|
|
121
|
+
- uses: actions/checkout@v4
|
|
122
|
+
- uses: astral-sh/setup-uv@v6
|
|
123
|
+
with:
|
|
124
|
+
enable-cache: true
|
|
125
|
+
- run: uv sync --frozen --extra redis --python '${{ matrix.python }}'
|
|
126
|
+
- name: Select aiogram compatibility endpoint
|
|
127
|
+
shell: bash
|
|
128
|
+
run: |
|
|
129
|
+
if [ '${{ matrix.aiogram }}' = minimum ]; then
|
|
130
|
+
uv pip install 'aiogram==3.22.0'
|
|
131
|
+
else
|
|
132
|
+
uv pip install --upgrade 'aiogram>=3.22,<4'
|
|
133
|
+
fi
|
|
134
|
+
- run: uv run --no-sync pytest
|
|
135
|
+
- run: uv run --no-sync python examples/demo.py --help
|
|
136
|
+
|
|
137
|
+
redis-compatibility:
|
|
138
|
+
runs-on: ubuntu-latest
|
|
139
|
+
timeout-minutes: 20
|
|
140
|
+
strategy:
|
|
141
|
+
fail-fast: false
|
|
142
|
+
matrix:
|
|
143
|
+
redis: ['redis:7-alpine', 'redis:8-alpine']
|
|
144
|
+
client: [minimum, latest]
|
|
145
|
+
services:
|
|
146
|
+
redis:
|
|
147
|
+
image: ${{ matrix.redis }}
|
|
148
|
+
ports: ['6379:6379']
|
|
149
|
+
options: --health-cmd "redis-cli ping" --health-interval 5s --health-timeout 3s --health-retries 10
|
|
150
|
+
env:
|
|
151
|
+
AIOGRAM_IDLE_REDIS_URL: redis://localhost:6379/0
|
|
152
|
+
AIOGRAM_IDLE_REQUIRE_REDIS: '1'
|
|
153
|
+
steps:
|
|
154
|
+
- uses: actions/checkout@v4
|
|
155
|
+
- uses: astral-sh/setup-uv@v6
|
|
156
|
+
- run: uv sync --frozen --extra redis --python 3.12
|
|
157
|
+
- name: Select Redis client endpoint
|
|
158
|
+
shell: bash
|
|
159
|
+
run: |
|
|
160
|
+
if [ '${{ matrix.client }}' = minimum ]; then
|
|
161
|
+
uv pip install 'redis==5.0.8'
|
|
162
|
+
else
|
|
163
|
+
uv pip install --upgrade 'redis>=5.0.8,<8'
|
|
164
|
+
fi
|
|
165
|
+
- run: uv run --no-sync pytest tests/test_redis*.py
|
|
166
|
+
|
|
167
|
+
navigation-integration:
|
|
168
|
+
runs-on: ubuntu-latest
|
|
169
|
+
timeout-minutes: 30
|
|
170
|
+
defaults:
|
|
171
|
+
run:
|
|
172
|
+
working-directory: aiogram-idle
|
|
173
|
+
services:
|
|
174
|
+
redis:
|
|
175
|
+
image: redis:7-alpine
|
|
176
|
+
ports: ['6379:6379']
|
|
177
|
+
options: --health-cmd "redis-cli ping" --health-interval 5s --health-timeout 3s --health-retries 10
|
|
178
|
+
env:
|
|
179
|
+
AIOGRAM_IDLE_REDIS_URL: redis://localhost:6379/0
|
|
180
|
+
AIOGRAM_IDLE_REQUIRE_REDIS: '1'
|
|
181
|
+
steps:
|
|
182
|
+
- uses: actions/checkout@v4
|
|
183
|
+
with:
|
|
184
|
+
path: aiogram-idle
|
|
185
|
+
- uses: actions/checkout@v4
|
|
186
|
+
with:
|
|
187
|
+
repository: ArmanAvanesyan/aiogram-nav
|
|
188
|
+
ref: main
|
|
189
|
+
path: aiogram-nav
|
|
190
|
+
- uses: astral-sh/setup-uv@v6
|
|
191
|
+
with:
|
|
192
|
+
enable-cache: true
|
|
193
|
+
cache-dependency-glob: aiogram-idle/uv.lock
|
|
194
|
+
- run: uv sync --frozen --extra redis --extra nav --python 3.12
|
|
195
|
+
- run: uv run --no-sync pyright --project pyright-nav.json
|
|
196
|
+
- run: uv run --no-sync pytest tests/test_nav*.py tests/test_component*.py tests/test_effect_scope_review.py
|
|
197
|
+
- run: uv run --no-sync python examples/navigation.py --help
|
|
198
|
+
|
|
199
|
+
required:
|
|
200
|
+
if: always()
|
|
201
|
+
needs: [quality, documentation, security, compatibility, redis-compatibility, navigation-integration]
|
|
202
|
+
runs-on: ubuntu-latest
|
|
203
|
+
timeout-minutes: 5
|
|
204
|
+
steps:
|
|
205
|
+
- name: Require every verification job to pass
|
|
206
|
+
env:
|
|
207
|
+
JOB_RESULTS: ${{ toJSON(needs) }}
|
|
208
|
+
run: |
|
|
209
|
+
python - <<'PY'
|
|
210
|
+
import json
|
|
211
|
+
import os
|
|
212
|
+
import sys
|
|
213
|
+
outcomes = json.loads(os.environ['JOB_RESULTS'])
|
|
214
|
+
failed = {name: job['result'] for name, job in outcomes.items() if job['result'] != 'success'}
|
|
215
|
+
if failed:
|
|
216
|
+
print('Required verification did not pass:', failed)
|
|
217
|
+
sys.exit(1)
|
|
218
|
+
print('All required verification passed.')
|
|
219
|
+
PY
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
name: Optional navigation integration
|
|
2
|
+
on:
|
|
3
|
+
workflow_dispatch:
|
|
4
|
+
inputs:
|
|
5
|
+
nav_repository:
|
|
6
|
+
description: Compatible aiogram-nav GitHub repository (owner/name)
|
|
7
|
+
required: true
|
|
8
|
+
type: string
|
|
9
|
+
nav_ref:
|
|
10
|
+
description: Compatible reviewed revision
|
|
11
|
+
required: true
|
|
12
|
+
type: string
|
|
13
|
+
permissions:
|
|
14
|
+
contents: read
|
|
15
|
+
jobs:
|
|
16
|
+
combined:
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
timeout-minutes: 30
|
|
19
|
+
defaults:
|
|
20
|
+
run:
|
|
21
|
+
working-directory: aiogram-idle
|
|
22
|
+
strategy:
|
|
23
|
+
fail-fast: false
|
|
24
|
+
matrix:
|
|
25
|
+
python: ['3.10', '3.11', '3.12', '3.13', '3.14']
|
|
26
|
+
redis: ['redis:7-alpine']
|
|
27
|
+
include:
|
|
28
|
+
- python: '3.12'
|
|
29
|
+
redis: 'redis:8-alpine'
|
|
30
|
+
services:
|
|
31
|
+
redis:
|
|
32
|
+
image: ${{ matrix.redis }}
|
|
33
|
+
ports: ['6379:6379']
|
|
34
|
+
options: --health-cmd "redis-cli ping" --health-interval 5s --health-timeout 3s --health-retries 10
|
|
35
|
+
env:
|
|
36
|
+
AIOGRAM_IDLE_REDIS_URL: redis://localhost:6379/0
|
|
37
|
+
AIOGRAM_IDLE_REQUIRE_REDIS: '1'
|
|
38
|
+
steps:
|
|
39
|
+
- uses: actions/checkout@v4
|
|
40
|
+
with:
|
|
41
|
+
path: aiogram-idle
|
|
42
|
+
- uses: actions/checkout@v4
|
|
43
|
+
with:
|
|
44
|
+
repository: ${{ inputs.nav_repository }}
|
|
45
|
+
ref: ${{ inputs.nav_ref }}
|
|
46
|
+
path: aiogram-nav
|
|
47
|
+
- uses: astral-sh/setup-uv@v6
|
|
48
|
+
- run: uv sync --frozen --extra redis --extra nav --python '${{ matrix.python }}'
|
|
49
|
+
- run: uv run --no-sync ruff check .
|
|
50
|
+
- run: uv run --no-sync ruff format --check .
|
|
51
|
+
- run: uv run --no-sync pyright --project pyright-nav.json
|
|
52
|
+
- run: uv run --no-sync pytest --cov=aiogram_idle --cov-config=coverage-nav.toml
|
|
53
|
+
- name: Check optional example CLI without sending messages
|
|
54
|
+
run: uv run --no-sync python examples/navigation.py --help
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
name: Publish verified release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
inputs:
|
|
6
|
+
manifest_sha256:
|
|
7
|
+
description: SHA256 of the independently verified GitHub release SHA256SUMS
|
|
8
|
+
required: true
|
|
9
|
+
type: string
|
|
10
|
+
|
|
11
|
+
permissions:
|
|
12
|
+
contents: read
|
|
13
|
+
|
|
14
|
+
concurrency:
|
|
15
|
+
group: publish-${{ github.ref }}
|
|
16
|
+
cancel-in-progress: false
|
|
17
|
+
|
|
18
|
+
jobs:
|
|
19
|
+
verify:
|
|
20
|
+
if: github.ref_type == 'tag' && startsWith(github.ref_name, 'v')
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
timeout-minutes: 10
|
|
23
|
+
steps:
|
|
24
|
+
- uses: actions/checkout@v6
|
|
25
|
+
with:
|
|
26
|
+
persist-credentials: false
|
|
27
|
+
- uses: astral-sh/setup-uv@v7
|
|
28
|
+
with:
|
|
29
|
+
python-version: '3.12'
|
|
30
|
+
- name: Download the exact reviewed release artifacts
|
|
31
|
+
env:
|
|
32
|
+
GH_TOKEN: ${{ github.token }}
|
|
33
|
+
run: gh release download "$GITHUB_REF_NAME" --pattern '*.whl' --pattern '*.tar.gz' --pattern SHA256SUMS --dir dist
|
|
34
|
+
- name: Validate tag, distribution names and independently supplied checksums
|
|
35
|
+
env:
|
|
36
|
+
MANIFEST_SHA256: ${{ inputs.manifest_sha256 }}
|
|
37
|
+
run: |
|
|
38
|
+
python - <<'PYVERIFY'
|
|
39
|
+
import hashlib
|
|
40
|
+
import os
|
|
41
|
+
import re
|
|
42
|
+
import tomllib
|
|
43
|
+
from pathlib import Path
|
|
44
|
+
project = tomllib.loads(Path('pyproject.toml').read_text())['project']
|
|
45
|
+
version = project['version']
|
|
46
|
+
if os.environ['GITHUB_REF_NAME'] != 'v' + version:
|
|
47
|
+
raise SystemExit('Tag does not match project version')
|
|
48
|
+
expected_digest = os.environ['MANIFEST_SHA256']
|
|
49
|
+
if not re.fullmatch(r'[a-f0-9]{64}', expected_digest):
|
|
50
|
+
raise SystemExit('Expected an independently verified SHA256 digest')
|
|
51
|
+
manifest = Path('dist/SHA256SUMS')
|
|
52
|
+
if hashlib.sha256(manifest.read_bytes()).hexdigest() != expected_digest:
|
|
53
|
+
raise SystemExit('Release checksum manifest differs from reviewed input')
|
|
54
|
+
name = project['name'].replace('-', '_')
|
|
55
|
+
expected = {f'{name}-{version}-py3-none-any.whl', f'{name}-{version}.tar.gz'}
|
|
56
|
+
if {p.name for p in Path('dist').iterdir()} != expected | {'SHA256SUMS'}:
|
|
57
|
+
raise SystemExit('Unexpected release files')
|
|
58
|
+
checked = set()
|
|
59
|
+
for line in manifest.read_text().splitlines():
|
|
60
|
+
digest, filename = line.split()
|
|
61
|
+
if filename not in expected or filename in checked:
|
|
62
|
+
raise SystemExit('Unexpected or repeated checksum entry')
|
|
63
|
+
if hashlib.sha256((Path('dist') / filename).read_bytes()).hexdigest() != digest:
|
|
64
|
+
raise SystemExit('Distribution checksum mismatch')
|
|
65
|
+
checked.add(filename)
|
|
66
|
+
if checked != expected:
|
|
67
|
+
raise SystemExit('Missing distribution checksum')
|
|
68
|
+
PYVERIFY
|
|
69
|
+
- run: uvx twine check dist/*.whl dist/*.tar.gz
|
|
70
|
+
- uses: actions/upload-artifact@v6
|
|
71
|
+
with:
|
|
72
|
+
name: verified-distributions
|
|
73
|
+
path: |
|
|
74
|
+
dist/*.whl
|
|
75
|
+
dist/*.tar.gz
|
|
76
|
+
if-no-files-found: error
|
|
77
|
+
|
|
78
|
+
publish:
|
|
79
|
+
needs: verify
|
|
80
|
+
runs-on: ubuntu-latest
|
|
81
|
+
timeout-minutes: 10
|
|
82
|
+
environment: pypi
|
|
83
|
+
permissions:
|
|
84
|
+
id-token: write
|
|
85
|
+
steps:
|
|
86
|
+
- uses: actions/download-artifact@v7
|
|
87
|
+
with:
|
|
88
|
+
name: verified-distributions
|
|
89
|
+
path: dist
|
|
90
|
+
- name: Publish verified distributions using PyPI Trusted Publishing
|
|
91
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
.venv/
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
.pytest_cache/
|
|
5
|
+
.ruff_cache/
|
|
6
|
+
.coverage
|
|
7
|
+
htmlcov/
|
|
8
|
+
dist/
|
|
9
|
+
*.egg-info/
|
|
10
|
+
.env
|
|
11
|
+
.env.*
|
|
12
|
+
!.env.example
|
|
13
|
+
site/
|
|
14
|
+
.mypy_cache/
|
|
15
|
+
.pyright/
|
|
16
|
+
coverage.xml
|
|
17
|
+
bandit-report.json
|
|
18
|
+
pip-audit-report.json
|
|
19
|
+
reports/generated/
|
|
20
|
+
.DS_Store
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
<!-- towncrier release notes start -->
|
|
4
|
+
|
|
5
|
+
## [0.1.0] - 2026-11-10
|
|
6
|
+
|
|
7
|
+
### Miscellaneous
|
|
8
|
+
|
|
9
|
+
- Align repository automation, typed distribution checks, community
|
|
10
|
+
documentation
|
|
11
|
+
and changelog fragments with the related aiogram libraries. Runtime version
|
|
12
|
+
reporting now reads the installed distribution metadata.
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Core library
|
|
16
|
+
|
|
17
|
+
- Introduce standalone conversation identity, session/activity guards, policies and typed adapter contracts.
|
|
18
|
+
- Add single-process memory storage and a recoverable inactivity lifecycle.
|
|
19
|
+
- Add reply, inline, command and hybrid prompt controls with aiogram dispatcher integration.
|
|
20
|
+
- Include ordinary-handler and FSM examples, deterministic tests and packaging checks.
|
|
21
|
+
- Add optional Redis 7+ storage, renewable distributed coordination, atomic indexes, retained operation receipts and explicit administrative index recovery.
|
|
22
|
+
- Exercise real-server worker races, process crashes, lease expiry, uncertain commits, corruption and memory/Redis parity.
|
|
23
|
+
- Include optional navigation integration as detailed below.
|
|
24
|
+
|
|
25
|
+
### Memory and lifecycle audit fixes
|
|
26
|
+
|
|
27
|
+
- Share backend coordination across runtimes and verify FSM borrowing/scope lifetimes.
|
|
28
|
+
- Enforce grace deadlines during answer admission and preserve original delivery grace on recovery.
|
|
29
|
+
- Serialize effect stages, preserve typed receipts and retain confirmed subeffects across retry/recovery.
|
|
30
|
+
- Guard reserved business work from reentrant/public mutations and ordinary handler admission.
|
|
31
|
+
- Protect pending evidence, expire administrative receipts, route namespace/bot claims and tag operation lookup results.
|
|
32
|
+
- Expand automated coverage to 168 tests, including 1,000 abandoned-claim recoveries.
|
|
33
|
+
|
|
34
|
+
### Redis audit fixes
|
|
35
|
+
|
|
36
|
+
- Fence namespace/configuration history, storage profiles and delegated effect checkpoints atomically.
|
|
37
|
+
- Prevent revoked-scope reentry and enforce shared FSM lock order and lossless Redis FSM keys.
|
|
38
|
+
- Repair missing/orphan queue jobs, isolate corrupt work and bind claims/quarantine to validated snapshots.
|
|
39
|
+
- Preserve unknown business-effect ledgers and validate action receipts before completion.
|
|
40
|
+
- Recheck grace before delivery, order heartbeat retries and preserve external cancellation through timeouts.
|
|
41
|
+
- Document backend scope contracts and explicit recovery in docs/redis-audit.md.
|
|
42
|
+
|
|
43
|
+
### Optional navigation integration
|
|
44
|
+
|
|
45
|
+
- Add the optional nav extra and aiogram_idle.integrations.nav projection without peer imports in either core.
|
|
46
|
+
- Commit navigation/activity/deadline updates in one memory/Redis transaction with mapped sessions and acceptance counters.
|
|
47
|
+
- Resolve inherited node policies, explicit activity groups and guarded touch/restart semantics.
|
|
48
|
+
- Share one coordinator and combined dispatcher middleware with verified FSM borrowing and prompt-control precedence.
|
|
49
|
+
- Stage Continue/Cancel/restore rendering under existing idle operations and preserve exact worker claims through recovery.
|
|
50
|
+
- Park unknown/schema-incompatible recovery without resending; retain typed receipts across process exit and final-commit failure.
|
|
51
|
+
- Restore temporary reply/hybrid keyboard overlays through durable background work and silence restoration sends.
|
|
52
|
+
- Preserve application cancellation uncertainty in a nested business effect stage.
|
|
53
|
+
- Add a runnable four-style example, standalone artifact checks and a separate optional-integration CI matrix.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Changelog fragments
|
|
2
|
+
|
|
3
|
+
Add a short reStructuredText fragment for a user-visible change. Name it
|
|
4
|
+
`<issue>.<type>.rst`, or `+<description>.<type>.rst` when no issue exists.
|
|
5
|
+
Supported types are `feature`, `bugfix`, `doc`, `removal` and `misc`.
|
|
6
|
+
|
|
7
|
+
Describe the resulting behavior for a library consumer. Keep internal details in
|
|
8
|
+
the pull request unless they explain a compatibility or recovery constraint.
|
|
9
|
+
|
|
10
|
+
Preview release notes without modifying files:
|
|
11
|
+
|
|
12
|
+
```sh
|
|
13
|
+
uv run --no-sync towncrier build --draft --version 0.1.0
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Maintainers assemble the changelog as part of the documented release process;
|
|
17
|
+
adding a fragment does not publish or release the package.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Code of conduct
|
|
2
|
+
|
|
3
|
+
Contributors and maintainers should treat each other respectfully. Discuss ideas
|
|
4
|
+
and code on their merits, welcome questions and differing experience, and give
|
|
5
|
+
specific, constructive feedback.
|
|
6
|
+
|
|
7
|
+
Harassment, threats, discriminatory language, personal attacks and disclosure of
|
|
8
|
+
someone else's private information are not acceptable in project spaces.
|
|
9
|
+
|
|
10
|
+
Maintainers may edit or remove inappropriate contributions and restrict access
|
|
11
|
+
when needed to protect participants. Report conduct concerns privately through
|
|
12
|
+
the contact information on the [maintainer's GitHub profile](https://github.com/ArmanAvanesyan),
|
|
13
|
+
sharing only the information needed to investigate. Maintainers should handle
|
|
14
|
+
reports discreetly and consider context when deciding a response.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Explain the problem in an issue or pull request and include a minimal reproduction
|
|
4
|
+
for bugs. Keep library behavior independent of a consuming bot's business flows.
|
|
5
|
+
Follow the [code of conduct](CODE_OF_CONDUCT.md) and use the
|
|
6
|
+
[security reporting guidance](SECURITY.md) for sensitive findings.
|
|
7
|
+
|
|
8
|
+
## Development
|
|
9
|
+
|
|
10
|
+
Use Python 3.10 or newer. From the checkout:
|
|
11
|
+
|
|
12
|
+
```sh
|
|
13
|
+
uv sync --frozen --extra redis --python 3.12
|
|
14
|
+
uv run --no-sync ruff check .
|
|
15
|
+
uv run --no-sync ruff format --check .
|
|
16
|
+
uv run --no-sync pyright
|
|
17
|
+
uv run --no-sync pytest
|
|
18
|
+
uv run --no-sync mkdocs build --strict
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Tests requiring a real Redis server are skipped without its URL. For storage,
|
|
22
|
+
recovery or coordination changes, run the required real-server suite as described
|
|
23
|
+
in [testing](docs/testing.md). Use a dedicated server because tests may flush the
|
|
24
|
+
script cache. Never use production Redis or real Telegram credentials in tests.
|
|
25
|
+
|
|
26
|
+
Optional navigation tests require the adjacent compatible `aiogram-nav` checkout
|
|
27
|
+
and `uv sync --frozen --extra nav --extra redis`. Standalone changes must continue
|
|
28
|
+
working without that peer. The [navigation guide](docs/navigation.md) explains
|
|
29
|
+
the separate type and coverage profiles.
|
|
30
|
+
|
|
31
|
+
## Change conventions
|
|
32
|
+
|
|
33
|
+
- Keep public interfaces typed and include behavior-focused regression tests.
|
|
34
|
+
- Preserve operation receipts, ownership checks and uncertain-effect evidence.
|
|
35
|
+
- Add a [Towncrier fragment](CHANGES/README.md) for user-visible changes.
|
|
36
|
+
- Document compatibility and recovery changes alongside the public behavior.
|
|
37
|
+
- Keep repository automation in `tools/`; do not commit generated audit reports,
|
|
38
|
+
build output, credentials or local environment files.
|
|
39
|
+
|
|
40
|
+
See [testing](docs/testing.md), [repository tools](tools/README.md) and the
|
|
41
|
+
[maintainer release process](docs/releasing.md) for verification commands.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 aiogram-idle contributors
|
|
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.
|