youpdated 0.2.0__tar.gz → 0.3.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.
- {youpdated-0.2.0 → youpdated-0.3.0}/ADDING_SOURCES.md +21 -0
- youpdated-0.3.0/CHANGELOG.md +244 -0
- {youpdated-0.2.0 → youpdated-0.3.0}/PKG-INFO +119 -17
- {youpdated-0.2.0 → youpdated-0.3.0}/README.md +118 -16
- {youpdated-0.2.0 → youpdated-0.3.0}/pyproject.toml +1 -1
- {youpdated-0.2.0 → youpdated-0.3.0}/youpdated/__init__.py +1 -1
- {youpdated-0.2.0 → youpdated-0.3.0}/youpdated/cli.py +118 -10
- {youpdated-0.2.0 → youpdated-0.3.0}/youpdated/config.py +107 -1
- {youpdated-0.2.0 → youpdated-0.3.0}/youpdated/http.py +132 -19
- {youpdated-0.2.0 → youpdated-0.3.0}/youpdated/models.py +11 -0
- {youpdated-0.2.0 → youpdated-0.3.0}/youpdated/render/json_out.py +3 -0
- {youpdated-0.2.0 → youpdated-0.3.0}/youpdated/render/terminal.py +28 -3
- youpdated-0.3.0/youpdated/runner.py +192 -0
- {youpdated-0.2.0 → youpdated-0.3.0}/youpdated/sources/feed.py +38 -4
- {youpdated-0.2.0 → youpdated-0.3.0}/youpdated/sources/generic.py +8 -8
- {youpdated-0.2.0 → youpdated-0.3.0}/youpdated/sources/github.py +23 -1
- {youpdated-0.2.0 → youpdated-0.3.0}/youpdated/sources/itch.py +4 -4
- {youpdated-0.2.0 → youpdated-0.3.0}/youpdated/sources/npm.py +3 -0
- {youpdated-0.2.0 → youpdated-0.3.0}/youpdated/sources/youtube.py +10 -5
- {youpdated-0.2.0 → youpdated-0.3.0}/youpdated/state.py +119 -5
- {youpdated-0.2.0 → youpdated-0.3.0}/youpdated.egg-info/PKG-INFO +119 -17
- youpdated-0.2.0/CHANGELOG.md +0 -111
- youpdated-0.2.0/youpdated/runner.py +0 -134
- {youpdated-0.2.0 → youpdated-0.3.0}/CONTRIBUTING.md +0 -0
- {youpdated-0.2.0 → youpdated-0.3.0}/LICENSE +0 -0
- {youpdated-0.2.0 → youpdated-0.3.0}/MANIFEST.in +0 -0
- {youpdated-0.2.0 → youpdated-0.3.0}/SECURITY.md +0 -0
- {youpdated-0.2.0 → youpdated-0.3.0}/setup.cfg +0 -0
- {youpdated-0.2.0 → youpdated-0.3.0}/youpdated/__main__.py +0 -0
- {youpdated-0.2.0 → youpdated-0.3.0}/youpdated/cleanup.py +0 -0
- {youpdated-0.2.0 → youpdated-0.3.0}/youpdated/crypto.py +0 -0
- {youpdated-0.2.0 → youpdated-0.3.0}/youpdated/py.typed +0 -0
- {youpdated-0.2.0 → youpdated-0.3.0}/youpdated/registry.py +0 -0
- {youpdated-0.2.0 → youpdated-0.3.0}/youpdated/render/__init__.py +0 -0
- {youpdated-0.2.0 → youpdated-0.3.0}/youpdated/render/rss_out.py +0 -0
- {youpdated-0.2.0 → youpdated-0.3.0}/youpdated/sources/__init__.py +0 -0
- {youpdated-0.2.0 → youpdated-0.3.0}/youpdated/sources/base.py +0 -0
- {youpdated-0.2.0 → youpdated-0.3.0}/youpdated/sources/browser.py +0 -0
- {youpdated-0.2.0 → youpdated-0.3.0}/youpdated/sources/steam.py +0 -0
- {youpdated-0.2.0 → youpdated-0.3.0}/youpdated.egg-info/SOURCES.txt +0 -0
- {youpdated-0.2.0 → youpdated-0.3.0}/youpdated.egg-info/dependency_links.txt +0 -0
- {youpdated-0.2.0 → youpdated-0.3.0}/youpdated.egg-info/entry_points.txt +0 -0
- {youpdated-0.2.0 → youpdated-0.3.0}/youpdated.egg-info/requires.txt +0 -0
- {youpdated-0.2.0 → youpdated-0.3.0}/youpdated.egg-info/top_level.txt +0 -0
|
@@ -216,6 +216,26 @@ uid=str(index) # broken: shifts as items are added
|
|
|
216
216
|
|
|
217
217
|
When an upstream gives you no id at all, fingerprint the content: hash the fields that define the item. [itch.py](youpdated/sources/itch.py) does this for game builds: filenames, sizes, and the update timestamp hash into one uid.
|
|
218
218
|
|
|
219
|
+
### Tags are what users filter on
|
|
220
|
+
|
|
221
|
+
`tags`: a user's `ignore:` rules match against them, so a type
|
|
222
|
+
untagged is not able to be hidden. Tag each update with what it *is*,
|
|
223
|
+
using the vocabulary already in use where it fits: `release`, `prerelease`,
|
|
224
|
+
`tag`, `commit`, `latest`, `devlog`, `news`, `video`, `item`, and add your own
|
|
225
|
+
where needed.
|
|
226
|
+
|
|
227
|
+
```python
|
|
228
|
+
tags=("release",) + (("prerelease",) if item["draft_or_rc"] else ())
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
Tags are additive, and an update is dropped if **any** of its tags is ignored.
|
|
232
|
+
So describing an item from several angles at once is the point: tagging a
|
|
233
|
+
release candidate `("release", "prerelease")` lets one user ignore every
|
|
234
|
+
release and another ignore only the candidates.
|
|
235
|
+
|
|
236
|
+
You do not need to handle `ignore` yourself. It is lifted off the config entry
|
|
237
|
+
before `targets()` ever sees it, and applied to whatever you return.
|
|
238
|
+
|
|
219
239
|
### Filling in a label during fetch
|
|
220
240
|
|
|
221
241
|
Sometimes the friendly name is only available from the response. Assign it to `target.label`; the renderers pick it up:
|
|
@@ -329,5 +349,6 @@ Users then configure it like any built-in source. A plugin that fails to import
|
|
|
329
349
|
- [ ] Expected non-200s handled with `soft_statuses`; real failures left to raise
|
|
330
350
|
- [ ] Returns `[]` rather than inventing an update
|
|
331
351
|
- [ ] `published` is timezone-aware UTC (or `None`)
|
|
352
|
+
- [ ] `tags` describe the update type, so `ignore:` rules can match them
|
|
332
353
|
- [ ] Tests cover parsing, both config shapes, and uid stability
|
|
333
354
|
- [ ] Registered — `@register` plus an import, or an entry point
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to
|
|
5
|
+
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
## [0.3.0] — 2026-10-07
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **`ignore:` rules drop update types** (#11). Every update already
|
|
12
|
+
carried tags describing what it is; those are now filterable. A bare list
|
|
13
|
+
applies everywhere, a mapping narrows it to one source (`'*'` for all), and a per-entry
|
|
14
|
+
`ignore:` adds to whichever applies:
|
|
15
|
+
|
|
16
|
+
```yaml
|
|
17
|
+
ignore:
|
|
18
|
+
'*': [prerelease]
|
|
19
|
+
github: [commit]
|
|
20
|
+
|
|
21
|
+
sources:
|
|
22
|
+
github:
|
|
23
|
+
- repo: astral-sh/uv
|
|
24
|
+
ignore: [tag] # global rules + `tag`
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
An update is dropped if it carries any listed tag. Ignored items are not
|
|
28
|
+
recorded as seen, so removing a rule later shows what it was hiding.
|
|
29
|
+
The run summary and `--json` both count what was dropped.
|
|
30
|
+
See [Ignoring update types](README.md#ignoring-update-types) for the tag vocabulary per source.
|
|
31
|
+
|
|
32
|
+
- **GitHub prereleases are tagged without a token.** `prerelease` previously came only from the
|
|
33
|
+
REST API, which needs `GITHUB_TOKEN`, so on the anonymous `.atom` path nothing was tagged.
|
|
34
|
+
It is now inferred from the tag name (`v3.15.0rc2`, `1.2.0-beta.1`, `0.12.0-alpha`), and a
|
|
35
|
+
token takes precedence. Platform and build suffixes (`v1.0.0-linux`, `v4.2.0+build.7`)
|
|
36
|
+
are untouched.
|
|
37
|
+
|
|
38
|
+
- **npm prereleases are tagged.** Any semver version with a prerelease suffix now carries
|
|
39
|
+
`prerelease`. Build metadata (`+build-7`) does not count, even with a hyphen in it.
|
|
40
|
+
|
|
41
|
+
- **Runs report progress instead of going quiet.** A check printed nothing until the last target
|
|
42
|
+
landed, so a slow source was indistinguishable from a hang. Interactive runs now show a live
|
|
43
|
+
line naming the targets still in flight, with a count and elapsed time. It is suppressed under
|
|
44
|
+
`-v`, `--test`, and when output is not a terminal.
|
|
45
|
+
|
|
46
|
+
- **A desktop example app** (#13), in `examples/desktop/`. A simple window checks on a schedule,
|
|
47
|
+
sends a notification, and lists what changed.
|
|
48
|
+
Uses standard library and youpdated. It reads the same config and
|
|
49
|
+
history as the CLI, and prompts for a passphrase when either is encrypted.
|
|
50
|
+
Its own CI covers Linux, macOS, and Windows, and runs only when `examples/` changes
|
|
51
|
+
|
|
52
|
+
### Changed
|
|
53
|
+
|
|
54
|
+
- **Install docs now with `pipx install youpdated`.** The package has been on PyPI since 0.1.0,
|
|
55
|
+
but the README still told people to clone the repo and install from the working tree.
|
|
56
|
+
Installing from source is now a subsection.
|
|
57
|
+
The scheduling examples point at a `pipx` path rather than a repo virtualenv.
|
|
58
|
+
- **itch builds are tagged `build`, and page-only changes `page`,** instead of both being
|
|
59
|
+
`release`. The tags say what changed, so `ignore:` can tell them apart. Anything filtering
|
|
60
|
+
`--json` or `--rss` output on `release` for itch should use `build`.
|
|
61
|
+
|
|
62
|
+
### Fixed
|
|
63
|
+
|
|
64
|
+
- **A host's pacing lock is no longer held across its own wait.** `_pace` slept while holding the
|
|
65
|
+
per-host lock, so every other thread bound for that host blocked in `acquire` for the whole
|
|
66
|
+
gap, with no way to be interrupted, and a thread waiting its turn could be scheduled
|
|
67
|
+
ahead of one ready to do real work. Each caller now reserves a slot under the lock and waits
|
|
68
|
+
outside it. The spacing between requests to one host is unchanged.
|
|
69
|
+
- **Progress bar moved around** (#14). The target names led the line, so the bar
|
|
70
|
+
and counter shifted sideways every time they changed, and a long name squeezed the bar out.
|
|
71
|
+
The spinner, bar, count, elapsed time and failure count now come first at a fixed
|
|
72
|
+
width, and the names fill what is left of the line, truncated with an ellipsis. Names are
|
|
73
|
+
shown literally, so one containing brackets (`[/x]`) no longer crashes the bar.
|
|
74
|
+
- **Rate-limit retries honor `Retry-After`** (#17). A 429 or 503 was retried on a fixed
|
|
75
|
+
rate ignoring any potential 'Retry-After' sent. The wait now follows the header (seconds or an HTTP-date)
|
|
76
|
+
and holds the whole host, so other targets on that site wait too.
|
|
77
|
+
A server asking for more than 60s fails that request at once instead of stalling the run.
|
|
78
|
+
Without the header, the fixed backoff applies as before.
|
|
79
|
+
- **The history database is pruned** (#18). Seen items were added, so the
|
|
80
|
+
database grew, including for targets removed from the config, and with
|
|
81
|
+
encryption, the whole thing is decrypted and re-encrypted each run. An item missing from
|
|
82
|
+
every fetch for longer than the new top-level `expiry:` (default `1y`, `never` to keep
|
|
83
|
+
everything) is now forgotten, and the file is compacted. Items still listed by their source are
|
|
84
|
+
never pruned, and stored validators older than half the expiry are skipped once to
|
|
85
|
+
force a full fetch. Targets that failed or were left out with `--source` keep their history.
|
|
86
|
+
A removed target's baseline expires with its history, so re-adding it later records a new
|
|
87
|
+
baseline instead of reporting everything it has.
|
|
88
|
+
`--json` counts what was pruned, and `--since` now also accepts years (`1y`).
|
|
89
|
+
- **A target added later no longer dumps a backlog.** The first run baseline applied to the
|
|
90
|
+
whole state, so after it, a newly added target reported every item it had as new. Same
|
|
91
|
+
for a target that failed the first run, or `-s`. Baselines are now
|
|
92
|
+
recorded per target on its first successful fetch, and the run names those targets. State
|
|
93
|
+
from earlier versions is read as already baselined, so nothing is swallowed on upgrade.
|
|
94
|
+
A target's first fetch sends no stored validators, so a new channel on a document another
|
|
95
|
+
target already watches (Brave, Firefox, Edge) records a real baseline instead of a 304.
|
|
96
|
+
- **`--test` lines read `[test] GET`**, not `[test]] GET`.
|
|
97
|
+
|
|
98
|
+
## [0.2.1] — 2026-08-25
|
|
99
|
+
|
|
100
|
+
### Fixed
|
|
101
|
+
|
|
102
|
+
- **The release workflow's `attach` job failed on v0.2.0 and has been removed.** It copied the
|
|
103
|
+
built artifacts onto the GitHub Release, but this repo has immutable releases enabled, which
|
|
104
|
+
freezes assets when the release is published — and the job runs after that, so it could only ever
|
|
105
|
+
fail (`HTTP 422: Cannot upload assets to an immutable release`). Publishing to PyPI was
|
|
106
|
+
unaffected; 0.2.0 shipped correctly. Releases from now on will not carry attached `.whl`/`.tar.gz`
|
|
107
|
+
files. Get them from PyPI, where they are covered by a signed PEP 740 attestation binding each
|
|
108
|
+
digest to this repository and workflow — a stronger guarantee than the copy the job was making.
|
|
109
|
+
|
|
110
|
+
- **Targets sharing one upstream document lost their updates**
|
|
111
|
+
Every Brave channel is served by one GitHub releases document, and every Firefox channel by one
|
|
112
|
+
Mozilla JSON. Each channel fetched it separately, so the first stored an ETag and the rest were
|
|
113
|
+
answered `304 Not Modified` against that ETag moments later and reported *nothing*. With the
|
|
114
|
+
default `jitter`, per-host pacing serializes those requests, which is the case that loses: a
|
|
115
|
+
config watching five Firefox channels and three Brave channels reported 2 of 8. Under
|
|
116
|
+
concurrency it was non-deterministic: is a channel reported depended on thread timing, so a
|
|
117
|
+
loaded machine changed the result.
|
|
118
|
+
|
|
119
|
+
A run now reuses each document across the targets that share it (`Client.run_scope()`), so the
|
|
120
|
+
document is fetched once and every target sees it. The same config went from 8 requests
|
|
121
|
+
reporting 2 of 8 targets, to 2 requests reporting 8 of 8. (And 147 ms to 43 ms of wall time)
|
|
122
|
+
Outside a run the client is unchanged: one GET per call, and a 304 still means "nothing new".
|
|
123
|
+
|
|
124
|
+
- **The YouTube official-feed path never set the channel label.** `_label_and_parse` did not do
|
|
125
|
+
the labelling it was supposed to, so a channel that answered on the primary path was reported by
|
|
126
|
+
its raw `@handle` while the Invidious and Data API fallbacks named it properly. The label now
|
|
127
|
+
comes from the feed on all three paths.
|
|
128
|
+
|
|
129
|
+
- **`feed` sources parsed every feed twice**
|
|
130
|
+
A `feed:` entry given as a bare URL had no label, so `FeedSource.fetch` parsed the document once
|
|
131
|
+
to read the feed's own `<title>` and then handed the same bytes to `parse_feed`, which parsed
|
|
132
|
+
them again. Feed parsing is the most expensive part of a run, so this ~doubled the
|
|
133
|
+
CPU cost of every bare-URL feed. One parse now feeds both the label and the entries.
|
|
134
|
+
|
|
135
|
+
`parse_feed()` is unchanged for callers that only need entries; it is now a wrapper over the new
|
|
136
|
+
`parse_document()` / `parse_entries()` split in `youpdated.sources.feed`.
|
|
137
|
+
|
|
138
|
+
## [0.2.0] — 2026-08-19
|
|
139
|
+
|
|
140
|
+
### Added
|
|
141
|
+
|
|
142
|
+
- **Encryption at rest for the config and history** ([#5](https://github.com/Void1-1/youpdated/issues/5)).
|
|
143
|
+
`youpdated encrypt` (alias `set-encrypted`) converts an existing setup in place; `youpdated decrypt`
|
|
144
|
+
converts it back. Every command detects an encrypted setup on its own and asks for the passphrase
|
|
145
|
+
once, or reads it from `YOUPDATED_PASSPHRASE` for unattended runs.
|
|
146
|
+
|
|
147
|
+
Files are encrypted whole with AES-256-GCM under a scrypt-derived key (n=2¹⁶, r=8, p=1), with the
|
|
148
|
+
KDF parameters authenticated as additional data so they cannot be downgraded. Decryption happens
|
|
149
|
+
**in memory**: the config is parsed from a decrypted buffer and the SQLite database is
|
|
150
|
+
deserialized into an in-memory database and written back encrypted when the run ends, so no
|
|
151
|
+
plaintext copy is put on disk even mid-run. A read-only run leaves the file byte-for-byte alone.
|
|
152
|
+
|
|
153
|
+
Needs the `cryptography` package: `pip install 'youpdated[encryption]'`. Installs without it
|
|
154
|
+
behave exactly as before.
|
|
155
|
+
|
|
156
|
+
- **`youpdated init --encrypt`** writes the starter config already encrypted, so a setup that is
|
|
157
|
+
meant to be private never has a plaintext config on disk at all — unlike `init` then `encrypt`,
|
|
158
|
+
which leaves the original blocks in free space.
|
|
159
|
+
|
|
160
|
+
- **A proxy preflight.** When `privacy.proxy` is set, the proxy is checked before the run starts
|
|
161
|
+
and the run is refused with exit `1` if it is unreachable. `--test` reports it instead of
|
|
162
|
+
aborting.
|
|
163
|
+
|
|
164
|
+
- **`youpdated.crypto` is a documented standalone module.** `from youpdated import crypto` gives
|
|
165
|
+
you the container directly; everything in its `__all__` is a supported surface. It stays an
|
|
166
|
+
optional *dependency* rather than a separate distribution on purpose: nothing in it imports
|
|
167
|
+
`cryptography` at module scope, so a plain install already pays nothing for it, and shipping the
|
|
168
|
+
container format apart from the code that reads it would risk version skew on files that are the
|
|
169
|
+
user's only copy.
|
|
170
|
+
|
|
171
|
+
### Changed
|
|
172
|
+
|
|
173
|
+
- **A dead proxy now stops the run instead of failing every target.** Requests already failed
|
|
174
|
+
closed (httpx routes everything through the proxy and never falls back to a direct connection)
|
|
175
|
+
but with Tor off, a run would fail each target separately, record an empty baseline, and still
|
|
176
|
+
exit `0`. In a cron log that is indistinguishable from "nothing new". It now exits `1` before the
|
|
177
|
+
state database is even opened, so nothing is recorded.
|
|
178
|
+
|
|
179
|
+
### Fixed
|
|
180
|
+
|
|
181
|
+
- **A broken SOCKS handshake escaped the retry path.** `socksio` raises `ProtocolError`, which is
|
|
182
|
+
not an `httpx.HTTPError`, so a proxy port answering with something that is not SOCKS5 bypassed
|
|
183
|
+
the retries and surfaced as a raw exception rather than a `FetchError`. The client now treats
|
|
184
|
+
`SOCKSError` as a network error like any other.
|
|
185
|
+
|
|
186
|
+
- **`tests/test_cleanup.py` could delete a real `./youpdated.yaml`.** The fixture redirected the
|
|
187
|
+
config and data directories but not the working directory, so `find_traces()` picked up the
|
|
188
|
+
project config of whoever ran the suite from a directory that had one, and `remove_traces()`
|
|
189
|
+
deleted it. The fixture now chdirs to the temp directory.
|
|
190
|
+
|
|
191
|
+
## [0.1.1] — 2026-08-18
|
|
192
|
+
|
|
193
|
+
### Fixed
|
|
194
|
+
|
|
195
|
+
- **`browser` / Brave: a server error killed the target instead of falling back.** The Brave source
|
|
196
|
+
reads the GitHub REST API and keeps the `.atom` feed as a fallback, but only a 403 or 429 reached
|
|
197
|
+
it. A 5xx: a timeout, or a DNS failure, was retried, then raised, and the whole target was
|
|
198
|
+
reported as failed. Observed against `api.github.com` returning 504. Any failure the HTTP client
|
|
199
|
+
gives up on now falls back to the atom feed; a genuine outage of *both* still reports an error.
|
|
200
|
+
|
|
201
|
+
### Changed
|
|
202
|
+
|
|
203
|
+
- Retry backoff is configurable on the HTTP client (`retry_backoff`), so the test suite no longer spends real seconds exercising retry paths. The suite went from ~4.9s to ~0.4s.
|
|
204
|
+
|
|
205
|
+
### Added
|
|
206
|
+
|
|
207
|
+
- A `release` workflow that publishes to PyPI via trusted publishing when a GitHub Release is
|
|
208
|
+
published, gated on the full 13-job test matrix and on the tag matching the version in
|
|
209
|
+
`pyproject.toml`.
|
|
210
|
+
|
|
211
|
+
## [0.1.0] — 2026-08-18
|
|
212
|
+
|
|
213
|
+
First release.
|
|
214
|
+
|
|
215
|
+
### Added
|
|
216
|
+
|
|
217
|
+
- **Seven sources**, all working without accounts or API keys:
|
|
218
|
+
- `github` — releases, tags, and commits via `.atom` feeds
|
|
219
|
+
- `npm` — newly published versions from public registry
|
|
220
|
+
- `steam` — patch notes and news; resolves the store name from a bare appid
|
|
221
|
+
- `itch` — devlog posts and new builds, fingerprinted from the game page
|
|
222
|
+
- `youtube` — channels and playlists, with Invidious and Data API fallback
|
|
223
|
+
- `browser` — Chrome, Brave, Firefox, and Edge releases across platforms and channels
|
|
224
|
+
- `feed` — any RSS/Atom URL, for apps without a dedicated source
|
|
225
|
+
- **Plugin architecture**: sources register in-tree with `@register` or ship from a third-party package through a `youpdated.sources` entry point.
|
|
226
|
+
- **Config**: every source entry takes a bare value for the common case or a
|
|
227
|
+
mapping for advanced options
|
|
228
|
+
- **Incremental reporting**: a SQLite history so each run reports what changed. First run records a baseline.
|
|
229
|
+
- **Three output formats**: terminal report, `--json`, and `--rss`
|
|
230
|
+
- **Privacy controls**: optional SOCKS/HTTP proxy covering every request, user-agent rotation, per-host request pacing with jitter, per-request cookie clearing, and conditional GETs. `--test` prints URLs without sending
|
|
231
|
+
- **`youpdated uninstall`** to remove every file from the tool. Refuses directories it didn't create or have other files.
|
|
232
|
+
|
|
233
|
+
### Known issues
|
|
234
|
+
|
|
235
|
+
- YouTube's RSS endpoint throttles occassionaly and 404s valid URLs; fallback covers, but a run can still fail all three. Retry or set `privacy.proxy`.
|
|
236
|
+
- Some itch games publish no "Updated" timestamp, so build updates are reported undated.
|
|
237
|
+
- Firefox publishes current versions, so it reports one item per channel.
|
|
238
|
+
- Edge exposes release notes only for the stable and beta channels. (But like, it's Edge, why do you want to know when it updates?)
|
|
239
|
+
|
|
240
|
+
[0.3.0]: https://github.com/Void1-1/youpdated/releases/tag/v0.3.0
|
|
241
|
+
[0.2.1]: https://github.com/Void1-1/youpdated/releases/tag/v0.2.1
|
|
242
|
+
[0.2.0]: https://github.com/Void1-1/youpdated/releases/tag/v0.2.0
|
|
243
|
+
[0.1.1]: https://github.com/Void1-1/youpdated/releases/tag/v0.1.1
|
|
244
|
+
[0.1.0]: https://github.com/Void1-1/youpdated/releases/tag/v0.1.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: youpdated
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Simple update tracker for games, apps, and packages
|
|
5
5
|
Author-email: Void1-1 <161782542+Void1-1@users.noreply.github.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -84,7 +84,35 @@ Requires **Python 3.11 or newer** (`python3 --version` to check). CI runs the fu
|
|
|
84
84
|
**Linux, macOS, and Windows** across Python 3.11–3.14.
|
|
85
85
|
|
|
86
86
|
```sh
|
|
87
|
-
|
|
87
|
+
pipx install youpdated
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
[pipx](https://pipx.pypa.io) puts the `youpdated` command on your `PATH` in its own isolated
|
|
91
|
+
environment. Plain `pip` works too, but it installs into whatever environment is active:
|
|
92
|
+
|
|
93
|
+
```sh
|
|
94
|
+
pip install youpdated
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Add encryption support (see [Encryption at rest](#encryption-at-rest)) with either installer:
|
|
98
|
+
|
|
99
|
+
```sh
|
|
100
|
+
pipx install 'youpdated[encryption]'
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Verify:
|
|
104
|
+
|
|
105
|
+
```sh
|
|
106
|
+
youpdated --version # -> youpdated 0.2.1
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### From source
|
|
110
|
+
|
|
111
|
+
To run an unreleased version or work on the tool itself:
|
|
112
|
+
|
|
113
|
+
```sh
|
|
114
|
+
git clone https://github.com/Void1-1/youpdated
|
|
115
|
+
cd youpdated
|
|
88
116
|
python3 -m venv .venv
|
|
89
117
|
.venv/bin/pip install .
|
|
90
118
|
```
|
|
@@ -96,24 +124,17 @@ python -m venv .venv
|
|
|
96
124
|
.venv\Scripts\pip install .
|
|
97
125
|
```
|
|
98
126
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
it on your `PATH`:
|
|
127
|
+
Puts the command at `.venv/bin/youpdated` on macOS/Linux and `.venv\Scripts\youpdated.exe` on
|
|
128
|
+
Windows. Call it by that full path, or put the directory on your `PATH`:
|
|
102
129
|
|
|
103
130
|
```sh
|
|
104
131
|
export PATH="$PWD/.venv/bin:$PATH" # macOS / Linux
|
|
105
|
-
youpdated --version # -> youpdated 0.2.0
|
|
106
132
|
```
|
|
107
133
|
|
|
108
134
|
```powershell
|
|
109
135
|
$env:PATH = "$PWD\.venv\Scripts;$env:PATH" # Windows
|
|
110
|
-
youpdated --version
|
|
111
136
|
```
|
|
112
137
|
|
|
113
|
-
To make that permanent, add the `export` line to your `~/.zshrc`, or the `$env:PATH` line to your
|
|
114
|
-
PowerShell profile. Examples assume `youpdated` is on your `PATH`; if it isn't, substitute the full
|
|
115
|
-
path above.
|
|
116
|
-
|
|
117
138
|
## 2. Create your config
|
|
118
139
|
|
|
119
140
|
```sh
|
|
@@ -172,12 +193,19 @@ privacy:
|
|
|
172
193
|
concurrency: 4
|
|
173
194
|
timeout: 20
|
|
174
195
|
|
|
196
|
+
ignore: # update types you never want reported
|
|
197
|
+
'*': [prerelease] # '*' is every source
|
|
198
|
+
github: [commit]
|
|
199
|
+
|
|
200
|
+
expiry: 1y # forget items missing from every fetch this long
|
|
201
|
+
|
|
175
202
|
sources:
|
|
176
203
|
github:
|
|
177
204
|
- python/cpython # easy
|
|
178
205
|
- repo: astral-sh/uv # advanced:
|
|
179
206
|
watch: [releases, commits] # releases | tags | commits
|
|
180
207
|
branch: main # for `commits`
|
|
208
|
+
ignore: [tag] # adds to the rules above, this entry only
|
|
181
209
|
|
|
182
210
|
npm:
|
|
183
211
|
- express
|
|
@@ -214,6 +242,73 @@ sources:
|
|
|
214
242
|
limit: 5
|
|
215
243
|
```
|
|
216
244
|
|
|
245
|
+
### Ignoring update types
|
|
246
|
+
|
|
247
|
+
Every update carries one or more **tags** describing what it is. An `ignore:`
|
|
248
|
+
rule drops any update carrying a tag you list.
|
|
249
|
+
|
|
250
|
+
```yaml
|
|
251
|
+
ignore: [prerelease] # shorthand: applies to every source
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
```yaml
|
|
255
|
+
ignore:
|
|
256
|
+
'*': [prerelease] # every source
|
|
257
|
+
github: [commit, tag] # just this source
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
Either form can be combined with a per-entry rule, which **adds** to it:
|
|
261
|
+
|
|
262
|
+
```yaml
|
|
263
|
+
sources:
|
|
264
|
+
github:
|
|
265
|
+
- python/cpython # global rules only
|
|
266
|
+
- repo: astral-sh/uv
|
|
267
|
+
ignore: [tag] # global rules + `tag`
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
These are the tags each source sets:
|
|
271
|
+
|
|
272
|
+
| Source | Tags |
|
|
273
|
+
| --- | --- |
|
|
274
|
+
| `github` | `release`, `prerelease`, `tag`, `commit` |
|
|
275
|
+
| `npm` | `release`, `prerelease`, `latest` |
|
|
276
|
+
| `itch` | `devlog`, `build`, `page` |
|
|
277
|
+
| `browser` | `release`, plus the channel (`stable`, `beta`, `dev`, `canary`, `esr`, `nightly`) |
|
|
278
|
+
| `youtube` | `video` |
|
|
279
|
+
| `steam` | `news` |
|
|
280
|
+
| `feed` | `item` |
|
|
281
|
+
|
|
282
|
+
An update is dropped if it carries **any** tag listed. A GitHub release
|
|
283
|
+
candidate is tagged both `release` and `prerelease`, so ignoring `prerelease`
|
|
284
|
+
drops it while ordinary releases stay.
|
|
285
|
+
|
|
286
|
+
An itch build is tagged `build`, not `release`. It is a fingerprint of the
|
|
287
|
+
game page, not a published release. A page whose date moved with no new
|
|
288
|
+
files is tagged `page`. So `ignore: [build]` drops new builds while keeping
|
|
289
|
+
devlog posts, and ignoring `release` everywhere leaves itch alone.
|
|
290
|
+
(`watch: [devlog]` on an itch entry still turns builds off outright)
|
|
291
|
+
|
|
292
|
+
Without a `GITHUB_TOKEN`, `prerelease` is inferred from the tag name
|
|
293
|
+
(`v3.15.0rc2`, `1.2.0-beta.1`), since the anonymous `.atom` feeds carry no
|
|
294
|
+
prerelease flag. With a token set, GitHub's own flag is used instead.
|
|
295
|
+
|
|
296
|
+
### How long history is kept
|
|
297
|
+
|
|
298
|
+
The history database remembers every item it has reported so nothing shows up again.
|
|
299
|
+
To bound its growth, an item that hasn't appeared in any fetch for a year is
|
|
300
|
+
forgotten, along with everything from targets you've removed from the config. Change the
|
|
301
|
+
window with `expiry:`, or turn pruning off:
|
|
302
|
+
|
|
303
|
+
```yaml
|
|
304
|
+
expiry: 180d # 30d, 52w, 2y, ... (at least 1d)
|
|
305
|
+
# expiry: never # keep everything
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
Pruning is safe: an item still listed by its source is never forgotten, however old,
|
|
309
|
+
so nothing is reported a second time. Targets that failed this run, or were left out
|
|
310
|
+
with `--source`, keep their history until they next fetch cleanly.
|
|
311
|
+
|
|
217
312
|
Check your config without sending a request:
|
|
218
313
|
|
|
219
314
|
```sh
|
|
@@ -246,6 +341,12 @@ youpdated check --all
|
|
|
246
341
|
|
|
247
342
|
From then on, `youpdated check` prints only what changed since the previous run.
|
|
248
343
|
|
|
344
|
+
The same goes for a target you add later. Its first check records a baseline for that target, and the rest of the run reports as usual:
|
|
345
|
+
|
|
346
|
+
```text
|
|
347
|
+
Baseline recorded for 1 new target(s): npm:react. Their existing items were not reported.
|
|
348
|
+
```
|
|
349
|
+
|
|
249
350
|
---
|
|
250
351
|
|
|
251
352
|
## Command reference
|
|
@@ -253,7 +354,7 @@ From then on, `youpdated check` prints only what changed since the previous run.
|
|
|
253
354
|
```sh
|
|
254
355
|
youpdated check # what's new since last run (the default command)
|
|
255
356
|
youpdated check --all # everything currently published, ignoring history
|
|
256
|
-
youpdated check --since 7d # only items from the last week (30m, 12h, 7d, 2w)
|
|
357
|
+
youpdated check --since 7d # only items from the last week (30m, 12h, 7d, 2w, 1y)
|
|
257
358
|
youpdated check -s github -s npm # limit to some sources
|
|
258
359
|
youpdated check --json # machine-readable output
|
|
259
360
|
youpdated check --rss ~/feeds/you.xml # aggregated Atom feed for a reader
|
|
@@ -282,18 +383,18 @@ Exit codes: `0` success, `1` config, encryption, or proxy error, `2` with `--fai
|
|
|
282
383
|
|
|
283
384
|
## Running it on schedule
|
|
284
385
|
|
|
285
|
-
Once a day is plenty, most of these sources change slowly, and conditional requests make repeat runs cheap. Use absolute paths, since cron and launchd don't inherit your shell's `PATH
|
|
386
|
+
Once a day is plenty, most of these sources change slowly, and conditional requests make repeat runs cheap. Use absolute paths, since cron and launchd don't inherit your shell's `PATH`; `which youpdated` prints yours.
|
|
286
387
|
|
|
287
388
|
**cron** (`crontab -e`) run at 9am and append to a log:
|
|
288
389
|
|
|
289
390
|
```cron
|
|
290
|
-
0 9 * * * /
|
|
391
|
+
0 9 * * * /home/you/.local/bin/youpdated check >> ~/youpdated.log 2>&1
|
|
291
392
|
```
|
|
292
393
|
|
|
293
394
|
**Keep an RSS feed fresh** for a reader to poll. Note that `--rss` writes that run's items, so a plain `check --rss` leaves almost an empty file. For a feed that always holds a rolling window, ask for it. `--no-save` keeps this from interfering with your daily incremental run:
|
|
294
395
|
|
|
295
396
|
```cron
|
|
296
|
-
0 * * * * /
|
|
397
|
+
0 * * * * /home/you/.local/bin/youpdated check --all --since 30d --no-save --rss ~/feeds/youpdated.xml
|
|
297
398
|
```
|
|
298
399
|
|
|
299
400
|
**macOS launchd**: save as `~/Library/LaunchAgents/com.youpdated.check.plist`, then `launchctl load ~/Library/LaunchAgents/com.youpdated.check.plist`:
|
|
@@ -304,7 +405,7 @@ Once a day is plenty, most of these sources change slowly, and conditional reque
|
|
|
304
405
|
<key>Label</key><string>com.youpdated.check</string>
|
|
305
406
|
<key>ProgramArguments</key>
|
|
306
407
|
<array>
|
|
307
|
-
<string>/
|
|
408
|
+
<string>/Users/you/.local/bin/youpdated</string>
|
|
308
409
|
<string>check</string>
|
|
309
410
|
<string>--rss</string>
|
|
310
411
|
<string>/Users/you/feeds/youpdated.xml</string>
|
|
@@ -320,7 +421,8 @@ Once a day is plenty, most of these sources change slowly, and conditional reque
|
|
|
320
421
|
| Symptom | Cause and fix |
|
|
321
422
|
| --- | --- |
|
|
322
423
|
| `Config error: no config file found` | Run `youpdated init`, or pass `--config PATH`. |
|
|
323
|
-
| First run printed nothing | Working as intended
|
|
424
|
+
| First run printed nothing | Working as intended: it recorded a baseline. Run `youpdated check --all` to see current items. |
|
|
425
|
+
| A newly added target printed nothing | Same as above, for that one target. `youpdated check --all -s <source>` shows its current items. |
|
|
324
426
|
| `--all` shows fewer items than expected | Nothing is wrong; sources cap how much history they expose (10–20 items each). |
|
|
325
427
|
| A source appears in the yellow `problems` panel | That one source failed; the rest of the run still completed. Re-run with `-v` to see the request and status. |
|
|
326
428
|
| `unknown source 'X'` | Check spelling against `youpdated sources`. |
|