diff --git a/app/assets/images/auth_buttons/bitbucket_64.png b/app/assets/images/auth_buttons/bitbucket_64.png index 0edf7f52a11..06a68b9bf55 100644 Binary files a/app/assets/images/auth_buttons/bitbucket_64.png and b/app/assets/images/auth_buttons/bitbucket_64.png differ diff --git a/app/assets/images/auth_buttons/twitter_64.png b/app/assets/images/auth_buttons/twitter_64.png index a4f14de57ae..15596b0f30a 100644 Binary files a/app/assets/images/auth_buttons/twitter_64.png and b/app/assets/images/auth_buttons/twitter_64.png differ diff --git a/app/assets/javascripts/boards/components/issue_board_filtered_search.vue b/app/assets/javascripts/boards/components/issue_board_filtered_search.vue index 7fc87f9f672..6bfdbb674a2 100644 --- a/app/assets/javascripts/boards/components/issue_board_filtered_search.vue +++ b/app/assets/javascripts/boards/components/issue_board_filtered_search.vue @@ -157,6 +157,7 @@ export default { symbol: '%', token: MilestoneToken, unique: true, + shouldSkipSort: true, fetchMilestones: this.fetchMilestones, }, { diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/milestone_token.vue b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/milestone_token.vue index 0d3394788fa..11c081ab4f8 100644 --- a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/milestone_token.vue +++ b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/milestone_token.vue @@ -57,7 +57,12 @@ export default { .fetchMilestones(searchTerm) .then((response) => { const data = Array.isArray(response) ? response : response.data; - this.milestones = data.slice().sort(sortMilestonesByDueDate); + + if (this.config.shouldSkipSort) { + this.milestones = data; + } else { + this.milestones = data.slice().sort(sortMilestonesByDueDate); + } }) .catch(() => { createFlash({ message: __('There was a problem fetching milestones.') }); diff --git a/app/presenters/blobs/unfold_presenter.rb b/app/presenters/blobs/unfold_presenter.rb index b921b5bf670..90b471abf22 100644 --- a/app/presenters/blobs/unfold_presenter.rb +++ b/app/presenters/blobs/unfold_presenter.rb @@ -108,7 +108,7 @@ module Blobs def limit(lines) return lines if full? - lines[since - 1..to - 1] + lines[since - 1..to - 1] || [] end end end diff --git a/config/feature_flags/development/json_limited_encoder.yml b/config/feature_flags/development/json_limited_encoder.yml index 409bae16b90..346013230a3 100644 --- a/config/feature_flags/development/json_limited_encoder.yml +++ b/config/feature_flags/development/json_limited_encoder.yml @@ -5,4 +5,4 @@ rollout_issue_url: milestone: '13.3' type: development group: group::source code -default_enabled: false +default_enabled: true diff --git a/config/feature_flags/development/security_finding_build_disable_joins.yml b/config/feature_flags/development/security_finding_build_disable_joins.yml deleted file mode 100644 index 7432882eca8..00000000000 --- a/config/feature_flags/development/security_finding_build_disable_joins.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: security_finding_build_disable_joins -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/72738 -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/342380 -milestone: '14.4' -type: development -group: group::threat insights -default_enabled: true diff --git a/doc/api/deployments.md b/doc/api/deployments.md index 70b3e76c3dd..7fc870006ac 100644 --- a/doc/api/deployments.md +++ b/doc/api/deployments.md @@ -23,7 +23,7 @@ GET /projects/:id/deployments | `updated_after` | datetime | no | Return deployments updated after the specified date. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). | | `updated_before` | datetime | no | Return deployments updated before the specified date. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). | | `environment` | string | no | The [name of the environment](../ci/environments/index.md) to filter deployments by. | -| `status` | string | no | The status to filter deployments by. One of `created`, `running`, `success`, `failed`, `canceled`. +| `status` | string | no | The status to filter deployments by. One of `created`, `running`, `success`, `failed`, `canceled`, `blocked`. ```shell curl --header "PRIVATE-TOKEN: " "https://gitlab.example.com/api/v4/projects/1/deployments" @@ -201,6 +201,7 @@ Example response: "sha": "a91957a858320c0e17f3a0eca7cfacbff50ea29a", "created_at": "2016-08-11T11:32:35.444Z", "updated_at": "2016-08-11T11:34:01.123Z", + "status": "success", "user": { "name": "Administrator", "username": "root", @@ -264,6 +265,29 @@ Example response: } ``` +Deployments created by users on GitLab Premium or higher include the `approvals` and `pending_approval_count` properties: + +```json +{ + "status": "created", + "pending_approval_count": 0, + "approvals": [ + { + "user": { + "id": 49, + "username": "project_6_bot", + "name": "****", + "state": "active", + "avatar_url": "https://www.gravatar.com/avatar/e83ac685f68ea07553ad3054c738c709?s=80&d=identicon", + "web_url": "http://localhost:3000/project_6_bot" + }, + "status": "approved" + } + ], + ... +} +``` + ## Create a deployment ```plaintext @@ -311,6 +335,29 @@ Example response: } ``` +Deployments created by users on GitLab Premium or higher include the `approvals` and `pending_approval_count` properties: + +```json +{ + "status": "created", + "pending_approval_count": 0, + "approvals": [ + { + "user": { + "id": 49, + "username": "project_6_bot", + "name": "****", + "state": "active", + "avatar_url": "https://www.gravatar.com/avatar/e83ac685f68ea07553ad3054c738c709?s=80&d=identicon", + "web_url": "http://localhost:3000/project_6_bot" + }, + "status": "approved" + } + ], + ... +} +``` + ## Update a deployment ```plaintext @@ -354,6 +401,29 @@ Example response: } ``` +Deployments created by users on GitLab Premium or higher include the `approvals` and `pending_approval_count` properties: + +```json +{ + "status": "created", + "pending_approval_count": 0, + "approvals": [ + { + "user": { + "id": 49, + "username": "project_6_bot", + "name": "****", + "state": "active", + "avatar_url": "https://www.gravatar.com/avatar/e83ac685f68ea07553ad3054c738c709?s=80&d=identicon", + "web_url": "http://localhost:3000/project_6_bot" + }, + "status": "approved" + } + ], + ... +} +``` + ## List of merge requests associated with a deployment > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/35739) in GitLab 12.7. diff --git a/doc/ci/environments/deployment_approvals.md b/doc/ci/environments/deployment_approvals.md index 08d802c15cc..a82da42d24a 100644 --- a/doc/ci/environments/deployment_approvals.md +++ b/doc/ci/environments/deployment_approvals.md @@ -95,7 +95,11 @@ curl --data "status=approved" \ #### Using the API -Use the [Deployments API](../../api/deployments.md) to see deployments. The `status` field indicates if a deployment is blocked. +Use the [Deployments API](../../api/deployments.md) to see deployments. + +- The `status` field indicates if a deployment is blocked. +- The `pending_approval_count` field indicates how many approvals are remaining to run a deployment. +- The `approvals` field contains the deployment's approvals. ## Related features diff --git a/doc/ci/pipelines/settings.md b/doc/ci/pipelines/settings.md index 38516d83b89..7db57fc44ff 100644 --- a/doc/ci/pipelines/settings.md +++ b/doc/ci/pipelines/settings.md @@ -211,15 +211,25 @@ averaged. To define a coverage-parsing regular expression: -- In the GitLab UI: - - 1. On the top bar, select **Menu > Projects** and find your project. - 1. On the left sidebar, select **Settings > CI/CD**. - 1. Expand **General pipelines**. - 1. In the **Test coverage parsing** field, enter a regular expression. Leave blank to disable this feature. - - Using the project's `.gitlab-ci.yml`, provide a regular expression using the [`coverage`](../yaml/index.md#coverage) - keyword. + keyword. Setting the regular expression this way takes precedence over the project's CI/CD settings. + +- Using the Project's CI/CD settings: + - Set using the GitLab UI: + + 1. On the top bar, select **Menu > Projects** and find your project. + 1. On the left sidebar, select **Settings > CI/CD**. + 1. Expand **General pipelines**. + 1. In the **Test coverage parsing** field, enter a regular expression. Leave blank to disable this feature. + + - Set when [editing a project](../../api/projects.md#edit-project) or [creating a project](../../api/projects.md#create-project) + using the GitLab API with the `build_coverage_regex` attribute: + + ```shell + curl --request PUT --header "PRIVATE-TOKEN: " \ + --url 'https://gitlab.com/api/v4/projects/' \ + --data "build_coverage_regex=" + ``` You can use to test your regular expression. The regular expression returns the **last** match found in the output. diff --git a/doc/ci/yaml/index.md b/doc/ci/yaml/index.md index aba47d25332..c4c897571e7 100644 --- a/doc/ci/yaml/index.md +++ b/doc/ci/yaml/index.md @@ -1348,6 +1348,8 @@ In this example: **Additional details**: +- Coverage regular expressions set in `gitlab-ci.yml` take precedence over coverage regular expression set in the + [GitLab UI](../pipelines/settings.md#add-test-coverage-results-to-a-merge-request). - If there is more than one matched line in the job output, the last line is used (the first result of reverse search). - If there are multiple matches in a single line, the last match is searched diff --git a/lib/api/deployments.rb b/lib/api/deployments.rb index 486ff5d89bc..6939853c06b 100644 --- a/lib/api/deployments.rb +++ b/lib/api/deployments.rb @@ -47,7 +47,7 @@ module API desc 'Gets a specific deployment' do detail 'This feature was introduced in GitLab 8.11.' - success Entities::Deployment + success Entities::DeploymentExtended end params do requires :deployment_id, type: Integer, desc: 'The deployment ID' @@ -57,12 +57,12 @@ module API deployment = user_project.deployments.find(params[:deployment_id]) - present deployment, with: Entities::Deployment + present deployment, with: Entities::DeploymentExtended end desc 'Creates a new deployment' do detail 'This feature was introduced in GitLab 12.4' - success Entities::Deployment + success Entities::DeploymentExtended end params do requires :environment, @@ -106,7 +106,7 @@ module API deployment = service.execute if deployment.persisted? - present(deployment, with: Entities::Deployment, current_user: current_user) + present(deployment, with: Entities::DeploymentExtended, current_user: current_user) else render_validation_error!(deployment) end @@ -114,7 +114,7 @@ module API desc 'Updates an existing deployment' do detail 'This feature was introduced in GitLab 12.4' - success Entities::Deployment + success Entities::DeploymentExtended end params do requires :status, @@ -136,7 +136,7 @@ module API service = ::Deployments::UpdateService.new(deployment, declared_params) if service.execute - present(deployment, with: Entities::Deployment, current_user: current_user) + present(deployment, with: Entities::DeploymentExtended, current_user: current_user) else render_validation_error!(deployment) end diff --git a/lib/api/entities/deployment_extended.rb b/lib/api/entities/deployment_extended.rb new file mode 100644 index 00000000000..74cfb61388b --- /dev/null +++ b/lib/api/entities/deployment_extended.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +module API + module Entities + class DeploymentExtended < Deployment + end + end +end + +API::Entities::DeploymentExtended.prepend_mod diff --git a/lib/gitlab/json.rb b/lib/gitlab/json.rb index f1370a40222..c26ccb5c80d 100644 --- a/lib/gitlab/json.rb +++ b/lib/gitlab/json.rb @@ -248,7 +248,7 @@ module Gitlab # @return [String] # @raise [LimitExceeded] if the resulting json string is bigger than the specified limit def self.encode(object, limit: 25.megabytes) - return ::Gitlab::Json.dump(object) unless Feature.enabled?(:json_limited_encoder) + return ::Gitlab::Json.dump(object) unless Feature.enabled?(:json_limited_encoder, default_enabled: :yaml) buffer = StringIO.new buffer_size = 0 diff --git a/spec/fixtures/api/schemas/public_api/v4/deployment.json b/spec/fixtures/api/schemas/public_api/v4/deployment.json index 2371509edd6..5dab1456e7d 100644 --- a/spec/fixtures/api/schemas/public_api/v4/deployment.json +++ b/spec/fixtures/api/schemas/public_api/v4/deployment.json @@ -8,7 +8,8 @@ "created_at", "updated_at", "user", - "deployable" + "deployable", + "status" ], "properties": { "id": { "type": "integer" }, @@ -30,6 +31,5 @@ ] }, "status": { "type": "string" } - }, - "additionalProperties": false + } } diff --git a/spec/frontend/boards/mock_data.js b/spec/frontend/boards/mock_data.js index a081a60166b..25496582707 100644 --- a/spec/frontend/boards/mock_data.js +++ b/spec/frontend/boards/mock_data.js @@ -612,6 +612,7 @@ export const mockTokens = (fetchLabels, fetchAuthors, fetchMilestones, isSignedI title: __('Milestone'), symbol: '%', type: 'milestone', + shouldSkipSort: true, token: MilestoneToken, unique: true, fetchMilestones, diff --git a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/milestone_token_spec.js b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/milestone_token_spec.js index 332f865b874..de9ec863dd5 100644 --- a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/milestone_token_spec.js +++ b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/milestone_token_spec.js @@ -32,7 +32,7 @@ const defaultStubs = { function createComponent(options = {}) { const { - config = mockMilestoneToken, + config = { ...mockMilestoneToken, shouldSkipSort: true }, value = { data: '' }, active = false, stubs = defaultStubs, @@ -68,6 +68,27 @@ describe('MilestoneToken', () => { describe('methods', () => { describe('fetchMilestones', () => { + describe('when config.shouldSkipSort is true', () => { + beforeEach(() => { + wrapper.vm.config.shouldSkipSort = true; + }); + + afterEach(() => { + wrapper.vm.config.shouldSkipSort = false; + }); + it('does not call sortMilestonesByDueDate', async () => { + jest.spyOn(wrapper.vm.config, 'fetchMilestones').mockResolvedValue({ + data: mockMilestones, + }); + + wrapper.vm.fetchMilestones(); + + await waitForPromises(); + + expect(sortMilestonesByDueDate).toHaveBeenCalledTimes(0); + }); + }); + it('calls `config.fetchMilestones` with provided searchTerm param', () => { jest.spyOn(wrapper.vm.config, 'fetchMilestones'); @@ -77,10 +98,11 @@ describe('MilestoneToken', () => { }); it('sets response to `milestones` when request is successful', () => { + wrapper.vm.config.shouldSkipSort = false; + jest.spyOn(wrapper.vm.config, 'fetchMilestones').mockResolvedValue({ data: mockMilestones, }); - wrapper.vm.fetchMilestones(); return waitForPromises().then(() => { diff --git a/spec/lib/api/entities/deployment_extended_spec.rb b/spec/lib/api/entities/deployment_extended_spec.rb new file mode 100644 index 00000000000..733c47362be --- /dev/null +++ b/spec/lib/api/entities/deployment_extended_spec.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe API::Entities::DeploymentExtended do + describe '#as_json' do + subject { described_class.new(deployment).as_json } + + let(:deployment) { create(:deployment) } + + it 'includes fields from deployment entity' do + is_expected.to include(:id, :iid, :ref, :sha, :created_at, :updated_at, :user, :environment, :deployable, :status) + end + end +end diff --git a/spec/presenters/blobs/unfold_presenter_spec.rb b/spec/presenters/blobs/unfold_presenter_spec.rb index 4e9f83e8001..14c36461e90 100644 --- a/spec/presenters/blobs/unfold_presenter_spec.rb +++ b/spec/presenters/blobs/unfold_presenter_spec.rb @@ -206,6 +206,14 @@ RSpec.describe Blobs::UnfoldPresenter do end end + context 'when since exceeds number of lines' do + let(:params) { { since: 2 } } + + it 'returns an empty list' do + expect(subject.lines.size).to eq(0) + end + end + context 'when full is true' do let(:params) { { full: true } }