From 0b729e8d76578d548c8c935184578244cbd276d5 Mon Sep 17 00:00:00 2001 From: Jarvis-J-Jacob <287212888+Jarvis-J-Jacob@users.noreply.github.com> Date: Wed, 2 Sep 2026 20:30:32 +0530 Subject: [PATCH] Fix 500 in public#binary_packages for scmsync project packages Packages of scmsync projects live in the project's git repository, not in the database. #binary_packages guarded access with check_package_access (use_source: false) and then looked @pkg up with a plain DB query, which returns nil for a package of an scmsync project. @pkg stayed nil and the action crashed at @pkg.project.repositories with "undefined method 'project' for nil" -> HTTP 500. Look @pkg up with Package.get_by_project_and_name(use_source: false, follow_project_scmsync_links: true) instead. It resolves the same database packages the previous lookup did, and additionally builds a read-only Package from the backend package meta for scmsync projects, providing the project, its repositories and the name/title/description the action and its view need. Side effect: a request for a package that does not exist on the backend now returns 404 (Package::UnknownObjectError) instead of 500. Non-scmsync projects are unaffected: a normal missing package already raised in check_package_access before this line. Fixes #20224 --- src/api/app/controllers/public_controller.rb | 6 ++- .../controllers/public_controller_spec.rb | 45 +++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/src/api/app/controllers/public_controller.rb b/src/api/app/controllers/public_controller.rb index 1ecafc2a457..2468a6aaff7 100644 --- a/src/api/app/controllers/public_controller.rb +++ b/src/api/app/controllers/public_controller.rb @@ -145,7 +145,11 @@ def show_request # GET /public/binary_packages/:project/:package def binary_packages check_package_access(params[:project], params[:package], use_source: false) - @pkg = Package.find_by_project_and_name(params[:project], params[:package]) + # Packages of scmsync projects don't exist in the database. follow_project_scmsync_links + # builds a read-only instance from the backend package meta so we can still resolve the + # project, its repositories and the package name/title/description below. + @pkg = Package.get_by_project_and_name(params[:project], params[:package], + use_source: false, follow_project_scmsync_links: true) begin binaries = Xmlhash.parse(Backend::Api::Search.published_binaries_for_package(params[:project], params[:package])) diff --git a/src/api/spec/controllers/public_controller_spec.rb b/src/api/spec/controllers/public_controller_spec.rb index dcda24c8b68..fcfb998ad1d 100644 --- a/src/api/spec/controllers/public_controller_spec.rb +++ b/src/api/spec/controllers/public_controller_spec.rb @@ -189,4 +189,49 @@ it { expect(revisions.count).to eq(1) } end end + + # NOTE: defined last on purpose. VCR names cassettes for anonymous `it { ... }` + # examples after their scoped id, so inserting an example group earlier in this + # file would renumber the recorded cassettes of the groups that follow it. + describe 'GET #binary_packages for a package of an scmsync project' do + let(:scmsync_project) do + create(:project, name: 'scmsync_public_controller_project', scmsync: 'https://github.com/example/scmsync-project.git') + end + let(:package_meta) do + <<~XML + + eza title + eza description + + XML + end + + before do + allow(Backend::Api::Search).to receive(:published_binaries_for_package).and_return('') + end + + context 'when the package exists on the backend' do + before do + # scmsync packages don't exist in the database, they are served from the backend + allow(Backend::Api::Sources::Package).to receive(:meta).and_return(package_meta) + + get :binary_packages, params: { project: scmsync_project.name, package: 'eza' } + end + + it { expect(response).to have_http_status(:success) } + it { expect(assigns(:pkg)).to be_a(Package) } + it { expect(assigns(:pkg).name).to eq('eza') } + it { expect(assigns(:pkg).project).to eq(scmsync_project) } + end + + context 'when the package does not exist on the backend' do + before do + allow(Backend::Api::Sources::Package).to receive(:meta).and_raise(Backend::NotFoundError) + + get :binary_packages, params: { project: scmsync_project.name, package: 'does_not_exist' } + end + + it { expect(response).to have_http_status(:not_found) } + end + end end