GET /projects returns public_jobs, but POST and PUT still only take public_builds
Summary
The build
terminology has been deprecated since 9.0 but the project create and update API endpoints still require public_builds
as a param, rather than public_jobs
, which is the new, remapped param for the GET endpoint, and the only one returned in the GET response.
The build naming has been deprecated for so long you can't even find it in current docs anymore, see 12.10 for example: https://docs.gitlab.com/12.10/ee/ci/variables/deprecated_variables.html
Steps to reproduce
List projects/Get a single project and see that public_jobs
is returned:
curl -H 'Private-Token: <token>' "https://gitlab.example.com/api/v4/projects`
Create or update a project and see that only public_builds
is accepted:
curl -X POST -H 'Private-Token: <token>' --data '{"name":"<example-name>", "public_jobs":"false"}' https://gitlab.example.com/api/v4/projects
# 400: allow_merge_on_skipped_pipeline, autoclose_referenced_issues, auto_devops_enabled, auto_devops_deploy_strategy, auto_cancel_pending_pipelines, build_coverage_regex, build_git_strategy, build_timeout, builds_access_level, ci_config_path, ci_default_git_depth, ci_forward_deployment_enabled, container_registry_enabled, container_expiration_policy_attributes, default_branch, description, emails_disabled, forking_access_level, issues_access_level, lfs_enabled, merge_requests_access_level, merge_method, name, only_allow_merge_if_all_discussions_are_resolved, only_allow_merge_if_pipeline_succeeds, pages_access_level, path, printing_merge_request_link_enabled, public_builds, remove_source_branch_after_merge, repository_access_level, request_access_enabled, resolve_outdated_diff_discussions, restrict_user_defined_variables, shared_runners_enabled, snippets_access_level, tag_list, visibility, wiki_access_level, avatar, suggestion_commit_message, repository_storage, compliance_framework_setting, packages_enabled, service_desk_enabled, issues_enabled, jobs_enabled, merge_requests_enabled, wiki_enabled, snippets_enabled, approvals_before_merge, external_authorization_classification_label, fallback_approvals_required, import_url, issues_template, merge_requests_template are missing, at least one parameter must be provided
curl -X POST -H 'Private-Token: <token>' --data '{"name":"<example-name>", "public_builds":"false"}' https://gitlab.example.com/api/v4/projects
# 201 Created
Example Project
Try the above GET/PUT on any project on any instance.
What is the current bug behavior?
Get returns public_jobs
but this cannot be used to create/update projects. This is painful for people working with the API programmatically who expect to work with consistent attributes in their objects. See for example https://github.com/python-gitlab/python-gitlab/issues/1428.
What is the expected correct behavior?
A consistent public_jobs
param should be used for all endpoints.
Possible fixes
Although public_builds
is exposed as public_jobs
for the GET endpoint, public_builds
should probably be renamed instead. Maybe an opportunity to make a breaking change in 14.0? See it scattered around the codebase:
./app/views/projects/settings/ci_cd/_form.html.haml: = f.check_box :public_builds, { class: 'form-check-input' }
./app/views/projects/settings/ci_cd/_form.html.haml: = f.label :public_builds, class: 'form-check-label' do
./app/graphql/types/project_type.rb: field :public_jobs, GraphQL::BOOLEAN_TYPE, method: :public_builds, null: true,
./app/controllers/projects_controller.rb: :public_builds,
./app/controllers/projects/settings/ci_cd_controller.rb: :build_timeout_human_readable, :build_coverage_regex, :public_builds,
./app/policies/project_policy.rb: condition(:public_builds, scope: :subject, score: 0) { project.public_builds? }
./app/policies/project_policy.rb: rule { public_builds }.policy do
./app/policies/project_policy.rb: rule { public_builds & can?(:guest_access) }.policy do
./spec/finders/ci/jobs_finder_spec.rb: let_it_be(:project) { create(:project, :private, public_builds: false) }
./spec/lib/gitlab/cycle_analytics/permissions_spec.rb: let(:project) { create(:project, public_builds: false) }
./spec/lib/gitlab/ci/status/build/common_spec.rb: project.update!(public_builds: false)
./spec/lib/gitlab/import_export/safe_model_attributes.yml:- public_builds
./spec/presenters/ci/build_presenter_spec.rb: let(:project) { create(:project, public_builds: false) }
./spec/requests/api/merge_requests_spec.rb: project = create(:project, public_builds: false)
./spec/requests/api/jobs_spec.rb: create(:project, :repository, public_builds: false)
./spec/requests/api/jobs_spec.rb: project.update_column(:public_builds, true)
./spec/requests/api/jobs_spec.rb: project.update_column(:public_builds, true)
./spec/requests/api/jobs_spec.rb: project.update_column(:public_builds, true)
./spec/requests/api/jobs_spec.rb: project.update_column(:public_builds, false)
./spec/requests/api/jobs_spec.rb: project.update_column(:public_builds, true)
./spec/requests/api/jobs_spec.rb: project.update_column(:public_builds, true)
./spec/requests/api/jobs_spec.rb: let(:public_builds) { true }
./spec/requests/api/jobs_spec.rb: public_builds: public_builds)
./spec/requests/api/jobs_spec.rb: let(:public_builds) { true }
./spec/requests/api/jobs_spec.rb: let(:public_builds) { false }
./spec/requests/api/jobs_spec.rb: let(:public_builds) { true }
./spec/requests/api/jobs_spec.rb: let(:public_builds) { true }
./spec/requests/api/jobs_spec.rb: let(:public_builds) { true }
./spec/requests/api/jobs_spec.rb: where(:public_builds, :user_project_role, :expected_status) do
./spec/requests/api/jobs_spec.rb: project.update!(public_builds: public_builds)
./spec/requests/api/ci/pipelines_spec.rb: project.update!(public_builds: false)
./spec/requests/api/ci/pipelines_spec.rb: project.update!(public_builds: false)
./spec/requests/api/ci/pipeline_schedules_spec.rb: let_it_be(:project) { create(:project, :repository, public_builds: false) }
./spec/requests/api/ci/pipeline_schedules_spec.rb: let_it_be(:project) { create(:project, :repository, :public, public_builds: false) }
./spec/requests/api/project_attributes.yml: public_builds: public_jobs
./spec/requests/projects/cycle_analytics_events_spec.rb: let(:project) { create(:project, :repository, public_builds: false) }
./spec/features/security/project/public_access_spec.rb: project.update!(public_builds: true)
./spec/features/security/project/public_access_spec.rb: project.update!(public_builds: false)
./spec/features/security/project/public_access_spec.rb: project.update!(public_builds: true)
./spec/features/security/project/public_access_spec.rb: project.update!(public_builds: false)
./spec/features/security/project/public_access_spec.rb: project.update!(public_builds: true)
./spec/features/security/project/public_access_spec.rb: project.update!(public_builds: false)
./spec/features/security/project/internal_access_spec.rb: project.update!(public_builds: true)
./spec/features/security/project/internal_access_spec.rb: project.update!(public_builds: false)
./spec/features/security/project/internal_access_spec.rb: project.update!(public_builds: true)
./spec/features/security/project/internal_access_spec.rb: project.update!(public_builds: false)
./spec/features/security/project/internal_access_spec.rb: project.update!(public_builds: true)
./spec/features/security/project/internal_access_spec.rb: project.update!(public_builds: false)
./spec/features/security/project/private_access_spec.rb: let_it_be(:project, reload: true) { create(:project, :private, :repository, public_builds: false) }
./spec/features/security/project/private_access_spec.rb: project.update!(public_builds: true)
./spec/features/security/project/private_access_spec.rb: project.update!(public_builds: true)
./spec/features/security/project/private_access_spec.rb: project.update!(public_builds: true)
./spec/features/security/project/private_access_spec.rb: project.update!(public_builds: true)
./spec/features/security/project/private_access_spec.rb: project.public_builds = false
./spec/features/security/project/private_access_spec.rb: project.update!(public_builds: true)
./spec/features/security/project/private_access_spec.rb: project.update!(public_builds: false)
./spec/features/merge_request/user_sees_merge_widget_spec.rb: public_builds: false
./spec/features/dashboard/projects_spec.rb: project.update!(public_builds: false)
./spec/features/projects/jobs/permissions_spec.rb: project.update!(public_builds: false)
./spec/features/projects/jobs/permissions_spec.rb: project.update!(public_builds: true)
./spec/features/projects/jobs/permissions_spec.rb: project.update!(public_builds: false)
./spec/features/projects/jobs/permissions_spec.rb: where(:public_builds, :user_project_role, :ci_debug_trace, :expected_status_code) do
./spec/features/projects/jobs/permissions_spec.rb: project.update!(public_builds: public_builds)
./spec/features/projects/jobs/permissions_spec.rb: where(:public_builds, :user_project_role, :ci_debug_trace, :expected_status_code, :expected_msg) do
./spec/features/projects/jobs/permissions_spec.rb: project.update!(public_builds: public_builds)
./spec/features/projects/user_sees_sidebar_spec.rb: let(:project) { create(:project, :private, public_builds: false, namespace: user.namespace) }
./spec/features/projects/user_sees_sidebar_spec.rb: project.public_builds = true
./spec/features/projects/pipelines/pipeline_spec.rb: let(:project) { create(:project, :public, :repository, public_builds: false) }
./spec/features/projects/pipelines/pipeline_spec.rb: project.update!(public_builds: false)
./spec/features/projects/pipelines/pipeline_spec.rb: project.update!(public_builds: false)
./spec/features/commits_spec.rb: public_builds: false)
./spec/controllers/projects/graphs_controller_spec.rb: project.update_column(:public_builds, false)
./spec/controllers/projects/badges_controller_spec.rb: project.update!(public_builds: true)
./spec/controllers/projects/badges_controller_spec.rb: project.update!(public_builds: false)
./spec/support/shared_examples/requests/api/pipelines/visibility_table_shared_examples.rb: public_builds: public_builds
./spec/support/shared_examples/requests/api/pipelines/visibility_table_shared_examples.rb: where(:visibility_level, :builds_access_level, :public_builds, :is_admin, :user_role, :response_status) do
./spec/support/shared_examples/policies/project_policy_shared_examples.rb: project.update!(public_builds: false)
./spec/support/shared_examples/policies/project_policy_shared_examples.rb: project.update!(public_builds: false)
./spec/support/shared_examples/policies/project_policy_shared_examples.rb: project.update!(public_builds: false)
./spec/support/shared_examples/policies/project_policy_shared_examples.rb: project.update!(public_builds: false)
./spec/fixtures/lib/gitlab/import_export/designs/project.json: "public_builds":true,
./spec/fixtures/lib/gitlab/import_export/multi_pipeline_ref_one_external_pr/project.json: "public_builds": true,
./spec/fixtures/lib/gitlab/import_export/multi_pipeline_ref_one_external_pr/tree/project.json:{"id":5,"approvals_before_merge":0,"archived":false,"auto_cancel_pending_pipelines":"enabled","autoclose_referenced_issues":true,"build_allow_git_fetch":true,"build_coverage_regex":null,"build_timeout":3600,"ci_config_path":null,"delete_error":null,"description":"Vim, Tmux and others","disable_overriding_approvers_per_merge_request":null,"external_authorization_classification_label":"","external_webhook_token":"D3mVYFzZkgZ5kMfcW_wx","public_builds":true,"shared_runners_enabled":true,"visibility_level":20}
./spec/services/notification_service_spec.rb: project.update!(public_builds: false)
./spec/services/projects/update_service_spec.rb: result = update_project(project, admin, public_builds: true)
./spec/services/projects/update_service_spec.rb: expect(project.reload.public_builds).to be true
./spec/policies/ci/build_policy_spec.rb: project.update_attribute(:public_builds, false)
./lib/api/entities/project.rb: expose :public_builds, as: :public_jobs
./lib/api/helpers/projects_helpers.rb: optional :public_builds, type: Boolean, desc: 'Perform public builds'
./lib/api/helpers/projects_helpers.rb: :public_builds,
./db/structure.sql: public_builds boolean DEFAULT true NOT NULL,
./db/migrate/20181228175414_init_schema.rb: t.boolean "public_builds", default: true, null: false
./config/pseudonymizer.yml: - public_builds
./doc/api/projects.md:| `public_builds` | boolean | **{dotted-circle}** No | If `true`, jobs can be viewed by non-project members. |
./doc/api/projects.md:| `public_builds` | boolean | **{dotted-circle}** No | If `true`, jobs can be viewed by non-project-members. |
./doc/api/projects.md:| `public_builds` | boolean | **{dotted-circle}** No | If `true`, jobs can be viewed by non-project members. |
./ee/spec/requests/api/jobs_spec.rb: create(:project, :repository, public_builds: false)
./ee/spec/features/security/project/public_access_spec.rb: project.update!(public_builds: true)
./ee/spec/features/security/project/public_access_spec.rb: project.update!(public_builds: false)
./ee/spec/features/security/project/public_access_spec.rb: project.update!(public_builds: true)
./ee/spec/features/security/project/public_access_spec.rb: project.update!(public_builds: false)
./ee/spec/features/security/project/internal_access_spec.rb: project.update!(public_builds: true)
./ee/spec/features/security/project/internal_access_spec.rb: project.update!(public_builds: false)
./ee/spec/features/security/project/internal_access_spec.rb: project.update!(public_builds: true)
./ee/spec/features/security/project/internal_access_spec.rb: project.update!(public_builds: false)