A version header on every WarpEngine response, and a movable pipeline link
Every WarpEngine API response now carries `WarpEngine-Version`, so a client can branch on the engine's age without a round trip to ask. Set in a before_action rather than after: `rescue_from` never reaches an after_action, and a client needs the version most when something came back wrong. The name lives in `WarpEngine::VERSION_HEADER`. The host's own endpoints — the store registry — do not carry it, because they are not the engine. A software has one pipeline, and the newest assignment now wins. Two pipelines pointing at the same software was not an error the database caught; it was a link that silently did nothing, with the software still showing whichever row came first. Assigning a software another pipeline holds therefore moves it, the admin says which pipeline it was taken from, and `Pipeline#software_taken_from` carries that for anything else that cares. Deliberately a callback and not a unique index: rows here are soft-deleted, and a unique index counts deleted rows, so a pipeline removed last year would block its software from ever being linked again. The engine is 0.4.0. The site's /stores page and its screenshot follow the client's new name, and the shot is a fresh one showing the greyed-out titles the client now lists. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -59,4 +59,42 @@ RSpec.describe WarpEngine::Pipeline do
|
||||
describe "associations" do
|
||||
it { is_expected.to belong_to(:software).optional }
|
||||
end
|
||||
|
||||
# A software has one pipeline. Two pipelines pointing at the same one is not an error
|
||||
# the database catches, it is a link that silently does nothing — so the newest
|
||||
# assignment takes it, and says what it took it from.
|
||||
describe "assigning a software another pipeline already has" do
|
||||
it "moves the link and leaves the other pipeline without one" do
|
||||
software = create(:software)
|
||||
held_by = create(:pipeline, software: software, repo_owner: "games", repo_name: "old")
|
||||
taking = create(:pipeline, repo_owner: "games", repo_name: "new")
|
||||
|
||||
taking.update!(software: software)
|
||||
|
||||
expect(taking.reload.software).to eq(software)
|
||||
expect(held_by.reload.software).to be_nil
|
||||
expect(software.reload.pipeline).to eq(taking)
|
||||
end
|
||||
|
||||
it "names the pipelines it took the software from" do
|
||||
software = create(:software)
|
||||
create(:pipeline, software: software, repo_owner: "games", repo_name: "old")
|
||||
taking = create(:pipeline, repo_owner: "games", repo_name: "new")
|
||||
|
||||
taking.update!(software: software)
|
||||
|
||||
expect(taking.software_taken_from).to eq([ "games/old" ])
|
||||
end
|
||||
|
||||
it "leaves other pipelines alone when the software is cleared" do
|
||||
software = create(:software)
|
||||
keeps = create(:pipeline, software: software, repo_owner: "games", repo_name: "keeps")
|
||||
other = create(:pipeline, repo_owner: "games", repo_name: "other")
|
||||
|
||||
other.update!(software: nil)
|
||||
|
||||
expect(keeps.reload.software).to eq(software)
|
||||
expect(other.software_taken_from).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
require "rails_helper"
|
||||
|
||||
# Every response the engine serves carries the version that served it, so a client can
|
||||
# branch on the engine's age without asking a separate endpoint for it.
|
||||
RSpec.describe "the WarpEngine-Version header", type: :request do
|
||||
it "is on a normal response" do
|
||||
create(:software)
|
||||
|
||||
get "/api/software"
|
||||
|
||||
expect(response).to have_http_status(:ok)
|
||||
expect(response.headers["WarpEngine-Version"]).to eq(WarpEngine::VERSION)
|
||||
end
|
||||
|
||||
# The one a client needs most: something came back wrong, and it wants to know whether
|
||||
# the engine on the other end is old enough to explain it. `rescue_from` never reaches
|
||||
# an after_action, which is why the header is set before the action runs.
|
||||
it "is on an error response" do
|
||||
get "/api/image/999999"
|
||||
|
||||
expect(response).to have_http_status(:not_found)
|
||||
expect(response.headers["WarpEngine-Version"]).to eq(WarpEngine::VERSION)
|
||||
end
|
||||
|
||||
it "is on a served file" do
|
||||
get "/file/nothing-here.zip"
|
||||
|
||||
expect(response.headers["WarpEngine-Version"]).to eq(WarpEngine::VERSION)
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user