Commit 6e58bddd authored by GitLab Bot's avatar GitLab Bot

Automatic merge of gitlab-org/gitlab master

parents 7a4cb945 c399d7ab
......@@ -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
......@@ -5,4 +5,4 @@ rollout_issue_url:
milestone: '13.3'
type: development
group: group::source code
default_enabled: false
default_enabled: true
......@@ -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: <your_access_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.
......
......@@ -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
......
......@@ -211,15 +211,25 @@ averaged.
To define a coverage-parsing regular expression:
- In the GitLab UI:
- Using the project's `.gitlab-ci.yml`, provide a regular expression using the [`coverage`](../yaml/index.md#coverage)
keyword. Setting the regular expression this way takes precedence over the project's CI/CD settings.
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 CI/CD settings:
- Set using the GitLab UI:
- Using the project's `.gitlab-ci.yml`, provide a regular expression using the [`coverage`](../yaml/index.md#coverage)
keyword.
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: <your-token>" \
--url 'https://gitlab.com/api/v4/projects/<your-project-ID>' \
--data "build_coverage_regex=<your-regular-expression>"
```
You can use <https://rubular.com> to test your regular expression. The regular expression returns the **last**
match found in the output.
......
......@@ -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
......
......@@ -68,7 +68,9 @@ export default {
:sub-label="avatarSubLabel"
/>
</gl-avatar-link>
<span v-else class="gl-text-gray-500" data-testid="no-assignee-text">{{ __('None') }}</span>
<div v-else class="gl-text-gray-500 gl-line-height-14" data-testid="no-assignee-text">
{{ __('None') }}
</div>
</div>
<div
......
......@@ -66,10 +66,12 @@ export default {
</div>
<div class="hide-collapsed">
<div class="title">{{ $options.i18n.dueDateTitle }}</div>
<div class="value" data-testid="due-date-value">
<strong v-if="dueDate">{{ formattedDueDate }}</strong>
<span v-else class="no-value">{{ $options.i18n.none }}</span>
<div class="gl-line-height-20 gl-mb-2 gl-text-gray-900">
{{ $options.i18n.dueDateTitle }}
</div>
<div class="gl-line-height-14" data-testid="due-date-value">
<span v-if="dueDate" class="gl-font-weight-bold">{{ formattedDueDate }}</span>
<span v-else class="gl-text-gray-500">{{ $options.i18n.none }}</span>
</div>
</div>
</div>
......
......@@ -32,7 +32,9 @@ module EE
end
def pending_approval_count
environment.required_approval_count - approvals.approved.count
return 0 unless blocked?
environment.required_approval_count - approvals.count
end
end
end
# frozen_string_literal: true
module EE
module API
module Entities
module DeploymentExtended
extend ActiveSupport::Concern
prepended do
expose :pending_approval_count
expose :approvals, using: ::API::Entities::Deployments::Approval
end
end
end
end
end
{
"type": "object",
"allOf": [
{
"$ref": "../../../../../../../spec/fixtures/api/schemas/public_api/v4/deployment.json"
},
{
"required": [
"pending_approval_count",
"approvals"
],
"properties": {
"pending_approval_count": {
"type": "integer"
},
"approvals": {
"type": "array",
"items": {
"$ref": "deployment_approval.json"
}
},
"additionalProperties": false
}
}
]
}
......@@ -52,12 +52,14 @@ exports[`ExternalIssuesSidebarAssignee with no assignee template renders templat
numberofassignees="0"
/>
<span
class="gl-text-gray-500"
<div
class="gl-text-gray-500 gl-line-height-14"
data-testid="no-assignee-text"
>
None
</span>
</div>
</div>
<div
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe ::EE::API::Entities::DeploymentExtended do
subject { ::API::Entities::DeploymentExtended.new(deployment).as_json }
describe '#as_json' do
let(:deployment) { create(:deployment, :blocked) }
before do
stub_licensed_features(protected_environments: true)
create(:protected_environment, project_id: deployment.environment.project_id, name: deployment.environment.name, required_approval_count: 2)
create(:deployment_approval, :approved, deployment: deployment)
end
it 'includes fields from deployment entity' do
is_expected.to include(:id, :iid, :ref, :sha, :created_at, :updated_at, :user, :environment, :deployable, :status)
end
it 'includes pending_approval_count' do
expect(subject[:pending_approval_count]).to eq(1)
end
it 'includes approvals', :aggregate_failures do
expect(subject[:approvals].length).to eq(1)
expect(subject.dig(:approvals, 0, :status)).to eq("approved")
end
end
end
......@@ -28,7 +28,7 @@ RSpec.describe Deployment do
let_it_be(:project) { create(:project, :repository) }
let(:environment) { create(:environment, project: project) }
let(:deployment) { create(:deployment, project: project, environment: environment) }
let(:deployment) { create(:deployment, :blocked, project: project, environment: environment) }
context 'when Protected Environments feature is available' do
before do
......@@ -61,6 +61,14 @@ RSpec.describe Deployment do
expect(deployment.pending_approval_count).to eq(0)
end
end
context 'with a deployment that is not blocked' do
let(:deployment) { create(:deployment, :success, project: project, environment: environment) }
it 'returns zero' do
expect(deployment.pending_approval_count).to eq(0)
end
end
end
context 'when Protected Environments feature is not available' do
......
......@@ -11,7 +11,41 @@ RSpec.describe API::Deployments do
stub_licensed_features(protected_environments: true)
end
describe 'GET /projects/:id/deployments/:id' do
let(:deployment) { create(:deployment, :blocked, project: project) }
before do
create(:deployment_approval, :approved, deployment: deployment)
project.add_developer(user)
end
it 'matches the response schema' do
get api("/projects/#{project.id}/deployments/#{deployment.id}", user)
expect(response).to have_gitlab_http_status(:success)
expect(response).to match_response_schema('public_api/v4/deployment_extended', dir: 'ee')
end
end
describe 'POST /projects/:id/deployments' do
it 'matches the response schema' do
project.add_developer(user)
post(
api("/projects/#{project.id}/deployments", user),
params: {
environment: environment.name,
sha: 'b83d6e391c22777fca1ed3012fce84f633d7fed0',
ref: 'master',
tag: false,
status: 'success'
}
)
expect(response).to have_gitlab_http_status(:success)
expect(response).to match_response_schema('public_api/v4/deployment_extended', dir: 'ee')
end
context 'when deploying to a protected environment that requires maintainer access' do
before do
create(
......@@ -114,6 +148,18 @@ RSpec.describe API::Deployments do
)
end
it 'matches the response schema' do
project.add_developer(user)
put(
api("/projects/#{project.id}/deployments/#{deploy.id}", user),
params: { status: 'success' }
)
expect(response).to have_gitlab_http_status(:success)
expect(response).to match_response_schema('public_api/v4/deployment_extended', dir: 'ee')
end
context 'when updating a deployment for a protected environment that requires maintainer access' do
before do
create(
......
......@@ -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
......
# frozen_string_literal: true
module API
module Entities
class DeploymentExtended < Deployment
end
end
end
API::Entities::DeploymentExtended.prepend_mod
......@@ -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
......
......@@ -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
}
}
# 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
......@@ -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 } }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment