atfield 0.4.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 (136) hide show
  1. atfield-0.4.2/.github/workflows/ci.yml +78 -0
  2. atfield-0.4.2/.github/workflows/release.yml +291 -0
  3. atfield-0.4.2/.gitignore +61 -0
  4. atfield-0.4.2/CHANGELOG.md +357 -0
  5. atfield-0.4.2/LICENSE +21 -0
  6. atfield-0.4.2/LICENSE-third-party.md +57 -0
  7. atfield-0.4.2/PKG-INFO +277 -0
  8. atfield-0.4.2/README.md +235 -0
  9. atfield-0.4.2/at-field-tray/.gitignore +20 -0
  10. atfield-0.4.2/at-field-tray/README.md +66 -0
  11. atfield-0.4.2/at-field-tray/index.html +13 -0
  12. atfield-0.4.2/at-field-tray/package-lock.json +2771 -0
  13. atfield-0.4.2/at-field-tray/package.json +33 -0
  14. atfield-0.4.2/at-field-tray/public/app-icon.png +0 -0
  15. atfield-0.4.2/at-field-tray/scripts/gen_icons.py +224 -0
  16. atfield-0.4.2/at-field-tray/src/App.tsx +176 -0
  17. atfield-0.4.2/at-field-tray/src/components/AboutModal.tsx +199 -0
  18. atfield-0.4.2/at-field-tray/src/components/AdvancedRuleControls.tsx +272 -0
  19. atfield-0.4.2/at-field-tray/src/components/KillToast.tsx +117 -0
  20. atfield-0.4.2/at-field-tray/src/components/ProfilePresetRow.tsx +125 -0
  21. atfield-0.4.2/at-field-tray/src/components/RuleThresholdSlider.tsx +201 -0
  22. atfield-0.4.2/at-field-tray/src/components/Sparkline.tsx +345 -0
  23. atfield-0.4.2/at-field-tray/src/components/StatusHeader.tsx +98 -0
  24. atfield-0.4.2/at-field-tray/src/lib/api.ts +329 -0
  25. atfield-0.4.2/at-field-tray/src/lib/format.ts +601 -0
  26. atfield-0.4.2/at-field-tray/src/lib/hooks.ts +80 -0
  27. atfield-0.4.2/at-field-tray/src/lib/preferences.ts +62 -0
  28. atfield-0.4.2/at-field-tray/src/lib/signal-order.ts +69 -0
  29. atfield-0.4.2/at-field-tray/src/lib/signal-visibility.ts +107 -0
  30. atfield-0.4.2/at-field-tray/src/lib/tauri.ts +76 -0
  31. atfield-0.4.2/at-field-tray/src/lib/theme.ts +281 -0
  32. atfield-0.4.2/at-field-tray/src/main.tsx +13 -0
  33. atfield-0.4.2/at-field-tray/src/screens/EventsScreen.tsx +284 -0
  34. atfield-0.4.2/at-field-tray/src/screens/PrefsScreen.tsx +188 -0
  35. atfield-0.4.2/at-field-tray/src/screens/RulesScreen.tsx +255 -0
  36. atfield-0.4.2/at-field-tray/src/screens/SetupScreen.tsx +202 -0
  37. atfield-0.4.2/at-field-tray/src/screens/SignalDetailScreen.tsx +557 -0
  38. atfield-0.4.2/at-field-tray/src/screens/SignalsScreen.tsx +484 -0
  39. atfield-0.4.2/at-field-tray/src/screens/StatusScreen.tsx +274 -0
  40. atfield-0.4.2/at-field-tray/src/styles/globals.css +735 -0
  41. atfield-0.4.2/at-field-tray/src-tauri/Cargo.toml +43 -0
  42. atfield-0.4.2/at-field-tray/src-tauri/build.rs +3 -0
  43. atfield-0.4.2/at-field-tray/src-tauri/capabilities/default.json +19 -0
  44. atfield-0.4.2/at-field-tray/src-tauri/icons/128x128.png +0 -0
  45. atfield-0.4.2/at-field-tray/src-tauri/icons/128x128@2x.png +0 -0
  46. atfield-0.4.2/at-field-tray/src-tauri/icons/32x32.png +0 -0
  47. atfield-0.4.2/at-field-tray/src-tauri/icons/icon.ico +0 -0
  48. atfield-0.4.2/at-field-tray/src-tauri/icons/icon.png +0 -0
  49. atfield-0.4.2/at-field-tray/src-tauri/icons/tray.png +0 -0
  50. atfield-0.4.2/at-field-tray/src-tauri/installer/header.bmp +0 -0
  51. atfield-0.4.2/at-field-tray/src-tauri/installer/hooks.nsh +33 -0
  52. atfield-0.4.2/at-field-tray/src-tauri/installer/sidebar.bmp +0 -0
  53. atfield-0.4.2/at-field-tray/src-tauri/src/autostart.rs +147 -0
  54. atfield-0.4.2/at-field-tray/src-tauri/src/caption_color.rs +131 -0
  55. atfield-0.4.2/at-field-tray/src-tauri/src/lib.rs +519 -0
  56. atfield-0.4.2/at-field-tray/src-tauri/src/main.rs +6 -0
  57. atfield-0.4.2/at-field-tray/src-tauri/src/service_installer.rs +249 -0
  58. atfield-0.4.2/at-field-tray/src-tauri/tauri.conf.json +88 -0
  59. atfield-0.4.2/at-field-tray/tsconfig.json +21 -0
  60. atfield-0.4.2/at-field-tray/tsconfig.node.json +11 -0
  61. atfield-0.4.2/at-field-tray/vite.config.ts +29 -0
  62. atfield-0.4.2/brand/README.md +50 -0
  63. atfield-0.4.2/brand/logo_1024.png +0 -0
  64. atfield-0.4.2/brand/logo_1024_thick.png +0 -0
  65. atfield-0.4.2/brand/logo_16.png +0 -0
  66. atfield-0.4.2/brand/logo_28.png +0 -0
  67. atfield-0.4.2/brand/logo_32.png +0 -0
  68. atfield-0.4.2/brand/logo_48.png +0 -0
  69. atfield-0.4.2/docs/blog/awesome-list-submissions.md +55 -0
  70. atfield-0.4.2/docs/blog/launch-post.md +197 -0
  71. atfield-0.4.2/docs/faq.md +325 -0
  72. atfield-0.4.2/docs/footprint.md +170 -0
  73. atfield-0.4.2/docs/install.md +123 -0
  74. atfield-0.4.2/docs/packaging.md +192 -0
  75. atfield-0.4.2/docs/sensors.md +362 -0
  76. atfield-0.4.2/docs/tuning.md +169 -0
  77. atfield-0.4.2/helper/AtfieldSensors.cs +324 -0
  78. atfield-0.4.2/packaging/pyinstaller/atf_entry.py +40 -0
  79. atfield-0.4.2/packaging/pyinstaller/atfield.spec +173 -0
  80. atfield-0.4.2/packaging/pyinstaller/atfield_service_entry.py +36 -0
  81. atfield-0.4.2/pyproject.toml +90 -0
  82. atfield-0.4.2/scripts/bench_tick.py +128 -0
  83. atfield-0.4.2/scripts/build_helper.ps1 +41 -0
  84. atfield-0.4.2/scripts/config.example.toml +134 -0
  85. atfield-0.4.2/scripts/fetch_lhm.ps1 +118 -0
  86. atfield-0.4.2/scripts/gen_installer_images.py +94 -0
  87. atfield-0.4.2/scripts/grant_service_control.ps1 +77 -0
  88. atfield-0.4.2/scripts/install_service.ps1 +323 -0
  89. atfield-0.4.2/scripts/measure_idle.py +109 -0
  90. atfield-0.4.2/scripts/probe_lhm_lib.ps1 +88 -0
  91. atfield-0.4.2/scripts/uninstall_service.ps1 +56 -0
  92. atfield-0.4.2/scripts/vendor/nssm.exe +0 -0
  93. atfield-0.4.2/src/atfield/__init__.py +22 -0
  94. atfield-0.4.2/src/atfield/actuator.py +728 -0
  95. atfield-0.4.2/src/atfield/audit.py +221 -0
  96. atfield-0.4.2/src/atfield/cli.py +1216 -0
  97. atfield-0.4.2/src/atfield/collectors/__init__.py +195 -0
  98. atfield-0.4.2/src/atfield/collectors/amd.py +269 -0
  99. atfield-0.4.2/src/atfield/collectors/lhm.py +394 -0
  100. atfield-0.4.2/src/atfield/collectors/lhmlib.py +473 -0
  101. atfield-0.4.2/src/atfield/collectors/nvml.py +336 -0
  102. atfield-0.4.2/src/atfield/collectors/system.py +176 -0
  103. atfield-0.4.2/src/atfield/config.py +613 -0
  104. atfield-0.4.2/src/atfield/config_writer.py +393 -0
  105. atfield-0.4.2/src/atfield/forensics.py +331 -0
  106. atfield-0.4.2/src/atfield/http_api.py +1120 -0
  107. atfield-0.4.2/src/atfield/lhm_config.py +281 -0
  108. atfield-0.4.2/src/atfield/lhm_supervisor.py +734 -0
  109. atfield-0.4.2/src/atfield/policy.py +340 -0
  110. atfield-0.4.2/src/atfield/prometheus_exporter.py +234 -0
  111. atfield-0.4.2/src/atfield/rule_profiles.py +150 -0
  112. atfield-0.4.2/src/atfield/service.py +657 -0
  113. atfield-0.4.2/src/atfield/signals.py +356 -0
  114. atfield-0.4.2/tests/__init__.py +0 -0
  115. atfield-0.4.2/tests/conftest.py +41 -0
  116. atfield-0.4.2/tests/test_actuator.py +577 -0
  117. atfield-0.4.2/tests/test_amd_collector.py +168 -0
  118. atfield-0.4.2/tests/test_audit.py +215 -0
  119. atfield-0.4.2/tests/test_cli_doctor_setprofile.py +209 -0
  120. atfield-0.4.2/tests/test_cli_install.py +140 -0
  121. atfield-0.4.2/tests/test_cli_install_lhm.py +156 -0
  122. atfield-0.4.2/tests/test_collectors.py +241 -0
  123. atfield-0.4.2/tests/test_config.py +341 -0
  124. atfield-0.4.2/tests/test_config_writer.py +296 -0
  125. atfield-0.4.2/tests/test_forensics.py +248 -0
  126. atfield-0.4.2/tests/test_http_api.py +843 -0
  127. atfield-0.4.2/tests/test_lhm_config.py +189 -0
  128. atfield-0.4.2/tests/test_lhm_supervisor.py +621 -0
  129. atfield-0.4.2/tests/test_lhmlib.py +142 -0
  130. atfield-0.4.2/tests/test_multires_history.py +231 -0
  131. atfield-0.4.2/tests/test_policy.py +274 -0
  132. atfield-0.4.2/tests/test_prometheus_exporter.py +223 -0
  133. atfield-0.4.2/tests/test_rule_profiles.py +79 -0
  134. atfield-0.4.2/tests/test_signals.py +261 -0
  135. atfield-0.4.2/vendor/lhm/.gitignore +8 -0
  136. atfield-0.4.2/vendor/lhm/README.md +70 -0
@@ -0,0 +1,78 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ jobs:
13
+ test:
14
+ name: Test (${{ matrix.os }} / Python ${{ matrix.python-version }})
15
+ runs-on: ${{ matrix.os }}
16
+ strategy:
17
+ fail-fast: false
18
+ matrix:
19
+ # Production target is Windows; cross-platform tests guard with skipif
20
+ # so the suite is green on Linux too. Two OS rows ensures we don't
21
+ # accidentally introduce a Windows-only or Linux-only regression.
22
+ os: [windows-latest, ubuntu-latest]
23
+ python-version: ['3.10', '3.11', '3.12']
24
+ steps:
25
+ - uses: actions/checkout@v4
26
+
27
+ - name: Set up Python ${{ matrix.python-version }}
28
+ uses: actions/setup-python@v5
29
+ with:
30
+ python-version: ${{ matrix.python-version }}
31
+ cache: pip
32
+ cache-dependency-path: pyproject.toml
33
+
34
+ - name: Install package + dev deps
35
+ run: |
36
+ python -m pip install --upgrade pip
37
+ python -m pip install -e ".[dev]"
38
+
39
+ - name: Lint with ruff
40
+ run: ruff check src/ tests/
41
+
42
+ - name: Run tests
43
+ run: pytest tests/ -v --tb=short
44
+
45
+ smoke:
46
+ # Fast end-to-end sanity check: build the wheel, install it into a fresh
47
+ # venv, run `atf inputs` (which exercises every collector probe) and `atf
48
+ # version`. Catches packaging regressions that pure pytest can miss.
49
+ name: Wheel + CLI smoke
50
+ runs-on: windows-latest
51
+ needs: test
52
+ steps:
53
+ - uses: actions/checkout@v4
54
+ - uses: actions/setup-python@v5
55
+ with:
56
+ python-version: '3.12'
57
+
58
+ - name: Build wheel
59
+ run: |
60
+ python -m pip install --upgrade pip build
61
+ python -m build --wheel
62
+
63
+ - name: Install built wheel into a fresh venv
64
+ run: |
65
+ python -m venv ci-smoke-venv
66
+ ci-smoke-venv\Scripts\python.exe -m pip install --upgrade pip
67
+ $wheel = (Get-ChildItem -Path dist -Filter atfield-*.whl | Select-Object -First 1).FullName
68
+ ci-smoke-venv\Scripts\python.exe -m pip install $wheel
69
+ shell: pwsh
70
+
71
+ - name: atf version
72
+ run: ci-smoke-venv\Scripts\atf.exe version
73
+
74
+ - name: atf inputs (probes every collector)
75
+ # CI has no NVIDIA GPU and no LHM, so this is the
76
+ # capability-negotiation-on-bare-metal scenario. Should report system
77
+ # collector OK, NVML unavailable, LHM unavailable -- and exit 0.
78
+ run: ci-smoke-venv\Scripts\atf.exe inputs
@@ -0,0 +1,291 @@
1
+ name: Release
2
+
3
+ # Triggered on annotated tag pushes that match v* (semver tags).
4
+ # Builds the Python wheel + the standalone PyInstaller bundle + the
5
+ # Tauri tray app installer, then attaches everything to a GitHub
6
+ # Release. Manual runs are also allowed via workflow_dispatch so you
7
+ # can produce a draft release without tagging first.
8
+ on:
9
+ push:
10
+ tags:
11
+ - "v*"
12
+ workflow_dispatch:
13
+ inputs:
14
+ tag:
15
+ description: "Release tag to publish (e.g. v0.2.0). Leave blank to draft from current main."
16
+ required: false
17
+ default: ""
18
+
19
+ permissions:
20
+ # Need write access to publish a release + upload assets. The
21
+ # built-in GITHUB_TOKEN is used; no PAT.
22
+ contents: write
23
+
24
+ # Avoid two concurrent release runs (e.g. tag push + manual dispatch
25
+ # at the same time) racing for the same release upload.
26
+ concurrency:
27
+ group: release-${{ github.ref }}
28
+ cancel-in-progress: false
29
+
30
+ jobs:
31
+ # ─────────────────────────────────────────────────────────────────
32
+ # 1. Pure-Python wheel (cross-platform, no GPU deps in the build).
33
+ # ─────────────────────────────────────────────────────────────────
34
+ wheel:
35
+ name: Build wheel + sdist
36
+ runs-on: ubuntu-latest
37
+ steps:
38
+ - uses: actions/checkout@v4
39
+
40
+ - uses: actions/setup-python@v5
41
+ with:
42
+ python-version: "3.12"
43
+ cache: pip
44
+ cache-dependency-path: pyproject.toml
45
+
46
+ - name: Build wheel + sdist
47
+ run: |
48
+ python -m pip install --upgrade pip build
49
+ python -m build
50
+
51
+ - name: Upload artifacts
52
+ uses: actions/upload-artifact@v4
53
+ with:
54
+ name: python-dists
55
+ path: dist/*
56
+ retention-days: 7
57
+
58
+ # ─────────────────────────────────────────────────────────────────
59
+ # 1b. Publish the wheel + sdist to PyPI via Trusted Publishing (OIDC).
60
+ #
61
+ # No API token/secret: PyPI verifies the GitHub OIDC identity. This
62
+ # requires a one-time "pending publisher" set up on PyPI pointing at
63
+ # this repo + workflow + the `pypi` environment (see docs/packaging.md).
64
+ # Runs only for real (non-prerelease) version tags. A failure here does
65
+ # NOT block the GitHub Release job below -- they're independent.
66
+ # ─────────────────────────────────────────────────────────────────
67
+ publish-pypi:
68
+ name: Publish to PyPI
69
+ needs: wheel
70
+ runs-on: ubuntu-latest
71
+ if: startsWith(github.ref, 'refs/tags/v') && !contains(github.ref_name, '-')
72
+ environment:
73
+ name: pypi
74
+ url: https://pypi.org/project/atfield/
75
+ permissions:
76
+ id-token: write
77
+ steps:
78
+ - name: Download wheel + sdist
79
+ uses: actions/download-artifact@v4
80
+ with:
81
+ name: python-dists
82
+ path: dist
83
+
84
+ - name: Publish to PyPI
85
+ uses: pypa/gh-action-pypi-publish@release/v1
86
+
87
+ # ─────────────────────────────────────────────────────────────────
88
+ # 2. Standalone PyInstaller bundle (Windows-only).
89
+ # Produces dist/atfield/ with atfield-service.exe + atf.exe and
90
+ # a shared _internal/. Zips it for the GitHub release.
91
+ # ─────────────────────────────────────────────────────────────────
92
+ standalone:
93
+ name: Standalone Windows bundle
94
+ runs-on: windows-latest
95
+ steps:
96
+ - uses: actions/checkout@v4
97
+
98
+ - uses: actions/setup-python@v5
99
+ with:
100
+ python-version: "3.12"
101
+ cache: pip
102
+ cache-dependency-path: pyproject.toml
103
+
104
+ - name: Install package + PyInstaller
105
+ run: |
106
+ python -m pip install --upgrade pip
107
+ python -m pip install -e ".[dev]"
108
+ python -m pip install "pyinstaller>=6.20"
109
+
110
+ - name: Build standalone bundle
111
+ run: pyinstaller --noconfirm --clean packaging/pyinstaller/atfield.spec
112
+
113
+ # Vendor LHM into the bundle so the watchdog ships VRAM-junction
114
+ # and CPU-package temp support out of the box. fetch_lhm.ps1 drops
115
+ # LibreHardwareMonitor.exe + its DLLs into dist/atfield/ where
116
+ # find_lhm_executable() looks first (next to atfield-service.exe).
117
+ - name: Vendor LibreHardwareMonitor
118
+ shell: pwsh
119
+ run: pwsh scripts/fetch_lhm.ps1
120
+
121
+ # Compile the headless sensor helper (atfield-sensors.exe) against the
122
+ # LibreHardwareMonitorLib.dll just vendored. Uses the in-box .NET
123
+ # Framework csc.exe (present on windows-latest), so no SDK install.
124
+ # The tray installer's resources manifest REQUIRES this exe, so the
125
+ # tray-installer job would fail without it.
126
+ - name: Build headless sensor helper
127
+ shell: pwsh
128
+ run: pwsh scripts/build_helper.ps1
129
+
130
+ - name: Smoke-test bundled binaries
131
+ # Make sure the frozen exes at least respond to --help before we
132
+ # ship them. Catches missing hidden imports / data files, and that
133
+ # the sensor helper actually built.
134
+ shell: pwsh
135
+ run: |
136
+ dist/atfield/atf.exe version
137
+ dist/atfield/atf.exe inputs
138
+ if (-not (Test-Path dist/atfield/atfield-sensors.exe)) {
139
+ throw "atfield-sensors.exe missing -- build_helper.ps1 did not produce the sensor helper."
140
+ }
141
+
142
+ - name: Zip standalone bundle
143
+ shell: pwsh
144
+ run: |
145
+ $version = "${{ github.ref_name }}".TrimStart('v')
146
+ if (-not $version) { $version = "dev" }
147
+ Compress-Archive -Path dist/atfield/* -DestinationPath "dist/atfield-$version-windows-x64.zip"
148
+
149
+ - name: Upload standalone bundle
150
+ uses: actions/upload-artifact@v4
151
+ with:
152
+ name: standalone-bundle
153
+ path: dist/atfield-*-windows-x64.zip
154
+ retention-days: 7
155
+
156
+ # ─────────────────────────────────────────────────────────────────
157
+ # 3. Tauri tray app installer (Windows NSIS).
158
+ #
159
+ # Depends on `standalone` so we can stage dist/atfield/ -- the Tauri
160
+ # config references it as bundle.resources, producing a single
161
+ # installer that ships BOTH the tray app AND the watchdog binaries.
162
+ # ─────────────────────────────────────────────────────────────────
163
+ tray-installer:
164
+ name: Tauri tray installer
165
+ runs-on: windows-latest
166
+ needs: standalone
167
+ steps:
168
+ - uses: actions/checkout@v4
169
+
170
+ - uses: actions/setup-node@v4
171
+ with:
172
+ node-version: "20"
173
+ cache: "npm"
174
+ cache-dependency-path: at-field-tray/package-lock.json
175
+
176
+ - uses: dtolnay/rust-toolchain@stable
177
+ with:
178
+ targets: x86_64-pc-windows-msvc
179
+
180
+ - uses: Swatinem/rust-cache@v2
181
+ with:
182
+ workspaces: "at-field-tray/src-tauri -> target"
183
+
184
+ # Re-stage the standalone bundle artifact at dist/atfield/ so
185
+ # bundle.resources in tauri.conf.json resolves correctly.
186
+ - name: Download standalone bundle
187
+ uses: actions/download-artifact@v4
188
+ with:
189
+ name: standalone-bundle
190
+ path: standalone-zip
191
+
192
+ - name: Stage standalone at dist/atfield/
193
+ shell: pwsh
194
+ run: |
195
+ $zip = Get-ChildItem standalone-zip -Filter atfield-*-windows-x64.zip |
196
+ Select-Object -First 1
197
+ if (-not $zip) { throw "Standalone bundle artifact missing." }
198
+ New-Item -ItemType Directory -Path dist/atfield -Force | Out-Null
199
+ Expand-Archive -Path $zip.FullName -DestinationPath dist/atfield -Force
200
+ Get-ChildItem dist/atfield | Format-Table -AutoSize
201
+
202
+ - name: Install npm deps
203
+ working-directory: at-field-tray
204
+ run: npm ci
205
+
206
+ - name: Build Tauri NSIS installer
207
+ working-directory: at-field-tray
208
+ run: npm run tauri build -- --bundles nsis
209
+
210
+ - name: Locate installer
211
+ id: locate
212
+ shell: pwsh
213
+ run: |
214
+ $exe = Get-ChildItem -Path at-field-tray/src-tauri/target/release/bundle/nsis -Filter *-setup.exe |
215
+ Select-Object -First 1
216
+ if (-not $exe) { throw "No NSIS installer produced." }
217
+ "exe=$($exe.FullName)" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
218
+
219
+ - name: Upload tray installer
220
+ uses: actions/upload-artifact@v4
221
+ with:
222
+ name: tray-installer
223
+ path: ${{ steps.locate.outputs.exe }}
224
+ retention-days: 7
225
+
226
+ # ─────────────────────────────────────────────────────────────────
227
+ # 4. Publish: gather every artifact and create / update the release.
228
+ # ─────────────────────────────────────────────────────────────────
229
+ publish:
230
+ name: Publish GitHub Release
231
+ needs: [wheel, standalone, tray-installer]
232
+ runs-on: ubuntu-latest
233
+ # Only publish for real on actual tag pushes. workflow_dispatch
234
+ # runs upload artifacts but skip the release step (lets you
235
+ # validate the build without making noise).
236
+ if: startsWith(github.ref, 'refs/tags/v')
237
+ steps:
238
+ - uses: actions/checkout@v4
239
+ # Need full history so the release notes generator can walk
240
+ # since the previous tag.
241
+ with:
242
+ fetch-depth: 0
243
+
244
+ - name: Download all artifacts
245
+ uses: actions/download-artifact@v4
246
+ with:
247
+ path: release-assets
248
+ merge-multiple: true
249
+
250
+ - name: List collected assets
251
+ run: ls -lh release-assets/
252
+
253
+ - name: Extract changelog section for this version
254
+ id: changelog
255
+ shell: bash
256
+ run: |
257
+ set -euo pipefail
258
+ version_tag="${GITHUB_REF_NAME}" # e.g. v0.2.0
259
+ version_no_v="${version_tag#v}" # e.g. 0.2.0
260
+ # Pull the section between `## [<version>]` and the next `## `.
261
+ # Falls back to a generic message if no matching section.
262
+ notes=$(awk -v ver="$version_no_v" '
263
+ BEGIN { inside=0 }
264
+ /^## \[/ {
265
+ if (inside) exit
266
+ if ($0 ~ "\\[" ver "\\]") { inside=1; next }
267
+ }
268
+ inside { print }
269
+ ' CHANGELOG.md || true)
270
+ if [ -z "$notes" ]; then
271
+ notes="See CHANGELOG.md for details."
272
+ fi
273
+ # Multi-line outputs go through GITHUB_OUTPUT with a delimiter.
274
+ delim=$(openssl rand -hex 8)
275
+ {
276
+ printf 'notes<<%s\n' "$delim"
277
+ printf '%s\n' "$notes"
278
+ printf '%s\n' "$delim"
279
+ } >> "$GITHUB_OUTPUT"
280
+
281
+ - name: Create / update GitHub Release
282
+ uses: softprops/action-gh-release@v2
283
+ with:
284
+ tag_name: ${{ github.ref_name }}
285
+ name: AT-Field ${{ github.ref_name }}
286
+ body: ${{ steps.changelog.outputs.notes }}
287
+ files: release-assets/*
288
+ draft: false
289
+ prerelease: ${{ contains(github.ref_name, '-') }}
290
+ env:
291
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -0,0 +1,61 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ .Python
7
+ build/
8
+ dist/
9
+ *.egg-info/
10
+ *.egg
11
+ .eggs/
12
+ .pytest_cache/
13
+ .mypy_cache/
14
+ .ruff_cache/
15
+ .tox/
16
+ .coverage
17
+ htmlcov/
18
+
19
+ # Virtual environments
20
+ .venv/
21
+ venv/
22
+ env/
23
+ ENV/
24
+
25
+ # IDE
26
+ .vscode/
27
+ .idea/
28
+ *.swp
29
+ *.swo
30
+ .cursor/
31
+
32
+ # OS
33
+ .DS_Store
34
+ Thumbs.db
35
+ desktop.ini
36
+
37
+ # AT-Field local artifacts
38
+ local_state/
39
+ *.log
40
+ events.jsonl
41
+ nssm.exe
42
+ # ...but the vendored NSSM that ships in the installer IS tracked on purpose
43
+ # (install_service.ps1 copies it so setup never depends on the flaky nssm.cc).
44
+ !scripts/vendor/nssm.exe
45
+ resources/LibreHardwareMonitor/
46
+
47
+ # PyInstaller -- generated specs only. Keep our authored spec under packaging/.
48
+ /*.spec
49
+ /build/
50
+ /dist/
51
+
52
+ # Planning / handoff docs — kept local, not in git history
53
+ /PLANNING.md
54
+ /docs/chat-history.md
55
+ /docs/planning/
56
+
57
+ # Ad-hoc diagnostic scripts + reports (crash investigations, font A/Bs, etc.)
58
+ /diag-*.ps1
59
+ /diag-*-report.txt
60
+ /font-comparison.html
61
+ /nul