|
| 1 | +### Added |
| 2 | + |
| 3 | +**Where Perry asks "what is the latest version?" is now a choice.** It used to |
| 4 | +walk one fixed list — an override, the config, Perry Hub, then the GitHub |
| 5 | +releases API — and read a GitHub-releases-shaped document from whichever |
| 6 | +answered first. That is fine while everyone installs the same way, and wrong as |
| 7 | +soon as they do not: an npm user's "latest" is whatever the registry's `latest` |
| 8 | +dist-tag says, and asking GitHub instead can announce a version their package |
| 9 | +manager cannot install yet. |
| 10 | + |
| 11 | +```toml |
| 12 | +[update] |
| 13 | +source = "npm" # gh-releases | npm | gh-registry | custom |
| 14 | +package = "@perryts/perry" # npm-shaped sources; defaults to Perry's own |
| 15 | +registry = "..." # npm-shaped sources; defaults to the public registry |
| 16 | +server = "..." # the URL for `custom`, and the mirror override |
| 17 | +``` |
| 18 | + |
| 19 | +Unset keeps the historical ladder, so nothing changes for anyone who does not |
| 20 | +set it — except on an **npm-managed install**, which now defaults to asking npm, |
| 21 | +because that is the version its own package manager can actually install. |
| 22 | + |
| 23 | +<details> |
| 24 | +<summary><b>The split that matters: checking is not downloading</b></summary> |
| 25 | + |
| 26 | +A check source answers one question and returns a version, a link, a publish |
| 27 | +time and a headline. It does **not** decide where the binary comes from. |
| 28 | +Artifacts and their signed manifest always resolve from the release |
| 29 | +infrastructure, whatever the check source is. |
| 30 | + |
| 31 | +That separation is load-bearing rather than tidy. The manifest — Ed25519 over |
| 32 | +the artifact's digest and version — is what makes a self-update trustworthy, |
| 33 | +and a check source is a URL a user can point anywhere. Letting it redirect the |
| 34 | +download would turn a configuration setting into a way to install an arbitrary |
| 35 | +binary. Whoever answers "what is new?" never gets to answer "what should I |
| 36 | +run?", and there is a test that fails if a source ever leaks into the artifact |
| 37 | +ladder. |
| 38 | + |
| 39 | +The old `get_update_servers` and its private config reader are **deleted** |
| 40 | +rather than left beside the new code, so the compiler enforces that both call |
| 41 | +sites moved. A new abstraction with the old ladder still wired up underneath is |
| 42 | +the shape where four sources exist, pass their own tests, and are never |
| 43 | +reached. |
| 44 | +</details> |
| 45 | + |
| 46 | +<details> |
| 47 | +<summary><b>Credentials go to exactly one of the four</b></summary> |
| 48 | + |
| 49 | +The npm shapes ask for the *abbreviated* packument |
| 50 | +(`Accept: application/vnd.npm.install-v1+json`) — smaller, cacheable, and the |
| 51 | +document npm itself requests for this question. It also avoids GitHub's |
| 52 | +unauthenticated API rate limit, which the old ladder shared with everything |
| 53 | +else on the machine. |
| 54 | + |
| 55 | +The public registry is asked **without credentials**, and a test asserts no |
| 56 | +`Authorization` header is sent: a token there would be a leak, not a |
| 57 | +convenience. GitHub Packages does need one, so that shape reads `GH_TOKEN` / |
| 58 | +`GITHUB_TOKEN` and fails with a sentence naming the fix when neither is set, |
| 59 | +rather than retrying anonymously and reporting the resulting 404 as "up to |
| 60 | +date". |
| 61 | + |
| 62 | +A configured source does not fall back to the ladder when it errors. Somebody |
| 63 | +who said "ask npm" and got a failure wants to hear that, not a version from |
| 64 | +somewhere they never named. |
| 65 | +</details> |
| 66 | + |
| 67 | +<details> |
| 68 | +<summary><b>Tests</b></summary> |
| 69 | + |
| 70 | +11 new, all parsing real response shapes from string fixtures so no network is |
| 71 | +involved: |
| 72 | + |
| 73 | +- a GitHub release document, including that the `v` prefix is stripped; |
| 74 | +- an abbreviated packument, which has no `time` map — so the publish date reads |
| 75 | + "unknown" rather than being invented, which matters because the release |
| 76 | + cooldown in the next slice depends on it; |
| 77 | +- a full packument, which does supply it; |
| 78 | +- a custom manifest with only a `version`, and one with every optional field; |
| 79 | +- that each shape **rejects the others' documents** rather than reading a field |
| 80 | + that happens to be present — a registry answering a gh-releases request must |
| 81 | + be an error, not a version of `""`; |
| 82 | +- that a scoped package's `/` is percent-encoded, or the registry reads the |
| 83 | + scope as a path segment and answers 404; |
| 84 | +- that an unknown `source` name falls back instead of failing, so a config |
| 85 | + written by a newer Perry does not break an older one; |
| 86 | +- that `custom` with no URL is treated as a missing key rather than a default; |
| 87 | +- that an npm install defaults to npm and every other channel keeps the ladder; |
| 88 | +- that no check source can reach the artifact ladder; |
| 89 | +- and both credential rules. |
| 90 | + |
| 91 | +`cargo test -p perry`: 925 passed, 0 failed. |
| 92 | +</details> |
0 commit comments