removed update
Pull request / build (pull_request) Successful in 52s

This commit is contained in:
2026-08-13 14:58:26 +05:00
parent 460ce52017
commit dd0bbdea42
30 changed files with 183 additions and 2333 deletions
+33 -88
View File
@@ -78,8 +78,8 @@ WPF took goes back to the system. The price is a cold start of half a second or
the next time the window is asked for.
`CursorLang.Core.dll` is what both hold in common: the models, the settings file,
the layout tracking, the updates. No UI, and it must stay that way — whatever lands
there lands in the background process.
the layout tracking. No UI, and it must stay that way — whatever lands there lands
in the background process.
The connection between the two is `settings.json` and nothing else. The window
writes it — whole, into a temporary file moved into place in one step — and then
@@ -162,57 +162,22 @@ after a switch of the mode, and that is a question asked once.
## Updates
The check runs when the settings window is opened, not when the machine is
switched on: the background half no longer goes to the network at all, and there
would be nothing in it to show the answer. The setting in the window says as much.
The Store updates the app, and the app itself does nothing about it: there is no
updates section in the window, no request to the network and no code for either.
The app looks for new versions among the releases of its own repository. A
release counts when its tag is a plain version — `v1.2.3` or `1.2.3` — and an
MSIX package is attached to it. A tag with anything else in it, `v1.2.3-beta`
among them, is passed over: a pre-release version is asked for on purpose, not
offered by the app.
That is not a matter of taste but of what a signature costs. Windows installs an
MSIX only when it trusts the signature on it, and a publicly trusted code signing
certificate has turned out to be beyond reach — the certificate authorities that
sell them will not issue one here, and those that would keep the key in a cloud
HSM will not either, while a certificate on a USB token cannot be shipped in.
Without a signature a package installs nowhere but a machine in developer mode,
so there is nothing to hand out from a release and nothing for an updater to
find. The Store signs the package with its own certificate and updates the app
by itself, which leaves the whole question to it.
Out of the attached files the `.msixbundle` is preferred — it carries both
architectures. Failing that, the package whose name holds the architecture of
this machine is taken: `CursorLang-1.2.3.0-x64.msix`. Those are the names
`build-msix.ps1` produces, so a release is made by attaching what it built.
The package is downloaded to the temp folder and handed to the Windows app
installer: it shows the publisher, asks for a confirmation and replaces the
installed version. Windows checks the signature, so the package attached to a
release has to be signed — an unsigned one installs nowhere but a machine in
developer mode; the pipeline signs what it attaches. The running app keeps
working off the old files until it is restarted.
An app installed from the Store has no updates section at all: the Store
updates it, and a package from the side is something Windows would not accept
over it anyway.
The app asks about releases every time the settings window appears, and the
answer stays in the section as it is — an unreachable network included. Opening
that window is a deliberate act, rare enough for a request to cost nothing, so
nothing is remembered between openings and there is nothing to turn off. The
button next to the version asks the same question again on demand.
Where the releases are looked for is set in `UpdateOptions` — the repository
belongs to whoever publishes the app, not to the user, so the values live in the
build rather than in `settings.json`:
```csharp
services.AddSingleton(new UpdateOptions
{
ServiceUri = new Uri("https://git.alrakis.kz/"), // the Gitea server itself
Project = "alrakis/cursor-lang",
});
```
The releases come from Gitea, and its API sits on the server itself: the address
is the one the repository is opened at in a browser, and the app asks
`/api/v1/repos/{owner}/{repo}/releases` under it.
A closed repository needs a token. It is read from the `CURSORLANG_UPDATE_TOKEN`
environment variable rather than kept in the source: a secret built into the app
is a secret handed to everyone who got the app.
The version of the running app is in the title of the settings window:
`CursorLang 1.2.3 — Settings`. It is nowhere else in the interface, and it is
there so that a bug report can name it.
## Tests
@@ -257,11 +222,10 @@ running agent and fails if any part of the WPF renderer is in it.
The pipelines live in `.gitea/workflows` and run on Gitea Actions. A pull
request into `master` is built and tested; a tag of the form `v1.2.3` is built,
tested, packed into an MSIX and published as a release with the signed packages
attached. The version is taken from the tag alone — a tag shaped any other way
stops the run right at the start. The package version ends up as `1.2.3.0`: the
Store takes four numbers and keeps the last one for itself, so the tag has no
say in it.
tested and packed into an MSIX, which is left in the artifacts of the run. The
version is taken from the tag alone — a tag shaped any other way stops the run
right at the start. The package version ends up as `1.2.3.0`: the Store takes
four numbers and keeps the last one for itself, so the tag has no say in it.
Both pipelines ask for a Windows runner labelled `windows-x64` with the .NET 10
SDK and git-lfs on it. The checkout pulls LFS files: without them the icon is a
@@ -271,31 +235,18 @@ need a desktop of their own — the end-to-end ones, and those that ask for the
foreground window or the caret — skip themselves on a runner that lives as a
service in session 0, where there is no desktop to show a window on.
The package is built twice over, because the Store and a release want different
things of it.
The package goes to the Store and nowhere else: it is unsigned, and Partner
Center puts its own signature on it. So the run leaves it in the artifacts under
the name `msix-1.2.3.0`, where whoever uploads it picks it up by hand; nothing
is attached to the release, which Gitea makes for the tag by itself and which
carries the tag alone. An unsigned package hanging off a release would look like
something to install and install nowhere — see the section on updates.
The Store one carries the identity reserved in Partner Center and no signature —
Partner Center signs it there. It never leaves the artifacts of the run: someone
picks it up and uploads it by hand. Its name says which one it is —
`CursorLang-1.2.3.0-store.msixbundle`. The identity comes from repository
variables and falls back to the defaults of the script when unset:
`MSIX_IDENTITY_NAME`, `MSIX_PUBLISHER` and `MSIX_PUBLISHER_DISPLAY_NAME`.
The one attached to the release is signed by the pipeline with a certificate
from SSL.com, whose private key stays in their HSM and never comes down to the
runner: eSigner CKA is installed on it as a key storage provider, and `signtool`
asks that for the key the way it would ask a token. The account is three
repository secrets — `ESIGNER_USERNAME`, `ESIGNER_PASSWORD` and
`ESIGNER_TOTP_SECRET`; the last one is the secret eSigner hands out for
automated signing, not a code read off a phone. They are checked before the
build rather than at the signing step: a release without a signed package is
not a release.
Its `Publisher` is not `MSIX_PUBLISHER` but the subject of that certificate,
read off it in the run — Windows takes a package naming anyone else for a
forgery. Together with the identity that subject decides the family name of the
package, so both have to stay as they are from release to release, or an update
installs beside the old version instead of replacing it.
The identity comes from repository variables and falls back to the defaults of
the script when unset: `MSIX_IDENTITY_NAME`, `MSIX_PUBLISHER` and
`MSIX_PUBLISHER_DISPLAY_NAME`. Together the identity and the publisher decide
the family name of the package, so both have to stay as they are from version to
version, or the Store takes the next one for a different app.
## Building the MSIX package
@@ -320,14 +271,8 @@ The result is `artifacts\packages\CursorLang-<version>.msixbundle` covering x64
and arm64; next to it lie the packages of single architectures. Upload the bundle
to Partner Center as it is.
`-CertificateThumbprint` signs everything the build produces, with the
certificate of that thumbprint out of the personal store of the current user;
`-Publisher` then has to name its subject, and the script says so before it
starts building rather than after. The pipeline is what normally signs, but the
same works by hand once eSigner CKA — or a certificate of any other kind — has
put a certificate into the store. `-PackageSuffix` goes at the end of the file
names: the builds for the Store and for a release differ in what is inside them
and in nothing else.
Nothing here is signed: the Store signs the package itself, and for installing it
on this machine the layout is registered instead — see `-Install` below.
The app ships with its own copy of .NET: Windows does not include .NET 10, and
MSIX cannot install a runtime as a package dependency.