A project can produce more than one APK
tdep-survey is one checkout with a backend and two independent Android clients, so a project has to be able to declare two Apk components. It could not: APK_PATTERNS was anchored at the project root, which reached the first client and stopped, and package, strip, the variant list and the download all came from AppEntry::apk_component()'s first match -- correct only while a project produced one APK. Each component's builds are now found under its own cwd, which is what cwd already meant everywhere else: the directory the build command runs in, the subtree staleness is scoped to, a server's WorkingDirectory. A component that declares none sits at the project root, so nothing about the single-APK case changes. The alternative -- naming the file on the component -- would have made a component a file path, and an app being a project path rather than a file path is this project's central invariant. Everything derived from an APK follows it onto the component: package, previousPackage, strip, size, mtime, variants and the rename note, in a nested `apk` block that a Server simply doesn't have. Two clients install over different packages, so first-wins would have checked the installed state of one and reported it as the other's -- which looks exactly like a correct answer. For the same reason a download naming no component is refused rather than guessed; naming none still answers for a project with one, which is what lets the frozen /self/apk keep working. On the phone the per-device state is keyed by project and component, so the variant picker moved inside the component's own card, beside the build it picks, and the installed state, size and icon are each their own component's. A project building two clients shows no single icon of its own rather than borrowing the first one's. Measured against the real thing: pointed at tdep-survey, the two clients resolve to their own builds (15 MB and 153 MB), strip applies only to the one that asked for it (153 MB served as 52 MB), pressing Install on one row installed that row's package and left the other row offering Install, and a download with no component named answers 400 saying which flag to pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
db47972a25
commit
c4e19c2e2b
9 files changed
+760
-309
No files matched your search
@@ -427,10 +427,46 @@ mutable at runtime from the phone.
|
||||
unaccepted, since taking commits runs git rather than the project's
|
||||
command, and it is how the new request arrives to be read.
|
||||
- **Which build variant to serve is the phone's choice, not the server's.**
|
||||
It arrives as `?variant=` on the download and is validated against the
|
||||
It arrives as `?variant=` on the download, beside the `?component=` that
|
||||
says whose build it is, and is validated against *that component's*
|
||||
discovered builds -- an unvalidated path would let a phone name any file
|
||||
on disk to be served. Storing it server-side meant one enrolled device
|
||||
silently changing what another was offered.
|
||||
silently changing what another was offered. The stored choice is keyed
|
||||
by project *and* component on the device, so pinning one client to a
|
||||
release build says nothing about the other.
|
||||
|
||||
- **A project can produce more than one APK, and each component's builds
|
||||
are found under its own `cwd`.** `APK_PATTERNS` is anchored at
|
||||
`project.join(cwd)` rather than at the project root, which is what
|
||||
`cwd` already meant everywhere else -- the directory the build command
|
||||
runs in, the subtree `subtree_head` scopes staleness to, a server's
|
||||
`WorkingDirectory`. A component that declares none sits at the root,
|
||||
which is what every single-APK project has always meant, so nothing
|
||||
about that case changed. tdep-survey is the project that needed it: one
|
||||
checkout, a backend and *two* independent Android clients, where the
|
||||
root-anchored patterns reached the first and stopped.
|
||||
The alternative -- naming the file on the component, `apk: "path"` --
|
||||
was rejected because it makes a component a file path, and **an app
|
||||
being a project path rather than a file path** is the invariant at the
|
||||
top of this document.
|
||||
Everything downstream is per component in consequence: `package`,
|
||||
`strip`, the variant list, the size, the mtime and the rename note.
|
||||
Those were all `apk_component()`'s *first* match before, which was
|
||||
correct only while a project had one. Two clients install over
|
||||
different packages and differ in whether their symbols are worth
|
||||
carrying to a phone, so first-wins would have checked the installed
|
||||
state of one app and reported it as the other's -- the expensive kind
|
||||
of wrong, because it looks exactly like an answer.
|
||||
**A download that names no component is refused, not guessed**
|
||||
(`ApiError::AmbiguousApk`), for that reason. Naming none still answers
|
||||
for a project with one, which is nearly all of them and is what lets
|
||||
the frozen `/self/apk` keep working -- it cannot carry a component, and
|
||||
the project it describes has a single APK.
|
||||
The measurement carried across an acceptance is keyed by name *and*
|
||||
`cwd` (`registry::component_id`): once the directory decides which
|
||||
builds a component has, a reused name is not the same APK, and handing
|
||||
it the old one's package would be wrong until that component happened
|
||||
to be downloaded.
|
||||
- **The self entry's project is the working directory itself**, so this
|
||||
server has to be started from the root of its own checkout -- which is
|
||||
where everything else here is driven from, and what the service unit
|
||||
|
||||
Reference in new issue
Block a user