# Upgrading the toolchain

> Replaces the osy you are running with a newer release — downloaded, checked against the release's checksums, and swapped in place — on every platform, with one command. `osy whats-new` prints what changed first, and the toolchain tells you, once a day, when there is something to upgrade to.

<!-- id: local-upgrading-the-toolchain · area: local · stability: preview · html: https://osysharp.com/reference/local/upgrading-the-toolchain/ -->

## Summary        {#summary}

`osy upgrade` fetches the newest release for your platform, verifies it against the checksums the release
publishes, and swaps it into the directory the running `osy` lives in. It works the same on macOS, Linux and
Windows, which the install script does not. `osy whats-new` prints a release's notes in the terminal, and the
toolchain prints one line a day when a newer version exists, ending with both commands.

## Signature      {#signature}

```console
osy upgrade                # to the newest release
osy upgrade 0.9.2          # to a particular version
osy upgrade --check        # only say whether a newer one exists
osy upgrade --check --json # the same, as one line of JSON for a program to read
osy upgrade 0.9.2 --reopen-studio   # install, then open again every Studio that was open
osy whats-new              # the newest release's notes
osy whats-new 0.9.2        # a particular version's
```

## Description    {#description}

**Where it comes from.** The download goes through the same door every install uses, so it is counted like any
download, and the archive is checked against the `SHA256SUMS` the release carries before a byte of it is
installed. A download that does not match is deleted and nothing changes. The version check and the notes come
from the public release page, so neither is a statistic and neither reaches us.

**How it swaps.** The files in the install directory are moved into a `.old/` folder beside them and the new files
moved in — a rename, never a copy over a running program, which is what makes it safe to run from the very
binary being replaced. The next run of `osy` removes the `.old/` folder. If anything fails during the swap the
old files are moved back.

**Your local platforms are kept, with their data.** After the swap every local platform the old version is running
is stopped — it is the old program — and every one is left on disk, apps, accounts, rows and Studio's settings with
them. The first time each starts under the new version it is brought onto it in place: each app's model is rebuilt on
the new compiler and its database gains what the new version adds, which makes that one start take a little longer.
If one will not start on the new version, the start says so; `osy instances --prune --idle-hours 0` lists the stopped
local platforms, `--yes` removes them, and the next `osy launch` starts afresh. Unused ones are still removed on
idleness ([Reclaiming the disk your local platforms use](https://osysharp.com/reference/local/reclaiming-local-disk/)). Your source, of course, is untouched.

**Your projects need nothing.** The kits your `osy` carries (`Osysharp.Ui`, `Osysharp.Workflow` and the rest of the
platform's own) are taken from the `osy` that is running and are not pinned in `osyrin.lock`, so the first command
after an upgrade uses the new ones — no `osy update`, and no error to clear first. The releases your project
fetched stay pinned exactly as they were; `osy update` is still how you move those. A lock written by an earlier
`osy` may list the carried kits: it is read without them, and the next `osy lock` or `osy update` writes it without
them ([package.osy](https://osysharp.com/reference/project/package/), *The kits your `osy` carries are not in the lock*). To keep a carried kit at one release
across upgrades, ask for it exactly — `use Osysharp.Ui@2.21.1;`.

**The editor extension is a thin client of the toolchain** — the language server is `osy lsp`, hosted in the
binary you just upgraded, so the editor is on the new compiler the moment it restarts the server. The extension
itself is versioned with the toolchain; if an editor on your PATH has it at the previous version, the upgrade
offers to update it, or names `osy vscode install` when it cannot ask ([Installing the editor extension](https://osysharp.com/reference/local/installing-the-editor-extension/)).

**What it refuses.** An installation that a package manager owns — Homebrew's, scoop's, winget's — because swapping
files under a manager leaves it wrong about what is installed; the message names the manager's own command
(`brew upgrade osy`, `scoop update osy`, `winget upgrade Osysharp.Osy`). And the toolchain inside the Mac's
**Osyrin Studio** app (`Osyrin Studio.app/Contents/Resources/osy`): the app is signed and notarised as a whole, and
changing a file inside it breaks its signature, after which macOS refuses to open it — so the message says to
download the new Osyrin Studio from the release page instead. The Windows Studio package is a folder with the
toolchain beside its launcher, an ordinary installation, and `osy upgrade` replaces it. The daily line names what
upgrades the installation you have, never a command that would refuse it.

**For a program: `--check --json`.** One line on standard output, always — a release channel that cannot be reached is
`"latest": null`, not an error:

```text title="osy upgrade --check --json" syntax
{"current":"0.14.2","latest":"0.15.0","newer":true,"installedBy":"osy","upgradesInPlace":true,
 "command":"osy upgrade","downloadUrl":null,"notesUrl":"https://github.com/osysharp/cli/releases/tag/v0.15.0"}
```

| key | |
|---|---|
| `newer` | a newer release exists **and** this installation may be offered it — always false for a development build (loose files from a checkout), whatever the channel names |
| `installedBy` | `osy` (ours to replace), `Homebrew`, `scoop`, `winget`, `the Osyrin Studio app`, or `development` |
| `upgradesInPlace` | `osy upgrade` installs it here — true only for `osy` |
| `command` | what upgrades this installation; null for the Studio app and a development build |
| `downloadUrl` | the page to download the Studio app again from; null otherwise |
| `notesUrl` | the newer release's notes; null when there is none |

`--daily` makes it the same once-a-day check the daily line uses: the release page is asked at most once a day, and not
at all when `"updateCheck": false` — then `latest` is null. Studio asks this way.

**`--reopen-studio`** is what Studio's **Update now** runs ([Opening Studio](https://osysharp.com/reference/local/opening-studio/)). It installs exactly as above;
a refusal or a failure changes nothing and Studio stays open. Once installed it notes which Studios are open, stops the
old local platforms (Studio's among them, kept with its settings), and runs the new version's `osy studio` for each, which opens a new
signed-in tab. Its lines also go to `~/.osy/logs/studio-update.log`, since once Studio has closed nothing else reads them.

**The daily line.** When a newer release exists the toolchain prints, before a command's own output and at most
once a day, a line naming the version and the two commands. It is on whether or not usage statistics are;
`"updateCheck": false` in `~/.osy/config.json` turns it off. See [Usage statistics (what is sent, and the three random ids — one per installation, project and session)](https://osysharp.com/reference/project/statistics/) for what the check sends
(a request to the release page with the toolchain's user agent, and nothing else).

**Against a platform, the platform says it too.** Every request `osy` (and `osycloud`) sends to a platform names the
toolchain's version, and a released platform answers with the release it runs. When that release is newer than your
toolchain, the command prints one line — once per platform — that `osy upgrade` installs it. The toolchain and the
platform are released together from one commit, under one version number, so "the platform runs 0.15.0" means an
`osy` 0.15.0 exists. A platform also names the **oldest toolchain it serves**; a released toolchain below it is
refused before the command does anything, with `426` and the remedy:

```text title="a toolchain below the platform's minimum" syntax
✗ This platform (release 0.20.1) serves osy 0.20.0 or newer, and this osy is 0.19.3. Run `osy upgrade` (osycloud:
  `scripts/release/install-osycloud.sh 0.20.1`), then the same command again.
```

A build from a checkout is newer than every release, so it is never told to upgrade and never refused; a browser or a
script that does not name a toolchain version is never refused either.

## Examples       {#examples}

```console
$ osy check
a newer osy is available: 0.9.2 (you have 0.9.1) — osy upgrade
  what's new: osy whats-new   ·   https://github.com/osysharp/cli/releases/tag/v0.9.2
…
$ osy whats-new
osy 0.9.2 (newer than yours)

- …

$ osy upgrade
asking the release channel for the newest version…
downloading osy 0.9.2 for macos-arm64…
checking it against the release's SHA256SUMS…
unpacking…
installing into /Users/you/.osy/bin…
✓ osy 0.9.2 installed (9 entries in /Users/you/.osy/bin).
  what changed: osy whats-new
  2 local platform(s) from 0.9.1 kept, with their apps and data (1 stopped). Each is brought onto the new version the next time it starts, which takes a little longer once.
  If one will not start on the new version: `osy instances --prune --idle-hours 0` lists the stopped ones, `--yes` removes them, and the next `osy launch` starts afresh.
  code has the Osy# extension at 0.9.1; update it to 0.9.2 now? [Y/n]
```

## See also       {#see-also}

[Installing the editor extension](https://osysharp.com/reference/local/installing-the-editor-extension/) — the extension that ships with each release.

[Usage statistics (what is sent, and the three random ids — one per installation, project and session)](https://osysharp.com/reference/project/statistics/) — what the toolchain sends, and what the version check does not.
