Install end to end against a real Jellyfin server - #138
Merged
Conversation
The needs-jellyfin requirement was carried by nothing, so asking for it refused rather than running, and the job of that name supplied neither of the two things the requirement is named for. Both are here. internal/pairing/server_test.go carries the tag. It sets a fresh server up, adds a repository address, waits for the catalogue to fill in, installs the plugin and reads the server's own plugin list back. The mismatched-checksum half runs first, against a catalogue the check serves itself on a port the operating system chooses, because decisions/manifest-address.md treats the published address as a promise that cannot be withdrawn and a knowingly broken manifest there is served to every operator polling it. Only the positive half points at the published address, and it runs second so the refusal above cannot be read off a server that had already installed. The failure this prevents is the one nothing on this side can see. A manifest that passes every leg of the gate can still render as an empty repository or a refused install because of a field the server reads differently, and the install step is where decisions/artifact-checksum-pairing.md is finally decided against real bytes rather than against a fixture. The job brings the server up and hands the address in, which is the shape needs-network already uses for MANIFEST_ADDRESS. Unset is a refusal in the check rather than a skip, so a job that started a server and forgot to pass the address fails as a refusing check rather than as a green run over nothing. The image is pinned by digest beside its version, the way every action in that file is pinned by commit beside its tag. Nothing here reaches the gate. The tag keeps the file out of every leg, and the gate's own report goes on saying the harness did not run. Signed-off-by: Nils Lehnen <30603423+iderex@users.noreply.github.com>
The first run against a server answered 500 to the install request itself rather than accepting it and refusing while it hashed the archive, so the half reading only the plugin list failed on a refusal that had already happened. Both spellings are the refusal now and the plugin list is still what decides. A refusal on its own says nothing about which field caused it, so the same catalogue with its own checksum is served from the same address and installs. That is the one-change neighbour: same server, same archive, one field. It uninstalls afterwards, and refuses to carry on until the server says the plugin is gone, because the half after it has to watch an install arrive rather than find one already there. Signed-off-by: Nils Lehnen <30603423+iderex@users.noreply.github.com>
…54) A job here reports under a requirement's own name and carries a one-line step running the entry point, and until now that was everything anything read. A job that also has to hand the check an address could set it, or not, and every reader passed either one. The check it starts then refuses for want of an environment nobody set, which reads as a broken check rather than as a job missing a line. Requirement.Supplies names what the runner puts in the environment, and SuppliedIn reads which names each job sets, in both spellings this file uses: a mapping on the job and a mapping on a step. needs-network already had the shape and is now declared with it, so the guard reads two jobs rather than the one it was written for. Watched refusing, on the one-character mistake somebody makes: - JELLYFIN_ADDRESS: http://127.0.0.1:8096 + JELLYFIN_ADRESS: http://127.0.0.1:8096 go test ./internal/harness -run TestEveryJobSuppliesWhatItsRequirementSaysIsHandedIn -count=1 --- FAIL: TestEveryJobSuppliesWhatItsRequirementSaysIsHandedIn (0.01s) harness_test.go:160: requirement needs-jellyfin is handed JELLYFIN_ADDRESS, and the job of that name in .github/workflows/harness.yml sets [JELLYFIN_ADRESS] FAIL Reverted afterwards. What it cannot see is stated where it is written: a job that sets the address and starts nothing at it passes this, and what catches that half is the check refusing on an environment it cannot use. CONTRIBUTING.md says how to run the requirement here, because a contributor on a laptop cannot have a test bring a server up for them and should not have to discover that by watching it refuse. Signed-off-by: Nils Lehnen <30603423+iderex@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #54.
The needs-jellyfin requirement was carried by nothing, so asking for it refused
rather than running, and the job of that name supplied neither of the two things
the requirement is named for. Both are here, and the run below is against a real
server rather than against this repository's idea of one.
The run
Run 2026-08-23 at
4b2e224, which is this branch's head. It came back green, andwhat it printed is the evidence for every clause of the done-condition:
Three halves, and the order is the argument
The mismatched checksum runs first, so nothing has installed yet and a plugin
that is absent afterwards is absent because the install was refused. It runs
against a catalogue the check serves itself, because
decisions/manifest-address.mdtreats the published address as a promise thatcannot be withdrawn, and a knowingly broken manifest published there is served
to every operator polling it.
The same catalogue with its own checksum runs second, over the same server, the
same address and the same archive URL. That is the one-change neighbour, and
without it the refusal above would sit equally well with a server that could not
use an address of that shape at all. It uninstalls afterwards and refuses to
carry on until the server says the plugin is gone, so the half after it watches
an install arrive rather than finding one already there.
The published address runs last, on a server the half before it left clean.
What the first run against a server corrected
The check as first written read only the plugin list, on the assumption that the
server accepts an install request and refuses later while it hashes what it
downloaded. It answers the request itself instead, with a 500, so that first run
failed on a refusal that had already happened. Both spellings are read now and
the plugin list is still what decides.
The gate never depends on any of it
Taking all six out of the tracked tree, in a throwaway clone of this branch at
4b2e224that was pushed nowhere:The count of three is what says the removal reached the whole harness rather
than part of it.
The job supplies both, and something reads that now
The job brings the server up and hands the address in, which is the shape
needs-network already uses. Unset is a refusal in the check rather than a skip,
so a job that started a server and forgot to pass the address fails as a
refusing check rather than as a green run over nothing.
Nothing read a job definition that delivers half of what it names.
Requirement.Suppliesnames what the runner puts in the environment andSuppliedInreads which names each job sets. Watched refusing, on theone-character mistake somebody makes:
Reverted afterwards. What it cannot see is written where it is written: a job
that sets the address and starts nothing at it passes this reader, because a
step bringing a server up has no shape a reader of that file could recognise.
That half is caught by the check refusing on an environment it cannot use, which
is why the check refuses rather than skipping.
The means
Go and the test runner that arrives with it, which is what
decisions/means.mdalready settled for every check here, and no new dependency: the check talks to
the server over
net/httpand the standard library's JSON, andgo.modisunchanged. A container library would have been a dependency added in the same
week as the browser driver, for a server this job can start with one line
instead. The server image is pinned by digest beside its version, the way every
action in that file is pinned by commit beside its tag.
What is not covered
The negative half needs the server to reach a listener on the machine running
the check, which is why the job puts the server on this machine's own network. A
server that cannot route back fails as the catalogue never filling in, and the
failure says so.
The check refuses a server that has already been set up, because it takes the
administrator account on the one it talks to.
CONTRIBUTING.mdsays how tobring a fresh one up here rather than leaving somebody to find that out by
watching the check refuse.
Nothing in this ran with elevation, and no step asks for any. The server is an
ordinary container on a runner that is destroyed afterwards, and the check's own
listener takes a port the operating system chooses, which is above 1024 by
construction.
Who has read this
Nobody but me. There is no second reader on this change, and the run above and
the commands beside it stand in place of one rather than a claim that somebody
checked them.
An earlier branch for this issue swept an untracked directory into a commit that
was no part of this work. Nothing was rewritten: the corrected history is this
branch, under a new name, and the other one is abandoned rather than forced over.