Public Access
Merge branch 'claude/android-release'
This commit is contained in:
@@ -228,6 +228,56 @@ changes.
|
||||
token, and it puts a compellable third party in the signing path — which is ADR 0011 rule 3's shape one
|
||||
layer down, declined there for reasons that do not stop applying because the vendor changed.
|
||||
|
||||
### 9. There is a second desktop channel, published by CI, and it is a second application
|
||||
|
||||
[ADR 0014](0014-android-updates.md) gave the phone a nightly channel and rule 3 above gives the desktop
|
||||
none, which left the two heads with different answers to the same question — *how does somebody try what
|
||||
is on main?* — for no reason other than the order the work happened in. This is the desktop's answer, and
|
||||
it is the phone's arrangement with one difference that changes how much of it has to be built.
|
||||
|
||||
**Android gets the separation from the platform. Windows does not.** Two Android channels cannot replace
|
||||
one another because the installer refuses a package signed by a different key; a mistake there is loud.
|
||||
Velopack applies what its feed serves and verifies no signature, so on this head the separation is entirely
|
||||
construction:
|
||||
|
||||
| | release | nightly |
|
||||
| --- | --- | --- |
|
||||
| pack id | `DodoSSH.Desktop` | `DodoSSH.Desktop.Nightly` |
|
||||
| Velopack channel | `win` | `win-nightly` |
|
||||
| feed | newest non-prerelease release | the rolling `nightly-desktop` prerelease |
|
||||
| profile | `%LOCALAPPDATA%\DodoSSH` | `%LOCALAPPDATA%\DodoSSH.Nightly` |
|
||||
| cut by | a person, from a `v*` tag | CI, on every push to main |
|
||||
|
||||
Four separations rather than one, because each closes a different door. The pack id decides the install
|
||||
directory and what an installed client matches an update against, so it is what makes a nightly install
|
||||
*beside* rather than *over*. The channel names the index file on the feed, so neither build ever reads the
|
||||
other's — and the release channel additionally refuses prereleases, which is belt and braces in the one
|
||||
direction that matters: an unsigned CI build must never reach a machine somebody is trusting with their
|
||||
credentials. The profile is the one that is easy to skip and would hurt most: the cache schema is migrated
|
||||
on every launch, before unlock, so a shared profile means a nightly quietly upgrading a database the
|
||||
release build then opens.
|
||||
|
||||
**The prerelease flag is also how the two heads stay out of each other's way.** The phone's release channel
|
||||
reads `releases/latest`, which skips prereleases. A desktop nightly published as a stable release would
|
||||
become the newest release in this repository, and every phone on the release channel would start failing
|
||||
its check against a release carrying no Android manifest. The nightly is published with `--pre` for its own
|
||||
sake and for that one.
|
||||
|
||||
**What this costs, stated where somebody will read it before installing:** whoever can write a release on
|
||||
this repository can put a build on every nightly desktop, because the client applies what the feed serves
|
||||
without verifying a signature. That set includes CI and therefore everyone who can change a workflow file.
|
||||
It is the same trade [ADR 0014](0014-android-updates.md) accepted for the phone's nightly, and it is
|
||||
acceptable for the same reason and only that reason: this is not the channel anybody's real credentials are
|
||||
on. Rule 3 is untouched — the release channel still has no job, no token and no runner.
|
||||
|
||||
**The version gets a floor, on this channel only.** MinVer answers `0.0.0-alpha.0.N` until the first `v*`
|
||||
tag and `vpk` refuses to pack anything below `0.0.1`, so the nightly job lifts the patch digit and keeps
|
||||
the prerelease height. It is applied through `MinVerVersionOverride`, which moves the assembly version too
|
||||
— packing a number the assemblies disagree with would put one version in the installer and another on the
|
||||
preferences screen, which is the screen somebody reads when asked which nightly they are running. Ordering
|
||||
across the floor holds: heights rise within a floored version, and the first real tag moves past all of
|
||||
them. The release script gets no floor and must not have one; there, `0.0.0` should be refused.
|
||||
|
||||
## Consequences
|
||||
|
||||
**The desktop gets what ADR 0011 had to refuse the phone.** Discovery is still manual — somebody has to be
|
||||
|
||||
@@ -116,6 +116,15 @@ is the entire reason there are two.
|
||||
other is an uninstall and a fresh enrolment. There is no migration and there will not be one — the two
|
||||
caches are encrypted under keys held by two package identities the platform keeps apart.
|
||||
|
||||
◆ **The desktop has since taken this arrangement, and it had to build what Android is given.**
|
||||
[ADR 0013](0013-desktop-distribution-and-updates.md) decision 9 is this ADR applied to Windows: a nightly
|
||||
CI publishes from main, installing beside the release build rather than over it. The difference worth
|
||||
carrying back here is that every separation this ADR gets from the platform — different package identity,
|
||||
signature-checked updates, a per-app data directory — is on Windows a thing somebody had to choose and can
|
||||
therefore undo. The paragraph above about the attack surface applies there word for word, with one line
|
||||
removed: on the desktop the packages are not signed at all, so the feed is the only thing standing between
|
||||
a nightly install and an arbitrary build.
|
||||
|
||||
**ADR 0011's "no automatic update" consequence is now wrong for the release channel and remains true for
|
||||
reach.** Discovery, MDM deployment and the sideloading permission are all unchanged. What changed is only
|
||||
that an installed copy can now learn a newer one exists.
|
||||
|
||||
@@ -2005,6 +2005,29 @@ is wrong. Reinstalling then asks for the passphrase rather than for a server.
|
||||
lost their offline unlock and any change that was still in the outbox. That is the failure ADR 0013
|
||||
decision 2 exists to prevent, and it is why 16.1 checks the same thing from the other end.
|
||||
|
||||
### 16.10 The nightly installs beside the release build and not over it · **the one CI cannot check**
|
||||
|
||||
Needs both: a release build installed by walking 16.1, and `DodoSSH.Desktop.Nightly-win-nightly-Setup.exe`
|
||||
from the `nightly-desktop` release. Install the nightly second, and then start **both**.
|
||||
|
||||
**Pass:** two entries in the Start menu, one *DodoSSH* and one *DodoSSH Nightly*; two directories,
|
||||
`%LOCALAPPDATA%\DodoSSH.Desktop` and `%LOCALAPPDATA%\DodoSSH.Desktop.Nightly`; two profiles,
|
||||
`%LOCALAPPDATA%\DodoSSH` and `%LOCALAPPDATA%\DodoSSH.Nightly`, each with its own `cache.db` and
|
||||
`device.key`. The nightly's titlebar says **DodoSSH Nightly** and the release build's says **DodoSSH** —
|
||||
which is the only difference visible while somebody is typing a passphrase into one of them. Signing in to
|
||||
the nightly leaves the release build signed in and untouched, and the account shows a second device.
|
||||
|
||||
**Failure means:** if there is one profile directory, the two builds are sharing a cache and a device key,
|
||||
and the next nightly carrying a schema migration will upgrade the database the release build opens. If
|
||||
there is one install directory, the pack ids collide and the nightly has replaced the release build
|
||||
outright — which is the thing ADR 0013 decision 9 is constructed to make impossible, so it means one of the
|
||||
four separations has been undone.
|
||||
|
||||
**And the direction that matters most:** on the release build, PREFERENCES → UPDATES → CHECK NOW must not
|
||||
offer a nightly, ever, however many have been published since. It reads a different index and refuses
|
||||
prereleases; if a nightly version is ever offered there, stop and treat it as a release-channel incident
|
||||
rather than as a bug in the nightly.
|
||||
|
||||
---
|
||||
|
||||
## Phase 17 — Installing and updating the phone
|
||||
|
||||
+17
-3
@@ -595,9 +595,23 @@ targeted, not which RID. Only signing needs Windows tooling, which is why `ci.ym
|
||||
without a target *evaluates* the project and runs nothing, while MinVer computes the version inside a
|
||||
target — so the read comes back as the SDK default on a full checkout with every tag present, which looks
|
||||
exactly like a version that was never configured. `-t:MinVer` makes `-getProperty` report the value after
|
||||
that target has run, and both readers of it — the tag check in `ci.yml` and `scripts/release-windows.ps1`
|
||||
— pass it. Neither had, and neither had ever run: the CI check is `if:` a tag ref and there are no tags
|
||||
yet, so the first release would have been refused by its own guard, which would have blamed `fetch-depth`.
|
||||
that target has run, and every reader of it — the tag check in `ci.yml`, the desktop nightly job, and
|
||||
`scripts/release-windows.ps1` — passes it. Neither of the first two had, and neither had ever run: the CI
|
||||
check is `if:` a tag ref and there are no tags yet, so the first release would have been refused by its own
|
||||
guard, which would have blamed `fetch-depth`.
|
||||
|
||||
**And naming that target requires a restore first, which is a second failure wearing a very different
|
||||
face.** MinVer arrives as a package, so its target is imported from `obj/*.nuget.g.targets` and does not
|
||||
exist at all on a clean checkout:
|
||||
|
||||
```
|
||||
error MSB4057: The target "MinVer" does not exist in the project.
|
||||
```
|
||||
|
||||
That reads like a typo in the workflow rather than like a missing restore, and it does not reproduce on any
|
||||
machine that has built the project before — which is every developer machine and no fresh runner. The
|
||||
build job's tag check is safe because it runs after that job's own restore; the desktop nightly job and the
|
||||
release script each restore before reading, deliberately and with a comment saying why.
|
||||
|
||||
**The Android head's lock file is outside the solution, so nothing checks it until the android job runs
|
||||
— and the android job was broken for an unrelated reason for the whole of the release that went stale.**
|
||||
|
||||
Reference in New Issue
Block a user