warp_engine: CiRepository -> Pipeline rename everywhere, ci_ service prefixes dropped, unknown platform allowed
This commit is contained in:
+20
-20
@@ -10,7 +10,7 @@
|
||||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema[8.1].define(version: 2026_08_06_000001) do
|
||||
ActiveRecord::Schema[8.1].define(version: 2026_08_06_000002) do
|
||||
create_table "application_tokens", id: { type: :bigint, unsigned: true }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
|
||||
t.datetime "created_at", precision: 3
|
||||
t.datetime "deleted_at", precision: 3
|
||||
@@ -29,24 +29,6 @@ ActiveRecord::Schema[8.1].define(version: 2026_08_06_000001) do
|
||||
t.index ["token_digest"], name: "idx_application_tokens_token_digest", unique: true
|
||||
end
|
||||
|
||||
create_table "ci_repositories", id: { type: :bigint, unsigned: true }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
|
||||
t.boolean "active", default: true, null: false
|
||||
t.datetime "created_at", precision: 3
|
||||
t.datetime "deleted_at", precision: 3
|
||||
t.datetime "last_pipeline_at", precision: 3
|
||||
t.string "last_pipeline_status", limit: 32
|
||||
t.string "platform", limit: 32, null: false
|
||||
t.string "repo_name", limit: 128, null: false
|
||||
t.string "repo_owner", limit: 128, null: false
|
||||
t.bigint "software_id", unsigned: true
|
||||
t.datetime "updated_at", precision: 3
|
||||
t.bigint "woodpecker_repo_id", null: false, unsigned: true
|
||||
t.index ["deleted_at"], name: "idx_ci_repos_deleted"
|
||||
t.index ["repo_owner", "repo_name"], name: "idx_ci_repos_owner_name", unique: true
|
||||
t.index ["software_id"], name: "idx_ci_repos_software"
|
||||
t.index ["woodpecker_repo_id"], name: "idx_ci_repos_wp_id", unique: true
|
||||
end
|
||||
|
||||
create_table "downloads", id: { type: :bigint, unsigned: true }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
|
||||
t.datetime "created_at", precision: 3
|
||||
t.datetime "deleted_at", precision: 3
|
||||
@@ -82,6 +64,24 @@ ActiveRecord::Schema[8.1].define(version: 2026_08_06_000001) do
|
||||
t.index ["deleted_at"], name: "idx_images_deleted_at"
|
||||
end
|
||||
|
||||
create_table "pipelines", id: { type: :bigint, unsigned: true }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
|
||||
t.boolean "active", default: true, null: false
|
||||
t.datetime "created_at", precision: 3
|
||||
t.datetime "deleted_at", precision: 3
|
||||
t.datetime "last_pipeline_at", precision: 3
|
||||
t.string "last_pipeline_status", limit: 32
|
||||
t.string "platform", limit: 32, null: false
|
||||
t.string "repo_name", limit: 128, null: false
|
||||
t.string "repo_owner", limit: 128, null: false
|
||||
t.bigint "software_id", unsigned: true
|
||||
t.datetime "updated_at", precision: 3
|
||||
t.bigint "woodpecker_repo_id", null: false, unsigned: true
|
||||
t.index ["deleted_at"], name: "idx_pipelines_deleted"
|
||||
t.index ["repo_owner", "repo_name"], name: "idx_pipelines_owner_name", unique: true
|
||||
t.index ["software_id"], name: "idx_pipelines_software"
|
||||
t.index ["woodpecker_repo_id"], name: "idx_pipelines_wp_id", unique: true
|
||||
end
|
||||
|
||||
create_table "platform_links", id: { type: :bigint, unsigned: true }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
|
||||
t.datetime "created_at", precision: 3
|
||||
t.datetime "deleted_at", precision: 3
|
||||
@@ -155,9 +155,9 @@ ActiveRecord::Schema[8.1].define(version: 2026_08_06_000001) do
|
||||
t.datetime "updated_at", precision: 3
|
||||
end
|
||||
|
||||
add_foreign_key "ci_repositories", "softwares", name: "fk_ci_repos_software", on_delete: :nullify
|
||||
add_foreign_key "downloads", "releases", name: "fk_downloads_release", on_delete: :nullify
|
||||
add_foreign_key "external_links", "softwares", name: "fk_softwares_external_links", on_delete: :cascade
|
||||
add_foreign_key "pipelines", "softwares", name: "fk_pipelines_software", on_delete: :nullify
|
||||
add_foreign_key "release_assets", "releases", name: "fk_releases_release_assets", on_delete: :cascade
|
||||
add_foreign_key "releases", "softwares", name: "fk_softwares_releases", on_delete: :cascade
|
||||
add_foreign_key "software_images", "images"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
FactoryBot.define do
|
||||
factory :ci_repository, class: "WarpEngine::CiRepository" do
|
||||
factory :pipeline, class: "WarpEngine::Pipeline" do
|
||||
sequence(:woodpecker_repo_id) { |n| n }
|
||||
repo_owner { "testorg" }
|
||||
sequence(:repo_name) { |n| "game-#{n}" }
|
||||
@@ -1,8 +1,8 @@
|
||||
require "rails_helper"
|
||||
|
||||
RSpec.describe WarpEngine::CiRepository do
|
||||
RSpec.describe WarpEngine::Pipeline do
|
||||
describe "validations" do
|
||||
subject { build(:ci_repository) }
|
||||
subject { build(:pipeline) }
|
||||
|
||||
it { is_expected.to validate_presence_of(:woodpecker_repo_id) }
|
||||
it { is_expected.to validate_uniqueness_of(:woodpecker_repo_id) }
|
||||
@@ -11,29 +11,34 @@ RSpec.describe WarpEngine::CiRepository do
|
||||
it { is_expected.to validate_presence_of(:platform) }
|
||||
|
||||
it "rejects unsupported platforms" do
|
||||
repo = build(:ci_repository, platform: "amiga")
|
||||
repo = build(:pipeline, platform: "amiga")
|
||||
expect(repo).not_to be_valid
|
||||
expect(repo.errors[:platform]).to be_present
|
||||
end
|
||||
|
||||
WarpEngine::PlatformLink::SUPPORTED_PLATFORMS.each do |p|
|
||||
it "accepts #{p}" do
|
||||
repo = build(:ci_repository, platform: p)
|
||||
repo = build(:pipeline, platform: p)
|
||||
expect(repo).to be_valid
|
||||
end
|
||||
end
|
||||
|
||||
it "accepts the unknown placeholder platform" do
|
||||
repo = build(:pipeline, platform: WarpEngine::Pipeline::UNKNOWN_PLATFORM)
|
||||
expect(repo).to be_valid
|
||||
end
|
||||
end
|
||||
|
||||
describe "#full_name" do
|
||||
it "returns owner/name" do
|
||||
repo = build(:ci_repository, repo_owner: "games", repo_name: "mygame")
|
||||
repo = build(:pipeline, repo_owner: "games", repo_name: "mygame")
|
||||
expect(repo.full_name).to eq("games/mygame")
|
||||
end
|
||||
end
|
||||
|
||||
describe "default_scope" do
|
||||
it "excludes soft-deleted records" do
|
||||
repo = create(:ci_repository)
|
||||
repo = create(:pipeline)
|
||||
repo.update_column(:deleted_at, Time.current)
|
||||
|
||||
expect(described_class.all).not_to include(repo)
|
||||
@@ -43,8 +48,8 @@ RSpec.describe WarpEngine::CiRepository do
|
||||
|
||||
describe ".active" do
|
||||
it "returns only active repos" do
|
||||
active = create(:ci_repository, active: true)
|
||||
inactive = create(:ci_repository, active: false)
|
||||
active = create(:pipeline, active: true)
|
||||
inactive = create(:pipeline, active: false)
|
||||
|
||||
expect(described_class.active).to include(active)
|
||||
expect(described_class.active).not_to include(inactive)
|
||||
@@ -7,11 +7,11 @@ RSpec.describe "CI API", type: :request do
|
||||
allow(WarpEngine.config).to receive(:update_secret).and_return("s3cret")
|
||||
end
|
||||
|
||||
describe "GET /api/ci/repos" do
|
||||
describe "GET /api/ci/pipelines" do
|
||||
it "returns active repos" do
|
||||
repo = create(:ci_repository, repo_name: "mygame", platform: "tic80")
|
||||
repo = create(:pipeline, repo_name: "mygame", platform: "tic80")
|
||||
|
||||
get "/api/ci/repos"
|
||||
get "/api/ci/pipelines"
|
||||
|
||||
expect(response).to have_http_status(:ok)
|
||||
json = JSON.parse(response.body)
|
||||
@@ -22,45 +22,45 @@ RSpec.describe "CI API", type: :request do
|
||||
it "returns 503 when woodpecker not configured" do
|
||||
allow(WarpEngine.config).to receive(:woodpecker_url).and_return(nil)
|
||||
|
||||
get "/api/ci/repos"
|
||||
get "/api/ci/pipelines"
|
||||
|
||||
expect(response).to have_http_status(:service_unavailable)
|
||||
end
|
||||
end
|
||||
|
||||
describe "GET /api/ci/repos/:id/status" do
|
||||
describe "GET /api/ci/pipelines/:id/status" do
|
||||
it "returns repo with pipeline status" do
|
||||
repo = create(:ci_repository, repo_owner: "org", repo_name: "game")
|
||||
repo = create(:pipeline, repo_owner: "org", repo_name: "game")
|
||||
client = instance_double(WarpEngine::WoodpeckerClient)
|
||||
allow(WarpEngine::WoodpeckerClient).to receive(:new).and_return(client)
|
||||
allow(client).to receive(:get_pipeline)
|
||||
.and_return({ "number" => 1, "status" => "success" })
|
||||
|
||||
get "/api/ci/repos/#{repo.id}/status"
|
||||
get "/api/ci/pipelines/#{repo.id}/status"
|
||||
|
||||
expect(response).to have_http_status(:ok)
|
||||
json = JSON.parse(response.body)
|
||||
expect(json["repo"]["repo_name"]).to eq("game")
|
||||
expect(json["pipeline"]["repo_name"]).to eq("game")
|
||||
end
|
||||
end
|
||||
|
||||
describe "POST /api/ci/repos/:id/trigger" do
|
||||
describe "POST /api/ci/pipelines/:id/trigger" do
|
||||
it "requires authentication" do
|
||||
repo = create(:ci_repository)
|
||||
repo = create(:pipeline)
|
||||
|
||||
post "/api/ci/repos/#{repo.id}/trigger"
|
||||
post "/api/ci/pipelines/#{repo.id}/trigger"
|
||||
|
||||
expect(response).to have_http_status(:unauthorized)
|
||||
end
|
||||
|
||||
it "triggers a pipeline with valid secret" do
|
||||
repo = create(:ci_repository, repo_owner: "org", repo_name: "game")
|
||||
repo = create(:pipeline, repo_owner: "org", repo_name: "game")
|
||||
client = instance_double(WarpEngine::WoodpeckerClient)
|
||||
allow(WarpEngine::WoodpeckerClient).to receive(:new).and_return(client)
|
||||
allow(client).to receive(:trigger_pipeline)
|
||||
.and_return({ "number" => 7, "status" => "pending" })
|
||||
|
||||
post "/api/ci/repos/#{repo.id}/trigger",
|
||||
post "/api/ci/pipelines/#{repo.id}/trigger",
|
||||
headers: { "X-Update-Secret" => "s3cret" }
|
||||
|
||||
expect(response).to have_http_status(:ok)
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
require "rails_helper"
|
||||
|
||||
RSpec.describe WarpEngine::CiPipelineService do
|
||||
RSpec.describe WarpEngine::PipelineService do
|
||||
let(:client) { instance_double(WarpEngine::WoodpeckerClient) }
|
||||
let(:service) { described_class.new(client: client) }
|
||||
|
||||
describe "#trigger" do
|
||||
it "delegates to client" do
|
||||
repo = build(:ci_repository, repo_owner: "org", repo_name: "game")
|
||||
repo = build(:pipeline, repo_owner: "org", repo_name: "game")
|
||||
allow(client).to receive(:trigger_pipeline).and_return({ "number" => 6 })
|
||||
|
||||
result = service.trigger(repo, branch: "main")
|
||||
@@ -18,7 +18,7 @@ RSpec.describe WarpEngine::CiPipelineService do
|
||||
|
||||
describe "#list_pipelines" do
|
||||
it "returns paginated pipelines and refreshes the repo's cached last pipeline" do
|
||||
repo = create(:ci_repository, repo_owner: "org", repo_name: "game")
|
||||
repo = create(:pipeline, repo_owner: "org", repo_name: "game")
|
||||
pipelines = [
|
||||
{ "number" => 2, "status" => "failure", "created" => 1_754_500_000 },
|
||||
{ "number" => 1, "status" => "success", "created" => 1_754_400_000 }
|
||||
@@ -33,7 +33,7 @@ RSpec.describe WarpEngine::CiPipelineService do
|
||||
end
|
||||
|
||||
it "does not touch the cache on later pages" do
|
||||
repo = create(:ci_repository, repo_owner: "org", repo_name: "game",
|
||||
repo = create(:pipeline, repo_owner: "org", repo_name: "game",
|
||||
last_pipeline_status: "success")
|
||||
allow(client).to receive(:list_pipelines).with(repo.woodpecker_repo_id, page: 2)
|
||||
.and_return([{ "number" => 1, "status" => "failure", "created" => 1_754_400_000 }])
|
||||
@@ -44,7 +44,7 @@ RSpec.describe WarpEngine::CiPipelineService do
|
||||
end
|
||||
|
||||
it "leaves the cache alone when the repo has no pipelines" do
|
||||
repo = create(:ci_repository, repo_owner: "org", repo_name: "game",
|
||||
repo = create(:pipeline, repo_owner: "org", repo_name: "game",
|
||||
last_pipeline_status: "success")
|
||||
allow(client).to receive(:list_pipelines).and_return([])
|
||||
|
||||
+5
-5
@@ -1,11 +1,11 @@
|
||||
require "rails_helper"
|
||||
|
||||
RSpec.describe WarpEngine::CiRepoSyncService do
|
||||
RSpec.describe WarpEngine::PipelineSyncService do
|
||||
let(:client) { instance_double(WarpEngine::WoodpeckerClient) }
|
||||
let(:service) { described_class.new(client: client) }
|
||||
|
||||
describe "#sync_all" do
|
||||
it "creates new CiRepository records from Woodpecker" do
|
||||
it "creates new Pipeline records from Woodpecker" do
|
||||
allow(client).to receive(:list_repos).and_return([
|
||||
{ "id" => 1, "name" => "mygame", "owner" => "org", "active" => true }
|
||||
])
|
||||
@@ -20,7 +20,7 @@ RSpec.describe WarpEngine::CiRepoSyncService do
|
||||
end
|
||||
|
||||
it "updates existing records" do
|
||||
existing = create(:ci_repository, woodpecker_repo_id: 1, repo_name: "old", platform: "tic80")
|
||||
existing = create(:pipeline, woodpecker_repo_id: 1, repo_name: "old", platform: "tic80")
|
||||
allow(client).to receive(:list_repos).and_return([
|
||||
{ "id" => 1, "name" => "newname", "owner" => "org", "active" => true }
|
||||
])
|
||||
@@ -32,7 +32,7 @@ RSpec.describe WarpEngine::CiRepoSyncService do
|
||||
end
|
||||
|
||||
it "deactivates repos missing from Woodpecker" do
|
||||
orphan = create(:ci_repository, woodpecker_repo_id: 99, active: true)
|
||||
orphan = create(:pipeline, woodpecker_repo_id: 99, active: true)
|
||||
allow(client).to receive(:list_repos).and_return([])
|
||||
|
||||
result = service.sync_all
|
||||
@@ -70,7 +70,7 @@ RSpec.describe WarpEngine::CiRepoSyncService do
|
||||
|
||||
describe "#deactivate" do
|
||||
it "calls client and marks repo inactive" do
|
||||
repo = create(:ci_repository, woodpecker_repo_id: 42, active: true)
|
||||
repo = create(:pipeline, woodpecker_repo_id: 42, active: true)
|
||||
allow(client).to receive(:deactivate_repo).with(42)
|
||||
|
||||
service.deactivate(42)
|
||||
+18
-18
@@ -1,6 +1,6 @@
|
||||
require "rails_helper"
|
||||
|
||||
RSpec.describe WarpEngine::CiSecretSyncService do
|
||||
RSpec.describe WarpEngine::SecretSyncService do
|
||||
let(:client) { instance_double(WarpEngine::WoodpeckerClient) }
|
||||
let(:service) { described_class.new(client: client) }
|
||||
|
||||
@@ -11,11 +11,11 @@ RSpec.describe WarpEngine::CiSecretSyncService do
|
||||
|
||||
describe "#provision" do
|
||||
it "creates secrets on repos that don't have one" do
|
||||
repo = create(:ci_repository)
|
||||
repo = create(:pipeline)
|
||||
allow(client).to receive(:list_secrets).with(repo.woodpecker_repo_id).and_return([])
|
||||
allow(client).to receive(:create_secret)
|
||||
|
||||
result = service.provision("plaintoken", repos: [ repo ])
|
||||
result = service.provision("plaintoken", pipelines: [ repo ])
|
||||
|
||||
expect(result[:synced]).to eq([ repo ])
|
||||
expect(client).to have_received(:create_secret).with(
|
||||
@@ -24,12 +24,12 @@ RSpec.describe WarpEngine::CiSecretSyncService do
|
||||
end
|
||||
|
||||
it "updates secrets on repos that already have one" do
|
||||
repo = create(:ci_repository)
|
||||
repo = create(:pipeline)
|
||||
allow(client).to receive(:list_secrets).with(repo.woodpecker_repo_id)
|
||||
.and_return([ { "name" => "application_token" } ])
|
||||
allow(client).to receive(:update_secret)
|
||||
|
||||
result = service.provision("newtoken", repos: [ repo ])
|
||||
result = service.provision("newtoken", pipelines: [ repo ])
|
||||
|
||||
expect(result[:synced]).to eq([ repo ])
|
||||
expect(client).to have_received(:update_secret).with(
|
||||
@@ -38,12 +38,12 @@ RSpec.describe WarpEngine::CiSecretSyncService do
|
||||
end
|
||||
|
||||
it "records failed repos without raising" do
|
||||
repo = create(:ci_repository)
|
||||
repo = create(:pipeline)
|
||||
allow(client).to receive(:list_secrets).and_raise(
|
||||
WarpEngine::WoodpeckerClient::ConnectionError, "unreachable"
|
||||
)
|
||||
|
||||
result = service.provision("tok", repos: [ repo ])
|
||||
result = service.provision("tok", pipelines: [ repo ])
|
||||
|
||||
expect(result[:synced]).to be_empty
|
||||
expect(result[:failed].size).to eq(1)
|
||||
@@ -54,7 +54,7 @@ RSpec.describe WarpEngine::CiSecretSyncService do
|
||||
it "deletes secrets from all relevant repos" do
|
||||
token = create(:application_token)
|
||||
sw = create(:software, name: "game1", owner: token.owner)
|
||||
repo = create(:ci_repository, :with_software, software: sw)
|
||||
repo = create(:pipeline, :with_software, software: sw)
|
||||
|
||||
allow(client).to receive(:delete_secret)
|
||||
|
||||
@@ -68,7 +68,7 @@ RSpec.describe WarpEngine::CiSecretSyncService do
|
||||
it "creates new token, provisions, revokes old" do
|
||||
token = create(:application_token)
|
||||
sw = create(:software, name: "game1", owner: token.owner)
|
||||
repo = create(:ci_repository, :with_software, software: sw)
|
||||
repo = create(:pipeline, :with_software, software: sw)
|
||||
|
||||
allow(client).to receive(:list_secrets).and_return([])
|
||||
allow(client).to receive(:create_secret)
|
||||
@@ -83,7 +83,7 @@ RSpec.describe WarpEngine::CiSecretSyncService do
|
||||
it "rolls back if all repos fail" do
|
||||
token = create(:application_token)
|
||||
sw = create(:software, name: "game1", owner: token.owner)
|
||||
create(:ci_repository, :with_software, software: sw)
|
||||
create(:pipeline, :with_software, software: sw)
|
||||
|
||||
allow(client).to receive(:list_secrets).and_raise(
|
||||
WarpEngine::WoodpeckerClient::ConnectionError, "down"
|
||||
@@ -101,17 +101,17 @@ RSpec.describe WarpEngine::CiSecretSyncService do
|
||||
result = service.rotate(token)
|
||||
|
||||
expect(result[:rotated]).to be false
|
||||
expect(result[:reason]).to eq("no repos")
|
||||
expect(result[:reason]).to eq("no pipelines")
|
||||
end
|
||||
end
|
||||
|
||||
describe "#repos_for_token" do
|
||||
describe "#pipelines_for_token" do
|
||||
it "returns all active repos for unrestricted tokens" do
|
||||
token = create(:application_token, :unrestricted)
|
||||
repo1 = create(:ci_repository)
|
||||
create(:ci_repository, :inactive)
|
||||
repo1 = create(:pipeline)
|
||||
create(:pipeline, :inactive)
|
||||
|
||||
repos = service.repos_for_token(token)
|
||||
repos = service.pipelines_for_token(token)
|
||||
|
||||
expect(repos).to eq([ repo1 ])
|
||||
end
|
||||
@@ -120,10 +120,10 @@ RSpec.describe WarpEngine::CiSecretSyncService do
|
||||
token = create(:application_token)
|
||||
own_sw = create(:software, name: "mine", owner: token.owner)
|
||||
other_sw = create(:software, name: "theirs", owner: create(:test_owner))
|
||||
own_repo = create(:ci_repository, :with_software, software: own_sw)
|
||||
create(:ci_repository, :with_software, software: other_sw)
|
||||
own_repo = create(:pipeline, :with_software, software: own_sw)
|
||||
create(:pipeline, :with_software, software: other_sw)
|
||||
|
||||
repos = service.repos_for_token(token)
|
||||
repos = service.pipelines_for_token(token)
|
||||
|
||||
expect(repos).to eq([ own_repo ])
|
||||
end
|
||||
Reference in New Issue
Block a user