pychrony 2.0.0__tar.gz → 2.0.2__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.
Files changed (143) hide show
  1. {pychrony-2.0.0 → pychrony-2.0.2}/.github/dependabot.yml +15 -0
  2. {pychrony-2.0.0 → pychrony-2.0.2}/.github/workflows/ci.yml +52 -11
  3. {pychrony-2.0.0 → pychrony-2.0.2}/.github/workflows/docs.yml +13 -5
  4. pychrony-2.0.2/.github/workflows/release.yml +181 -0
  5. {pychrony-2.0.0/src/pychrony.egg-info → pychrony-2.0.2}/PKG-INFO +1 -1
  6. {pychrony-2.0.0 → pychrony-2.0.2/src/pychrony.egg-info}/PKG-INFO +1 -1
  7. {pychrony-2.0.0 → pychrony-2.0.2}/src/pychrony.egg-info/SOURCES.txt +0 -1
  8. {pychrony-2.0.0 → pychrony-2.0.2}/src/pychrony.egg-info/scm_file_list.json +0 -1
  9. pychrony-2.0.2/src/pychrony.egg-info/scm_version.json +8 -0
  10. {pychrony-2.0.0 → pychrony-2.0.2}/uv.lock +38 -38
  11. pychrony-2.0.0/.github/workflows/publish.yml +0 -61
  12. pychrony-2.0.0/.github/workflows/release.yml +0 -135
  13. pychrony-2.0.0/src/pychrony.egg-info/scm_version.json +0 -8
  14. {pychrony-2.0.0 → pychrony-2.0.2}/.claude/commands/speckit.analyze.md +0 -0
  15. {pychrony-2.0.0 → pychrony-2.0.2}/.claude/commands/speckit.checklist.md +0 -0
  16. {pychrony-2.0.0 → pychrony-2.0.2}/.claude/commands/speckit.clarify.md +0 -0
  17. {pychrony-2.0.0 → pychrony-2.0.2}/.claude/commands/speckit.constitution.md +0 -0
  18. {pychrony-2.0.0 → pychrony-2.0.2}/.claude/commands/speckit.implement.md +0 -0
  19. {pychrony-2.0.0 → pychrony-2.0.2}/.claude/commands/speckit.plan.md +0 -0
  20. {pychrony-2.0.0 → pychrony-2.0.2}/.claude/commands/speckit.specify.md +0 -0
  21. {pychrony-2.0.0 → pychrony-2.0.2}/.claude/commands/speckit.tasks.md +0 -0
  22. {pychrony-2.0.0 → pychrony-2.0.2}/.claude/commands/speckit.taskstoissues.md +0 -0
  23. {pychrony-2.0.0 → pychrony-2.0.2}/.dockerignore +0 -0
  24. {pychrony-2.0.0 → pychrony-2.0.2}/.gitignore +0 -0
  25. {pychrony-2.0.0 → pychrony-2.0.2}/.gitmodules +0 -0
  26. {pychrony-2.0.0 → pychrony-2.0.2}/.python-version +0 -0
  27. {pychrony-2.0.0 → pychrony-2.0.2}/.specify/memory/constitution.md +0 -0
  28. {pychrony-2.0.0 → pychrony-2.0.2}/.specify/scripts/bash/check-prerequisites.sh +0 -0
  29. {pychrony-2.0.0 → pychrony-2.0.2}/.specify/scripts/bash/common.sh +0 -0
  30. {pychrony-2.0.0 → pychrony-2.0.2}/.specify/scripts/bash/create-new-feature.sh +0 -0
  31. {pychrony-2.0.0 → pychrony-2.0.2}/.specify/scripts/bash/execute-task-generation.sh +0 -0
  32. {pychrony-2.0.0 → pychrony-2.0.2}/.specify/scripts/bash/setup-plan.sh +0 -0
  33. {pychrony-2.0.0 → pychrony-2.0.2}/.specify/scripts/bash/update-agent-context.sh +0 -0
  34. {pychrony-2.0.0 → pychrony-2.0.2}/.specify/templates/agent-file-template.md +0 -0
  35. {pychrony-2.0.0 → pychrony-2.0.2}/.specify/templates/checklist-template.md +0 -0
  36. {pychrony-2.0.0 → pychrony-2.0.2}/.specify/templates/plan-template.md +0 -0
  37. {pychrony-2.0.0 → pychrony-2.0.2}/.specify/templates/spec-template.md +0 -0
  38. {pychrony-2.0.0 → pychrony-2.0.2}/.specify/templates/tasks-template.md +0 -0
  39. {pychrony-2.0.0 → pychrony-2.0.2}/CLAUDE.md +0 -0
  40. {pychrony-2.0.0 → pychrony-2.0.2}/LICENSE +0 -0
  41. {pychrony-2.0.0 → pychrony-2.0.2}/LICENSES/LGPL-2.1-or-later.txt +0 -0
  42. {pychrony-2.0.0 → pychrony-2.0.2}/README.md +0 -0
  43. {pychrony-2.0.0 → pychrony-2.0.2}/TESTING.md +0 -0
  44. {pychrony-2.0.0 → pychrony-2.0.2}/docker/Dockerfile.test +0 -0
  45. {pychrony-2.0.0 → pychrony-2.0.2}/docker/docker-compose.test.yml +0 -0
  46. {pychrony-2.0.0 → pychrony-2.0.2}/docs/api/index.md +0 -0
  47. {pychrony-2.0.0 → pychrony-2.0.2}/docs/index.md +0 -0
  48. {pychrony-2.0.0 → pychrony-2.0.2}/mkdocs.yml +0 -0
  49. {pychrony-2.0.0 → pychrony-2.0.2}/pyproject.toml +0 -0
  50. {pychrony-2.0.0 → pychrony-2.0.2}/setup.cfg +0 -0
  51. {pychrony-2.0.0 → pychrony-2.0.2}/setup.py +0 -0
  52. {pychrony-2.0.0 → pychrony-2.0.2}/specs/001-python-scaffold/checklists/requirements.md +0 -0
  53. {pychrony-2.0.0 → pychrony-2.0.2}/specs/001-python-scaffold/contracts/package.md +0 -0
  54. {pychrony-2.0.0 → pychrony-2.0.2}/specs/001-python-scaffold/data-model.md +0 -0
  55. {pychrony-2.0.0 → pychrony-2.0.2}/specs/001-python-scaffold/plan.md +0 -0
  56. {pychrony-2.0.0 → pychrony-2.0.2}/specs/001-python-scaffold/quickstart.md +0 -0
  57. {pychrony-2.0.0 → pychrony-2.0.2}/specs/001-python-scaffold/research.md +0 -0
  58. {pychrony-2.0.0 → pychrony-2.0.2}/specs/001-python-scaffold/spec.md +0 -0
  59. {pychrony-2.0.0 → pychrony-2.0.2}/specs/001-python-scaffold/tasks.md +0 -0
  60. {pychrony-2.0.0 → pychrony-2.0.2}/specs/002-tracking-binding/checklists/requirements.md +0 -0
  61. {pychrony-2.0.0 → pychrony-2.0.2}/specs/002-tracking-binding/contracts/python-api.md +0 -0
  62. {pychrony-2.0.0 → pychrony-2.0.2}/specs/002-tracking-binding/data-model.md +0 -0
  63. {pychrony-2.0.0 → pychrony-2.0.2}/specs/002-tracking-binding/plan.md +0 -0
  64. {pychrony-2.0.0 → pychrony-2.0.2}/specs/002-tracking-binding/quickstart.md +0 -0
  65. {pychrony-2.0.0 → pychrony-2.0.2}/specs/002-tracking-binding/research.md +0 -0
  66. {pychrony-2.0.0 → pychrony-2.0.2}/specs/002-tracking-binding/spec.md +0 -0
  67. {pychrony-2.0.0 → pychrony-2.0.2}/specs/002-tracking-binding/tasks.md +0 -0
  68. {pychrony-2.0.0 → pychrony-2.0.2}/specs/003-multiple-reports-bindings/checklists/requirements.md +0 -0
  69. {pychrony-2.0.0 → pychrony-2.0.2}/specs/003-multiple-reports-bindings/contracts/api.md +0 -0
  70. {pychrony-2.0.0 → pychrony-2.0.2}/specs/003-multiple-reports-bindings/contracts/python-api.md +0 -0
  71. {pychrony-2.0.0 → pychrony-2.0.2}/specs/003-multiple-reports-bindings/data-model.md +0 -0
  72. {pychrony-2.0.0 → pychrony-2.0.2}/specs/003-multiple-reports-bindings/plan.md +0 -0
  73. {pychrony-2.0.0 → pychrony-2.0.2}/specs/003-multiple-reports-bindings/quickstart.md +0 -0
  74. {pychrony-2.0.0 → pychrony-2.0.2}/specs/003-multiple-reports-bindings/research.md +0 -0
  75. {pychrony-2.0.0 → pychrony-2.0.2}/specs/003-multiple-reports-bindings/spec.md +0 -0
  76. {pychrony-2.0.0 → pychrony-2.0.2}/specs/003-multiple-reports-bindings/tasks.md +0 -0
  77. {pychrony-2.0.0 → pychrony-2.0.2}/specs/004-categorical-enums/checklists/requirements.md +0 -0
  78. {pychrony-2.0.0 → pychrony-2.0.2}/specs/004-categorical-enums/data-model.md +0 -0
  79. {pychrony-2.0.0 → pychrony-2.0.2}/specs/004-categorical-enums/plan.md +0 -0
  80. {pychrony-2.0.0 → pychrony-2.0.2}/specs/004-categorical-enums/quickstart.md +0 -0
  81. {pychrony-2.0.0 → pychrony-2.0.2}/specs/004-categorical-enums/research.md +0 -0
  82. {pychrony-2.0.0 → pychrony-2.0.2}/specs/004-categorical-enums/spec.md +0 -0
  83. {pychrony-2.0.0 → pychrony-2.0.2}/specs/004-categorical-enums/tasks.md +0 -0
  84. {pychrony-2.0.0 → pychrony-2.0.2}/specs/005-pypi-packaging/checklists/requirements.md +0 -0
  85. {pychrony-2.0.0 → pychrony-2.0.2}/specs/005-pypi-packaging/plan.md +0 -0
  86. {pychrony-2.0.0 → pychrony-2.0.2}/specs/005-pypi-packaging/quickstart.md +0 -0
  87. {pychrony-2.0.0 → pychrony-2.0.2}/specs/005-pypi-packaging/research.md +0 -0
  88. {pychrony-2.0.0 → pychrony-2.0.2}/specs/005-pypi-packaging/spec.md +0 -0
  89. {pychrony-2.0.0 → pychrony-2.0.2}/specs/005-pypi-packaging/tasks.md +0 -0
  90. {pychrony-2.0.0 → pychrony-2.0.2}/specs/006-protocol-test-mocks/analysis-report.md +0 -0
  91. {pychrony-2.0.0 → pychrony-2.0.2}/specs/006-protocol-test-mocks/checklists/requirements.md +0 -0
  92. {pychrony-2.0.0 → pychrony-2.0.2}/specs/006-protocol-test-mocks/contracts/mock-api.md +0 -0
  93. {pychrony-2.0.0 → pychrony-2.0.2}/specs/006-protocol-test-mocks/data-model.md +0 -0
  94. {pychrony-2.0.0 → pychrony-2.0.2}/specs/006-protocol-test-mocks/plan.md +0 -0
  95. {pychrony-2.0.0 → pychrony-2.0.2}/specs/006-protocol-test-mocks/quickstart.md +0 -0
  96. {pychrony-2.0.0 → pychrony-2.0.2}/specs/006-protocol-test-mocks/research.md +0 -0
  97. {pychrony-2.0.0 → pychrony-2.0.2}/specs/006-protocol-test-mocks/spec.md +0 -0
  98. {pychrony-2.0.0 → pychrony-2.0.2}/specs/006-protocol-test-mocks/tasks.md +0 -0
  99. {pychrony-2.0.0 → pychrony-2.0.2}/specs/007-api-docs-github-pages/tasks.md +0 -0
  100. {pychrony-2.0.0 → pychrony-2.0.2}/src/pychrony/__init__.py +0 -0
  101. {pychrony-2.0.0 → pychrony-2.0.2}/src/pychrony/_core/__init__.py +0 -0
  102. {pychrony-2.0.0 → pychrony-2.0.2}/src/pychrony/_core/_bindings.py +0 -0
  103. {pychrony-2.0.0 → pychrony-2.0.2}/src/pychrony/_core/_build_bindings.py +0 -0
  104. {pychrony-2.0.0 → pychrony-2.0.2}/src/pychrony/_core/_fields.py +0 -0
  105. {pychrony-2.0.0 → pychrony-2.0.2}/src/pychrony/exceptions.py +0 -0
  106. {pychrony-2.0.0 → pychrony-2.0.2}/src/pychrony/models.py +0 -0
  107. {pychrony-2.0.0 → pychrony-2.0.2}/src/pychrony/py.typed +0 -0
  108. {pychrony-2.0.0 → pychrony-2.0.2}/src/pychrony/testing.py +0 -0
  109. {pychrony-2.0.0 → pychrony-2.0.2}/src/pychrony.egg-info/dependency_links.txt +0 -0
  110. {pychrony-2.0.0 → pychrony-2.0.2}/src/pychrony.egg-info/entry_points.txt +0 -0
  111. {pychrony-2.0.0 → pychrony-2.0.2}/src/pychrony.egg-info/requires.txt +0 -0
  112. {pychrony-2.0.0 → pychrony-2.0.2}/src/pychrony.egg-info/top_level.txt +0 -0
  113. {pychrony-2.0.0 → pychrony-2.0.2}/tests/__init__.py +0 -0
  114. {pychrony-2.0.0 → pychrony-2.0.2}/tests/conftest.py +0 -0
  115. {pychrony-2.0.0 → pychrony-2.0.2}/tests/contract/__init__.py +0 -0
  116. {pychrony-2.0.0 → pychrony-2.0.2}/tests/contract/test_api.py +0 -0
  117. {pychrony-2.0.0 → pychrony-2.0.2}/tests/contract/test_api_stability.py +0 -0
  118. {pychrony-2.0.0 → pychrony-2.0.2}/tests/integration/__init__.py +0 -0
  119. {pychrony-2.0.0 → pychrony-2.0.2}/tests/integration/conftest.py +0 -0
  120. {pychrony-2.0.0 → pychrony-2.0.2}/tests/integration/test_connection.py +0 -0
  121. {pychrony-2.0.0 → pychrony-2.0.2}/tests/integration/test_enums.py +0 -0
  122. {pychrony-2.0.0 → pychrony-2.0.2}/tests/integration/test_rtcdata.py +0 -0
  123. {pychrony-2.0.0 → pychrony-2.0.2}/tests/integration/test_sources.py +0 -0
  124. {pychrony-2.0.0 → pychrony-2.0.2}/tests/integration/test_sourcestats.py +0 -0
  125. {pychrony-2.0.0 → pychrony-2.0.2}/tests/integration/test_tracking.py +0 -0
  126. {pychrony-2.0.0 → pychrony-2.0.2}/tests/mocks/__init__.py +0 -0
  127. {pychrony-2.0.0 → pychrony-2.0.2}/tests/mocks/config.py +0 -0
  128. {pychrony-2.0.0 → pychrony-2.0.2}/tests/mocks/context.py +0 -0
  129. {pychrony-2.0.0 → pychrony-2.0.2}/tests/mocks/scenarios.py +0 -0
  130. {pychrony-2.0.0 → pychrony-2.0.2}/tests/mocks/session.py +0 -0
  131. {pychrony-2.0.0 → pychrony-2.0.2}/tests/test_import.py +0 -0
  132. {pychrony-2.0.0 → pychrony-2.0.2}/tests/unit/__init__.py +0 -0
  133. {pychrony-2.0.0 → pychrony-2.0.2}/tests/unit/test_connection.py +0 -0
  134. {pychrony-2.0.0 → pychrony-2.0.2}/tests/unit/test_enums.py +0 -0
  135. {pychrony-2.0.0 → pychrony-2.0.2}/tests/unit/test_exceptions.py +0 -0
  136. {pychrony-2.0.0 → pychrony-2.0.2}/tests/unit/test_leap_scenarios.py +0 -0
  137. {pychrony-2.0.0 → pychrony-2.0.2}/tests/unit/test_mock_config.py +0 -0
  138. {pychrony-2.0.0 → pychrony-2.0.2}/tests/unit/test_mock_scenarios.py +0 -0
  139. {pychrony-2.0.0 → pychrony-2.0.2}/tests/unit/test_models.py +0 -0
  140. {pychrony-2.0.0 → pychrony-2.0.2}/tests/unit/test_rtc_scenarios.py +0 -0
  141. {pychrony-2.0.0 → pychrony-2.0.2}/tests/unit/test_source_scenarios.py +0 -0
  142. {pychrony-2.0.0 → pychrony-2.0.2}/tests/unit/test_testing.py +0 -0
  143. {pychrony-2.0.0 → pychrony-2.0.2}/tests/unit/test_validation.py +0 -0
@@ -9,15 +9,30 @@ updates:
9
9
  directory: "/"
10
10
  schedule:
11
11
  interval: "daily"
12
+ cooldown:
13
+ default-days: 7
14
+ groups:
15
+ uv-minor-and-patch:
16
+ patterns: ["*"]
17
+ update-types: ["minor", "patch"]
12
18
  - package-ecosystem: "github-actions"
13
19
  directory: "/"
14
20
  schedule:
15
21
  interval: "daily"
22
+ cooldown:
23
+ default-days: 7
24
+ groups:
25
+ github-actions:
26
+ patterns: ["*"]
16
27
  - package-ecosystem: "docker"
17
28
  directory: "/docker"
18
29
  schedule:
19
30
  interval: "daily"
31
+ cooldown:
32
+ default-days: 7
20
33
  - package-ecosystem: "gitsubmodule"
21
34
  directory: "/"
22
35
  schedule:
23
36
  interval: "daily"
37
+ cooldown:
38
+ default-days: 7
@@ -1,30 +1,42 @@
1
1
  name: CI
2
2
 
3
+ # pull_request has no branches filter: it matches the base branch, so a PR
4
+ # stacked on another PR's branch would get no checks at all.
3
5
  on:
4
6
  push:
5
7
  branches: [main]
6
8
  pull_request:
7
- branches: [main]
9
+ workflow_dispatch:
8
10
 
9
11
  permissions:
10
12
  contents: read
11
13
 
14
+ # A new push to a PR obsoletes the run before it. Runs on main are never
15
+ # cancelled, so every commit there gets a complete result.
16
+ concurrency:
17
+ group: ${{ github.workflow }}-${{ github.ref }}
18
+ cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
19
+
12
20
  jobs:
13
21
  # Unit, contract, and import tests across every supported interpreter.
14
22
  # Keep this matrix in sync with the classifiers and the cibuildwheel
15
23
  # `build` allowlist in pyproject.toml, and with release.yml.
16
24
  test:
25
+ name: Tests (Python ${{ matrix.python-version }})
17
26
  runs-on: ubuntu-latest
27
+ timeout-minutes: 10
18
28
  strategy:
19
29
  fail-fast: false
20
30
  matrix:
21
31
  python-version: ["3.10", "3.11", "3.12", "3.13", "3.14", "3.15"]
22
32
 
23
33
  steps:
24
- - uses: actions/checkout@v7
34
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
35
+ with:
36
+ persist-credentials: false
25
37
 
26
38
  - name: Install uv
27
- uses: astral-sh/setup-uv@v7
39
+ uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
28
40
  with:
29
41
  enable-cache: true
30
42
  python-version: ${{ matrix.python-version }}
@@ -38,12 +50,16 @@ jobs:
38
50
  # Lint and type checking. Results do not vary by interpreter, so this
39
51
  # runs once rather than once per matrix entry.
40
52
  lint:
53
+ name: Lint and type check
41
54
  runs-on: ubuntu-latest
55
+ timeout-minutes: 10
42
56
  steps:
43
- - uses: actions/checkout@v7
57
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
58
+ with:
59
+ persist-credentials: false
44
60
 
45
61
  - name: Install uv
46
- uses: astral-sh/setup-uv@v7
62
+ uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
47
63
  with:
48
64
  enable-cache: true
49
65
  python-version: "3.12"
@@ -61,13 +77,17 @@ jobs:
61
77
  run: uv run ty check src/
62
78
 
63
79
  docs:
80
+ name: Docs build
64
81
  runs-on: ubuntu-latest
82
+ timeout-minutes: 10
65
83
 
66
84
  steps:
67
- - uses: actions/checkout@v7
85
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
86
+ with:
87
+ persist-credentials: false
68
88
 
69
89
  - name: Install uv
70
- uses: astral-sh/setup-uv@v7
90
+ uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
71
91
  with:
72
92
  enable-cache: true
73
93
  python-version: "3.12"
@@ -85,17 +105,20 @@ jobs:
85
105
  # Full suite in Docker with libchrony - the only place integration
86
106
  # tests run against a live chronyd.
87
107
  docker-tests:
108
+ name: Integration tests (Docker)
88
109
  runs-on: ubuntu-latest
110
+ timeout-minutes: 20
89
111
  steps:
90
- - uses: actions/checkout@v7
112
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
91
113
  with:
114
+ persist-credentials: false
92
115
  submodules: true
93
116
 
94
117
  - name: Set up Docker Buildx
95
- uses: docker/setup-buildx-action@v4
118
+ uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1
96
119
 
97
120
  - name: Build Docker image
98
- uses: docker/build-push-action@v7
121
+ uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7.4.0
99
122
  with:
100
123
  context: .
101
124
  file: docker/Dockerfile.test
@@ -104,6 +127,24 @@ jobs:
104
127
  cache-from: type=gha
105
128
  cache-to: type=gha,mode=max
106
129
 
130
+ # chronyd answers commands before it has resolved its pool and server
131
+ # names, and the source list grows as each name resolves. Tests that
132
+ # walk the source list race that growth, so wait until no source has an
133
+ # unresolved address. chronyc also fails until the command socket is up.
107
134
  - name: Run tests
108
135
  run: |
109
- docker run --rm --cap-add=SYS_TIME pychrony-test:latest sh -c "chronyd && sleep 2 && pytest tests/ -v --tb=short"
136
+ docker run --rm --cap-add=SYS_TIME pychrony-test:latest sh -c '
137
+ chronyd
138
+ i=0
139
+ until chronyc -n activity 2>/dev/null | grep -q "^0 sources with unknown address"; do
140
+ i=$((i + 1))
141
+ if [ "$i" -ge 40 ]; then
142
+ echo "chronyd sources not resolved after 20s"
143
+ chronyc -n activity
144
+ exit 1
145
+ fi
146
+ sleep 0.5
147
+ done
148
+ echo "chronyd sources resolved after $i polls"
149
+ pytest tests/ -v --tb=short
150
+ '
@@ -6,7 +6,7 @@ on:
6
6
  tags: [ 'v*' ]
7
7
 
8
8
  permissions:
9
- contents: write
9
+ contents: read
10
10
 
11
11
  concurrency:
12
12
  group: docs-deploy
@@ -14,17 +14,25 @@ concurrency:
14
14
 
15
15
  jobs:
16
16
  deploy:
17
+ name: Deploy documentation
17
18
  runs-on: ubuntu-latest
19
+ timeout-minutes: 15
20
+ permissions:
21
+ contents: write # mike pushes the built site to the gh-pages branch
18
22
 
19
23
  steps:
20
- - uses: actions/checkout@v7
24
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
21
25
  with:
22
26
  fetch-depth: 0
27
+ # `mike deploy --push` pushes gh-pages with these credentials. This job
28
+ # uploads no artifacts, so nothing carries them off the runner.
29
+ persist-credentials: true
23
30
 
24
31
  - name: Install UV
25
- uses: astral-sh/setup-uv@v7
32
+ uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
26
33
  with:
27
- enable-cache: true
34
+ # A deploy job must not restore a cache that a PR run could have written.
35
+ enable-cache: false
28
36
  python-version: "3.12"
29
37
 
30
38
  - name: Install dependencies
@@ -50,5 +58,5 @@ jobs:
50
58
  if: startsWith(github.ref, 'refs/tags/v')
51
59
  run: |
52
60
  VERSION=${GITHUB_REF#refs/tags/}
53
- uv run mike deploy --push --update-aliases $VERSION latest
61
+ uv run mike deploy --push --update-aliases "$VERSION" latest
54
62
  uv run mike set-default --push latest
@@ -0,0 +1,181 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - 'v*'
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ # Never cancel a release in progress: a half-finished run can leave Test PyPI
12
+ # holding a version that production PyPI never gets.
13
+ concurrency:
14
+ group: release-${{ github.ref }}
15
+ cancel-in-progress: false
16
+
17
+ jobs:
18
+ build-wheels:
19
+ name: Build wheels (${{ matrix.arch }})
20
+ strategy:
21
+ matrix:
22
+ include:
23
+ - os: ubuntu-latest
24
+ arch: x86_64
25
+ - os: ubuntu-24.04-arm
26
+ arch: aarch64
27
+ runs-on: ${{ matrix.os }}
28
+ timeout-minutes: 30
29
+
30
+ steps:
31
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
32
+ with:
33
+ fetch-depth: 0 # Required for hatch-vcs versioning
34
+ submodules: recursive # Required for vendor/libchrony
35
+ persist-credentials: false
36
+
37
+ - name: Build wheels with cibuildwheel
38
+ uses: pypa/cibuildwheel@e090b81e30c4d855ea63bf4b6e59204c09a101ae # v4.2.1
39
+ with:
40
+ extras: uv
41
+ env:
42
+ CIBW_ARCHS_LINUX: ${{ matrix.arch }}
43
+
44
+ - name: Upload wheel artifacts
45
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
46
+ with:
47
+ name: wheels-${{ matrix.arch }}
48
+ path: wheelhouse/*.whl
49
+
50
+ build-sdist:
51
+ name: Build sdist
52
+ runs-on: ubuntu-latest
53
+ timeout-minutes: 10
54
+
55
+ steps:
56
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
57
+ with:
58
+ fetch-depth: 0 # Required for hatch-vcs versioning
59
+ persist-credentials: false
60
+
61
+ - name: Install UV
62
+ uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
63
+ with:
64
+ # The sdist ships to PyPI, so it must not build from a cache that a PR
65
+ # run could have written.
66
+ enable-cache: false
67
+
68
+ - name: Build sdist
69
+ run: uv build --sdist
70
+
71
+ - name: Upload sdist artifact
72
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
73
+ with:
74
+ name: sdist
75
+ path: dist/*.tar.gz
76
+
77
+ publish-testpypi:
78
+ name: Publish to Test PyPI
79
+ needs: [build-wheels, build-sdist]
80
+ runs-on: ubuntu-latest
81
+ environment: testpypi
82
+ timeout-minutes: 15
83
+ permissions:
84
+ contents: read
85
+ id-token: write # OIDC token for Test PyPI Trusted Publishing
86
+
87
+ steps:
88
+ - name: Download all artifacts
89
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
90
+ with:
91
+ path: dist/
92
+ merge-multiple: true
93
+
94
+ - name: Publish to Test PyPI
95
+ uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
96
+ with:
97
+ repository-url: https://test.pypi.org/legacy/
98
+
99
+ verify-testpypi:
100
+ name: Verify Test PyPI (${{ matrix.python }})
101
+ needs: publish-testpypi
102
+ runs-on: ubuntu-latest
103
+ timeout-minutes: 15
104
+ strategy:
105
+ fail-fast: false
106
+ matrix:
107
+ python: ["3.10", "3.12", "3.15"]
108
+
109
+ steps:
110
+ - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
111
+ with:
112
+ python-version: ${{ matrix.python }}
113
+ allow-prereleases: true
114
+
115
+ - name: Extract version from tag
116
+ id: version
117
+ run: echo "version=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT"
118
+
119
+ - name: Install from Test PyPI
120
+ env:
121
+ VERSION: ${{ steps.version.outputs.version }}
122
+ run: |
123
+ # --no-cache-dir is load-bearing. The simple index is served with
124
+ # `cache-control: max-age=600`, so pip stores the first response for ten
125
+ # minutes; without this flag every later attempt replays the same stale
126
+ # index from disk and the retry loop cannot outlast Test PyPI's CDN lag.
127
+ for i in $(seq 1 10); do
128
+ if pip install --no-cache-dir \
129
+ --index-url https://test.pypi.org/simple/ \
130
+ --extra-index-url https://pypi.org/simple/ \
131
+ "pychrony==$VERSION"; then
132
+ echo "Installed on attempt $i"
133
+ exit 0
134
+ fi
135
+ if [ "$i" -lt 10 ]; then
136
+ echo "Attempt $i failed, retrying in 30s..."
137
+ sleep 30
138
+ fi
139
+ done
140
+ echo "::error::pychrony==$VERSION never became installable from Test PyPI"
141
+ exit 1
142
+
143
+ - name: Verify import and version
144
+ env:
145
+ VERSION: ${{ steps.version.outputs.version }}
146
+ run: |
147
+ python -c "
148
+ import os
149
+ import pychrony
150
+ expected = os.environ['VERSION']
151
+ print(f'Installed version: {pychrony.__version__}')
152
+ assert pychrony.__version__ == expected, \
153
+ f'Version mismatch: {pychrony.__version__} != {expected}'
154
+ "
155
+
156
+ # Production publish lives in this workflow, after verification, so it can
157
+ # only ever upload the artifacts this tag-push run built and Test PyPI served.
158
+ # The `pypi` environment's required reviewer gates it. To retry a failed
159
+ # publish, re-run this job from the Release run.
160
+ publish-pypi:
161
+ name: Publish to Production PyPI
162
+ needs: verify-testpypi
163
+ runs-on: ubuntu-latest
164
+ environment: pypi
165
+ timeout-minutes: 15
166
+ permissions:
167
+ contents: read
168
+ id-token: write # OIDC token for PyPI Trusted Publishing
169
+
170
+ steps:
171
+ - name: Download all artifacts
172
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
173
+ with:
174
+ path: dist/
175
+ merge-multiple: true
176
+
177
+ - name: List artifacts
178
+ run: ls -la dist/
179
+
180
+ - name: Publish to PyPI
181
+ uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pychrony
3
- Version: 2.0.0
3
+ Version: 2.0.2
4
4
  Summary: Python bindings for chrony NTP client
5
5
  Author-email: arunderwood <arunderwood@users.noreply.github.com>
6
6
  License-Expression: MIT AND LGPL-2.1-or-later
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pychrony
3
- Version: 2.0.0
3
+ Version: 2.0.2
4
4
  Summary: Python bindings for chrony NTP client
5
5
  Author-email: arunderwood <arunderwood@users.noreply.github.com>
6
6
  License-Expression: MIT AND LGPL-2.1-or-later
@@ -22,7 +22,6 @@ uv.lock
22
22
  .github/dependabot.yml
23
23
  .github/workflows/ci.yml
24
24
  .github/workflows/docs.yml
25
- .github/workflows/publish.yml
26
25
  .github/workflows/release.yml
27
26
  .specify/memory/constitution.md
28
27
  .specify/scripts/bash/check-prerequisites.sh
@@ -13,7 +13,6 @@
13
13
  ".github/dependabot.yml",
14
14
  ".github/workflows/ci.yml",
15
15
  ".github/workflows/docs.yml",
16
- ".github/workflows/publish.yml",
17
16
  ".github/workflows/release.yml",
18
17
  ".gitignore",
19
18
  ".gitmodules",
@@ -0,0 +1,8 @@
1
+ {
2
+ "tag": "2.0.2",
3
+ "distance": 0,
4
+ "node": "g8cd3694007006be9e3da9440e32efb2c4ee38058",
5
+ "dirty": false,
6
+ "branch": "HEAD",
7
+ "node_date": "2026-09-24"
8
+ }
@@ -929,27 +929,27 @@ wheels = [
929
929
 
930
930
  [[package]]
931
931
  name = "ruff"
932
- version = "0.16.4"
932
+ version = "0.16.8"
933
933
  source = { registry = "https://pypi.org/simple" }
934
- sdist = { url = "https://files.pythonhosted.org/packages/00/8f/d8074b1f25e003164087a8bfe79a0f1a3945135764dbb6aaab04103dcaf9/ruff-0.16.4.tar.gz", hash = "sha256:13171aa9d9af2240ee3504e639de73122c67e74036de5ba2e1d01422cd17e3dc", size = 4899731, upload-time = "2026-08-20T17:43:59.196Z" }
934
+ sdist = { url = "https://files.pythonhosted.org/packages/ba/78/449cb84790bd5cc3823b2652ee405a4558856e5c4195aee3a16bf7b3eb5d/ruff-0.16.8.tar.gz", hash = "sha256:9247bf92b5f04d825c8639a4fe423ec2e4222acd9222e58412b0dab7e442798b", size = 4938814, upload-time = "2026-09-16T15:54:46.688Z" }
935
935
  wheels = [
936
- { url = "https://files.pythonhosted.org/packages/ff/80/779895ef584e089d22f2c6df0d0e99a65ec2df0805f1fffd439415b8c1f0/ruff-0.16.4-py3-none-linux_armv6l.whl", hash = "sha256:df4075f71ddac40b9934af60c3ec8a53047dd5a5fdc43224e6e4e8e9a27cb6f7", size = 10006909, upload-time = "2026-08-20T17:43:16.888Z" },
937
- { url = "https://files.pythonhosted.org/packages/a9/e6/f553199b5e8927a05cb5c422d921fd0656b29ab976e91c44802107c6b0da/ruff-0.16.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:0c95538517af68004306b0fb3214ff2f2af67a65092aee77cd9eb86db6656604", size = 10240201, upload-time = "2026-08-20T17:43:19.337Z" },
938
- { url = "https://files.pythonhosted.org/packages/1c/70/4a6dc4bb34da4dee35e30f09bbd1bfbdd26f33b62fb9b8df31f08a199cd2/ruff-0.16.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:963f83df8e69e575b64d67dd447ebbc917db41a14bf38d4593a4183e7aaa8255", size = 9835122, upload-time = "2026-08-20T17:43:21.708Z" },
939
- { url = "https://files.pythonhosted.org/packages/24/12/c6e22d686372c15bcb7af99831f1a1be96df696491babf4f24e4f942c527/ruff-0.16.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32a5057c7ff3f6e6480a48fccfb3a412a690f48a3d03ac5cf08177d6c2da3ade", size = 9977162, upload-time = "2026-08-20T17:43:24.236Z" },
940
- { url = "https://files.pythonhosted.org/packages/46/49/72b10ec912f5ab5854992eaf7aa7cd36729b6937d9dc4e0fb41b3bf428ec/ruff-0.16.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b3dce8d9b0c57c265b91885a66a567d8ea1372e8eb4e250fa8e5e3f579e99cff", size = 9829789, upload-time = "2026-08-20T17:43:26.966Z" },
941
- { url = "https://files.pythonhosted.org/packages/fa/80/0f30e32e7f6ee26edc39075502db9d368d788a44a79b55f763eb4ab03796/ruff-0.16.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7dc651db49283c69f8e72c834eec4fe5573e4c646856aebece0ce385dceb2a80", size = 10527949, upload-time = "2026-08-20T17:43:29.384Z" },
942
- { url = "https://files.pythonhosted.org/packages/52/3d/86e8ad3542169e56cac3859a343afdb9df2ad54d35a59ce1e67baee83421/ruff-0.16.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3817b87dbcabc92f13b05019257c5b89b5b4d51b5fb20f56fb5235ceb723cd07", size = 11333695, upload-time = "2026-08-20T17:43:31.872Z" },
943
- { url = "https://files.pythonhosted.org/packages/d0/16/481c29b380c20a0054a8261066665e1b3488e23636c49d0a43e75975b9bb/ruff-0.16.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e9fce1499134b2c8c68e5166f95705a5812062bb93aacc5f9873bb1a27084bc7", size = 10727741, upload-time = "2026-08-20T17:43:34.596Z" },
944
- { url = "https://files.pythonhosted.org/packages/5e/b6/56bc0b8cf45b54b28b3a5e6381c8945d51b5b18adf659454c32295209a31/ruff-0.16.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2d812e482f5a7e02eee26cd73d2a37ebbdf47d795ea63ba1b89110ae93e9fb3", size = 10286522, upload-time = "2026-08-20T17:43:37.288Z" },
945
- { url = "https://files.pythonhosted.org/packages/e8/8b/b345b4fb110f2fbe2bd31eabd271e5e8b3b7e4ee6c0e02f2dc6be78db000/ruff-0.16.4-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:6baaf984aa7976edf93d3b627fe2d1d22ee94bbca05fa6f90fc76d73924e3454", size = 10584182, upload-time = "2026-08-20T17:43:39.984Z" },
946
- { url = "https://files.pythonhosted.org/packages/29/e5/827b34041c35f58774a9681a4213994c164fc987800f4dddabcf451da0bf/ruff-0.16.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:bdfcf0b28662eb890372d50f92c283bb94e67e7635ed93c7fd533970acff7b2b", size = 10134195, upload-time = "2026-08-20T17:43:42.351Z" },
947
- { url = "https://files.pythonhosted.org/packages/0f/10/d0bffcdd6729b87afc82ba0ef377173356a7dc8e972f5179968cf2fdf98c/ruff-0.16.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:b66b02cb9b04f537643cadf5768e5f98dc461890d530cb67113d71c8c76e605d", size = 9825821, upload-time = "2026-08-20T17:43:44.532Z" },
948
- { url = "https://files.pythonhosted.org/packages/f5/32/0db2a863b796ca62d83e92a07a3ccf00921b14db02059347576a2fda3d4b/ruff-0.16.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:8528bf9a4b291a60bf02ea453511e8ce6215bd2b982ee80405b66b008b6c30a0", size = 10267658, upload-time = "2026-08-20T17:43:46.989Z" },
949
- { url = "https://files.pythonhosted.org/packages/b2/a0/fbdeb59e48c6261f523e56c8f12e9c08fbe693786595cc7e3959207a9232/ruff-0.16.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:fbd85d2875fdd67e833213a651f613bbf25303abf6aa822a5121f4531195678d", size = 10697071, upload-time = "2026-08-20T17:43:49.891Z" },
950
- { url = "https://files.pythonhosted.org/packages/aa/28/0c6dd865859c6d17bc8ccc34cb72b0e02d6c7eb25e8a1e22b5bea681e2c0/ruff-0.16.4-py3-none-win32.whl", hash = "sha256:312769988007aaeb8e189b443ccdd03c0e6374489e053467be6d96518ebff76e", size = 10021687, upload-time = "2026-08-20T17:43:52.281Z" },
951
- { url = "https://files.pythonhosted.org/packages/a3/03/e724450f621698117f9aa6dd241c94d0274ae96781378dc86745ae29f0e7/ruff-0.16.4-py3-none-win_amd64.whl", hash = "sha256:05d9d27a18c4bcbefada602480ec9e01e0bc949d432e0ced5df77edac195919c", size = 10567657, upload-time = "2026-08-20T17:43:54.78Z" },
952
- { url = "https://files.pythonhosted.org/packages/0e/fe/da8b9e1347696bb22120b77280ec5ce25d500ca5cb39d5ad6e5c18de19c1/ruff-0.16.4-py3-none-win_arm64.whl", hash = "sha256:a3a61621c9b6f6a89573e938a080e648f1695baa3f58570a3a707bc51ff65a21", size = 10451579, upload-time = "2026-08-20T17:43:57.135Z" },
936
+ { url = "https://files.pythonhosted.org/packages/ac/25/6071aabc530e9be7e2c195e8fe3f7aea2735405b6cf447212832d7811831/ruff-0.16.8-py3-none-linux_armv6l.whl", hash = "sha256:6ffbd6d87383c1edf5f6fa890f10200950240d7c1a16052a19a09d3a2307dd38", size = 10048966, upload-time = "2026-09-16T15:53:57.605Z" },
937
+ { url = "https://files.pythonhosted.org/packages/54/98/07f90ecbc74dd5fb5764f11f2bc774d6a7cffef92d2ff5f5b4e9e23c754e/ruff-0.16.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:42ed6b878ed61e3acca92f2730a17acff39286944ea82398544696366a6f925e", size = 10165498, upload-time = "2026-09-16T15:54:01.14Z" },
938
+ { url = "https://files.pythonhosted.org/packages/fe/1f/e6a712e3b47cad4a40600134105ed193cb773f618a42eb7ba323cb812cc0/ruff-0.16.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7ea781c7f2afba8c6a505ea0fb3f994020249e0c450635f5381286fea6b46170", size = 9830004, upload-time = "2026-09-16T15:54:03.998Z" },
939
+ { url = "https://files.pythonhosted.org/packages/23/f2/311a08776d75d81c7676e20b6b020ae63cbe881fcdc7a8dd64e6e18bdd93/ruff-0.16.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8efeae3bbe414a5efefda11a792dfb51ef90ac48d50c4830de2f644caf3e8659", size = 9986558, upload-time = "2026-09-16T15:54:06.804Z" },
940
+ { url = "https://files.pythonhosted.org/packages/f3/ed/37b6cb3d3ba8c73e68ae3eb1d502383beb5aa05a582bb7bb3a922f929f54/ruff-0.16.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3a79b795469fef7fc6e908b218eed2eb17332afd85031db6480dc864560e69b2", size = 9877332, upload-time = "2026-09-16T15:54:09.552Z" },
941
+ { url = "https://files.pythonhosted.org/packages/22/cc/40873a8f36ad084cc540d55fcca7077264d5b13b24659e9180c176fb2b08/ruff-0.16.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3fdc5563cdc50555e6fba39322850860e9267c1b3d12c26a74729d8604c3c812", size = 10507125, upload-time = "2026-09-16T15:54:12.152Z" },
942
+ { url = "https://files.pythonhosted.org/packages/c3/e4/fc91a642b78ccbab6b9477720f3644ae7a10a9bcce69a934679cd64f62bc/ruff-0.16.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:34508983c70665578dab88f5223d8e6228307e1135398ca8bfc8b7e9501e282b", size = 11336694, upload-time = "2026-09-16T15:54:15.489Z" },
943
+ { url = "https://files.pythonhosted.org/packages/c2/3d/bbd2a9a600a4e73dc3e7548a249c8d1671273464b55822c6fae50f602dff/ruff-0.16.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:644bb578569e0ffc575741232bd385dacdd6fbe123f1a729e7a225f54aa3957f", size = 10774448, upload-time = "2026-09-16T15:54:18.16Z" },
944
+ { url = "https://files.pythonhosted.org/packages/1a/41/d83af9879a7b6e8bf5fe16b1da0b134049d2f5d3afac12defb0897cb84bd/ruff-0.16.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15e7d226246961db9235098333caa13063906d3851136b84c2900b82f5daa1df", size = 10323796, upload-time = "2026-09-16T15:54:20.743Z" },
945
+ { url = "https://files.pythonhosted.org/packages/f5/2c/cefd07bfe914b84943ea769ade8d607bd22750b965d3228eefd7cebd15d0/ruff-0.16.8-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:a2bf6bc3e9ebdd4449abc6f06cf64b98051a2c61cf94d2fe9596518c881f1a1e", size = 10514115, upload-time = "2026-09-16T15:54:23.497Z" },
946
+ { url = "https://files.pythonhosted.org/packages/f3/9d/76a2e26c79a23be6e6e3664c57bec9e9fc8de155cfb9e4b67ea91b64f9d7/ruff-0.16.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:6ca111ba0849539165e9e59d2b442542f3c1e8060ebbdea82494f1ffbccb1e1f", size = 10072582, upload-time = "2026-09-16T15:54:26.185Z" },
947
+ { url = "https://files.pythonhosted.org/packages/2e/d4/f42edddb39668af1a559ceafa3823aedd65633a48dc9768e775485faa2c1/ruff-0.16.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:359a1e5b495448ee1e91018064382ebc86f90e8aac2fed222c7d0e4e8df85fd2", size = 9879644, upload-time = "2026-09-16T15:54:29.278Z" },
948
+ { url = "https://files.pythonhosted.org/packages/f8/d4/913e3195d95e0378786c6656945c865f534a3560e29139da4882aff630d1/ruff-0.16.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:59e8f5681349474110b24d62e93cfda6593f5fa3473446ca3705200cac1a08b9", size = 10231569, upload-time = "2026-09-16T15:54:32.036Z" },
949
+ { url = "https://files.pythonhosted.org/packages/2b/c4/8aa6ea0bdcedbd1bf87397e2fc4ed8406448ea5842f8660bc6e5f163039d/ruff-0.16.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:efa3e7a16d1baaa79957888dfdf8be9ef2e44db81cb032af06d76632ab59e773", size = 10663666, upload-time = "2026-09-16T15:54:34.838Z" },
950
+ { url = "https://files.pythonhosted.org/packages/3d/02/7f10ef4700bc223c30a3fdd10631a29830c45524b810a3c7ed947af64591/ruff-0.16.8-py3-none-win32.whl", hash = "sha256:55793ba85c69921e89be061426d91a78652d6e50317c962240922747a4eb713f", size = 10093472, upload-time = "2026-09-16T15:54:37.47Z" },
951
+ { url = "https://files.pythonhosted.org/packages/1e/5d/a509c07d714b6da88f2c518b4637cf6f1d46b074be8f0f1e5fb9ff5126fe/ruff-0.16.8-py3-none-win_amd64.whl", hash = "sha256:a6b85621fd3c81e31fc5f5add09c9c078b430db3595ca632efafdec9e64ebfaa", size = 10586899, upload-time = "2026-09-16T15:54:40.488Z" },
952
+ { url = "https://files.pythonhosted.org/packages/fe/a0/50787329e4f20bf9dc9f6230015d46ec69c51a97ace5bc202dae4755365d/ruff-0.16.8-py3-none-win_arm64.whl", hash = "sha256:d075e820af612102ce217f07cc93e69f9490b10ec13ea85fa87bd03d996cef8a", size = 10386316, upload-time = "2026-09-16T15:54:43.332Z" },
953
953
  ]
954
954
 
955
955
  [[package]]
@@ -1017,27 +1017,27 @@ wheels = [
1017
1017
 
1018
1018
  [[package]]
1019
1019
  name = "ty"
1020
- version = "0.0.75"
1020
+ version = "0.0.82"
1021
1021
  source = { registry = "https://pypi.org/simple" }
1022
- sdist = { url = "https://files.pythonhosted.org/packages/81/d0/d0c96f898d6974a4a3569ab3efdf9512c04ad99f9203effb55f72497fe97/ty-0.0.75.tar.gz", hash = "sha256:4c5eead33dfbf6e2ebb4f400f74b51ffc9bab702a6f23ddb648a1cbb740387e3", size = 6868326, upload-time = "2026-08-26T20:23:40.399Z" }
1022
+ sdist = { url = "https://files.pythonhosted.org/packages/f6/df/b9f35d0b8860f1bbfcb852fbef363301b5ca4b5590cadc6f2a810646d579/ty-0.0.82.tar.gz", hash = "sha256:586e3bf784cece42113929bb64b4761bed5c2127ae6cea294bb82da670066356", size = 7325958, upload-time = "2026-09-17T19:42:12.243Z" }
1023
1023
  wheels = [
1024
- { url = "https://files.pythonhosted.org/packages/cb/6c/b12d03505f17581f0cfa3c12273fe34c1d67b36dfda1bc561a6bdc16512b/ty-0.0.75-py3-none-linux_armv6l.whl", hash = "sha256:e5409f50db2246fd4bd039d93d261e0cfa1daa554a4fb77256f91072c570349a", size = 12972606, upload-time = "2026-08-26T20:22:59.716Z" },
1025
- { url = "https://files.pythonhosted.org/packages/d1/aa/30f11eecd9215a9f87e8fe8baaf48f3ce905f5d75b8e4aac70f0091f130c/ty-0.0.75-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:5e7b8b3472fb9bb2eeab314984b265df08a7a9d518867a9e6020eebc06570be2", size = 12527158, upload-time = "2026-08-26T20:23:02.767Z" },
1026
- { url = "https://files.pythonhosted.org/packages/f2/11/7fd7001b0b5c6610bfbad7357e47d5fe6f82d4e84e94c53776a478f5e9f8/ty-0.0.75-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c6ccf34169821fe0d23e3360deeef981d217963412f1d087b9bdd32ec57f7a57", size = 12400533, upload-time = "2026-08-26T20:23:04.965Z" },
1027
- { url = "https://files.pythonhosted.org/packages/fd/7f/1e284ea3d348d7be02f12d83bc22ed9ef193033f863f05b64db99027f141/ty-0.0.75-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:842ebb41e9c6c334b40768704e20b1a69d5c6b08805b289d5e0e2565f49f2de1", size = 12420592, upload-time = "2026-08-26T20:23:07.427Z" },
1028
- { url = "https://files.pythonhosted.org/packages/2d/ab/d813271543370c47fd74b5118f2066ab32b0983e907b1821f3f9a6d0fa7f/ty-0.0.75-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cf7a5a723c5f1e0fab4ffbfe9bd95123a526ed48f206e5f25cb2161ca294007a", size = 12739219, upload-time = "2026-08-26T20:23:09.809Z" },
1029
- { url = "https://files.pythonhosted.org/packages/31/5b/95b49cc5570fd92a7bf63732f649b31906158721e03c7fcb1b5be74ee3bf/ty-0.0.75-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54382f98e5da292fcd7104391afef5105c35bb2f312e29bea6f5fa419935255c", size = 13494046, upload-time = "2026-08-26T20:23:12.191Z" },
1030
- { url = "https://files.pythonhosted.org/packages/2c/0d/502d2dd68173cf020e1ad2bdbab9544c86776de0b0e2ed15f8c2fe006e3d/ty-0.0.75-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac13b180dc2aade2cd243f56b01650e78bf091a2e522ad3bc947245d7837c613", size = 13938899, upload-time = "2026-08-26T20:23:14.764Z" },
1031
- { url = "https://files.pythonhosted.org/packages/20/5b/f3b12a25c07224456219fc2bd20db0ad7e40b304be0ff6aad728da0135f9/ty-0.0.75-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:752df7951a443219d7f1ff817e3723c85d428565ff449e08a7a93ba821661526", size = 13656711, upload-time = "2026-08-26T20:23:17.145Z" },
1032
- { url = "https://files.pythonhosted.org/packages/51/7b/f090ad306e2b15a07b332d647138c5264b89d9758855ecce8b8a10bcb153/ty-0.0.75-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1fd399feedf7cee816563c1baec45fc1c0b3c89f1ea42364920b688004b5b7da", size = 13093499, upload-time = "2026-08-26T20:23:19.489Z" },
1033
- { url = "https://files.pythonhosted.org/packages/f1/4b/f69b99aaaca0c7c65d5f114b186b26b21666f767b0c69eec99a2bdccc061/ty-0.0.75-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:d7625f6f56c7dc1e873579fdc9e432a0e21e302afe847ab60704d2303442a92e", size = 13520580, upload-time = "2026-08-26T20:23:21.789Z" },
1034
- { url = "https://files.pythonhosted.org/packages/b6/e7/692c5f905c0345a15d2255fc74066d660f030254ae8dcdaf33f5a5c2f279/ty-0.0.75-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:89e7d527e95a2534b70cae29e94c104b84082760ea05927d23bb87280969c104", size = 12524095, upload-time = "2026-08-26T20:23:24.026Z" },
1035
- { url = "https://files.pythonhosted.org/packages/ba/9a/f42b12cf265ea95344bf554764c4791cfb273bdd628aadd7c209af7cadc3/ty-0.0.75-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:0843f134440740706e01bee5f88f4cfc10e9b018bddb9e4ef4c12dc9fc0c9aef", size = 12756591, upload-time = "2026-08-26T20:23:26.126Z" },
1036
- { url = "https://files.pythonhosted.org/packages/7c/5e/9b180c133cb9cce48179a7d2bf9e1802d992aa8176a918e0e05205760b42/ty-0.0.75-py3-none-musllinux_1_2_i686.whl", hash = "sha256:1bd0ec0e50ee1376875c88891efe6f549c3560fa5b2ddad79a425cd5a6218b9c", size = 12998754, upload-time = "2026-08-26T20:23:28.353Z" },
1037
- { url = "https://files.pythonhosted.org/packages/39/f6/3c6ef5dd550103e29905121c67fb96a374564f31a2f44c6faa1af98c2d61/ty-0.0.75-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:1f9eafd561f90110d5e29f589ec3e956c4686e2f6631348d99276436f5cbe4d1", size = 13316474, upload-time = "2026-08-26T20:23:30.857Z" },
1038
- { url = "https://files.pythonhosted.org/packages/bb/52/12776337874c821076bd5368e352ccd9e67174790abe3b856f749cb3524b/ty-0.0.75-py3-none-win32.whl", hash = "sha256:05063a6fafe2154b794a7f964515d148e51acd186d72d4a3acd347ee9fa19336", size = 12316315, upload-time = "2026-08-26T20:23:33.528Z" },
1039
- { url = "https://files.pythonhosted.org/packages/53/e6/bb51e16af5c7138c9f52f8f3d0a401a371c6798d092e3b74926f186a9814/ty-0.0.75-py3-none-win_amd64.whl", hash = "sha256:81cf1ba5f6b7536ad56747865214255d9bc8e80533a689dbb9ddeaad464b09f1", size = 12917267, upload-time = "2026-08-26T20:23:35.978Z" },
1040
- { url = "https://files.pythonhosted.org/packages/39/73/4542f829107468b5de4231af67f29927c093bfad11f3c1e5b2c08fb1206b/ty-0.0.75-py3-none-win_arm64.whl", hash = "sha256:541c9af5b7a0ad23d15ec315a7da81150833c359f48124ed3789ff25eacd6f42", size = 12711024, upload-time = "2026-08-26T20:23:38.159Z" },
1024
+ { url = "https://files.pythonhosted.org/packages/31/0e/259760638b7b1a875743f1f3975253392932ff74397c8e174df7bfb143c3/ty-0.0.82-py3-none-linux_armv6l.whl", hash = "sha256:be58e089e317e1ada7a655216828e76d369386dc958f7a3b3d455ab6dc7f02b6", size = 13833734, upload-time = "2026-09-17T19:41:35.134Z" },
1025
+ { url = "https://files.pythonhosted.org/packages/70/f6/c8a1f70989cf44cf2cada5527e268b4216015a7f28f1d8817e4ef1e84a52/ty-0.0.82-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:c080ba9a816cfbb3e34c0d3a0071829c602d14bb992bc799d885d4b30008b4ab", size = 13449689, upload-time = "2026-09-17T19:41:37.65Z" },
1026
+ { url = "https://files.pythonhosted.org/packages/d5/eb/31fc4bdecb2ff0053443d4ec1e6719a3f3171636ad6da7a8a642c879a4f4/ty-0.0.82-py3-none-macosx_11_0_arm64.whl", hash = "sha256:4f08bd5b751c3b2c46eabe8aee890e7fa54e3bcd97ffa4b8338b0a2508d928c2", size = 13410140, upload-time = "2026-09-17T19:41:40.024Z" },
1027
+ { url = "https://files.pythonhosted.org/packages/e7/14/e2af8c64004cd891691915b1522f105a5d601dd8d76a635fec112911f251/ty-0.0.82-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f5ef0401743c2eeac8039ef9ff1bc5dd612bd17ed0259b9b0794e19696548a6", size = 13435552, upload-time = "2026-09-17T19:41:42.084Z" },
1028
+ { url = "https://files.pythonhosted.org/packages/52/92/b9519a7854189aa8da4309ecb9c6ff5ab8ac91a433e201322b15c98f9388/ty-0.0.82-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c899643f2a4c1782c06a4bf4959909d1aa2d9d2cac7c1ac3de3161dfcb6909f8", size = 13601427, upload-time = "2026-09-17T19:41:44.242Z" },
1029
+ { url = "https://files.pythonhosted.org/packages/ce/be/0c7a39e277a25bca21bbd2dcf640eb4d80cf98f318ef611092cda1222399/ty-0.0.82-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7aa6e4e157c6e0b770c327a5d966371b2a7bc7ad4f22d662ac068623cba00eb", size = 14457370, upload-time = "2026-09-17T19:41:46.405Z" },
1030
+ { url = "https://files.pythonhosted.org/packages/6f/33/86ff8f1e15c71096838eb0fdfe0f5b578dc717575de6cba591d96baf3b06/ty-0.0.82-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:28c141ab481748f1b889eb572e803172ea452af621e514c30a2f30884753b020", size = 14888674, upload-time = "2026-09-17T19:41:48.6Z" },
1031
+ { url = "https://files.pythonhosted.org/packages/0a/d2/a1c6315907afd080b4e98d2972441b4803a70c9663a8c7a7f6fd554c7361/ty-0.0.82-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16fff2729fbaa3998ba1cab536dc7d176aef28927b09200588e50982394c56df", size = 14670369, upload-time = "2026-09-17T19:41:50.963Z" },
1032
+ { url = "https://files.pythonhosted.org/packages/ae/cd/956ceae4dbe0b116ebc9ecb75dcbe9a0294c50146f76c4c59004e6eb2d77/ty-0.0.82-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c2024a33209b37525ffa85a29036985e9ef53a013c0ff321231bd8b23987cf71", size = 14107030, upload-time = "2026-09-17T19:41:53.343Z" },
1033
+ { url = "https://files.pythonhosted.org/packages/b7/f8/6025627cec4d6370fb34a2c5cdcf36d89dd4e4ac75b023bda68231e17dfc/ty-0.0.82-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:14a0ae8591e08aca5911b545d100e51c9b796350a85b2b36d82219820552d662", size = 14485892, upload-time = "2026-09-17T19:41:55.745Z" },
1034
+ { url = "https://files.pythonhosted.org/packages/3a/b6/016f9b36096a9ccd0492ee000380bd75fb51dc4ec7bfef4d6d93f6b3ec2e/ty-0.0.82-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:2964202b09108c72232760119c2680cefc33191c428b09e6f620b6c0ab9c6b0f", size = 13383907, upload-time = "2026-09-17T19:41:57.937Z" },
1035
+ { url = "https://files.pythonhosted.org/packages/b4/19/1ccc2429399d6ef3d98180340c7abc6793bd90c5039ad41a9003cb8de6df/ty-0.0.82-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:f4ed8fc714e35dc11ef4209832e73ad461cf29c9161099eb6c2c541c6b9a3e0e", size = 13617005, upload-time = "2026-09-17T19:42:00.018Z" },
1036
+ { url = "https://files.pythonhosted.org/packages/d7/9d/a4a487db72e78413bba2ee9cd49f79b8f461be93e7f6c4dd790febae1cd7/ty-0.0.82-py3-none-musllinux_1_2_i686.whl", hash = "sha256:32a3fe7b1a50f20679fe76c85c0087280419365d900300898a7b95cbcfe510fc", size = 13906545, upload-time = "2026-09-17T19:42:01.942Z" },
1037
+ { url = "https://files.pythonhosted.org/packages/0c/4e/a58d530c0f5abe7b846e2d05af9e3f1fb44a1b620ae0abe3892cd3f9596c/ty-0.0.82-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:f751044ae63ea9e73ce2eae4c23a839efa4a04d6487d6ea411a590fbe43360b6", size = 14248518, upload-time = "2026-09-17T19:42:04.164Z" },
1038
+ { url = "https://files.pythonhosted.org/packages/a6/db/5505229835171d19adcaaeec3a15e0b533b578d24de1329db5da9306c4c6/ty-0.0.82-py3-none-win32.whl", hash = "sha256:0eb66c5ce58434a48d2cae2780d25285c7ca19e07a0d38b1fe39d1a0f75d6fa3", size = 13059890, upload-time = "2026-09-17T19:42:06.161Z" },
1039
+ { url = "https://files.pythonhosted.org/packages/13/22/faa427b65baf39915f52a6fa079ece4253e831f3364c567bf91bc1eef65a/ty-0.0.82-py3-none-win_amd64.whl", hash = "sha256:1e4a6c1ca2dc1a6929517d73a47cd626989bb8bd67811425fbe022d06eb2bf73", size = 14003730, upload-time = "2026-09-17T19:42:08.268Z" },
1040
+ { url = "https://files.pythonhosted.org/packages/8b/fc/3b95100993d28acb2f3bcf3a49846985f5b2aafab6562627d018f6d23788/ty-0.0.82-py3-none-win_arm64.whl", hash = "sha256:a6f0e8a1f961518746edbccc5986945c01a56a917ebe37117aa028a038d54ae3", size = 13567713, upload-time = "2026-09-17T19:42:10.248Z" },
1041
1041
  ]
1042
1042
 
1043
1043
  [[package]]
@@ -1,61 +0,0 @@
1
- name: Publish to PyPI
2
-
3
- on:
4
- # Auto-trigger after successful Release workflow
5
- workflow_run:
6
- workflows: ["Release"]
7
- types: [completed]
8
- # Manual fallback
9
- workflow_dispatch:
10
- inputs:
11
- release_tag:
12
- description: 'Release tag to publish (e.g., v0.1.0)'
13
- required: true
14
- type: string
15
-
16
- permissions:
17
- contents: read
18
- id-token: write
19
-
20
- jobs:
21
- publish-pypi:
22
- name: Publish to Production PyPI
23
- runs-on: ubuntu-latest
24
- environment: pypi
25
- # Only run if: workflow_run succeeded, OR manual dispatch
26
- if: >
27
- (github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success') ||
28
- github.event_name == 'workflow_dispatch'
29
-
30
- steps:
31
- - name: Download artifacts (from triggering workflow_run)
32
- if: github.event_name == 'workflow_run'
33
- uses: dawidd6/action-download-artifact@v24
34
- with:
35
- workflow: release.yml
36
- run_id: ${{ github.event.workflow_run.id }}
37
- name: ^(wheels-.*|sdist)$
38
- name_is_regexp: true
39
- path: dist/
40
- merge_multiple: true
41
- if_no_artifact_found: fail
42
-
43
- - name: Download artifacts (from manual dispatch)
44
- if: github.event_name == 'workflow_dispatch'
45
- uses: dawidd6/action-download-artifact@v24
46
- with:
47
- workflow: release.yml
48
- name: ^(wheels-.*|sdist)$
49
- name_is_regexp: true
50
- path: dist/
51
- merge_multiple: true
52
- search_artifacts: true
53
- workflow_conclusion: success
54
- branch: ${{ github.event.inputs.release_tag }}
55
- if_no_artifact_found: fail
56
-
57
- - name: List artifacts
58
- run: ls -la dist/
59
-
60
- - name: Publish to PyPI
61
- uses: pypa/gh-action-pypi-publish@release/v1